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"
34 #define __DEBUG_MODE__
40 //////////////////////////////////////////////////////////////////////////////
46 /// Returns the command value of the receive packet. If receive isn't
47 /// large enough, throws Error.
49 unsigned int Packet::Command() const
51 Protocol::CheckSize(m_receive
);
52 MAKE_PACKET(rpack
, m_receive
);
53 return rpack
->command
;
57 //////////////////////////////////////////////////////////////////////////////
60 ZeroPacket::ZeroPacket(Data
&send
, Data
&receive
)
61 : Packet(send
, receive
)
65 ZeroPacket::~ZeroPacket()
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.
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
;
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 //////////////////////////////////////////////////////////////////////////////
144 DBPacket::DBPacket(Mode::Desktop
&con
, Data
&send
, Data
&receive
)
145 : Packet(send
, receive
)
151 DBPacket::~DBPacket()
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
;
182 /// Builds a command packet for the GET_DBDB command code, placing the
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
;
230 /// Builds a command packet in the send buffer for the SET_RECORD_FLAGS
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
,
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
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
;
289 /// Builds a command packet in the send buffer for the GET_RECORD_BY_INDEX
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
;
314 /// Builds a command packet in the m_send buffer for the SET_RECORD_BY_INDEX
318 /// - true means success
319 /// - false means no data available from Builder object
321 bool DBPacket::SetRecordByIndex(unsigned int dbId
, unsigned int stateTableIndex
,
324 // get new data if available
325 if( !build
.Retrieve(dbId
) )
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
;
356 /// Builds a command packet in the send buffer for the GET_RECORDS
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
;
380 /// Builds a command packet in the m_send buffer for the SET_RECORD command
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
) )
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
;
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
;
430 throw Error("Attempting to extract a return code from the wrong response packet type");
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
;
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
455 /// \returns bool true - packet was recognized and parse was attempted
456 /// false - packet was not recognized
458 bool DBPacket::Parse(Parser
&parser
)
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
:
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
);
481 default: // unknown command