MythTV  master
parentalcontrols.cpp
Go to the documentation of this file.
1 #include <map>
2 
3 #include "mythdate.h"
4 #include "mythcontext.h"
5 #include "mythmainwindow.h"
6 #include "mythscreenstack.h"
7 #include "mythdialogbox.h"
8 
9 #include "parentalcontrols.h"
10 
11 namespace
12 {
13  ParentalLevel::Level boundedParentalLevel(ParentalLevel::Level pl)
14  {
15  if (pl < ParentalLevel::plNone)
16  return ParentalLevel::plNone;
17  if (pl > ParentalLevel::plHigh)
18  return ParentalLevel::plHigh;
19 
20  return pl;
21  }
22 
23  ParentalLevel::Level nextParentalLevel(ParentalLevel::Level cpl)
24  {
25  ParentalLevel::Level rpl(cpl);
26  switch (cpl)
27  {
29  rpl = ParentalLevel::plLowest; break;
31  rpl = ParentalLevel::plLow; break;
33  rpl = ParentalLevel::plMedium; break;
36  rpl = ParentalLevel::plHigh; break;
37  }
38 
39  return boundedParentalLevel(rpl);
40  }
41 
42  ParentalLevel::Level prevParentalLevel(ParentalLevel::Level cpl)
43  {
44  ParentalLevel::Level rpl(cpl);
45  switch (cpl)
46  {
48  rpl = ParentalLevel::plNone; break;
51  rpl = ParentalLevel::plLowest; break;
53  rpl = ParentalLevel::plLow; break;
55  rpl = ParentalLevel::plMedium; break;
56  }
57 
58  return boundedParentalLevel(rpl);
59  }
60 
61  ParentalLevel::Level toParentalLevel(int pl)
62  {
63  return boundedParentalLevel(static_cast<ParentalLevel::Level>(pl));
64  }
65 }
66 
68 {
69 }
70 
72 {
73  m_level = toParentalLevel(pl);
74 }
75 
77 {
78  *this = rhs;
79 }
80 
82 {
83  if (&rhs != this)
84  {
85  m_level = rhs.m_level;
86  m_hitlimit = rhs.m_hitlimit;
87  }
88 
89  return *this;
90 }
91 
93 {
94  m_level = boundedParentalLevel(pl);
95  return *this;
96 }
97 
99 {
100  Level last = m_level;
101  m_level = nextParentalLevel(m_level);
102  if (m_level == last)
103  m_hitlimit = true;
104  return *this;
105 }
106 
108 {
109  m_level = toParentalLevel(m_level + amount);
110  return *this;
111 }
112 
114 {
115  Level prev = m_level;
116  m_level = prevParentalLevel(m_level);
117  if (m_level == prev)
118  m_hitlimit = true;
119  return *this;
120 }
121 
123 {
124  m_level = toParentalLevel(m_level - amount);
125  return *this;
126 }
127 
129 {
130  return m_level;
131 }
132 
133 bool operator!=(const ParentalLevel &lhs, const ParentalLevel &rhs)
134 {
135  return lhs.GetLevel() != rhs.GetLevel();
136 }
137 
138 bool operator==(const ParentalLevel &lhs, const ParentalLevel &rhs)
139 {
140  return lhs.GetLevel() == rhs.GetLevel();
141 }
142 
143 bool operator<(const ParentalLevel &lhs, const ParentalLevel &rhs)
144 {
145  return lhs.GetLevel() < rhs.GetLevel();
146 }
147 
148 bool operator>(const ParentalLevel &lhs, const ParentalLevel &rhs)
149 {
150  return lhs.GetLevel() > rhs.GetLevel();
151 }
152 
153 bool operator<=(const ParentalLevel &lhs, const ParentalLevel &rhs)
154 {
155  return lhs.GetLevel() <= rhs.GetLevel();
156 }
157 
158 bool operator>=(const ParentalLevel &lhs, const ParentalLevel &rhs)
159 {
160  return lhs.GetLevel() >= rhs.GetLevel();
161 }
162 
163 namespace
164 {
165  class PasswordManager
166  {
167  private:
168  using pws = std::map<ParentalLevel::Level, QString>;
169 
170  public:
171  void Add(ParentalLevel::Level level, const QString &password)
172  {
173  m_passwords.insert(pws::value_type(level, password));
174  }
175 
176  QStringList AtOrAbove(ParentalLevel::Level level)
177  {
178  QStringList ret;
179  for (ParentalLevel i = level;
180  i <= ParentalLevel::plHigh && i.good(); ++i)
181  {
182  pws::const_iterator p = m_passwords.find(i.GetLevel());
183  if (p != m_passwords.end() && !p->second.isEmpty())
184  ret.push_back(p->second);
185  }
186 
187  return ret;
188  }
189 
190  QString FirstAtOrBelow(ParentalLevel::Level level)
191  {
192  QString ret;
193  for (ParentalLevel i = level;
194  i >= ParentalLevel::plLow && i.good(); --i)
195  {
196  pws::const_iterator p = m_passwords.find(i.GetLevel());
197  if (p != m_passwords.end() && !p->second.isEmpty())
198  {
199  ret = p->second;
200  break;
201  }
202  }
203 
204  return ret;
205  }
206 
207  private:
208  pws m_passwords;
209  };
210 }
211 
212 class ParentalLevelChangeCheckerPrivate : public QObject
213 {
214  Q_OBJECT
215 
216  public:
217  explicit ParentalLevelChangeCheckerPrivate(QObject *lparent) : QObject(lparent)
218  {
220  gCoreContext->GetSetting("VideoAdminPassword"));
222  gCoreContext->GetSetting("VideoAdminPasswordThree"));
224  gCoreContext->GetSetting("VideoAdminPasswordTwo"));
225 
226  m_passwordOK = false;
229  }
230 
232  {
233  m_fromLevel = fromLevel;
234  m_toLevel = toLevel;
235  if (DoCheck())
236  {
237  emit SigDone(true, toLevel);
238  }
239  }
240 
241  signals:
242  // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
243  void SigDone(bool passwordValid, ParentalLevel::Level toLevel);
244 
245  private:
246  // returns true if no completion is required
247  bool DoCheck()
248  {
249  ParentalLevel which_level(m_toLevel);
250 
251  // No password for level 1 and you can always switch down from your
252  // current level.
253  if (which_level == ParentalLevel::plLowest ||
254  which_level <= ParentalLevel(m_fromLevel))
255  return true;
256 
257  // If there isn't a password at the current level, and
258  // none of the levels below, we are done.
259  // The assumption is that if you password protected lower levels,
260  // and a higher level does not have a password it is something
261  // you've overlooked (rather than intended).
262  if (m_pm.FirstAtOrBelow(which_level.GetLevel()).isEmpty())
263  return true;
264 
265  // See if we recently (and successfully) asked for a password
266  QString last_time_stamp = gCoreContext->GetSetting("VideoPasswordTime");
267  int last_parent_lvl = gCoreContext->GetNumSetting("VideoPasswordLevel",
268  -1);
269 
270  if (!last_time_stamp.length() || last_parent_lvl == -1)
271  {
272  LOG(VB_GENERAL, LOG_ERR,
273  QString("%1: Could not read password/pin time "
274  "stamp. This is only an issue if it "
275  "happens repeatedly.").arg(__FILE__));
276  }
277  else
278  {
279  QDateTime curr_time = MythDate::current();
280  QDateTime last_time = MythDate::fromString(last_time_stamp);
281 
282  if (ParentalLevel(last_parent_lvl) >= which_level &&
283  last_time.secsTo(curr_time) < 120)
284  {
285  // Two minute window
286  last_time_stamp = curr_time.toString(Qt::ISODate);
287  gCoreContext->SaveSetting("VideoPasswordTime", last_time_stamp);
288  return true;
289  }
290  }
291 
292  m_validPasswords = m_pm.AtOrAbove(which_level.GetLevel());
293 
294  // If there isn't a password for this level or higher levels, treat
295  // the next lower password as valid. This is only done so people
296  // cannot lock themselves out of the setup.
297  if (m_validPasswords.empty())
298  {
299  QString pw = m_pm.FirstAtOrBelow(which_level.GetLevel());
300  if (pw.length())
301  m_validPasswords.push_back(pw);
302  }
303 
304  // There are no suitable passwords.
305  if (m_validPasswords.empty())
306  return true;
307 
308  // If we got here, there is a password, and there's no backing down.
309  m_passwordOK = false;
310  MythScreenStack *popupStack =
311  GetMythMainWindow()->GetStack("popup stack");
312 
313  auto *pwd = new MythTextInputDialog(popupStack,
314  tr("Parental PIN:"), FilterNone, true);
315 
316  connect(pwd, SIGNAL(haveResult(QString)),
317  SLOT(OnPasswordEntered(QString)));
318  connect(pwd, SIGNAL(Exiting()), SLOT(OnPasswordExit()));
319 
320  if (pwd->Create())
321  popupStack->AddScreen(pwd, false);
322 
323  return false;
324  }
325 
326  private slots:
327  void OnPasswordEntered(const QString& password)
328  {
329  m_passwordOK = false;
330 
331  for (QStringList::iterator p = m_validPasswords.begin();
332  p != m_validPasswords.end(); ++p)
333  {
334  if (password == *p)
335  {
336  m_passwordOK = true;
337  QString time_stamp = MythDate::current_iso_string();
338 
339  gCoreContext->SaveSetting("VideoPasswordTime", time_stamp);
340  gCoreContext->SaveSetting("VideoPasswordLevel", m_toLevel);
341 
342  break;
343  }
344  }
345  }
346 
348  {
350  }
351 
352  private:
356  PasswordManager m_pm;
357  QStringList m_validPasswords;
358 };
359 
361 {
363  connect(m_private, SIGNAL(SigDone(bool, ParentalLevel::Level)),
364  SLOT(OnResultReady(bool, ParentalLevel::Level)));
365 }
366 
368  ParentalLevel::Level toLevel)
369 {
370  m_private->Check(fromLevel, toLevel);
371 }
372 
374  ParentalLevel::Level newLevel)
375 {
376  emit SigResultReady(passwordValid, newLevel);
377 }
378 
379 #include "parentalcontrols.moc"
Level GetLevel() const
bool operator==(const ParentalLevel &lhs, const ParentalLevel &rhs)
bool operator>(const ParentalLevel &lhs, const ParentalLevel &rhs)
bool operator!=(const ParentalLevel &lhs, const ParentalLevel &rhs)
unsigned int slots[4]
Definition: element.c:38
void SigResultReady(bool passwordValid, ParentalLevel::Level newLevel)
void SaveSetting(const QString &key, int newValue)
ParentalLevelChangeCheckerPrivate(QObject *lparent)
QString current_iso_string(bool stripped)
Returns current Date and Time in UTC as a string.
Definition: mythdate.cpp:18
MythScreenStack * GetStack(const QString &stackname)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool operator<(const ParentalLevel &lhs, const ParentalLevel &rhs)
void Check(ParentalLevel::Level fromLevel, ParentalLevel::Level toLevel)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
bool operator<=(const ParentalLevel &lhs, const ParentalLevel &rhs)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
void SigDone(bool passwordValid, ParentalLevel::Level toLevel)
void OnResultReady(bool passwordValid, ParentalLevel::Level newLevel)
bool operator>=(const ParentalLevel &lhs, const ParentalLevel &rhs)
MythMainWindow * GetMythMainWindow(void)
int GetNumSetting(const QString &key, int defaultval=0)
ParentalLevel & operator=(const ParentalLevel &rhs)
Dialog prompting the user to enter a text string.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
ParentalLevel & operator+=(int amount)
void Check(ParentalLevel::Level fromLevel, ParentalLevel::Level toLevel)
void OnPasswordEntered(const QString &password)
class ParentalLevelChangeCheckerPrivate * m_private
Default UTC.
Definition: mythdate.h:14
ParentalLevel & operator-=(int amount)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
ParentalLevel(Level pl)
ParentalLevel & operator++()
ParentalLevel & operator--()