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-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.
25 #include "controller.h"
27 #include "protostructs.h"
34 #define __DEBUG_MODE__
40 //////////////////////////////////////////////////////////////////////////////
43 ZeroPacket::ZeroPacket(Data
&send
, Data
&receive
)
44 : Packet(send
, receive
)
48 ZeroPacket::~ZeroPacket()
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.
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
;
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 //////////////////////////////////////////////////////////////////////////////
96 DBPacket::DBPacket(Controller
&con
, Data
&send
, Data
&receive
)
97 : Packet(send
, receive
)
103 DBPacket::~DBPacket()
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
;
133 /// Builds a command packet for the GET_DBDB command code, placing the
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
;
179 /// Builds a command packet in the send buffer for the SET_RECORD_FLAGS
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
,
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
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
;
236 /// Builds a command packet in the send buffer for the GET_RECORD_BY_INDEX
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
;
260 /// Builds a command packet in the m_send buffer for the SET_RECORD_BY_INDEX
264 /// - true means success
265 /// - false means no data available from Builder object
267 bool DBPacket::SetRecordByIndex(unsigned int dbId
, unsigned int stateTableIndex
,
270 // get new data if available
271 if( !build
.Retrieve(dbId
) )
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
;
301 /// Builds a command packet in the send buffer for the GET_RECORDS
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
;
324 /// Builds a command packet in the m_send buffer for the SET_RECORD command
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
) )
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
;
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
;
386 throw Error("Attempting to extract a return code from the wrong response packet type");
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
;
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
411 /// \returns bool true - packet was recognized and parse was attempted
412 /// false - packet was not recognized
414 bool DBPacket::Parse(Parser
&parser
)
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
:
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
);
437 default: // unknown command