Reduced minimum password retry level from 6 to 3
[barry/pauldeden.git] / gui / src / DeviceIface.cc
blob6a3860b8aed23848ac32fee70c98b18077671cce
1 ///
2 /// \file DeviceIface.cc
3 /// Interface class for device backup and restore
4 ///
6 /*
7 Copyright (C) 2007-2008, 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 "DeviceIface.h"
23 #include "util.h"
24 #include <glibmm.h>
25 #include <iostream>
26 #include <iomanip>
27 #include <sstream>
28 #include <time.h>
29 #include <string.h>
30 #include <stdlib.h>
32 DeviceInterface::DeviceInterface()
33 : m_con(0)
34 , m_desktop(0)
35 , m_dbnameMutex(new Glib::Mutex) // this is just in an effort to
36 // avoid gtkmm headers in
37 // DeviceIface.h
38 , m_end_of_tar(false)
39 , m_tar_record_loaded(false)
40 , m_thread_quit(false)
44 DeviceInterface::~DeviceInterface()
46 delete m_desktop;
47 delete m_con;
48 delete m_dbnameMutex;
51 bool DeviceInterface::False(const std::string &msg)
53 m_last_error = msg;
54 return false;
57 void DeviceInterface::BackupThread()
59 bool error = false;
60 m_thread_quit = false;
61 m_last_thread_error = "";
63 try {
65 // cycle through all database names in the dbList
66 // and store them all
67 ConfigFile::DBListType::const_iterator name = m_dbList.begin();
68 for( ; name != m_dbList.end(); ++name ) {
69 // save current db name
70 SetThreadDBName(*name);
72 // call the controller to do the work
73 unsigned int dbId = m_desktop->GetDBID(*name);
74 m_desktop->LoadDatabase(dbId, *this);
78 catch( Glib::Exception &e ) {
79 m_last_thread_error = e.what();
80 error = true;
82 catch( std::exception &e ) {
83 m_last_thread_error = e.what();
84 error = true;
86 catch( Quit &q ) {
87 m_last_thread_error = "Terminated by user.";
90 m_tarback->Close();
91 m_tarback.reset();
93 if( error )
94 m_AppComm.m_error->emit();
96 // signal host thread that we're done
97 m_AppComm.m_done->emit();
99 // done!
100 m_AppComm.Invalidate();
103 void DeviceInterface::RestoreThread()
105 m_thread_quit = false;
106 m_last_thread_error = "";
108 try {
110 // cycle until m_end_of_tar
111 while( !m_end_of_tar ) {
112 try {
113 // call the controller to do the work
114 unsigned int dbId = m_desktop->GetDBID(m_current_dbname);
115 m_AppComm.m_erase_db->emit();
116 m_desktop->SaveDatabase(dbId, *this);
118 catch( Barry::Error &be ) {
119 // save thread error
120 m_last_thread_error = "Error while restoring ";
121 m_last_thread_error += m_current_dbname + ". ";
122 m_last_thread_error += be.what();
123 m_last_thread_error += " Will continue processing.";
125 // notify host thread
126 m_AppComm.m_error->emit();
128 // skip over records from this db
129 std::cerr << "Error on database: "
130 << m_current_dbname << std::endl;
131 SkipCurrentDB();
136 catch( Glib::Exception &e ) {
137 m_last_thread_error = e.what();
138 m_AppComm.m_error->emit();
140 catch( std::exception &e ) {
141 m_last_thread_error = e.what();
142 m_AppComm.m_error->emit();
144 catch( Quit &q ) {
145 m_last_thread_error = "Terminated by user.";
148 m_tar->Close();
149 m_tar.reset();
151 // signal host thread that we're done
152 m_AppComm.m_done->emit();
154 // done!
155 m_AppComm.Invalidate();
158 void DeviceInterface::RestoreAndBackupThread()
160 m_thread_quit = false;
161 m_last_thread_error = "";
163 try {
165 // cycle until m_end_of_tar
166 while( !m_end_of_tar ) {
167 unsigned int dbId = m_desktop->GetDBID(m_current_dbname);
169 try {
171 // do restore first
172 m_AppComm.m_erase_db->emit();
173 m_desktop->SaveDatabase(dbId, *this);
176 catch( Barry::Error &be ) {
177 // save thread error
178 m_last_thread_error = "Error while restoring ";
179 m_last_thread_error += m_current_dbname + ". ";
180 m_last_thread_error += be.what();
181 m_last_thread_error += " Will continue processing.";
183 // notify host thread
184 m_AppComm.m_error->emit();
186 // skip over records from this db
187 std::cerr << "Error on database: "
188 << m_current_dbname << std::endl;
189 SkipCurrentDB();
192 // then the backup, even if restore fails
193 m_desktop->LoadDatabase(dbId, *this);
198 catch( Glib::Exception &e ) {
199 m_last_thread_error = e.what();
200 m_AppComm.m_error->emit();
202 catch( std::exception &e ) {
203 m_last_thread_error = e.what();
204 m_AppComm.m_error->emit();
206 catch( Quit &q ) {
207 m_last_thread_error = "Terminated by user.";
210 m_tar->Close();
211 m_tar.reset();
212 m_tarback->Close();
213 m_tarback.reset();
215 // signal host thread that we're done
216 m_AppComm.m_done->emit();
218 // done!
219 m_AppComm.Invalidate();
222 std::string DeviceInterface::MakeFilename(const std::string &pin)
224 time_t t = time(NULL);
225 struct tm *lt = localtime(&t);
227 std::ostringstream tarfilename;
228 tarfilename << pin << "-"
229 << std::setw(4) << std::setfill('0') << (lt->tm_year + 1900)
230 << std::setw(2) << std::setfill('0') << (lt->tm_mon + 1)
231 << std::setw(2) << std::setfill('0') << lt->tm_mday
232 << "-"
233 << std::setw(2) << std::setfill('0') << lt->tm_hour
234 << std::setw(2) << std::setfill('0') << lt->tm_min
235 << std::setw(2) << std::setfill('0') << lt->tm_sec
236 << ".tar.gz";
237 return tarfilename.str();
240 int DeviceInterface::CountFiles(reuse::TarFile &tar,
241 const ConfigFile::DBListType &restoreList)
243 int count = 0;
244 std::string name, last_name;
245 bool good = false;
247 while( tar.ReadNextFilenameOnly(name) ) {
248 std::string::size_type pos = name.rfind('/');
249 if( pos == std::string::npos )
250 continue; // bad name
251 std::string dbname = name.substr(0, pos);
253 if( dbname != last_name ) {
254 last_name = dbname;
255 good = restoreList.IsSelected(dbname);
257 if( good )
258 count++;
260 return count;
263 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
264 /// Returns true if successful, false if tarpath is a bad name.
265 bool DeviceInterface::SplitTarPath(const std::string &tarpath,
266 std::string &dbname,
267 std::string &dbid_text,
268 uint8_t &dbrectype,
269 uint32_t &dbid)
271 std::string::size_type pos = tarpath.rfind('/');
272 if( pos == std::string::npos )
273 return false; // bad name
275 dbname = tarpath.substr(0, pos);
276 dbid_text = tarpath.substr(pos + 1);
277 if( dbname.size() == 0 || dbid_text.size() == 0 )
278 return false; // bad name
280 std::istringstream iss(dbid_text);
281 unsigned int temp;
282 iss >> std::hex >> dbid >> temp;
283 dbrectype = (uint8_t) temp;
285 return true;
288 void DeviceInterface::SetThreadDBName(const std::string &dbname)
290 Glib::Mutex::Lock lock(*m_dbnameMutex);
291 m_current_dbname_not_thread_safe = dbname;
292 m_current_dbname = dbname;
296 //////////////////////////////////////////////////////////////////////////////
297 // Public API
299 bool DeviceInterface::Connect(const Barry::ProbeResult &dev)
301 try {
302 Disconnect();
303 m_con = new Barry::Controller(dev);
304 m_desktop = new Barry::Mode::Desktop(*m_con);
305 m_desktop->Open();
306 return true;
308 catch( Barry::BadPassword & ) {
309 // pass on to the caller
310 throw;
312 catch( Barry::BadSize & ) {
313 // pass on to the caller
314 Disconnect();
315 throw;
317 catch( Barry::Error &e ) {
318 Disconnect();
319 return False(e.what());
323 bool DeviceInterface::Password(const char *password)
325 try {
326 m_desktop->RetryPassword(password);
327 return true;
329 catch( Barry::BadPassword & ) {
330 // pass on to the caller
331 throw;
333 catch( Barry::Error &e ) {
334 Disconnect();
335 return False(e.what());
339 void DeviceInterface::Disconnect()
341 delete m_desktop;
342 m_desktop = 0;
344 delete m_con;
345 m_con = 0;
348 // cycle through controller's DBDB and count the records in all the
349 // databases selected in the backupList
350 int DeviceInterface::GetDeviceRecordTotal(const ConfigFile::DBListType &backupList) const
352 int count = 0;
354 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator
355 i = m_desktop->GetDBDB().Databases.begin();
356 for( ; i != m_desktop->GetDBDB().Databases.end(); ++i ) {
357 if( backupList.IsSelected(i->Name) ) {
358 count += i->RecordCount;
361 return count;
364 /// returns name of database the thread is currently working on
365 std::string DeviceInterface::GetThreadDBName() const
367 Glib::Mutex::Lock lock(*m_dbnameMutex);
368 return m_current_dbname_not_thread_safe;
371 bool DeviceInterface::StartBackup(AppComm comm,
372 const ConfigFile::DBListType &backupList,
373 const std::string &directory,
374 const std::string &pin)
376 if( m_AppComm.IsValid() )
377 return False("Thread already running.");
379 try {
381 std::string filename = directory + "/" + MakeFilename(pin);
382 m_tarback.reset( new reuse::TarFile(filename.c_str(), true, &reuse::gztar_ops_nonthread, true) );
385 catch( reuse::TarFile::TarError &te ) {
386 return False(te.what());
389 // setup
390 m_AppComm = comm;
391 m_dbList = backupList;
393 // start the thread
394 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::BackupThread), false);
395 return true;
398 bool DeviceInterface::StartRestore(AppComm comm,
399 const ConfigFile::DBListType &restoreList,
400 const std::string &filename,
401 int *pRecordCount)
403 if( m_AppComm.IsValid() )
404 return False("Thread already running.");
406 try {
407 if( pRecordCount ) {
408 // caller is asking for a total, so we do a quick
409 // scan through the tar file first
410 m_tar.reset( new reuse::TarFile(filename.c_str(), false, &reuse::gztar_ops_nonthread, true) );
411 *pRecordCount = CountFiles(*m_tar, restoreList);
413 // close for next open
414 m_tar.reset();
417 // open for the main restore
418 m_tar.reset( new reuse::TarFile(filename.c_str(), false, &reuse::gztar_ops_nonthread, true) );
421 catch( reuse::TarFile::TarError &te ) {
422 return False(te.what());
425 // setup
426 m_AppComm = comm;
427 m_dbList = restoreList;
428 m_current_dbname_not_thread_safe = "";
429 m_current_dbname = "";
430 m_unique_id = 0;
431 m_end_of_tar = false;
432 m_tar_record_loaded = false;
434 // get first tar record
435 Retrieve(0);
437 // start the thread
438 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreThread), false);
439 return true;
442 bool DeviceInterface::StartRestoreAndBackup(AppComm comm,
443 const ConfigFile::DBListType &restoreAndBackupList,
444 const std::string &filename,
445 const std::string &directory, const std::string &pin,
446 int *pRecordCount)
448 if( m_AppComm.IsValid() )
449 return False("Thread already running.");
451 try {
452 if( pRecordCount ) {
453 // caller is asking for a total, so we do a quick
454 // scan through the tar file first
455 m_tar.reset( new reuse::TarFile(filename.c_str(), false, &reuse::gztar_ops_nonthread, true) );
456 *pRecordCount = CountFiles(*m_tar, restoreAndBackupList);
458 // close for next open
459 m_tar.reset();
462 // open for the main restore
463 m_tar.reset( new reuse::TarFile(filename.c_str(), false, &reuse::gztar_ops_nonthread, true) );
465 // open for secondary backup
466 std::string back = directory + "/" + MakeFilename(pin);
467 m_tarback.reset( new reuse::TarFile(back.c_str(), true, &reuse::gztar_ops_nonthread, true) );
470 catch( reuse::TarFile::TarError &te ) {
471 return False(te.what());
474 // setup
475 m_AppComm = comm;
476 m_dbList = restoreAndBackupList;
477 m_current_dbname_not_thread_safe = "";
478 m_current_dbname = "";
479 m_unique_id = 0;
480 m_end_of_tar = false;
481 m_tar_record_loaded = false;
483 // get first tar record
484 Retrieve(0);
486 // start the thread
487 Glib::Thread::create(sigc::mem_fun(*this, &DeviceInterface::RestoreAndBackupThread), false);
488 return true;
493 //////////////////////////////////////////////////////////////////////////////
494 // Barry::Parser overrides
496 void DeviceInterface::SetIds(uint8_t RecType, uint32_t UniqueId)
498 m_rec_type = RecType;
499 m_unique_id = UniqueId;
500 std::ostringstream oss;
501 oss << std::hex << m_unique_id << " " << (unsigned int)m_rec_type;
502 m_tar_id_text = oss.str();
503 if( m_tar_id_text.size() == 0 )
504 throw std::runtime_error("No unique ID available!");
507 void DeviceInterface::ParseFields(const Barry::Data &data, size_t &offset)
509 m_record_data.assign((const char*)data.GetData() + offset, data.GetSize() - offset);
512 void DeviceInterface::Store()
514 std::string tarname = m_current_dbname + "/" + m_tar_id_text;
515 m_tarback->AppendFile(tarname.c_str(), m_record_data);
517 m_AppComm.m_progress->emit();
519 // check quit flag
520 if( m_thread_quit ) {
521 throw Quit();
526 //////////////////////////////////////////////////////////////////////////////
527 // Barry::Builder overrides
529 bool DeviceInterface::Retrieve(unsigned int dbId)
531 if( m_end_of_tar )
532 return false;
534 // if loaded, we are likely on a database
535 // boundary, and the last read crossed it, so don't load again
536 if( m_tar_record_loaded )
537 return true;
539 // search for a valid record
540 for(;;) {
541 // load record data from tar file
542 std::string filename;
543 if( !m_tar->ReadNextFile(filename, m_record_data) ) {
544 // assume end of file
545 m_end_of_tar = true;
546 return false;
548 m_tar_record_loaded = true;
550 // split record filename into dbname and ID
551 std::string dbname;
552 if( !SplitTarPath(filename, dbname, m_tar_id_text, m_rec_type, m_unique_id) ) {
553 // invalid filename, skip it
554 std::cerr << "Skipping invalid tar record: " << filename << std::endl;
555 continue;
558 // are we working on the same dbname as last time? if so, go ahead!
559 if( m_current_dbname == dbname ) {
560 return true;
563 // DIFFERENT DBNAME from here on down!
565 // does the filter allow this record? if not, skip it and continue
566 // looking
567 if( !m_dbList.IsSelected(dbname) ) {
568 continue;
571 // all checks pass, load the new dbname, and return false
572 // if we are on a dbname boundary
573 bool r_val = false;
574 if( m_current_dbname.size() == 0 ) {
575 // this is the first time through Retrieve, so ok
576 r_val = true;
579 SetThreadDBName(dbname);
580 return r_val;
584 uint8_t DeviceInterface::GetRecType() const
586 return m_rec_type;
589 uint32_t DeviceInterface::GetUniqueId() const
591 return m_unique_id;
594 void DeviceInterface::BuildHeader(Barry::Data &data, size_t &offset)
596 // nothing to do
599 void DeviceInterface::BuildFields(Barry::Data &data, size_t &offset)
601 int packet_size = offset + m_record_data.size();
602 unsigned char *buf = data.GetBuffer(packet_size);
603 memcpy(buf + offset, m_record_data.data(), m_record_data.size());
604 offset += m_record_data.size();
605 data.ReleaseBuffer(packet_size);
607 // clear loaded flag, as it has now been used
608 m_tar_record_loaded = false;
610 m_AppComm.m_progress->emit();
613 // helper function for halding restore errors
614 void DeviceInterface::SkipCurrentDB() throw()
616 // skip all records until next DB
617 try {
618 while( Retrieve(0) ) {
619 std::cerr << "Skipping: "
620 << m_current_dbname << "/"
621 << m_tar_id_text << std::endl;
622 m_tar_record_loaded = false;
625 catch( reuse::TarFile::TarError & ) {
626 m_end_of_tar = true;
628 catch( ... ) {
629 // swallow all other exceptions
630 std::cerr << "EXCEPTION IN SkipCurrentDB()! "
631 "Please report to Barry mailing list." << std::endl;