Passwordfile library  3.2.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 
142 {
143  if (!parent()) {
144  return false;
145  }
146  if (parent() == entry) {
147  return true;
148  } else {
149  return parent()->isIndirectChildOf(entry);
150  }
151 }
152 
156 std::list<string> Entry::path() const
157 {
158  list<string> res;
159  path(res);
160  return res;
161 }
162 
166 void Entry::path(std::list<string> &res) const
167 {
168  if (m_parent) {
169  m_parent->path(res);
170  }
171  res.push_back(label());
172 }
173 
178 Entry *Entry::parse(istream &stream)
179 {
180  const auto version = static_cast<byte>(stream.peek());
181  if (denotesNodeEntry(version)) {
182  return new NodeEntry(stream);
183  } else {
184  return new AccountEntry(stream);
185  }
186 }
187 
214  : Entry()
215  , m_expandedByDefault(true)
216 {
217 }
218 
222 NodeEntry::NodeEntry(const string &label, NodeEntry *parent)
223  : Entry(label, parent)
224  , m_expandedByDefault(true)
225 {
226 }
227 
231 NodeEntry::NodeEntry(istream &stream)
232  : m_expandedByDefault(true)
233 {
234  BinaryReader reader(&stream);
235  const byte version = reader.readByte();
236  if (!denotesNodeEntry(version)) {
237  throw ParsingException("Node entry expected.");
238  }
239  if (version != 0x0 && version != 0x1) {
240  throw ParsingException("Entry version not supported.");
241  }
242  setLabel(reader.readLengthPrefixedString());
243  // read extended header for version 0x1
244  if (version == 0x1) {
245  uint16 extendedHeaderSize = reader.readUInt16BE();
246  if (extendedHeaderSize >= 1) {
247  byte flags = reader.readByte();
248  m_expandedByDefault = flags & 0x80;
249  extendedHeaderSize -= 1;
250  }
251  m_extendedData = reader.readString(extendedHeaderSize);
252  }
253  const uint32 childCount = reader.readUInt32BE();
254  for (uint32 i = 0; i != childCount; ++i) {
255  Entry::parse(stream)->setParent(this);
256  }
257 }
258 
265  : Entry(other)
266 {
267  for (Entry *const otherChild : other.m_children) {
268  Entry *clonedChild = otherChild->clone();
269  clonedChild->m_parent = this;
270  clonedChild->m_index = m_children.size();
271  m_children.push_back(clonedChild);
272  }
273 }
274 
279 {
280  for (Entry *const child : m_children) {
281  child->m_parent = nullptr;
282  delete child;
283  }
284 }
285 
292 void NodeEntry::deleteChildren(int begin, int end)
293 {
294  const auto endIterator = m_children.begin() + end;
295 
296  // delete the children
297  for (auto iterator = m_children.cbegin() + begin; iterator != endIterator; ++iterator) {
298  (*iterator)->m_parent = nullptr;
299  delete *iterator;
300  }
301 
302  // remove the children from the list
303  m_children.erase(m_children.begin() + begin, endIterator);
304 
305  // adjust indices of subsequent children
306  const int diff = end - begin;
307  for (auto iterator = m_children.begin() + begin, end = m_children.end(); iterator != end; ++iterator) {
308  (*iterator)->m_index -= diff;
309  }
310 }
311 
319 void NodeEntry::replaceChild(size_t at, Entry *newChild)
320 {
321  if (at >= m_children.size()) {
322  return;
323  }
324 
325  // detatch the old child
326  m_children[at]->m_parent = nullptr;
327  m_children[at]->m_index = -1;
328 
329  // detach new child from its previous parent
330  if (auto *newChildOldParent = newChild->m_parent) {
331  newChildOldParent->m_children.erase(newChildOldParent->m_children.begin() + newChild->m_index);
332  for (auto i = newChildOldParent->m_children.begin() + newChild->m_index; i < newChildOldParent->m_children.end(); ++i) {
333  (*i)->m_index -= 1;
334  }
335  }
336 
337  // do the actual assignment
338  newChild->m_parent = this;
339  newChild->m_index = at;
340  m_children[at] = newChild;
341 }
342 
353 Entry *NodeEntry::entryByPath(list<string> &path, bool includeThis, EntryType *creationType)
354 {
355  if (path.empty()) {
356  return nullptr;
357  }
358 
359  // check for current instance
360  if (includeThis) {
361  if (path.front() == label()) {
362  path.pop_front();
363  } else {
364  return nullptr;
365  }
366  }
367  if (path.empty()) {
368  return this;
369  }
370 
371  for (Entry *const child : m_children) {
372  if (path.front() != child->label()) {
373  continue;
374  }
375  path.pop_front();
376  if (path.empty()) {
377  return child;
378  } else if (child->type() == EntryType::Node) {
379  return static_cast<NodeEntry *>(child)->entryByPath(path, false, creationType);
380  } else {
381  return nullptr; // can not resolve path since an account entry can not have children
382  }
383  }
384 
385  // create a new entry
386  if (!creationType || path.size() != 1) {
387  return nullptr;
388  }
389  switch (*creationType) {
390  case EntryType::Account:
391  return new AccountEntry(path.front(), this);
392  case EntryType::Node:
393  return new NodeEntry(path.front(), this);
394  }
395  return nullptr;
396 }
397 
398 void NodeEntry::make(ostream &stream) const
399 {
400  BinaryWriter writer(&stream);
401  writer.writeByte(isExpandedByDefault() && m_extendedData.empty() ? 0x0 : 0x1); // version
402  writer.writeLengthPrefixedString(label());
403  if (!isExpandedByDefault() || !m_extendedData.empty()) {
404  writer.writeUInt16BE(1 + m_extendedData.size()); // extended header is 1 byte long
405  byte flags = 0x00;
406  if (isExpandedByDefault()) {
407  flags |= 0x80;
408  }
409  writer.writeByte(flags);
410  writer.writeString(m_extendedData);
411  }
412  writer.writeUInt32BE(m_children.size());
413  for (const Entry *const child : m_children) {
414  child->make(stream);
415  }
416 }
417 
419 {
420  return new NodeEntry(*this);
421 }
422 
429 {
430 }
431 
435 AccountEntry::AccountEntry(const string &label, NodeEntry *parent)
436  : Entry(label, parent)
437 {
438 }
439 
444 {
445  BinaryReader reader(&stream);
446  byte version = reader.readByte();
447  if (denotesNodeEntry(version)) {
448  throw ParsingException("Account entry expected.");
449  }
450  version ^= 0x80; // set first bit to zero
451  if (version != 0x0 && version != 0x1) {
452  throw ParsingException("Entry version not supported.");
453  }
454  setLabel(reader.readLengthPrefixedString());
455  // read extended header for version 0x1
456  if (version == 0x1) {
457  const uint16 extendedHeaderSize = reader.readUInt16BE();
458  // currently there's nothing to read here
459  m_extendedData = reader.readString(extendedHeaderSize);
460  }
461  const uint32 fieldCount = reader.readUInt32BE();
462  for (uint32 i = 0; i != fieldCount; ++i) {
463  m_fields.push_back(Field(this, stream));
464  }
465 }
466 
473  : Entry(other)
474 {
475  m_fields = other.m_fields;
476 }
477 
482 {
483 }
484 
485 void AccountEntry::make(ostream &stream) const
486 {
487  BinaryWriter writer(&stream);
488  writer.writeByte(0x80 | (m_extendedData.empty() ? 0x0 : 0x1)); // version
489  writer.writeLengthPrefixedString(label());
490  if (!m_extendedData.empty()) {
491  writer.writeUInt16BE(m_extendedData.size());
492  writer.writeString(m_extendedData);
493  }
494  writer.writeUInt32BE(m_fields.size());
495  for (const Field &field : m_fields) {
496  field.make(stream);
497  }
498 }
499 
501 {
502  return new AccountEntry(*this);
503 }
504 } // namespace Io
~NodeEntry()
Destroys the entry.
Definition: entry.cpp:278
NodeEntry * parent() const
Returns the parent entry.
Definition: entry.h:83
The NodeEntry class acts as parent for other entries.
Definition: entry.h:96
virtual void make(std::ostream &stream) const
Serializes the entry to the specified stream.
Definition: entry.cpp:398
const std::string & label() const
Returns the label.
Definition: entry.h:63
void setLabel(const std::string &label)
Sets the label.
Definition: entry.h:73
~AccountEntry()
Destroys the entry.
Definition: entry.cpp:481
bool isIndirectChildOf(NodeEntry *entry) const
Returns an indication whether the instance is an indirect child of the specified entry.
Definition: entry.cpp:141
NodeEntry()
Constructs a new node entry.
Definition: entry.cpp:213
STL namespace.
virtual Entry * clone() const =0
Clones the entry.
bool isExpandedByDefault() const
Definition: entry.h:131
friend class NodeEntry
Definition: entry.h:26
Contains all IO related classes.
int index() const
Returns the index of the entry within its parent.
Definition: entry.h:91
Entry * entryByPath(std::list< std::string > &path, bool includeThis=true, EntryType *creationType=nullptr)
Returns an entry specified by the provided path.
Definition: entry.cpp:353
virtual NodeEntry * clone() const
Clones the entry.
Definition: entry.cpp:418
std::list< std::string > path() const
Returns the path of the entry.
Definition: entry.cpp:156
std::string m_extendedData
Definition: entry.h:57
The Field class holds field information which consists of a name and a value and is able to serialize...
Definition: field.h:15
The exception that is thrown when a parsing error occurs.
Definition: entry.h:151
Entry(const std::string &label=std::string(), NodeEntry *parent=nullptr)
Constructs a new entry with the specified label and parent.
Definition: entry.cpp:33
virtual void make(std::ostream &stream) const
Serializes the entry to the specified stream.
Definition: entry.cpp:485
static Entry * parse(std::istream &stream)
Parses an entry from the specified stream.
Definition: entry.cpp:178
virtual AccountEntry * clone() const
Clones the entry.
Definition: entry.cpp:500
virtual ~Entry()
Destroys the entry.
Definition: entry.cpp:56
static bool denotesNodeEntry(byte version)
Definition: entry.h:141
const std::vector< Entry * > & children() const
Definition: entry.h:126
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
void deleteChildren(int begin, int end)
Deletes children from the node entry.
Definition: entry.cpp:292
void replaceChild(std::size_t at, Entry *newChild)
Replaces the child at the specified index with the specified newChild.
Definition: entry.cpp:319
EntryType
Specifies the entry type.
Definition: entry.h:18
The exception that is thrown when a parsing error occurs.
Instances of the Entry class form a hierarchic data strucutre used to store account information...
Definition: entry.h:25