Passwordfile library  5.0.0
C++ library to read/write passwords from/to encrypted files
entry.cpp
Go to the documentation of this file.
1 #include "./entry.h"
2 #include "./parsingexception.h"
3 
4 #include <c++utilities/conversion/stringbuilder.h>
5 #include <c++utilities/io/binaryreader.h>
6 #include <c++utilities/io/binarywriter.h>
7 
8 #include <algorithm>
9 #include <sstream>
10 
11 using namespace std;
12 using namespace CppUtilities;
13 
14 namespace Io {
15 
32 Entry::Entry(const string &label, NodeEntry *parent)
33  : m_parent(nullptr)
34  , m_index(-1)
35 {
37  setLabel(label);
38 }
39 
45 Entry::Entry(const Entry &other)
46  : m_label(other.m_label)
47  , m_parent(nullptr)
48  , m_index(-1)
49 {
50 }
51 
56 {
57  setParent(nullptr);
58 }
59 
65 {
66  if (!m_parent) {
67  return;
68  }
69  string newLabel(label());
70  for (unsigned int index = 2;; ++index) {
71  bool needsNewLabel = false;
72  for (Entry *const sibling : m_parent->children()) {
73  if (sibling == this || newLabel != sibling->label()) {
74  continue;
75  }
76  needsNewLabel = true;
77  newLabel = argsToString(label(), ' ', index);
78  break;
79  }
80  if (!needsNewLabel) {
81  break;
82  }
83  }
84  m_label.swap(newLabel);
85 }
86 
99 void Entry::setParent(NodeEntry *parent, int index)
100 {
101  // skip if \a parent already assigned and the index doesn't change, too
102  if (m_parent == parent && !(m_index != index && index >= 0)) {
103  return;
104  }
105 
106  // detach the current parent
107  if (m_parent) {
108  m_parent->m_children.erase(m_parent->m_children.begin() + m_index);
109  for (auto i = m_parent->m_children.begin() + m_index; i < m_parent->m_children.end(); ++i) {
110  (*i)->m_index -= 1;
111  }
112  }
113 
114  // attach the new parent
115  if (parent) {
116  if (index < 0 || static_cast<size_t>(index) >= parent->m_children.size()) {
117  m_index = parent->m_children.size();
118  parent->m_children.push_back(this);
119  } else {
120  for (auto i = parent->m_children.insert(parent->m_children.begin() + index, this) + 1; i != parent->m_children.end(); ++i) {
121  (*i)->m_index += 1;
122  }
123  m_index = index;
124  }
125  } else {
126  m_index = -1;
127  }
128 
129  // actually assign the parent
130  m_parent = parent;
131 
132  // ensure the label is still unique within the new parent
133  makeLabelUnique();
134 }
135 
139 bool Entry::isIndirectChildOf(const NodeEntry *entry) const
140 {
141  if (!parent()) {
142  return false;
143  }
144  if (parent() == entry) {
145  return true;
146  } else {
147  return parent()->isIndirectChildOf(entry);
148  }
149 }
150 
154 std::list<string> Entry::path() const
155 {
156  list<string> res;
157  path(res);
158  return res;
159 }
160 
164 void Entry::path(std::list<string> &res) const
165 {
166  if (m_parent) {
167  m_parent->path(res);
168  }
169  res.push_back(label());
170 }
171 
176 Entry *Entry::parse(istream &stream)
177 {
178  const auto version = static_cast<std::uint8_t>(stream.peek());
179  if (denotesNodeEntry(version)) {
180  return new NodeEntry(stream);
181  } else {
182  return new AccountEntry(stream);
183  }
184 }
185 
212  : Entry()
213  , m_expandedByDefault(true)
214 {
215 }
216 
220 NodeEntry::NodeEntry(const string &label, NodeEntry *parent)
221  : Entry(label, parent)
222  , m_expandedByDefault(true)
223 {
224 }
225 
229 NodeEntry::NodeEntry(istream &stream)
230  : m_expandedByDefault(true)
231 {
232  BinaryReader reader(&stream);
233  const std::uint8_t version = reader.readByte();
234  if (!denotesNodeEntry(version)) {
235  throw ParsingException("Node entry expected.");
236  }
237  if (version != 0x0 && version != 0x1) {
238  throw ParsingException("Entry version not supported.");
239  }
240  setLabel(reader.readLengthPrefixedString());
241  // read extended header for version 0x1
242  if (version == 0x1) {
243  std::uint16_t extendedHeaderSize = reader.readUInt16BE();
244  if (extendedHeaderSize >= 1) {
245  std::uint8_t flags = reader.readByte();
246  m_expandedByDefault = flags & 0x80;
247  extendedHeaderSize -= 1;
248  }
249  m_extendedData = reader.readString(extendedHeaderSize);
250  }
251  const std::uint32_t childCount = reader.readUInt32BE();
252  for (std::uint32_t i = 0; i != childCount; ++i) {
253  Entry::parse(stream)->setParent(this);
254  }
255 }
256 
263  : Entry(other)
264 {
265  for (Entry *const otherChild : other.m_children) {
266  Entry *clonedChild = otherChild->clone();
267  clonedChild->m_parent = this;
268  clonedChild->m_index = m_children.size();
269  m_children.push_back(clonedChild);
270  }
271 }
272 
277 {
278  for (Entry *const child : m_children) {
279  child->m_parent = nullptr;
280  delete child;
281  }
282 }
283 
290 void NodeEntry::deleteChildren(int begin, int end)
291 {
292  const auto endIterator = m_children.begin() + end;
293 
294  // delete the children
295  for (auto iterator = m_children.cbegin() + begin; iterator != endIterator; ++iterator) {
296  (*iterator)->m_parent = nullptr;
297  delete *iterator;
298  }
299 
300  // remove the children from the list
301  m_children.erase(m_children.begin() + begin, endIterator);
302 
303  // adjust indices of subsequent children
304  const int diff = end - begin;
305  for (auto iterator = m_children.begin() + begin, end = m_children.end(); iterator != end; ++iterator) {
306  (*iterator)->m_index -= diff;
307  }
308 }
309 
317 void NodeEntry::replaceChild(size_t at, Entry *newChild)
318 {
319  if (at >= m_children.size()) {
320  return;
321  }
322 
323  // detatch the old child
324  m_children[at]->m_parent = nullptr;
325  m_children[at]->m_index = -1;
326 
327  // detach new child from its previous parent
328  if (auto *newChildOldParent = newChild->m_parent) {
329  newChildOldParent->m_children.erase(newChildOldParent->m_children.begin() + newChild->m_index);
330  for (auto i = newChildOldParent->m_children.begin() + newChild->m_index; i < newChildOldParent->m_children.end(); ++i) {
331  (*i)->m_index -= 1;
332  }
333  }
334 
335  // do the actual assignment
336  newChild->m_parent = this;
337  newChild->m_index = at;
338  m_children[at] = newChild;
339 }
340 
350 Entry *NodeEntry::entryByPath(list<string> &path, bool includeThis, const EntryType *creationType)
351 {
352  if (path.empty()) {
353  return nullptr;
354  }
355 
356  // check for current instance
357  if (includeThis) {
358  if (path.front() == label()) {
359  path.pop_front();
360  } else {
361  return nullptr;
362  }
363  }
364  if (path.empty()) {
365  return this;
366  }
367 
368  for (Entry *const child : m_children) {
369  if (path.front() != child->label()) {
370  continue;
371  }
372  path.pop_front();
373  if (path.empty()) {
374  return child;
375  } else if (child->type() == EntryType::Node) {
376  return static_cast<NodeEntry *>(child)->entryByPath(path, false, creationType);
377  } else {
378  return nullptr; // can not resolve path since an account entry can not have children
379  }
380  }
381 
382  // create a new entry
383  if (!creationType || path.size() != 1) {
384  return nullptr;
385  }
386  switch (*creationType) {
387  case EntryType::Account:
388  return new AccountEntry(path.front(), this);
389  case EntryType::Node:
390  return new NodeEntry(path.front(), this);
391  }
392  return nullptr;
393 }
394 
395 void NodeEntry::make(ostream &stream) const
396 {
397  BinaryWriter writer(&stream);
398  writer.writeByte(isExpandedByDefault() && m_extendedData.empty() ? 0x0 : 0x1); // version
399  writer.writeLengthPrefixedString(label());
400  if (!isExpandedByDefault() || !m_extendedData.empty()) {
401  writer.writeUInt16BE(1 + m_extendedData.size()); // extended header is 1 byte long
402  std::uint8_t flags = 0x00;
403  if (isExpandedByDefault()) {
404  flags |= 0x80;
405  }
406  writer.writeByte(flags);
407  writer.writeString(m_extendedData);
408  }
409  writer.writeUInt32BE(m_children.size());
410  for (const Entry *const child : m_children) {
411  child->make(stream);
412  }
413 }
414 
416 {
417  return new NodeEntry(*this);
418 }
419 
424 {
425  ++stats.nodeCount;
426  for (const auto *children : children()) {
427  children->accumulateStatistics(stats);
428  }
429 }
430 
437 {
438 }
439 
443 AccountEntry::AccountEntry(const string &label, NodeEntry *parent)
444  : Entry(label, parent)
445 {
446 }
447 
452 {
453  BinaryReader reader(&stream);
454  std::uint8_t version = reader.readByte();
455  if (denotesNodeEntry(version)) {
456  throw ParsingException("Account entry expected.");
457  }
458  version ^= 0x80; // set first bit to zero
459  if (version != 0x0 && version != 0x1) {
460  throw ParsingException("Entry version not supported.");
461  }
462  setLabel(reader.readLengthPrefixedString());
463  // read extended header for version 0x1
464  if (version == 0x1) {
465  const std::uint16_t extendedHeaderSize = reader.readUInt16BE();
466  // currently there's nothing to read here
467  m_extendedData = reader.readString(extendedHeaderSize);
468  }
469  const std::uint32_t fieldCount = reader.readUInt32BE();
470  for (std::uint32_t i = 0; i != fieldCount; ++i) {
471  m_fields.push_back(Field(this, stream));
472  }
473 }
474 
481  : Entry(other)
482 {
483  m_fields = other.m_fields;
484 }
485 
490 {
491 }
492 
493 void AccountEntry::make(ostream &stream) const
494 {
495  BinaryWriter writer(&stream);
496  writer.writeByte(0x80 | (m_extendedData.empty() ? 0x0 : 0x1)); // version
497  writer.writeLengthPrefixedString(label());
498  if (!m_extendedData.empty()) {
499  writer.writeUInt16BE(m_extendedData.size());
500  writer.writeString(m_extendedData);
501  }
502  writer.writeUInt32BE(m_fields.size());
503  for (const Field &field : m_fields) {
504  field.make(stream);
505  }
506 }
507 
509 {
510  return new AccountEntry(*this);
511 }
512 
517 {
518  stats.accountCount += 1;
519  stats.fieldCount += fields().size();
520 }
521 } // namespace Io
Io::NodeEntry
The NodeEntry class acts as parent for other entries.
Definition: entry.h:114
Io::EntryStatistics::fieldCount
std::size_t fieldCount
Definition: entry.h:25
Io::ParsingException
The exception that is thrown when a parsing error occurs.
Definition: parsingexception.h:11
Io::NodeEntry::deleteChildren
void deleteChildren(int begin, int end)
Deletes children from the node entry.
Definition: entry.cpp:290
Io::NodeEntry::entryByPath
Entry * entryByPath(std::list< std::string > &path, bool includeThis=true, const EntryType *creationType=nullptr)
Returns an entry specified by the provided path.
Definition: entry.cpp:350
Io::Entry::parent
NodeEntry * parent() const
Returns the parent entry.
Definition: entry.h:90
Io::Entry::isIndirectChildOf
bool isIndirectChildOf(const NodeEntry *entry) const
Returns an indication whether the instance is an indirect child of the specified entry.
Definition: entry.cpp:139
Io::EntryStatistics::nodeCount
std::size_t nodeCount
Definition: entry.h:23
parsingexception.h
Io::NodeEntry::children
const std::vector< Entry * > & children() const
Definition: entry.h:145
Io::AccountEntry::~AccountEntry
~AccountEntry() override
Destroys the entry.
Definition: entry.cpp:489
Io::NodeEntry::isExpandedByDefault
bool isExpandedByDefault() const
Definition: entry.h:150
Io::Entry::clone
virtual Entry * clone() const =0
Clones the entry.
Io::Entry::label
const std::string & label() const
Returns the label.
Definition: entry.h:70
Io::Entry
Instances of the Entry class form a hierarchic data strucutre used to store account information.
Definition: entry.h:30
Io::AccountEntry::clone
AccountEntry * clone() const override
Clones the entry.
Definition: entry.cpp:508
Io::AccountEntry::fields
const std::vector< Field > & fields() const
Definition: entry.h:194
Io::Entry::NodeEntry
friend class NodeEntry
Definition: entry.h:31
Io::EntryType::Node
entry.h
Io::NodeEntry::accumulateStatistics
void accumulateStatistics(EntryStatistics &stats) const override
Accumulates the statistics for this node entry and its children.
Definition: entry.cpp:423
Io::Entry::parse
static Entry * parse(std::istream &stream)
Parses an entry from the specified stream.
Definition: entry.cpp:176
Io::Entry::index
int index() const
Returns the index of the entry within its parent.
Definition: entry.h:98
Io::Entry::~Entry
virtual ~Entry()
Destroys the entry.
Definition: entry.cpp:55
Io::Entry::Entry
Entry(const std::string &label=std::string(), NodeEntry *parent=nullptr)
Constructs a new entry with the specified label and parent.
Definition: entry.cpp:32
Io
Contains all IO related classes.
Definition: cryptoexception.h:9
Io::EntryStatistics
Definition: entry.h:22
Io::Entry::path
std::list< std::string > path() const
Returns the path of the entry.
Definition: entry.cpp:154
CppUtilities
Definition: utils.h:12
Io::NodeEntry::replaceChild
void replaceChild(std::size_t at, Entry *newChild)
Replaces the child at the specified index with the specified newChild.
Definition: entry.cpp:317
Io::Entry::m_extendedData
std::string m_extendedData
Definition: entry.h:64
Io::Field
The Field class holds field information which consists of a name and a value and is able to serialize...
Definition: field.h:15
Io::NodeEntry::make
void make(std::ostream &stream) const override
Serializes the entry to the specified stream.
Definition: entry.cpp:395
Io::Entry::makeLabelUnique
void makeLabelUnique()
Internally called to make the entry's label unique within the parent.
Definition: entry.cpp:64
Io::AccountEntry::AccountEntry
AccountEntry()
Definition: entry.cpp:436
Io::Entry::setParent
void setParent(NodeEntry *parent, int index=-1)
Sets the parent for the entry.
Definition: entry.cpp:99
Io::Entry::denotesNodeEntry
static bool denotesNodeEntry(std::uint8_t version)
Definition: entry.h:160
Io::AccountEntry::make
void make(std::ostream &stream) const override
Serializes the entry to the specified stream.
Definition: entry.cpp:493
Io::NodeEntry::NodeEntry
NodeEntry()
Constructs a new node entry.
Definition: entry.cpp:211
Io::NodeEntry::clone
NodeEntry * clone() const override
Clones the entry.
Definition: entry.cpp:415
Io::NodeEntry::~NodeEntry
~NodeEntry() override
Destroys the entry.
Definition: entry.cpp:276
Io::EntryType
EntryType
Specifies the entry type.
Definition: entry.h:17
Io::AccountEntry
The exception that is thrown when a parsing error occurs.
Definition: entry.h:170
Io::EntryType::Account
Io::Entry::setLabel
void setLabel(const std::string &label)
Sets the label.
Definition: entry.h:80
Io::EntryStatistics::accountCount
std::size_t accountCount
Definition: entry.h:24
Io::AccountEntry::accumulateStatistics
void accumulateStatistics(EntryStatistics &stats) const override
Accumulates the statistics for this account entry and its fields.
Definition: entry.cpp:516