3 /// Low level protocol packet builder class.
4 /// Has knowledge of specific protocol commands in order
5 /// to hide protocol details behind an API.
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.
25 #include "m_desktop.h"
27 #include "protostructs.h"
35 #define __DEBUG_MODE__
41 //////////////////////////////////////////////////////////////////////////////
47 /// Returns the command value of the receive packet. If receive isn't
48 /// large enough, throws Error.
50 unsigned int Packet::Command() const
52 Protocol::CheckSize(m_receive
);
53 MAKE_PACKET(rpack
, m_receive
);
54 return rpack
->command
;
58 //////////////////////////////////////////////////////////////////////////////
61 ZeroPacket::ZeroPacket(Data
&send
, Data
&receive
)
62 : Packet(send
, receive
)
66 ZeroPacket::~ZeroPacket()
73 /// Builds a command packet for the initial socket-0 handshakes
74 /// that fetch certain (some unknown) attributes. The attributes
75 /// appear to exist in an object/attribute sequence, so that's
76 /// how we address them here.
78 void ZeroPacket::GetAttribute(unsigned int object
, unsigned int attribute
)
80 size_t size
= SB_SOCKET_PACKET_HEADER_SIZE
+ ATTRIBUTE_FETCH_COMMAND_SIZE
;
81 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(size
));
82 Protocol::Packet
&packet
= *cpack
;
85 packet
.size
= htobs(size
);
86 packet
.command
= SB_COMMAND_FETCH_ATTRIBUTE
;
87 packet
.u
.socket
.socket
= htobs(0x00ff); // default non-socket request
88 packet
.u
.socket
.sequence
= 0; // filled in by Socket class
89 packet
.u
.socket
.u
.fetch
.object
= htobs(object
);
90 packet
.u
.socket
.u
.fetch
.attribute
= htobs(attribute
);
92 m_send
.ReleaseBuffer(size
);
95 unsigned int ZeroPacket::ObjectID() const
97 Protocol::CheckSize(m_receive
, SB_SOCKET_PACKET_HEADER_SIZE
);
98 MAKE_PACKET(rpack
, m_receive
);
99 return btohs(rpack
->u
.socket
.u
.fetch
.object
);
102 unsigned int ZeroPacket::AttributeID() const
104 Protocol::CheckSize(m_receive
, SB_SOCKET_PACKET_HEADER_SIZE
);
105 MAKE_PACKET(rpack
, m_receive
);
106 return btohs(rpack
->u
.socket
.u
.fetch
.attribute
);
109 uint32_t ZeroPacket::ChallengeSeed() const
111 Protocol::CheckSize(m_receive
, SB_SOCKET_PACKET_HEADER_SIZE
+
112 PASSWORD_CHALLENGE_SEED_SIZE
);
113 MAKE_PACKET(rpack
, m_receive
);
114 return btohl(rpack
->u
.socket
.u
.password
.u
.seed
);
117 unsigned int ZeroPacket::RemainingTries() const
119 Protocol::CheckSize(m_receive
, SB_SOCKET_PACKET_HEADER_SIZE
+
120 PASSWORD_CHALLENGE_HEADER_SIZE
);
121 MAKE_PACKET(rpack
, m_receive
);
122 // this is a byte, so no byte swapping needed
123 return rpack
->u
.socket
.u
.password
.remaining_tries
;
126 unsigned int ZeroPacket::SocketResponse() const
128 Protocol::CheckSize(m_receive
, SB_SOCKET_PACKET_HEADER_SIZE
);
129 MAKE_PACKET(rpack
, m_receive
);
130 return btohs(rpack
->u
.socket
.socket
);
133 unsigned char ZeroPacket::SocketSequence() const
135 Protocol::CheckSize(m_receive
, SB_SOCKET_PACKET_HEADER_SIZE
);
136 MAKE_PACKET(rpack
, m_receive
);
137 return rpack
->u
.socket
.sequence
; // sequence is a byte
142 //////////////////////////////////////////////////////////////////////////////
145 DBPacket::DBPacket(Mode::Desktop
&con
, Data
&send
, Data
&receive
)
146 : Packet(send
, receive
)
152 DBPacket::~DBPacket()
159 /// Builds a command packet for the CLEAR_DATABASE command code, placing
160 /// the data in the send buffer.
162 void DBPacket::ClearDatabase(unsigned int dbId
)
164 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(9));
165 Protocol::Packet
&packet
= *cpack
;
167 // socket class should override this for us
168 // packet.socket = htobs(m_con.m_socket->GetSocket());
169 packet
.size
= htobs(9);
170 packet
.command
= SB_COMMAND_DB_DATA
;
171 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
172 packet
.u
.db
.u
.command
.operation
= SB_DBOP_CLEAR_DATABASE
;
173 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
175 m_send
.ReleaseBuffer(9);
177 m_last_dbop
= SB_DBOP_CLEAR_DATABASE
;
183 /// Builds a command packet for the GET_DBDB command code, placing the
186 void DBPacket::GetDBDB()
188 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(7));
189 Protocol::Packet
&packet
= *cpack
;
191 // socket class should override this for us
192 // packet.socket = htobs(m_con.m_socket->GetSocket());
193 packet
.size
= htobs(7);
194 packet
.command
= SB_COMMAND_DB_DATA
;
195 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
196 // packet.u.db.u.command.operation = SB_DBOP_GET_DBDB;
197 packet
.u
.db
.u
.command
.operation
= SB_DBOP_OLD_GET_DBDB
;
199 m_send
.ReleaseBuffer(7);
201 m_last_dbop
= SB_DBOP_OLD_GET_DBDB
;
205 // GetRecordStateTable
207 /// Builds a command packet in the send buffer for the
208 /// GET_RECORD_STATE_TABLE command.
210 void DBPacket::GetRecordStateTable(unsigned int dbId
)
212 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(9));
213 Protocol::Packet
&packet
= *cpack
;
215 // socket class should override this for us
216 // packet.socket = htobs(m_con.m_socket->GetSocket());
217 packet
.size
= htobs(9);
218 packet
.command
= SB_COMMAND_DB_DATA
;
219 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
220 packet
.u
.db
.u
.command
.operation
= SB_DBOP_GET_RECORD_STATE_TABLE
;
221 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
223 m_send
.ReleaseBuffer(9);
225 m_last_dbop
= SB_DBOP_GET_RECORD_STATE_TABLE
;
231 /// Builds a command packet in the send buffer for the SET_RECORD_FLAGS
234 /// FIXME - this API call is incomplete, since there are unknown flags
235 /// in the SetRecordFlags protocol packet. Currently it is only
236 /// used to set all flags to zero.
238 void DBPacket::SetRecordFlags(unsigned int dbId
, unsigned int stateTableIndex
,
241 size_t size
= SB_PACKET_COMMAND_HEADER_SIZE
+ DBC_RECORD_FLAGS_SIZE
;
242 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(size
));
243 Protocol::Packet
&packet
= *cpack
;
245 // socket class should override this for us
246 // packet.socket = htobs(m_con.m_socket->GetSocket());
247 packet
.size
= htobs(size
);
248 packet
.command
= SB_COMMAND_DB_DATA
;
249 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
250 packet
.u
.db
.u
.command
.operation
= SB_DBOP_SET_RECORD_FLAGS
;
251 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
252 packet
.u
.db
.u
.command
.u
.flags
.unknown
= flag1
;
253 packet
.u
.db
.u
.command
.u
.flags
.index
= htobs(stateTableIndex
);
254 memset(packet
.u
.db
.u
.command
.u
.flags
.unknown2
, 0, sizeof(packet
.u
.db
.u
.command
.u
.flags
.unknown2
));
256 m_send
.ReleaseBuffer(size
);
258 m_last_dbop
= SB_DBOP_SET_RECORD_FLAGS
;
262 // DeleteRecordByIndex
264 /// Builds a command packet in the send buffer for the DELETE_RECORD_BY_INDEX
267 void DBPacket::DeleteRecordByIndex(unsigned int dbId
, unsigned int stateTableIndex
)
269 size_t size
= SB_PACKET_COMMAND_HEADER_SIZE
+ DBC_RECORD_HEADER_SIZE
;
270 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(size
));
271 Protocol::Packet
&packet
= *cpack
;
273 // socket class should override this for us
274 // packet.socket = htobs(m_con.m_socket->GetSocket());
275 packet
.size
= htobs(size
);
276 packet
.command
= SB_COMMAND_DB_DATA
;
277 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
278 packet
.u
.db
.u
.command
.operation
= SB_DBOP_DELETE_RECORD_BY_INDEX
;
279 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
280 packet
.u
.db
.u
.command
.u
.record
.recordIndex
= htobs(stateTableIndex
);
282 m_send
.ReleaseBuffer(size
);
284 m_last_dbop
= SB_DBOP_DELETE_RECORD_BY_INDEX
;
290 /// Builds a command packet in the send buffer for the GET_RECORD_BY_INDEX
293 void DBPacket::GetRecordByIndex(unsigned int dbId
, unsigned int stateTableIndex
)
295 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(11));
296 Protocol::Packet
&packet
= *cpack
;
298 // socket class should override this for us
299 // packet.socket = htobs(m_con.m_socket->GetSocket());
300 packet
.size
= htobs(11);
301 packet
.command
= SB_COMMAND_DB_DATA
;
302 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
303 packet
.u
.db
.u
.command
.operation
= SB_DBOP_GET_RECORD_BY_INDEX
;
304 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
305 packet
.u
.db
.u
.command
.u
.record
.recordIndex
= htobs(stateTableIndex
);
307 m_send
.ReleaseBuffer(11);
309 m_last_dbop
= SB_DBOP_GET_RECORD_BY_INDEX
;
315 /// Builds a command packet in the m_send buffer for the SET_RECORD_BY_INDEX
319 /// - true means success
320 /// - false means no data available from Builder object
322 bool DBPacket::SetRecordByIndex(unsigned int dbId
, unsigned int stateTableIndex
,
325 // get new data if available
326 if( !build
.Retrieve(dbId
) )
330 size_t header_size
= SB_PACKET_COMMAND_HEADER_SIZE
+ DBC_INDEXED_UPLOAD_HEADER_SIZE
;
331 build
.BuildFields(m_send
, header_size
);
332 size_t total_size
= m_send
.GetSize();
334 // fill in the header values
335 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(total_size
));
336 Protocol::Packet
&packet
= *cpack
;
338 // socket class should override this for us
339 // packet.socket = htobs(m_con.m_socket->GetSocket());
340 packet
.size
= htobs(total_size
);
341 packet
.command
= SB_COMMAND_DB_DATA
;
342 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
343 packet
.u
.db
.u
.command
.operation
= SB_DBOP_SET_RECORD_BY_INDEX
;
344 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
345 packet
.u
.db
.u
.command
.u
.index_upload
.unknown
= 0;
346 packet
.u
.db
.u
.command
.u
.index_upload
.index
= htobs(stateTableIndex
);
348 m_send
.ReleaseBuffer(total_size
);
350 m_last_dbop
= SB_DBOP_SET_RECORD_BY_INDEX
;
357 /// Builds a command packet in the send buffer for the GET_RECORDS
360 void DBPacket::GetRecords(unsigned int dbId
)
362 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(9));
363 Protocol::Packet
&packet
= *cpack
;
365 // socket class should override this for us
366 // packet.socket = htobs(m_con.m_socket->GetSocket());
367 packet
.size
= htobs(9);
368 packet
.command
= SB_COMMAND_DB_DATA
;
369 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
370 packet
.u
.db
.u
.command
.operation
= SB_DBOP_OLD_GET_RECORDS
;
371 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
373 m_send
.ReleaseBuffer(9);
375 m_last_dbop
= SB_DBOP_OLD_GET_RECORDS
;
381 /// Builds a command packet in the m_send buffer for the SET_RECORD command
385 /// - true means success
386 /// - false means no data available from Builder object
388 bool DBPacket::SetRecord(unsigned int dbId
, Builder
&build
)
390 // get new data if available
391 if( !build
.Retrieve(dbId
) )
395 size_t header_size
= SB_PACKET_COMMAND_HEADER_SIZE
+ DBC_TAGGED_UPLOAD_HEADER_SIZE
;
396 build
.BuildHeader(m_send
, header_size
);
397 build
.BuildFields(m_send
, header_size
);
398 size_t total_size
= m_send
.GetSize();
400 // fill in the header values
401 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(total_size
));
402 Protocol::Packet
&packet
= *cpack
;
404 // socket class should override this for us
405 // packet.socket = htobs(m_con.m_socket->GetSocket());
406 packet
.size
= htobs(total_size
);
407 packet
.command
= SB_COMMAND_DB_DATA
;
408 packet
.u
.db
.tableCmd
= m_con
.GetDBCommand(Mode::Desktop::DatabaseAccess
);
409 packet
.u
.db
.u
.command
.operation
= SB_DBOP_SET_RECORD
;
410 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
411 packet
.u
.db
.u
.command
.u
.tag_upload
.rectype
= build
.GetRecType();
412 packet
.u
.db
.u
.command
.u
.tag_upload
.uniqueId
= htobl(build
.GetUniqueId());
413 packet
.u
.db
.u
.command
.u
.tag_upload
.unknown2
= 1; // unknown observed value
415 m_send
.ReleaseBuffer(total_size
);
417 m_last_dbop
= SB_DBOP_SET_RECORD
;
422 // throws FIXME if packet doesn't support it
423 unsigned int DBPacket::ReturnCode() const
425 if( Command() == SB_COMMAND_DB_DONE
) {
426 Protocol::CheckSize(m_receive
, SB_PACKET_DBACCESS_HEADER_SIZE
+ SB_DBACCESS_RETURN_CODE_SIZE
);
427 MAKE_PACKET(rpack
, m_receive
);
428 return rpack
->u
.db
.u
.return_code
;
431 throw Error("Attempting to extract a return code from the wrong response packet type");
438 /// Returns the database operation code from the receive packet, assuming
439 /// that receive contains a response packet. If receive isn't large
440 /// enough, throws Error.
442 unsigned int DBPacket::DBOperation() const
444 Protocol::CheckSize(m_receive
, SB_PACKET_RESPONSE_HEADER_SIZE
);
445 MAKE_PACKET(rpack
, m_receive
);
446 return rpack
->u
.db
.u
.response
.operation
;
452 /// Parses the data in the receive buffer, and attempts to be smart about it,
453 /// using the last send command as guidance for what to expect in the
456 /// \returns bool true - packet was recognized and parse was attempted
457 /// false - packet was not recognized
459 bool DBPacket::Parse(Parser
&parser
)
462 MAKE_PACKET(rpack
, m_receive
);
464 switch( m_last_dbop
)
466 case SB_DBOP_OLD_GET_RECORDS
:
467 case SB_DBOP_GET_RECORD_BY_INDEX
:
470 offset
= SB_PACKET_RESPONSE_HEADER_SIZE
+ DBR_OLD_TAGGED_RECORD_HEADER_SIZE
;
471 Protocol::CheckSize(m_receive
, offset
);
472 // FIXME - this may need adjustment for email records... they
473 // don't seem to have uniqueID's
474 parser
.SetIds(rpack
->u
.db
.u
.response
.u
.tagged
.rectype
,
475 btohl(rpack
->u
.db
.u
.response
.u
.tagged
.uniqueId
));
477 parser
.ParseHeader(m_receive
, offset
);
478 parser
.ParseFields(m_receive
, offset
);
482 default: // unknown command