aboutsummaryrefslogtreecommitdiff
path: root/src/saved_auth.cpp
blob: aa1c69676b01a85229d1e510181555c789f468ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "saved_auth.h"

#include <QDir>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>

#include "file_functions.h"

// I wish I could use INI for settings to let users edit it manually if they
// wanted to, like ~/.ssh/config. Unfortunately, due to adherence to idiotic
// conventions, INI keys are case-insensitive on Windows, while the usernames
// aren't. Rather than defining my own stupid encoding to work around this
// issue (thus ruining convenience I originally strived for), I'll ditch INI
// altogether. I could use CBOR for consistency, but I'm using JSON because
// it's supposed to be manually editable, and I don't want to complicate
// things by creating a new settings format.
bool SavedAuth::load()
{
  QFile saved_auth_file(QDir(get_app_path()).filePath("saved_auth.json"));
  if (!saved_auth_file.open(QIODevice::ReadOnly))
  {
    return false;
  }
  QByteArray raw_data = saved_auth_file.readAll();
  saved_auth_file.close();

  QJsonParseError err;
  QJsonDocument doc = QJsonDocument::fromJson(raw_data, &err);
  if (err.error != QJsonParseError::NoError || !doc.isObject())
  {
    return false;
  }

  m_table.clear();
  QJsonObject root = doc.object();
  for (auto host_it = root.begin(); host_it != root.end(); ++host_it)
  {
    if (!host_it.value().isObject())
    {
      continue;
    }

    QByteArray host = host_it.key().toUtf8();
    QJsonObject users = host_it.value().toObject();

    for (auto user_it = users.begin(); user_it != users.end(); ++user_it)
    {
      if (!user_it.value().isString())
      {
        continue;
      }

      QByteArray user = user_it.key().toUtf8();
      QByteArray saved_key = QByteArray::fromBase64(user_it.value().toString().toUtf8());

      m_table[host + '\0' + user] = saved_key;
    }
  }

  return true;
}

QByteArray SavedAuth::lookup(QByteArrayView host, QByteArrayView user) const
{
  return m_table.value(flatten_key(host, user));
}

void SavedAuth::insert(QByteArrayView host, QByteArrayView user, QByteArrayView saved_key)
{
  m_table[flatten_key(host, user)] = QByteArray(saved_key);
  save();
}

void SavedAuth::remove(QByteArrayView host, QByteArrayView user)
{
  m_table.remove(flatten_key(host, user));
  save();
}

bool SavedAuth::save() const
{
  QJsonObject root;

  for (auto it = m_table.begin(); it != m_table.end(); ++it)
  {
    const QByteArray &flat = it.key();
    const QByteArray &key_id = it.value();

    int sep = flat.indexOf('\0');
    if (sep < 0)
    {
      continue;
    }

    QString host = QString::fromUtf8(flat.left(sep));
    QString user = QString::fromUtf8(flat.mid(sep + 1));

    QJsonObject host_obj = root.value(host).toObject();

    host_obj[user] = QString::fromUtf8(key_id.toBase64(QByteArray::OmitTrailingEquals));

    root[host] = host_obj;
  }

  QJsonDocument doc(root);

  QFile f(QDir(get_app_path()).filePath("saved_auth.json"));
  if (!f.open(QIODevice::WriteOnly))
    return false;

  f.write(doc.toJson(QJsonDocument::Indented));
  return true;
}

QByteArray SavedAuth::flatten_key(QByteArrayView host, QByteArrayView user) const
{
  QByteArray key;
  key.reserve(host.size() + 1 + user.size());
  key.append(host);
  key.append('\0');
  key.append(user);
  return key;
}