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