MythTV  0.26-pre
parentalcontrols.cpp
Go to the documentation of this file.
00001 #include <map>
00002 
00003 #include "mythcontext.h"
00004 #include "mythmainwindow.h"
00005 #include "mythscreenstack.h"
00006 #include "mythdialogbox.h"
00007 
00008 #include "parentalcontrols.h"
00009 
00010 namespace
00011 {
00012     ParentalLevel::Level boundedParentalLevel(ParentalLevel::Level pl)
00013     {
00014         if (pl < ParentalLevel::plNone)
00015             return ParentalLevel::plNone;
00016         else if (pl > ParentalLevel::plHigh)
00017             return ParentalLevel::plHigh;
00018 
00019         return pl;
00020     }
00021 
00022     ParentalLevel::Level nextParentalLevel(ParentalLevel::Level cpl)
00023     {
00024         ParentalLevel::Level rpl(cpl);
00025         switch (cpl)
00026         {
00027             case ParentalLevel::plNone:
00028             { rpl = ParentalLevel::plLowest; break; }
00029             case ParentalLevel::plLowest: { rpl = ParentalLevel::plLow; break; }
00030             case ParentalLevel::plLow: { rpl = ParentalLevel::plMedium; break; }
00031             case ParentalLevel::plMedium:
00032             { rpl = ParentalLevel::plHigh; break; }
00033             case ParentalLevel::plHigh: { rpl = ParentalLevel::plHigh; break; }
00034         }
00035 
00036         return boundedParentalLevel(rpl);
00037     }
00038 
00039     ParentalLevel::Level prevParentalLevel(ParentalLevel::Level cpl)
00040     {
00041         ParentalLevel::Level rpl(cpl);
00042         switch (cpl)
00043         {
00044             case ParentalLevel::plNone: { rpl = ParentalLevel::plNone; break; }
00045             case ParentalLevel::plLowest:
00046             { rpl = ParentalLevel::plLowest; break; }
00047             case ParentalLevel::plLow: { rpl = ParentalLevel::plLowest; break; }
00048             case ParentalLevel::plMedium: { rpl = ParentalLevel::plLow; break; }
00049             case ParentalLevel::plHigh:
00050             { rpl = ParentalLevel::plMedium; break; }
00051         }
00052 
00053         return boundedParentalLevel(rpl);
00054     }
00055 
00056     ParentalLevel::Level toParentalLevel(int pl)
00057     {
00058         return boundedParentalLevel(static_cast<ParentalLevel::Level>(pl));
00059     }
00060 }
00061 
00062 ParentalLevel::ParentalLevel(Level pl) : m_level(pl),
00063     m_hitlimit(false)
00064 {
00065 }
00066 
00067 ParentalLevel::ParentalLevel(int pl) :  m_hitlimit(false)
00068 {
00069     m_level = toParentalLevel(pl);
00070 }
00071 
00072 ParentalLevel::ParentalLevel(const ParentalLevel &rhs) : m_hitlimit(false)
00073 {
00074     *this = rhs;
00075 }
00076 
00077 ParentalLevel &ParentalLevel::operator=(const ParentalLevel &rhs)
00078 {
00079     if (&rhs != this)
00080     {
00081         m_level = rhs.m_level;
00082     }
00083 
00084     return *this;
00085 }
00086 
00087 ParentalLevel &ParentalLevel::operator=(Level pl)
00088 {
00089     m_level = boundedParentalLevel(pl);
00090     return *this;
00091 }
00092 
00093 ParentalLevel &ParentalLevel::operator++()
00094 {
00095     Level last = m_level;
00096     m_level = nextParentalLevel(m_level);
00097     if (m_level == last)
00098         m_hitlimit = true;
00099     return *this;
00100 }
00101 
00102 ParentalLevel &ParentalLevel::operator+=(int amount)
00103 {
00104     m_level = toParentalLevel(m_level + amount);
00105     return *this;
00106 }
00107 
00108 ParentalLevel &ParentalLevel::operator--()
00109 {
00110     Level prev = m_level;
00111     m_level = prevParentalLevel(m_level);
00112     if (m_level == prev)
00113         m_hitlimit = true;
00114     return *this;
00115 }
00116 
00117 ParentalLevel &ParentalLevel::operator-=(int amount)
00118 {
00119     m_level = toParentalLevel(m_level - amount);
00120     return *this;
00121 }
00122 
00123 ParentalLevel::Level ParentalLevel::GetLevel() const
00124 {
00125     return m_level;
00126 }
00127 
00128 bool operator!=(const ParentalLevel &lhs, const ParentalLevel &rhs)
00129 {
00130     return lhs.GetLevel() != rhs.GetLevel();
00131 }
00132 
00133 bool operator==(const ParentalLevel &lhs, const ParentalLevel &rhs)
00134 {
00135     return lhs.GetLevel() == rhs.GetLevel();
00136 }
00137 
00138 bool operator<(const ParentalLevel &lhs, const ParentalLevel &rhs)
00139 {
00140     return lhs.GetLevel() < rhs.GetLevel();
00141 }
00142 
00143 bool operator>(const ParentalLevel &lhs, const ParentalLevel &rhs)
00144 {
00145     return lhs.GetLevel() > rhs.GetLevel();
00146 }
00147 
00148 bool operator<=(const ParentalLevel &lhs, const ParentalLevel &rhs)
00149 {
00150     return lhs.GetLevel() <= rhs.GetLevel();
00151 }
00152 
00153 bool operator>=(const ParentalLevel &lhs, const ParentalLevel &rhs)
00154 {
00155     return lhs.GetLevel() >= rhs.GetLevel();
00156 }
00157 
00158 namespace
00159 {
00160     class PasswordManager
00161     {
00162       private:
00163         typedef std::map<ParentalLevel::Level, QString> pws;
00164 
00165       public:
00166         void Add(ParentalLevel::Level level, const QString &password)
00167         {
00168             m_passwords.insert(pws::value_type(level, password));
00169         }
00170 
00171         QStringList AtOrAbove(ParentalLevel::Level level)
00172         {
00173             QStringList ret;
00174             for (ParentalLevel i = level;
00175                     i <= ParentalLevel::plHigh && i.good(); ++i)
00176             {
00177                 pws::const_iterator p = m_passwords.find(i.GetLevel());
00178                 if (p != m_passwords.end() && p->second.length())
00179                     ret.push_back(p->second);
00180             }
00181 
00182             return ret;
00183         }
00184 
00185         QString FirstAtOrBelow(ParentalLevel::Level level)
00186         {
00187             QString ret;
00188             for (ParentalLevel i = level;
00189                     i >= ParentalLevel::plLow && i.good(); --i)
00190             {
00191                 pws::const_iterator p = m_passwords.find(i.GetLevel());
00192                 if (p != m_passwords.end() && p->second.length())
00193                 {
00194                     ret = p->second;
00195                     break;
00196                 }
00197             }
00198 
00199             return ret;
00200         }
00201 
00202       private:
00203         pws m_passwords;
00204     };
00205 }
00206 
00207 class ParentalLevelChangeCheckerPrivate : public QObject
00208 {
00209     Q_OBJECT
00210 
00211   public:
00212     ParentalLevelChangeCheckerPrivate(QObject *lparent) : QObject(lparent)
00213     {
00214         m_pm.Add(ParentalLevel::plHigh,
00215                 gCoreContext->GetSetting("VideoAdminPassword"));
00216         m_pm.Add(ParentalLevel::plMedium,
00217                 gCoreContext->GetSetting("VideoAdminPasswordThree"));
00218         m_pm.Add(ParentalLevel::plLow,
00219                 gCoreContext->GetSetting("VideoAdminPasswordTwo"));
00220 
00221         m_passwordOK = false;
00222     }
00223 
00224     void Check(ParentalLevel::Level fromLevel, ParentalLevel::Level toLevel)
00225     {
00226         m_fromLevel = fromLevel;
00227         m_toLevel = toLevel;
00228         if (DoCheck())
00229         {
00230             emit SigDone(true, toLevel);
00231         }
00232     }
00233 
00234   signals:
00235     void SigDone(bool passwordValid, ParentalLevel::Level toLevel);
00236 
00237   private:
00238     // returns true if no completion is required
00239     bool DoCheck()
00240     {
00241         ParentalLevel which_level(m_toLevel);
00242 
00243         // No password for level 1 and you can always switch down from your
00244         // current level.
00245         if (which_level == ParentalLevel::plLowest ||
00246             which_level <= ParentalLevel(m_fromLevel))
00247             return true;
00248 
00249         // If there isn't a password at the current level, and
00250         // none of the levels below, we are done.
00251         // The assumption is that if you password protected lower levels,
00252         // and a higher level does not have a password it is something
00253         // you've overlooked (rather than intended).
00254         if (!m_pm.FirstAtOrBelow(which_level.GetLevel()).length())
00255             return true;
00256 
00257         // See if we recently (and successfully) asked for a password
00258         QString last_time_stamp = gCoreContext->GetSetting("VideoPasswordTime");
00259         int last_parent_lvl = gCoreContext->GetNumSetting("VideoPasswordLevel",
00260                                                           -1);
00261 
00262         if (!last_time_stamp.length() || last_parent_lvl == -1)
00263         {
00264             LOG(VB_GENERAL, LOG_ERR,
00265                 QString("%1: Could not read password/pin time "
00266                         "stamp. This is only an issue if it "
00267                         "happens repeatedly.").arg(__FILE__));
00268         }
00269         else
00270         {
00271             QDateTime curr_time = QDateTime::currentDateTime();
00272             QDateTime last_time =
00273                     QDateTime::fromString(last_time_stamp, Qt::ISODate);
00274 
00275             if (ParentalLevel(last_parent_lvl) >= which_level &&
00276                 last_time.secsTo(curr_time) < 120)
00277             {
00278                 // Two minute window
00279                 last_time_stamp = curr_time.toString(Qt::ISODate);
00280                 gCoreContext->SaveSetting("VideoPasswordTime", last_time_stamp);
00281                 return true;
00282             }
00283         }
00284 
00285         m_validPasswords = m_pm.AtOrAbove(which_level.GetLevel());
00286 
00287         // If there isn't a password for this level or higher levels, treat
00288         // the next lower password as valid. This is only done so people
00289         // cannot lock themselves out of the setup.
00290         if (!m_validPasswords.size())
00291         {
00292             QString pw = m_pm.FirstAtOrBelow(which_level.GetLevel());
00293             if (pw.length())
00294                 m_validPasswords.push_back(pw);
00295         }
00296 
00297         // There are no suitable passwords.
00298         if (!m_validPasswords.size())
00299             return true;
00300 
00301         // If we got here, there is a password, and there's no backing down.
00302         m_passwordOK = false;
00303         MythScreenStack *popupStack =
00304                 GetMythMainWindow()->GetStack("popup stack");
00305 
00306         MythTextInputDialog *pwd =
00307                 new MythTextInputDialog(popupStack,
00308                         QObject::tr("Parental PIN:"), FilterNone, true);
00309 
00310         connect(pwd, SIGNAL(haveResult(QString)),
00311                 SLOT(OnPasswordEntered(QString)));
00312         connect(pwd, SIGNAL(Exiting()), SLOT(OnPasswordExit()));
00313 
00314         if (pwd->Create())
00315             popupStack->AddScreen(pwd, false);
00316 
00317         return false;
00318     }
00319 
00320   private slots:
00321     void OnPasswordEntered(QString password)
00322     {
00323         m_passwordOK = false;
00324 
00325         for (QStringList::iterator p = m_validPasswords.begin();
00326                 p != m_validPasswords.end(); ++p)
00327         {
00328             if (password == *p)
00329             {
00330                 m_passwordOK = true;
00331                 QString time_stamp =
00332                         QDateTime::currentDateTime().toString(Qt::ISODate);
00333 
00334                 gCoreContext->SaveSetting("VideoPasswordTime", time_stamp);
00335                 gCoreContext->SaveSetting("VideoPasswordLevel", m_toLevel);
00336 
00337                 break;
00338             }
00339         }
00340     }
00341 
00342     void OnPasswordExit()
00343     {
00344         emit SigDone(m_passwordOK, m_passwordOK ? m_toLevel : m_fromLevel);
00345     }
00346 
00347   private:
00348     bool m_passwordOK;
00349     ParentalLevel::Level m_fromLevel;
00350     ParentalLevel::Level m_toLevel;
00351     PasswordManager m_pm;
00352     QStringList m_validPasswords;
00353 };
00354 
00355 ParentalLevelChangeChecker::ParentalLevelChangeChecker()
00356 {
00357     m_private = new ParentalLevelChangeCheckerPrivate(this);
00358     connect(m_private, SIGNAL(SigDone(bool, ParentalLevel::Level)),
00359             SLOT(OnResultReady(bool, ParentalLevel::Level)));
00360 }
00361 
00362 void ParentalLevelChangeChecker::Check(ParentalLevel::Level fromLevel,
00363         ParentalLevel::Level toLevel)
00364 {
00365     m_private->Check(fromLevel, toLevel);
00366 }
00367 
00368 void ParentalLevelChangeChecker::OnResultReady(bool passwordValid,
00369         ParentalLevel::Level newLevel)
00370 {
00371     emit SigResultReady(passwordValid, newLevel);
00372 }
00373 
00374 #include "parentalcontrols.moc"
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends