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-2006, 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"
37 #define __DEBUG_MODE__
43 Packet::Packet(Controller
&con
, Data
&send
, Data
&receive
)
58 /// Builds a command packet for the CLEAR_DATABASE command code, placing
59 /// the data in the send buffer.
61 void Packet::ClearDatabase(unsigned int dbId
)
63 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(9));
64 Protocol::Packet
&packet
= *cpack
;
66 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
67 packet
.size
= htobs(9);
68 packet
.command
= SB_COMMAND_DB_DATA
;
69 packet
.u
.db
.tableCmd
= m_con
.GetCommand(Controller::DatabaseAccess
);
70 packet
.u
.db
.u
.command
.operation
= SB_DBOP_CLEAR_DATABASE
;
71 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
73 m_send
.ReleaseBuffer(9);
75 m_last_dbop
= SB_DBOP_CLEAR_DATABASE
;
81 /// Builds a command packet for the GET_DBDB command code, placing the
84 void Packet::GetDBDB()
86 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(7));
87 Protocol::Packet
&packet
= *cpack
;
89 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
90 packet
.size
= htobs(7);
91 packet
.command
= SB_COMMAND_DB_DATA
;
92 packet
.u
.db
.tableCmd
= m_con
.GetCommand(Controller::DatabaseAccess
);
93 // packet.u.db.u.command.operation = SB_DBOP_GET_DBDB;
94 packet
.u
.db
.u
.command
.operation
= SB_DBOP_OLD_GET_DBDB
;
96 m_send
.ReleaseBuffer(7);
98 m_last_dbop
= SB_DBOP_OLD_GET_DBDB
;
102 // GetRecordStateTable
104 /// Builds a command packet in the send buffer for the
105 /// GET_RECORD_STATE_TABLE command.
107 void Packet::GetRecordStateTable(unsigned int dbId
)
109 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(9));
110 Protocol::Packet
&packet
= *cpack
;
112 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
113 packet
.size
= htobs(9);
114 packet
.command
= SB_COMMAND_DB_DATA
;
115 packet
.u
.db
.tableCmd
= m_con
.GetCommand(Controller::DatabaseAccess
);
116 packet
.u
.db
.u
.command
.operation
= SB_DBOP_GET_RECORD_STATE_TABLE
;
117 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
119 m_send
.ReleaseBuffer(9);
121 m_last_dbop
= SB_DBOP_GET_RECORD_STATE_TABLE
;
127 /// Builds a command packet in the send buffer for the SET_RECORD_FLAGS
130 /// FIXME - this API call is incomplete, since there are unknown flags
131 /// in the SetRecordFlags protocol packet. Currently it is only
132 /// used to set all flags to zero.
134 void Packet::SetRecordFlags(unsigned int dbId
, unsigned int stateTableIndex
,
137 size_t size
= SB_PACKET_COMMAND_HEADER_SIZE
+ DBC_RECORD_FLAGS_SIZE
;
138 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(size
));
139 Protocol::Packet
&packet
= *cpack
;
141 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
142 packet
.size
= htobs(size
);
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_SET_RECORD_FLAGS
;
146 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
147 packet
.u
.db
.u
.command
.u
.flags
.unknown
= flag1
;
148 packet
.u
.db
.u
.command
.u
.flags
.index
= htobs(stateTableIndex
);
149 memset(packet
.u
.db
.u
.command
.u
.flags
.unknown2
, 0, sizeof(packet
.u
.db
.u
.command
.u
.flags
.unknown2
));
151 m_send
.ReleaseBuffer(size
);
153 m_last_dbop
= SB_DBOP_SET_RECORD_FLAGS
;
157 // DeleteRecordByIndex
159 /// Builds a command packet in the send buffer for the DELETE_RECORD_BY_INDEX
162 void Packet::DeleteRecordByIndex(unsigned int dbId
, unsigned int stateTableIndex
)
164 size_t size
= SB_PACKET_COMMAND_HEADER_SIZE
+ DBC_RECORD_HEADER_SIZE
;
165 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(size
));
166 Protocol::Packet
&packet
= *cpack
;
168 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
169 packet
.size
= htobs(size
);
170 packet
.command
= SB_COMMAND_DB_DATA
;
171 packet
.u
.db
.tableCmd
= m_con
.GetCommand(Controller::DatabaseAccess
);
172 packet
.u
.db
.u
.command
.operation
= SB_DBOP_DELETE_RECORD_BY_INDEX
;
173 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
174 packet
.u
.db
.u
.command
.u
.record
.recordIndex
= htobs(stateTableIndex
);
176 m_send
.ReleaseBuffer(size
);
178 m_last_dbop
= SB_DBOP_DELETE_RECORD_BY_INDEX
;
184 /// Builds a command packet in the send buffer for the GET_RECORD_BY_INDEX
187 void Packet::GetRecordByIndex(unsigned int dbId
, unsigned int stateTableIndex
)
189 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(11));
190 Protocol::Packet
&packet
= *cpack
;
192 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
193 packet
.size
= htobs(11);
194 packet
.command
= SB_COMMAND_DB_DATA
;
195 packet
.u
.db
.tableCmd
= m_con
.GetCommand(Controller::DatabaseAccess
);
196 packet
.u
.db
.u
.command
.operation
= SB_DBOP_GET_RECORD_BY_INDEX
;
197 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
198 packet
.u
.db
.u
.command
.u
.record
.recordIndex
= htobs(stateTableIndex
);
200 m_send
.ReleaseBuffer(11);
202 m_last_dbop
= SB_DBOP_GET_RECORD_BY_INDEX
;
208 /// Builds a command packet in the m_send buffer for the SET_RECORD_BY_INDEX
212 /// - true means success
213 /// - false means no data available from Builder object
215 bool Packet::SetRecordByIndex(unsigned int dbId
, unsigned int stateTableIndex
,
218 // get new data if available
219 if( !build
.Retrieve(dbId
) )
223 size_t header_size
= SB_PACKET_COMMAND_HEADER_SIZE
+ DBC_INDEXED_UPLOAD_HEADER_SIZE
;
224 build
.BuildFields(m_send
, header_size
);
225 size_t total_size
= m_send
.GetSize();
227 // fill in the header values
228 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(total_size
));
229 Protocol::Packet
&packet
= *cpack
;
231 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
232 packet
.size
= htobs(total_size
);
233 packet
.command
= SB_COMMAND_DB_DATA
;
234 packet
.u
.db
.tableCmd
= m_con
.GetCommand(Controller::DatabaseAccess
);
235 packet
.u
.db
.u
.command
.operation
= SB_DBOP_SET_RECORD_BY_INDEX
;
236 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
237 packet
.u
.db
.u
.command
.u
.index_upload
.unknown
= 0;
238 packet
.u
.db
.u
.command
.u
.index_upload
.index
= htobs(stateTableIndex
);
240 m_send
.ReleaseBuffer(total_size
);
242 m_last_dbop
= SB_DBOP_SET_RECORD_BY_INDEX
;
249 /// Builds a command packet in the send buffer for the GET_RECORDS
252 void Packet::GetRecords(unsigned int dbId
)
254 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(9));
255 Protocol::Packet
&packet
= *cpack
;
257 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
258 packet
.size
= htobs(9);
259 packet
.command
= SB_COMMAND_DB_DATA
;
260 packet
.u
.db
.tableCmd
= m_con
.GetCommand(Controller::DatabaseAccess
);
261 packet
.u
.db
.u
.command
.operation
= SB_DBOP_OLD_GET_RECORDS
;
262 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
264 m_send
.ReleaseBuffer(9);
266 m_last_dbop
= SB_DBOP_OLD_GET_RECORDS
;
272 /// Builds a command packet in the m_send buffer for the SET_RECORD command
276 /// - true means success
277 /// - false means no data available from Builder object
279 bool Packet::SetRecord(unsigned int dbId
, Builder
&build
)
281 // get new data if available
282 if( !build
.Retrieve(dbId
) )
286 size_t header_size
= SB_PACKET_COMMAND_HEADER_SIZE
+ DBC_TAGGED_UPLOAD_HEADER_SIZE
;
287 build
.BuildHeader(m_send
, header_size
);
288 build
.BuildFields(m_send
, header_size
);
289 size_t total_size
= m_send
.GetSize();
291 // fill in the header values
292 MAKE_PACKETPTR_BUF(cpack
, m_send
.GetBuffer(total_size
));
293 Protocol::Packet
&packet
= *cpack
;
295 packet
.socket
= htobs(m_con
.m_socket
.GetSocket());
296 packet
.size
= htobs(total_size
);
297 packet
.command
= SB_COMMAND_DB_DATA
;
298 packet
.u
.db
.tableCmd
= m_con
.GetCommand(Controller::DatabaseAccess
);
299 packet
.u
.db
.u
.command
.operation
= SB_DBOP_SET_RECORD
;
300 packet
.u
.db
.u
.command
.databaseId
= htobs(dbId
);
301 packet
.u
.db
.u
.command
.u
.tag_upload
.unknown
= 0;
302 packet
.u
.db
.u
.command
.u
.tag_upload
.uniqueId
= htobl(build
.GetUniqueId());
303 packet
.u
.db
.u
.command
.u
.tag_upload
.unknown2
= 1; // unknown observed value
305 m_send
.ReleaseBuffer(total_size
);
307 m_last_dbop
= SB_DBOP_SET_RECORD
;
315 /// Returns the command value of the receive packet. If receive isn't
316 /// large enough, throws BError.
318 unsigned int Packet::Command() const
320 Protocol::CheckSize(m_receive
);
321 MAKE_PACKET(rpack
, m_receive
);
322 return rpack
->command
;
325 // throws FIXME if packet doesn't support it
326 unsigned int Packet::ReturnCode() const
328 if( Command() == SB_COMMAND_DB_DONE
) {
329 Protocol::CheckSize(m_receive
, SB_PACKET_DBACCESS_HEADER_SIZE
+ SB_DBACCESS_RETURN_CODE_SIZE
);
330 MAKE_PACKET(rpack
, m_receive
);
331 return rpack
->u
.db
.u
.return_code
;
334 throw BError("Attempting to extract a return code from the wrong response packet type");
341 /// Returns the database operation code from the receive packet, assuming
342 /// that receive contains a response packet. If receive isn't large
343 /// enough, throws BError.
345 unsigned int Packet::DBOperation() const
347 Protocol::CheckSize(m_receive
, SB_PACKET_RESPONSE_HEADER_SIZE
);
348 MAKE_PACKET(rpack
, m_receive
);
349 return rpack
->u
.db
.u
.response
.operation
;
355 /// Parses the data in the receive buffer, and attempts to be smart about it,
356 /// using the last send command as guidance for what to expect in the
359 /// \returns bool true - packet was recognized and parse was attempted
360 /// false - packet was not recognized
362 bool Packet::Parse(Parser
&parser
)
365 MAKE_PACKET(rpack
, m_receive
);
367 switch( m_last_dbop
)
369 case SB_DBOP_OLD_GET_RECORDS
:
370 case SB_DBOP_GET_RECORD_BY_INDEX
:
373 offset
= SB_PACKET_RESPONSE_HEADER_SIZE
+ DBR_OLD_TAGGED_RECORD_HEADER_SIZE
;
374 Protocol::CheckSize(m_receive
, offset
);
375 // FIXME - this may need adjustment for email records... they
376 // don't seem to have uniqueID's
377 parser
.SetUniqueId(btohl(rpack
->u
.db
.u
.response
.u
.tagged
.uniqueId
));
379 parser
.ParseHeader(m_receive
, offset
);
380 parser
.ParseFields(m_receive
, offset
);
384 default: // unknown command