passwordscfg.cpp
1 //------------------------------------------------------------------------------
2 // passwordscfg.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2013 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "passwordscfg.h"
24 
25 #include "configuration/doomseekerconfig.h"
26 #include "configuration/serverpassword.h"
27 #include "configuration/serverpasswordsummary.h"
28 #include "ini/ini.h"
29 #include "ini/inisection.h"
30 #include "ini/inivariable.h"
31 #include "ini/settingsproviderqt.h"
32 #include "serverapi/server.h"
33 #include "serverapi/serversummary.h"
34 #include <cassert>
35 #include <QDebug>
36 
37 const QString SECTION_NAME = "Passwords";
38 
39 const QString MAX_NUMBER_OF_SERVERS_PER_PASSWORD_KEY = "MaxNumberOfServersPerPassword";
40 const QString REMEMBER_CONNECT_PASSWORD = "RememberConnectPassword";
41 const QString SERVER_PASSWORDS_KEY = "ServerPasswords";
42 
43 // TODO: [Zalewa] We have 3 different ini files, all instantiated
44 // in Main and kept in different places. Perhaps we should move them
45 // out to a separate singleton? If not, then perhaps we could at least
46 // move the instantiation out of Main.
47 Ini *PasswordsCfg::ini = nullptr;
48 QSettings *PasswordsCfg::settings = nullptr;
49 
50 
51 DClass<PasswordsCfg>
52 {
53 public:
54  IniSection section;
55 };
56 
57 DPointered(PasswordsCfg)
58 
60 {
61  assert(ini != nullptr && "instantiated PasswordsCfg() without initing ini");
62  d->section = ini->section(SECTION_NAME);
63 }
64 
65 PasswordsCfg::~PasswordsCfg()
66 {
67 }
68 
69 void PasswordsCfg::cutServers(QList<ServerPassword> &passwords) const
70 {
71  QMutableListIterator<ServerPassword> it(passwords);
72  while (it.hasNext())
73  {
74  ServerPassword &password = it.next();
75  QList<ServerPasswordSummary> sortedServers = password.servers();
76  std::sort(sortedServers.begin(), sortedServers.end(), serverDateDescending);
77  password.setServers(sortedServers.mid(0, maxNumberOfServersPerPassword()));
78  }
79 }
80 
81 void PasswordsCfg::cutStoredServers()
82 {
83  QList<ServerPassword> passwords = serverPasswords();
84  cutServers(passwords);
85  storeServerPasswords(passwords);
86 }
87 
88 void PasswordsCfg::initIni(const QString &path)
89 {
90  assert(ini == nullptr && "tried to re-init password ini");
91  if (ini != nullptr)
92  {
93  qDebug() << "Error: tried to re-init password ini";
94  return;
95  }
96  settings = new QSettings(path, QSettings::IniFormat);
97  // SettingsProviderQt won't be deleted. Not really a memory leak, as
98  // this is expected to remain memory for whole program runtime.
99  ini = new Ini(new SettingsProviderQt(settings));
100 }
101 
102 bool PasswordsCfg::isHidingPasswords() const
103 {
104  return gConfig.doomseeker.bHidePasswords;
105 }
106 
107 bool PasswordsCfg::isRememberingConnectPhrase() const
108 {
109  return d->section.value(REMEMBER_CONNECT_PASSWORD, false).toBool();
110 }
111 
112 int PasswordsCfg::maxNumberOfServersPerPassword() const
113 {
114  return d->section.value(MAX_NUMBER_OF_SERVERS_PER_PASSWORD_KEY, 5).toInt();
115 }
116 
117 void PasswordsCfg::removeServerPhrase(const QString &phrase)
118 {
119  QList<ServerPassword> allPasswords = serverPasswords();
120  QMutableListIterator<ServerPassword> it(allPasswords);
121  while (it.hasNext())
122  {
123  ServerPassword existingPass = it.next();
124  if (existingPass.phrase() == phrase)
125  it.remove();
126  }
127  storeServerPasswords(allPasswords);
128 }
129 
130 void PasswordsCfg::saveServerPhrase(const QString &phrase, const Server *server,
131  const QString &type)
132 {
133  if (phrase.isEmpty())
134  return;
135 
136  ServerPasswordSummary serverInfo;
137  if (server != nullptr)
138  {
139  ServerSummary serverSummary(server);
140  serverSummary.setTime(QDateTime::currentDateTime());
141  serverInfo.setServerSummary(serverSummary);
142  }
143  serverInfo.setType(type);
144  QList<ServerPassword> allPasswords = serverPasswords();
145  QMutableListIterator<ServerPassword> it(allPasswords);
146  while (it.hasNext())
147  {
148  // Try to add server to existing password.
149  ServerPassword &existingPass = it.next();
150  if (existingPass.phrase() == phrase)
151  {
152  if (serverInfo.isValid())
153  existingPass.addServer(serverInfo);
154  setServerPasswords(allPasswords);
155  return;
156  }
157  }
158  // Add new.
159  ServerPassword pass;
160  pass.setPhrase(phrase);
161  pass.addServer(serverInfo);
162  allPasswords << pass;
163  storeServerPasswords(allPasswords);
164 }
165 
166 bool PasswordsCfg::serverDateDescending(ServerPasswordSummary &s1, ServerPasswordSummary &s2)
167 {
168  return s1.time() > s2.time();
169 }
170 
171 void PasswordsCfg::setHidePasswords(bool val)
172 {
173  gConfig.doomseeker.bHidePasswords = val;
174 }
175 
176 QList<ServerPassword> PasswordsCfg::serverPasswords() const
177 {
178  QList<ServerPassword> result;
179  QVariantList vars = d->section[SERVER_PASSWORDS_KEY].value().toList();
180  for (const QVariant &var : vars)
181  {
182  result << ServerPassword::deserializeQVariant(var);
183  }
184  return result;
185 }
186 
187 QStringList PasswordsCfg::serverPhrases() const
188 {
189  QStringList result;
190  for (const ServerPassword &pass : serverPasswords())
191  {
192  result << pass.phrase();
193  }
194  return result;
195 }
196 
197 void PasswordsCfg::setMaxNumberOfServersPerPassword(int val)
198 {
199  bool shallCut = val < maxNumberOfServersPerPassword();
200  d->section.setValue(MAX_NUMBER_OF_SERVERS_PER_PASSWORD_KEY, val);
201  if (shallCut)
202  cutStoredServers();
203 }
204 
205 void PasswordsCfg::setRememberConnectPhrase(bool val)
206 {
207  return d->section.setValue(REMEMBER_CONNECT_PASSWORD, val);
208 }
209 
210 void PasswordsCfg::setServerPasswords(const QList<ServerPassword> &val)
211 {
212  QList<ServerPassword> passwords = val;
213  cutServers(passwords);
214  storeServerPasswords(passwords);
215 }
216 
217 void PasswordsCfg::storeServerPasswords(const QList<ServerPassword> &val)
218 {
219  QVariantList vars;
220  for (const ServerPassword obj : val)
221  {
222  vars << obj.serializeQVariant();
223  }
224  d->section.setValue(SERVER_PASSWORDS_KEY, vars);
225 }
226 
227 ServerPassword PasswordsCfg::suggestPassword(const Server *server, const QString &type)
228 {
229  // This method would probably work better as a separate class.
230  // If there's ever any need to expand it, extract it
231  // to a PasswordSuggester or something like that first.
232  ServerPasswordSummary serverSummary(server, type);
233 
234  ServerPassword password;
235  ServerPasswordSummary bestFit;
236  for (const ServerPassword &potentialPassword : serverPasswords())
237  {
238  float newSimilarity;
239  ServerPasswordSummary candidate = potentialPassword.mostSimilarServer(serverSummary, &newSimilarity);
240  if (candidate.isValid())
241  {
242  if (newSimilarity > bestFit.similarity(serverSummary))
243  {
244  bestFit = candidate;
245  password = potentialPassword;
246  }
247  else if (qFuzzyCompare(newSimilarity, bestFit.similarity(serverSummary))
248  && candidate.time() > bestFit.time())
249  {
250  bestFit = candidate;
251  password = potentialPassword;
252  }
253  }
254  }
255  return password;
256 }