desktop: CalEditDlg: fixed dialog title bar
[barry.git] / src / restore.cc
blob686a83e4bb655b1791c38a4289a1c00437e16c96
1 ///
2 /// \file restore.cc
3 /// Builder class for restoring from Barry Backup files
4 ///
6 /*
7 Copyright (C) 2010-2012, 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 "restore.h"
23 #include "tarfile.h"
24 #include "error.h"
25 #include <sstream>
26 #include <iomanip>
27 #include <iostream>
28 #include <string.h>
29 #include <algorithm>
31 using namespace std;
33 namespace Barry {
35 namespace {
37 int CountFiles(reuse::TarFile &tar,
38 const Barry::Restore::DBListType &restoreList,
39 Barry::Restore::DBListType *available,
40 bool default_all_db)
42 int count = 0;
43 std::string name, last_name;
44 bool good = false;
46 while( tar.ReadNextFilenameOnly(name) ) {
47 std::string::size_type pos = name.rfind('/');
48 if( pos == std::string::npos )
49 continue; // bad name
50 std::string dbname = name.substr(0, pos);
52 if( dbname != last_name ) {
53 last_name = dbname;
54 good = (default_all_db && restoreList.size() == 0) ||
55 restoreList.IsSelected(dbname);
57 if( good && available )
58 available->push_back(dbname);
60 if( good )
61 count++;
63 return count;
68 //////////////////////////////////////////////////////////////////////////////
69 // Static Restore members
71 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
72 /// Returns true if successful, false if tarpath is a bad name.
73 bool Restore::SplitTarPath(const std::string &tarpath,
74 std::string &dbname,
75 std::string &dbid_text,
76 uint8_t &dbrectype,
77 uint32_t &dbid)
79 std::string::size_type pos = tarpath.rfind('/');
80 if( pos == std::string::npos )
81 return false; // bad name
83 dbname = tarpath.substr(0, pos);
84 dbid_text = tarpath.substr(pos + 1);
85 if( dbname.size() == 0 || dbid_text.size() == 0 )
86 return false; // bad name
88 std::istringstream iss(dbid_text);
89 unsigned int temp;
90 iss >> std::hex >> dbid >> temp;
91 dbrectype = (uint8_t) temp;
93 return true;
97 //////////////////////////////////////////////////////////////////////////////
98 // Restore - constructors
100 Restore::Restore(const std::string &tarpath, bool default_all_db)
101 : m_tarpath(tarpath)
102 , m_default_all_db(default_all_db)
103 , m_tar_record_state(RS_EMPTY)
104 , m_rec_type(0)
105 , m_unique_id(0)
107 try {
108 m_tar.reset( new reuse::TarFile(tarpath.c_str(), false,
109 &reuse::gztar_ops_nonthread, true) );
111 catch( reuse::TarFile::TarError &te ) {
112 throw Barry::RestoreError(te.what());
116 Restore::~Restore()
121 //////////////////////////////////////////////////////////////////////////////
122 // Restore - Protected helpers
124 bool Restore::IsSelected(const std::string &dbName) const
126 // if in skip list, always return false,
127 // if nothing is in the main list, use default
128 // otherwise, only return true if specifically selected
129 if( m_dbSkipList.IsSelected(dbName) )
130 return false;
131 else if( m_dbList.size() == 0 )
132 return m_default_all_db;
133 else
134 return m_dbList.IsSelected(dbName);
138 //////////////////////////////////////////////////////////////////////////////
139 // Restore - Public API
141 void Restore::AddDB(const std::string &dbName)
143 if( find(m_dbList.begin(), m_dbList.end(), dbName) == m_dbList.end() ) {
144 // only add it if it is not already in the list
145 m_dbList.push_back(dbName);
149 void Restore::Add(const DBListType &dbList)
151 for( DBListType::const_iterator i = dbList.begin();
152 i != dbList.end();
153 ++i )
155 AddDB(*i);
159 void Restore::Add(const DatabaseDatabase &dbdb)
161 for( DatabaseDatabase::DatabaseArrayType::const_iterator i = dbdb.Databases.begin();
162 i != dbdb.Databases.end();
163 ++i )
165 AddDB(i->Name);
169 void Restore::AddSkipDB(const std::string &dbName)
171 if( find(m_dbSkipList.begin(), m_dbSkipList.end(), dbName) == m_dbSkipList.end() ) {
172 // only add it if it is not already in the list
173 m_dbSkipList.push_back(dbName);
177 void Restore::SkipCurrentDB()
179 // skip all records until next DB
180 try {
181 Restore::RetrievalState state;
182 while( (state = Retrieve(m_record_data)) == RS_NEXT ) {
183 std::cerr << "Skipping: "
184 << m_current_dbname << "/"
185 << m_tar_id_text << std::endl;
186 m_tar_record_state = RS_EMPTY;
189 if( state == RS_DBEND ) {
190 // process the end of database, so that user is free
191 // to call GetNextMeta() or Retrieve() or BuildRecord()
192 m_tar_record_state = RS_NEXT;
195 catch( reuse::TarFile::TarError & ) {
196 m_tar_record_state = RS_EOF;
200 unsigned int Restore::GetRecordTotal() const
202 return GetRecordTotal(m_tarpath, m_dbList, m_default_all_db);
205 unsigned int Restore::GetRecordTotal(const std::string &tarpath,
206 const DBListType &dbList,
207 bool default_all_db)
209 unsigned int count = 0;
211 std::auto_ptr<reuse::TarFile> tar;
213 try {
214 // do a scan through the tar file
215 tar.reset( new reuse::TarFile(tarpath.c_str(), false,
216 &reuse::gztar_ops_nonthread, true) );
217 count = CountFiles(*tar, dbList, 0, default_all_db);
219 catch( reuse::TarFile::TarError &te ) {
220 throw Barry::RestoreError(te.what());
222 return count;
225 Barry::Restore::DBListType Restore::GetDBList() const
227 return GetDBList(m_tarpath);
230 Barry::Restore::DBListType Restore::GetDBList(const std::string &tarpath)
232 unsigned int count = 0;
234 std::auto_ptr<reuse::TarFile> tar;
235 DBListType available, empty;
237 try {
238 // do a scan through the tar file
239 tar.reset( new reuse::TarFile(tarpath.c_str(), false,
240 &reuse::gztar_ops_nonthread, true) );
241 count = CountFiles(*tar, empty, &available, true);
242 return available;
244 catch( reuse::TarFile::TarError &te ) {
245 throw Barry::RestoreError(te.what());
249 bool Restore::GetNextMeta(DBData &data)
251 // always use m_record_data here, so that we don't lose access
252 // to the actual record data for future calls to BuildRecord()
253 // and FetchRecord()
254 if( m_tar_record_state == RS_EMPTY ) {
255 Retrieve(m_record_data);
258 // fill in the meta data that will be returned in the next call
259 // to BuildRecord() or FetchRecord()... this is only valid if
260 // the state is RS_NEXT
261 switch( m_tar_record_state )
263 case RS_NEXT:
264 data.SetVersion(Barry::DBData::REC_VERSION_1);
265 data.SetDBName(m_current_dbname);
266 data.SetIds(m_rec_type, m_unique_id);
267 data.SetOffset(0);
268 return true;
270 default:
271 return false;
276 //////////////////////////////////////////////////////////////////////////////
277 // Barry::Builder overrides
279 Restore::RetrievalState Restore::Retrieve(Data &record_data)
281 // don't do anything unless we're empty
282 if( m_tar_record_state != RS_EMPTY )
283 return m_tar_record_state;
285 // search for a valid record
286 for(;;) {
287 // load record data from tar file
288 std::string filename;
289 if( !m_tar->ReadNextFile(filename, record_data) ) {
290 // assume end of file
291 return m_tar_record_state = RS_EOF;
293 m_tar_record_state = RS_UNKNOWN;
295 // split record filename into dbname and ID
296 std::string dbname;
297 if( !SplitTarPath(filename, dbname, m_tar_id_text, m_rec_type, m_unique_id) ) {
298 // invalid filename, skip it
299 std::cerr << "Skipping invalid tar record: " << filename << std::endl;
300 continue;
303 // are we working on the same dbname as last time?
304 // if so, go ahead!
305 if( m_current_dbname == dbname ) {
306 return m_tar_record_state = RS_NEXT;
309 // DIFFERENT DBNAME from here on down!
310 m_tar_record_state = RS_DBEND;
312 // does the filter allow this record?
313 // if not, skip it and continue looking
314 if( !IsSelected(dbname) ) {
315 continue;
318 // all checks pass, load the new dbname, and return DBEND
319 // if we are on a dbname boundary
320 if( m_current_dbname.size() == 0 ) {
321 // this is the first time through Retrieve, so ok
322 m_tar_record_state = RS_NEXT;
325 m_current_dbname = dbname;
326 return m_tar_record_state;
330 bool Restore::BuildRecord(Barry::DBData &data,
331 size_t &offset,
332 const Barry::IConverter *ic)
334 // in this case, we are loading into m_record_data anyway,
335 // so no special handling is needed, like FetchRecord() needs.
336 switch( Retrieve(m_record_data) )
338 case RS_NEXT:
340 data.SetVersion(Barry::DBData::REC_VERSION_1);
341 data.SetDBName(m_current_dbname);
342 data.SetIds(m_rec_type, m_unique_id);
343 data.SetOffset(offset);
345 int packet_size = offset + m_record_data.GetSize();
346 unsigned char *buf = data.UseData().GetBuffer(packet_size);
347 memcpy(buf + offset, m_record_data.GetData(), m_record_data.GetSize());
348 offset += m_record_data.GetSize();
349 data.UseData().ReleaseBuffer(packet_size);
351 // clear loaded flag, as it has now been used
352 m_tar_record_state = RS_EMPTY;
353 return true;
356 case RS_EMPTY:
357 case RS_UNKNOWN:
358 default:
359 throw std::logic_error("Invalid state in Restore::BuildRecord()");
361 case RS_DBEND:
362 // process the end of database by returning false
363 // the next round will be valid, so set to RS_NEXT
364 m_tar_record_state = RS_NEXT;
365 return false;
367 case RS_EOF:
368 // always return false at end of file
369 return false;
373 bool Restore::FetchRecord(Barry::DBData &data, const Barry::IConverter *ic)
375 // if the record has not yet been loaded, we can optimize
376 // the buffer, and pass in our own... otherwise, just copy
377 // the current buffer from m_record_data
379 // it is assumed here that Builder users will not alternate
380 // between calls to BuildRecord() and FetchRecord()
382 if( m_tar_record_state == RS_EMPTY ) {
383 // BUT, if RS_DBEND is the next value, then we need
384 // to save the data for the next round... this
385 // optimization is almost more bother than it's worth :-)
386 if( Retrieve(data.UseData()) == RS_DBEND ) {
387 m_record_data = data.GetData();
388 m_tar_record_state = RS_NEXT;
389 return false;
392 else {
393 data.UseData() = m_record_data;
396 switch( m_tar_record_state )
398 case RS_NEXT:
399 data.SetVersion(Barry::DBData::REC_VERSION_1);
400 data.SetDBName(m_current_dbname);
401 data.SetIds(m_rec_type, m_unique_id);
402 data.SetOffset(0);
404 // clear loaded flag, as it has now been used
405 m_tar_record_state = RS_EMPTY;
406 return true;
408 case RS_EMPTY:
409 case RS_UNKNOWN:
410 default:
411 throw std::logic_error("Invalid state in Restore::FetchRecord()");
413 case RS_DBEND:
414 // process the end of database by returning false
415 // the next round will be valid, so set to RS_NEXT
416 m_tar_record_state = RS_NEXT;
417 return false;
419 case RS_EOF:
420 // always return false at end of file
421 return false;
425 bool Restore::EndOfFile() const
427 return m_tar_record_state == RS_EOF;
430 } // namespace Barry