3 /// Mode class for the Desktop mode
7 Copyright (C) 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "m_desktop.h"
25 #include "protostructs.h"
30 #include "controller.h"
36 namespace Barry
{ namespace Mode
{
39 ///////////////////////////////////////////////////////////////////////////////
42 Desktop::Desktop(Controller
&con
)
52 ///////////////////////////////////////////////////////////////////////////////
55 void Desktop::LoadCommandTable()
57 char rawCommand
[] = { 6, 0, 0x0a, 0, 0x40, 0, 0, 1, 0, 0 };
58 *((uint16_t*) rawCommand
) = htobs(m_socket
->GetSocket());
60 Data
command(rawCommand
, sizeof(rawCommand
));
64 m_socket
->Packet(command
, response
);
66 MAKE_PACKET(rpack
, response
);
67 while( rpack
->command
!= SB_COMMAND_DB_DONE
) {
68 m_socket
->NextRecord(response
);
70 rpack
= (const Protocol::Packet
*) response
.GetData();
71 if( rpack
->command
== SB_COMMAND_DB_DATA
&& btohs(rpack
->size
) > 10 ) {
72 // second packet is generally large, and contains
74 m_commandTable
.Clear();
75 m_commandTable
.Parse(response
, 6);
79 ddout(m_commandTable
);
82 catch( Usb::Error
& ) {
83 eout("Controller: error getting command table");
84 eeout(command
, response
);
89 void Desktop::LoadDBDB()
91 Data command
, response
;
92 DBPacket
packet(*this, command
, response
);
95 m_socket
->Packet(packet
);
97 while( packet
.Command() != SB_COMMAND_DB_DONE
) {
98 if( packet
.Command() == SB_COMMAND_DB_DATA
) {
100 m_dbdb
.Parse(response
);
104 m_socket
->NextRecord(response
);
110 ///////////////////////////////////////////////////////////////////////////////
116 /// Select device mode. This is required before using any other mode-based
117 /// operations, such as GetDBDB() and LoadDatabase().
119 /// This function opens a socket to the device for communicating in Desktop
120 /// mode. If the device requires it, specify the password with a const char*
121 /// string in password. The password will not be stored in memory
122 /// inside this class, only a hash will be generated from it. After
123 /// using the hash, the hash memory will be set to 0. The application
124 /// is responsible for safely handling the raw password data.
126 /// You can retry the password by catching Barry::BadPassword and
127 /// calling RetryPassword() with the new password.
129 /// \exception Barry::Error
130 /// Thrown on protocol error.
132 /// \exception std::logic_error()
133 /// Thrown if unsupported mode is requested, or if socket
136 /// \exception Barry::BadPassword
137 /// Thrown when password is invalid or if not enough retries
138 /// left in the device.
140 void Desktop::Open(const char *password
)
148 m_ModeSocket
= m_con
.SelectMode(Controller::Desktop
);
149 RetryPassword(password
);
155 /// Retry a failed password attempt from the first call to Open().
156 /// Only call this function in response to Barry::BadPassword exceptions
157 /// that are thrown from Open().
159 /// \exception Barry::Error
160 /// Thrown on protocol error.
162 /// \exception std::logic_error()
163 /// Thrown if in unsupported mode, or if socket already open.
165 /// \exception Barry::BadPassword
166 /// Thrown when password is invalid or if not enough retries
167 /// left in the device.
169 void Desktop::RetryPassword(const char *password
)
171 if( m_socket
.get() != 0 )
172 throw std::logic_error("Socket alreay open in RetryPassword");
174 m_socket
= m_con
.m_zero
.Open(m_ModeSocket
, password
);
176 // get command table and database database
184 /// Get numeric database ID by name.
186 /// \param[in] name Name of database, which matches one of the
187 /// names listed in GetDBDB()
189 /// \exception Barry::Error
190 /// Thrown if name not found.
192 unsigned int Desktop::GetDBID(const std::string
&name
) const
195 // FIXME - this needs a better error handler...
196 if( !m_dbdb
.GetDBNumber(name
, ID
) ) {
197 throw Error("Controller: database name not found: " + name
);
205 /// Get database command from command table. Must call Open()
208 unsigned int Desktop::GetDBCommand(CommandType ct
)
210 unsigned int cmd
= 0;
211 const char *cmdName
= "Unknown";
216 cmdName
= "Database Access";
217 cmd
= m_commandTable
.GetCommand(cmdName
);
220 throw std::logic_error("Controller: unknown command type");
224 std::ostringstream oss
;
225 oss
<< "Controller: unable to get command code: " << cmdName
;
226 throw Error(oss
.str());
233 // GetRecordStateTable
235 /// Retrieve the record state table from the handheld device, using the given
236 /// database ID. Results will be stored in result, which will be cleared
239 void Desktop::GetRecordStateTable(unsigned int dbId
, RecordStateTable
&result
)
241 dout("Database ID: " << dbId
);
246 Data command
, response
;
247 DBPacket
packet(*this, command
, response
);
248 packet
.GetRecordStateTable(dbId
);
250 m_socket
->Packet(packet
);
251 result
.Parse(response
);
253 // flush the command sequence
254 while( packet
.Command() != SB_COMMAND_DB_DONE
)
255 m_socket
->NextRecord(response
);
261 /// Adds a record to the specified database. RecordId is
262 /// retrieved from build, and duplicate IDs are allowed by the device
263 /// (i.e. you can have two records with the same ID)
264 /// but *not* recommended!
266 void Desktop::AddRecord(unsigned int dbId
, Builder
&build
)
268 dout("Database ID: " << dbId
);
270 Data command
, response
;
271 DBPacket
packet(*this, command
, response
);
273 if( packet
.SetRecord(dbId
, build
) ) {
275 std::ostringstream oss
;
277 m_socket
->Packet(packet
);
279 // successful packet transfer, so check the network return code
280 if( packet
.Command() != SB_COMMAND_DB_DONE
) {
281 oss
<< "Controller: device responded with unexpected packet command code: "
282 << "0x" << std::hex
<< packet
.Command();
283 throw Error(oss
.str());
286 if( packet
.ReturnCode() != 0 ) {
287 oss
<< "Controller: device responded with error code (command: "
288 << packet
.Command() << ", code: "
289 << packet
.ReturnCode() << ")";
290 throw Error(oss
.str());
298 /// Retrieves a specific record from the specified database.
299 /// The stateTableIndex comes from the GetRecordStateTable()
300 /// function. GetRecord() does not clear the dirty flag.
302 void Desktop::GetRecord(unsigned int dbId
,
303 unsigned int stateTableIndex
,
306 dout("Database ID: " << dbId
);
308 Data command
, response
;
309 DBPacket
packet(*this, command
, response
);
310 packet
.GetRecordByIndex(dbId
, stateTableIndex
);
312 m_socket
->Packet(packet
);
314 // perform copious packet checks
315 if( response
.GetSize() < SB_PACKET_RESPONSE_HEADER_SIZE
) {
316 eeout(command
, response
);
318 std::ostringstream oss
;
319 oss
<< "Controller: invalid response packet size of "
320 << std::dec
<< response
.GetSize();
322 throw Error(oss
.str());
324 if( packet
.Command() != SB_COMMAND_DB_DATA
) {
325 eeout(command
, response
);
327 std::ostringstream oss
;
328 oss
<< "Controller: unexpected command of 0x"
329 << std::setbase(16) << packet
.Command()
330 << " instead of expected 0x"
331 << std::setbase(16) << (unsigned int)SB_COMMAND_DB_DATA
;
333 throw Error(oss
.str());
337 packet
.Parse(parser
);
339 // flush the command sequence
340 while( packet
.Command() != SB_COMMAND_DB_DONE
)
341 m_socket
->NextRecord(response
);
347 /// Overwrites a specific record in the device as identified by the
350 void Desktop::SetRecord(unsigned int dbId
, unsigned int stateTableIndex
,
353 dout("Database ID: " << dbId
<< " Index: " << stateTableIndex
);
355 Data command
, response
;
356 DBPacket
packet(*this, command
, response
);
358 // loop until builder object has no more data
359 if( !packet
.SetRecordByIndex(dbId
, stateTableIndex
, build
) ) {
360 throw std::logic_error("Controller: no data available in SetRecord");
363 m_socket
->Packet(packet
);
365 std::ostringstream oss
;
367 // successful packet transfer, so check the network return code
368 if( packet
.Command() != SB_COMMAND_DB_DONE
) {
369 oss
<< "Controller: device responded with unexpected packet command code: "
370 << "0x" << std::hex
<< packet
.Command();
371 throw Error(oss
.str());
374 if( packet
.ReturnCode() != 0 ) {
375 oss
<< "Controller: device responded with error code (command: "
376 << packet
.Command() << ", code: "
377 << packet
.ReturnCode() << ")";
378 throw Error(oss
.str());
385 /// Clears the dirty flag on the specified record in the specified database.
387 void Desktop::ClearDirty(unsigned int dbId
, unsigned int stateTableIndex
)
389 dout("Database ID: " << dbId
);
391 Data command
, response
;
392 DBPacket
packet(*this, command
, response
);
393 packet
.SetRecordFlags(dbId
, stateTableIndex
, 0);
395 m_socket
->Packet(packet
);
397 // flush the command sequence
398 while( packet
.Command() != SB_COMMAND_DB_DONE
)
399 m_socket
->NextRecord(response
);
405 /// Deletes the specified record in the specified database.
407 void Desktop::DeleteRecord(unsigned int dbId
, unsigned int stateTableIndex
)
409 dout("Database ID: " << dbId
);
411 Data command
, response
;
412 DBPacket
packet(*this, command
, response
);
413 packet
.DeleteRecordByIndex(dbId
, stateTableIndex
);
415 m_socket
->Packet(packet
);
417 // flush the command sequence
418 while( packet
.Command() != SB_COMMAND_DB_DONE
)
419 m_socket
->NextRecord(response
);
425 /// Retrieve a database from the handheld device, using the given parser
426 /// to parse the resulting data, and optionally store it.
428 /// See the RecordParser<> template to create a parser object. The
429 /// RecordParser<> template allows custom storage based on the type of
430 /// database record retrieved. The database ID and the parser Record
433 /// \param[in] dbId Database Database ID - use GetDBID()
434 /// \param[out] parser Parser object which parses the resulting
435 /// protocol data, and optionally stores it in
436 /// a custom fashion. See the RecordParser<>
439 /// \exception Barry::Error
440 /// Thrown on protocol error.
442 /// \exception std::logic_error
443 /// Thrown if not in Desktop mode.
445 void Desktop::LoadDatabase(unsigned int dbId
, Parser
&parser
)
447 dout("Database ID: " << dbId
);
449 Data command
, response
;
450 DBPacket
packet(*this, command
, response
);
451 packet
.GetRecords(dbId
);
453 m_socket
->Packet(packet
);
455 while( packet
.Command() != SB_COMMAND_DB_DONE
) {
456 if( packet
.Command() == SB_COMMAND_DB_DATA
) {
457 // this size is the old header size, since using
459 packet
.Parse(parser
);
463 m_socket
->NextRecord(response
);
467 void Desktop::SaveDatabase(unsigned int dbId
, Builder
&builder
)
469 dout("Database ID: " << dbId
);
471 // Protocol note: so far in testing, this CLEAR_DATABASE operation is
472 // required, since every record sent via SET_RECORD
473 // is treated like a hypothetical "ADD_RECORD" (perhaps
474 // SET_RECORD should be renamed)... I don't know if
475 // there is a real SET_RECORD... all I know is from
476 // the Windows USB captures, which uses this same
478 Data command
, response
;
479 DBPacket
packet(*this, command
, response
);
480 packet
.ClearDatabase(dbId
);
482 // wait up to a minute here for old, slower devices with lots of data
483 m_socket
->Packet(packet
, 60000);
484 if( packet
.ReturnCode() != 0 ) {
485 std::ostringstream oss
;
486 oss
<< "Controller: could not clear database: (command: "
487 << "0x" << std::hex
<< packet
.Command() << ", code: "
488 << "0x" << std::hex
<< packet
.ReturnCode() << ")";
489 throw Error(oss
.str());
492 // check response to clear command was successful
493 if( packet
.Command() != SB_COMMAND_DB_DONE
) {
494 eeout(command
, response
);
495 throw Error("Controller: error clearing database, bad response");
498 // loop until builder object has no more data
500 while( packet
.SetRecord(dbId
, builder
) ) {
501 dout("Database ID: " << dbId
);
503 m_socket
->Packet(packet
, first
? 60000 : -1);
506 std::ostringstream oss
;
507 // successful packet transfer, so check the network return code
508 if( packet
.Command() != SB_COMMAND_DB_DONE
) {
509 oss
<< "Controller: device responded with unexpected packet command code: "
510 << "0x" << std::hex
<< packet
.Command();
511 throw Error(oss
.str());
514 if( packet
.ReturnCode() != 0 ) {
515 oss
<< "Controller: device responded with error code (command: "
516 << packet
.Command() << ", code: "
517 << packet
.ReturnCode() << ")";
518 throw Error(oss
.str());
523 }} // namespace Barry::Mode