Typo fix
[barry/pauldeden.git] / src / packet.cc
blob49b51ee5727165093c4811f692a976af31e25870
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-2008, 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 "m_desktop.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 // Packet base class
44 // Command
46 /// Returns the command value of the receive packet. If receive isn't
47 /// large enough, throws Error.
48 ///
49 unsigned int Packet::Command() const
51 Protocol::CheckSize(m_receive);
52 MAKE_PACKET(rpack, m_receive);
53 return rpack->command;
57 //////////////////////////////////////////////////////////////////////////////
58 // ZeroPacket class
60 ZeroPacket::ZeroPacket(Data &send, Data &receive)
61 : Packet(send, receive)
65 ZeroPacket::~ZeroPacket()
70 // GetAttribute
72 /// Builds a command packet for the initial socket-0 handshakes
73 /// that fetch certain (some unknown) attributes. The attributes
74 /// appear to exist in an object/attribute sequence, so that's
75 /// how we address them here.
76 ///
77 void ZeroPacket::GetAttribute(unsigned int object, unsigned int attribute)
79 size_t size = SB_SOCKET_PACKET_HEADER_SIZE + ATTRIBUTE_FETCH_COMMAND_SIZE;
80 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(size));
81 Protocol::Packet &packet = *cpack;
83 packet.socket = 0;
84 packet.size = htobs(size);
85 packet.command = SB_COMMAND_FETCH_ATTRIBUTE;
86 packet.u.socket.socket = htobs(0x00ff); // default non-socket request
87 packet.u.socket.sequence = 0; // filled in by Socket class
88 packet.u.socket.u.fetch.object = htobs(object);
89 packet.u.socket.u.fetch.attribute = htobs(attribute);
91 m_send.ReleaseBuffer(size);
94 unsigned int ZeroPacket::ObjectID() const
96 Protocol::CheckSize(m_receive, SB_SOCKET_PACKET_HEADER_SIZE);
97 MAKE_PACKET(rpack, m_receive);
98 return btohs(rpack->u.socket.u.fetch.object);
101 unsigned int ZeroPacket::AttributeID() const
103 Protocol::CheckSize(m_receive, SB_SOCKET_PACKET_HEADER_SIZE);
104 MAKE_PACKET(rpack, m_receive);
105 return btohs(rpack->u.socket.u.fetch.attribute);
108 uint32_t ZeroPacket::ChallengeSeed() const
110 Protocol::CheckSize(m_receive, SB_SOCKET_PACKET_HEADER_SIZE +
111 PASSWORD_CHALLENGE_SEED_SIZE);
112 MAKE_PACKET(rpack, m_receive);
113 return btohl(rpack->u.socket.u.password.u.seed);
116 unsigned int ZeroPacket::RemainingTries() const
118 Protocol::CheckSize(m_receive, SB_SOCKET_PACKET_HEADER_SIZE +
119 PASSWORD_CHALLENGE_HEADER_SIZE);
120 MAKE_PACKET(rpack, m_receive);
121 // this is a byte, so no byte swapping needed
122 return rpack->u.socket.u.password.remaining_tries;
125 unsigned int ZeroPacket::SocketResponse() const
127 Protocol::CheckSize(m_receive, SB_SOCKET_PACKET_HEADER_SIZE);
128 MAKE_PACKET(rpack, m_receive);
129 return btohs(rpack->u.socket.socket);
132 unsigned char ZeroPacket::SocketSequence() const
134 Protocol::CheckSize(m_receive, SB_SOCKET_PACKET_HEADER_SIZE);
135 MAKE_PACKET(rpack, m_receive);
136 return rpack->u.socket.sequence; // sequence is a byte
141 //////////////////////////////////////////////////////////////////////////////
142 // DBPacket class
144 DBPacket::DBPacket(Mode::Desktop &con, Data &send, Data &receive)
145 : Packet(send, receive)
146 , m_con(con)
147 , m_last_dbop(0)
151 DBPacket::~DBPacket()
156 // ClearDatabase
158 /// Builds a command packet for the CLEAR_DATABASE command code, placing
159 /// the data in the send buffer.
161 void DBPacket::ClearDatabase(unsigned int dbId)
163 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(9));
164 Protocol::Packet &packet = *cpack;
166 // socket class should override this for us
167 // packet.socket = htobs(m_con.m_socket->GetSocket());
168 packet.size = htobs(9);
169 packet.command = SB_COMMAND_DB_DATA;
170 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
171 packet.u.db.u.command.operation = SB_DBOP_CLEAR_DATABASE;
172 packet.u.db.u.command.databaseId = htobs(dbId);
174 m_send.ReleaseBuffer(9);
176 m_last_dbop = SB_DBOP_CLEAR_DATABASE;
180 // GetDBDB
182 /// Builds a command packet for the GET_DBDB command code, placing the
183 /// data in m_send.
185 void DBPacket::GetDBDB()
187 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(7));
188 Protocol::Packet &packet = *cpack;
190 // socket class should override this for us
191 // packet.socket = htobs(m_con.m_socket->GetSocket());
192 packet.size = htobs(7);
193 packet.command = SB_COMMAND_DB_DATA;
194 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
195 // packet.u.db.u.command.operation = SB_DBOP_GET_DBDB;
196 packet.u.db.u.command.operation = SB_DBOP_OLD_GET_DBDB;
198 m_send.ReleaseBuffer(7);
200 m_last_dbop = SB_DBOP_OLD_GET_DBDB;
204 // GetRecordStateTable
206 /// Builds a command packet in the send buffer for the
207 /// GET_RECORD_STATE_TABLE command.
209 void DBPacket::GetRecordStateTable(unsigned int dbId)
211 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(9));
212 Protocol::Packet &packet = *cpack;
214 // socket class should override this for us
215 // packet.socket = htobs(m_con.m_socket->GetSocket());
216 packet.size = htobs(9);
217 packet.command = SB_COMMAND_DB_DATA;
218 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
219 packet.u.db.u.command.operation = SB_DBOP_GET_RECORD_STATE_TABLE;
220 packet.u.db.u.command.databaseId = htobs(dbId);
222 m_send.ReleaseBuffer(9);
224 m_last_dbop = SB_DBOP_GET_RECORD_STATE_TABLE;
228 // SetRecordFlags
230 /// Builds a command packet in the send buffer for the SET_RECORD_FLAGS
231 /// command code.
233 /// FIXME - this API call is incomplete, since there are unknown flags
234 /// in the SetRecordFlags protocol packet. Currently it is only
235 /// used to set all flags to zero.
237 void DBPacket::SetRecordFlags(unsigned int dbId, unsigned int stateTableIndex,
238 uint8_t flag1)
240 size_t size = SB_PACKET_COMMAND_HEADER_SIZE + DBC_RECORD_FLAGS_SIZE;
241 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(size));
242 Protocol::Packet &packet = *cpack;
244 // socket class should override this for us
245 // packet.socket = htobs(m_con.m_socket->GetSocket());
246 packet.size = htobs(size);
247 packet.command = SB_COMMAND_DB_DATA;
248 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
249 packet.u.db.u.command.operation = SB_DBOP_SET_RECORD_FLAGS;
250 packet.u.db.u.command.databaseId = htobs(dbId);
251 packet.u.db.u.command.u.flags.unknown = flag1;
252 packet.u.db.u.command.u.flags.index = htobs(stateTableIndex);
253 memset(packet.u.db.u.command.u.flags.unknown2, 0, sizeof(packet.u.db.u.command.u.flags.unknown2));
255 m_send.ReleaseBuffer(size);
257 m_last_dbop = SB_DBOP_SET_RECORD_FLAGS;
261 // DeleteRecordByIndex
263 /// Builds a command packet in the send buffer for the DELETE_RECORD_BY_INDEX
264 /// command code.
266 void DBPacket::DeleteRecordByIndex(unsigned int dbId, unsigned int stateTableIndex)
268 size_t size = SB_PACKET_COMMAND_HEADER_SIZE + DBC_RECORD_HEADER_SIZE;
269 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(size));
270 Protocol::Packet &packet = *cpack;
272 // socket class should override this for us
273 // packet.socket = htobs(m_con.m_socket->GetSocket());
274 packet.size = htobs(size);
275 packet.command = SB_COMMAND_DB_DATA;
276 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
277 packet.u.db.u.command.operation = SB_DBOP_DELETE_RECORD_BY_INDEX;
278 packet.u.db.u.command.databaseId = htobs(dbId);
279 packet.u.db.u.command.u.record.recordIndex = htobs(stateTableIndex);
281 m_send.ReleaseBuffer(size);
283 m_last_dbop = SB_DBOP_DELETE_RECORD_BY_INDEX;
287 // GetRecordByIndex
289 /// Builds a command packet in the send buffer for the GET_RECORD_BY_INDEX
290 /// command code.
292 void DBPacket::GetRecordByIndex(unsigned int dbId, unsigned int stateTableIndex)
294 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(11));
295 Protocol::Packet &packet = *cpack;
297 // socket class should override this for us
298 // packet.socket = htobs(m_con.m_socket->GetSocket());
299 packet.size = htobs(11);
300 packet.command = SB_COMMAND_DB_DATA;
301 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
302 packet.u.db.u.command.operation = SB_DBOP_GET_RECORD_BY_INDEX;
303 packet.u.db.u.command.databaseId = htobs(dbId);
304 packet.u.db.u.command.u.record.recordIndex = htobs(stateTableIndex);
306 m_send.ReleaseBuffer(11);
308 m_last_dbop = SB_DBOP_GET_RECORD_BY_INDEX;
312 // SetRecordByIndex
314 /// Builds a command packet in the m_send buffer for the SET_RECORD_BY_INDEX
315 /// command code.
317 /// \return bool
318 /// - true means success
319 /// - false means no data available from Builder object
321 bool DBPacket::SetRecordByIndex(unsigned int dbId, unsigned int stateTableIndex,
322 Builder &build)
324 // get new data if available
325 if( !build.Retrieve(dbId) )
326 return false;
328 // build packet data
329 size_t header_size = SB_PACKET_COMMAND_HEADER_SIZE + DBC_INDEXED_UPLOAD_HEADER_SIZE;
330 build.BuildFields(m_send, header_size);
331 size_t total_size = m_send.GetSize();
333 // fill in the header values
334 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(total_size));
335 Protocol::Packet &packet = *cpack;
337 // socket class should override this for us
338 // packet.socket = htobs(m_con.m_socket->GetSocket());
339 packet.size = htobs(total_size);
340 packet.command = SB_COMMAND_DB_DATA;
341 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
342 packet.u.db.u.command.operation = SB_DBOP_SET_RECORD_BY_INDEX;
343 packet.u.db.u.command.databaseId = htobs(dbId);
344 packet.u.db.u.command.u.index_upload.unknown = 0;
345 packet.u.db.u.command.u.index_upload.index = htobs(stateTableIndex);
347 m_send.ReleaseBuffer(total_size);
349 m_last_dbop = SB_DBOP_SET_RECORD_BY_INDEX;
350 return true;
354 // GetRecords
356 /// Builds a command packet in the send buffer for the GET_RECORDS
357 /// command code.
359 void DBPacket::GetRecords(unsigned int dbId)
361 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(9));
362 Protocol::Packet &packet = *cpack;
364 // socket class should override this for us
365 // packet.socket = htobs(m_con.m_socket->GetSocket());
366 packet.size = htobs(9);
367 packet.command = SB_COMMAND_DB_DATA;
368 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
369 packet.u.db.u.command.operation = SB_DBOP_OLD_GET_RECORDS;
370 packet.u.db.u.command.databaseId = htobs(dbId);
372 m_send.ReleaseBuffer(9);
374 m_last_dbop = SB_DBOP_OLD_GET_RECORDS;
378 // SetRecord
380 /// Builds a command packet in the m_send buffer for the SET_RECORD command
381 /// code.
383 /// \return bool
384 /// - true means success
385 /// - false means no data available from Builder object
387 bool DBPacket::SetRecord(unsigned int dbId, Builder &build)
389 // get new data if available
390 if( !build.Retrieve(dbId) )
391 return false;
393 // build packet data
394 size_t header_size = SB_PACKET_COMMAND_HEADER_SIZE + DBC_TAGGED_UPLOAD_HEADER_SIZE;
395 build.BuildHeader(m_send, header_size);
396 build.BuildFields(m_send, header_size);
397 size_t total_size = m_send.GetSize();
399 // fill in the header values
400 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(total_size));
401 Protocol::Packet &packet = *cpack;
403 // socket class should override this for us
404 // packet.socket = htobs(m_con.m_socket->GetSocket());
405 packet.size = htobs(total_size);
406 packet.command = SB_COMMAND_DB_DATA;
407 packet.u.db.tableCmd = m_con.GetDBCommand(Mode::Desktop::DatabaseAccess);
408 packet.u.db.u.command.operation = SB_DBOP_SET_RECORD;
409 packet.u.db.u.command.databaseId = htobs(dbId);
410 packet.u.db.u.command.u.tag_upload.rectype = build.GetRecType();
411 packet.u.db.u.command.u.tag_upload.uniqueId = htobl(build.GetUniqueId());
412 packet.u.db.u.command.u.tag_upload.unknown2 = 1; // unknown observed value
414 m_send.ReleaseBuffer(total_size);
416 m_last_dbop = SB_DBOP_SET_RECORD;
417 return true;
421 // throws FIXME if packet doesn't support it
422 unsigned int DBPacket::ReturnCode() const
424 if( Command() == SB_COMMAND_DB_DONE ) {
425 Protocol::CheckSize(m_receive, SB_PACKET_DBACCESS_HEADER_SIZE + SB_DBACCESS_RETURN_CODE_SIZE);
426 MAKE_PACKET(rpack, m_receive);
427 return rpack->u.db.u.return_code;
429 else {
430 throw Error("Attempting to extract a return code from the wrong response packet type");
435 // DBOperation
437 /// Returns the database operation code from the receive packet, assuming
438 /// that receive contains a response packet. If receive isn't large
439 /// enough, throws Error.
441 unsigned int DBPacket::DBOperation() const
443 Protocol::CheckSize(m_receive, SB_PACKET_RESPONSE_HEADER_SIZE);
444 MAKE_PACKET(rpack, m_receive);
445 return rpack->u.db.u.response.operation;
449 // Parse
451 /// Parses the data in the receive buffer, and attempts to be smart about it,
452 /// using the last send command as guidance for what to expect in the
453 /// response.
455 /// \returns bool true - packet was recognized and parse was attempted
456 /// false - packet was not recognized
458 bool DBPacket::Parse(Parser &parser)
460 size_t offset = 0;
461 MAKE_PACKET(rpack, m_receive);
463 switch( m_last_dbop )
465 case SB_DBOP_OLD_GET_RECORDS:
466 case SB_DBOP_GET_RECORD_BY_INDEX:
467 parser.Clear();
469 offset = SB_PACKET_RESPONSE_HEADER_SIZE + DBR_OLD_TAGGED_RECORD_HEADER_SIZE;
470 Protocol::CheckSize(m_receive, offset);
471 // FIXME - this may need adjustment for email records... they
472 // don't seem to have uniqueID's
473 parser.SetIds(rpack->u.db.u.response.u.tagged.rectype,
474 btohl(rpack->u.db.u.response.u.tagged.uniqueId));
476 parser.ParseHeader(m_receive, offset);
477 parser.ParseFields(m_receive, offset);
478 parser.Store();
479 return true;
481 default: // unknown command
482 return false;
486 } // namespace Barry