1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "GUIDialogNumeric.h"
10 
11 #include "ServiceBroker.h"
12 #include "guilib/GUIComponent.h"
13 #include "guilib/GUILabelControl.h"
14 #include "guilib/GUIWindowManager.h"
15 #include "guilib/LocalizeStrings.h"
16 #include "input/Key.h"
17 #include "input/XBMC_vkeys.h"
18 #include "interfaces/AnnouncementManager.h"
19 #include "messaging/helpers/DialogOKHelper.h"
20 #include "utils/Digest.h"
21 #include "utils/StringUtils.h"
22 #include "utils/Variant.h"
23 
24 #include <cassert>
25 
26 #define CONTROL_HEADING_LABEL  1
27 #define CONTROL_INPUT_LABEL    4
28 #define CONTROL_NUM0          10
29 #define CONTROL_NUM9          19
30 #define CONTROL_PREVIOUS      20
31 #define CONTROL_ENTER         21
32 #define CONTROL_NEXT          22
33 #define CONTROL_BACKSPACE     23
34 
35 using namespace KODI::MESSAGING;
36 using KODI::UTILITY::CDigest;
37 
CGUIDialogNumeric(void)38 CGUIDialogNumeric::CGUIDialogNumeric(void)
39   : CGUIDialog(WINDOW_DIALOG_NUMERIC, "DialogNumeric.xml")
40   , m_bConfirmed{false}
41   , m_bCanceled{false}
42   , m_mode{INPUT_PASSWORD}
43   , m_block{0}
44   , m_lastblock{0}
45   , m_dirty{false}
46 {
47   memset(&m_datetime, 0, sizeof(KODI::TIME::SystemTime));
48   m_loadType = KEEP_IN_MEMORY;
49 }
50 
51 CGUIDialogNumeric::~CGUIDialogNumeric(void) = default;
52 
OnInitWindow()53 void CGUIDialogNumeric::OnInitWindow()
54 {
55   CGUIDialog::OnInitWindow();
56 
57   CVariant data;
58   switch (m_mode)
59   {
60   case INPUT_TIME:
61     data["type"] = "time";
62     break;
63   case INPUT_DATE:
64     data["type"] = "date";
65     break;
66   case INPUT_IP_ADDRESS:
67     data["type"] = "ip";
68     break;
69   case INPUT_PASSWORD:
70     data["type"] = "numericpassword";
71     break;
72   case INPUT_NUMBER:
73     data["type"] = "number";
74     break;
75   case INPUT_TIME_SECONDS:
76     data["type"] = "seconds";
77     break;
78   default:
79     data["type"] = "keyboard";
80     break;
81   }
82 
83   const CGUIControl *control = GetControl(CONTROL_HEADING_LABEL);
84   if (control != nullptr)
85     data["title"] = control->GetDescription();
86 
87   data["value"] = GetOutputString();
88 
89   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Input, "OnInputRequested", data);
90 }
91 
OnDeinitWindow(int nextWindowID)92 void CGUIDialogNumeric::OnDeinitWindow(int nextWindowID)
93 {
94   // call base class
95   CGUIDialog::OnDeinitWindow(nextWindowID);
96 
97   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Input, "OnInputFinished");
98 }
99 
OnAction(const CAction & action)100 bool CGUIDialogNumeric::OnAction(const CAction &action)
101 {
102   if (action.GetID() == ACTION_NEXT_ITEM)
103     OnNext();
104   else if (action.GetID() == ACTION_PREV_ITEM)
105     OnPrevious();
106   else if (action.GetID() == ACTION_BACKSPACE)
107     OnBackSpace();
108   else if (action.GetID() == ACTION_ENTER)
109     OnOK();
110   else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
111     OnNumber(action.GetID() - REMOTE_0);
112   else if (action.GetID() >= KEY_VKEY && action.GetID() < KEY_UNICODE)
113   {
114     // input from the keyboard (vkey, not ascii)
115     uint8_t b = action.GetID() & 0xFF;
116     if (b == XBMCVK_LEFT)
117       OnPrevious();
118     else if (b == XBMCVK_RIGHT)
119       OnNext();
120     else if (b == XBMCVK_RETURN || b == XBMCVK_NUMPADENTER)
121       OnOK();
122     else if (b == XBMCVK_BACK)
123       OnBackSpace();
124     else if (b == XBMCVK_ESCAPE)
125       OnCancel();
126   }
127   else if (action.GetID() == KEY_UNICODE)
128   { // input from the keyboard
129     if (action.GetUnicode() == 10 || action.GetUnicode() == 13)
130       OnOK(); // enter
131     else if (action.GetUnicode() == 8)
132       OnBackSpace(); // backspace
133     else if (action.GetUnicode() == 27)
134       OnCancel(); // escape
135     else if (action.GetUnicode() == 46)
136       OnNext(); // '.'
137     else if (action.GetUnicode() >= 48 && action.GetUnicode() < 58)  // number
138       OnNumber(action.GetUnicode() - 48);
139   }
140   else
141     return CGUIDialog::OnAction(action);
142 
143   return true;
144 }
145 
OnBack(int actionID)146 bool CGUIDialogNumeric::OnBack(int actionID)
147 {
148   OnCancel();
149   return true;
150 }
151 
OnMessage(CGUIMessage & message)152 bool CGUIDialogNumeric::OnMessage(CGUIMessage& message)
153 {
154   switch ( message.GetMessage() )
155   {
156   case GUI_MSG_WINDOW_INIT:
157     {
158       m_bConfirmed = false;
159       m_bCanceled = false;
160       m_dirty = false;
161       return CGUIDialog::OnMessage(message);
162     }
163     break;
164 
165   case GUI_MSG_CLICKED:
166     {
167       int iControl = message.GetSenderId();
168       m_bConfirmed = false;
169       m_bCanceled = false;
170       if (CONTROL_NUM0 <= iControl && iControl <= CONTROL_NUM9)  // User numeric entry via dialog button UI
171       {
172         OnNumber(iControl - 10);
173         return true;
174       }
175       else if (iControl == CONTROL_PREVIOUS)
176       {
177         OnPrevious();
178         return true;
179       }
180       else if (iControl == CONTROL_NEXT)
181       {
182         OnNext();
183         return true;
184       }
185       else if (iControl == CONTROL_BACKSPACE)
186       {
187         OnBackSpace();
188         return true;
189       }
190       else if (iControl == CONTROL_ENTER)
191       {
192         OnOK();
193         return true;
194       }
195     }
196     break;
197 
198   case GUI_MSG_SET_TEXT:
199     SetMode(m_mode, message.GetLabel());
200 
201     // close the dialog if requested
202     if (message.GetParam1() > 0)
203       OnOK();
204     break;
205   }
206   return CGUIDialog::OnMessage(message);
207 }
208 
OnBackSpace()209 void CGUIDialogNumeric::OnBackSpace()
210 {
211   if (!m_dirty && m_block)
212   {
213     --m_block;
214     return;
215   }
216   if (m_mode == INPUT_NUMBER || m_mode == INPUT_PASSWORD)
217   { // just go back one character
218     if (!m_number.empty())
219       m_number.erase(m_number.length() - 1);
220   }
221   else if (m_mode == INPUT_IP_ADDRESS)
222   {
223     if (m_ip[m_block])
224       m_ip[m_block] /= 10;
225     else if (m_block)
226     {
227       --m_block;
228       m_dirty = false;
229     }
230   }
231   else if (m_mode == INPUT_TIME)
232   {
233     if (m_block == 0)
234       m_datetime.hour /= 10;
235     else if (m_datetime.minute)
236       m_datetime.minute /= 10;
237     else
238     {
239       m_block = 0;
240       m_dirty = false;
241     }
242   }
243   else if (m_mode == INPUT_TIME_SECONDS)
244   {
245     if (m_block == 0)
246       m_datetime.hour /= 10;
247     else if (m_block == 1)
248     {
249       if (m_datetime.minute)
250         m_datetime.minute /= 10;
251       else
252       {
253         m_block = 0;
254         m_dirty = false;
255       }
256     }
257     else if (m_datetime.second)
258       m_datetime.minute /= 10;
259     else
260     {
261       m_block = 0;
262       m_dirty = false;
263     }
264   }
265   else if (m_mode == INPUT_DATE)
266   {
267     if (m_block == 0)
268       m_datetime.day /= 10;
269     else if (m_block == 1)
270     {
271       if (m_datetime.month)
272         m_datetime.month /= 10;
273       else
274       {
275         m_block = 0;
276         m_dirty = false;
277       }
278     }
279     else if (m_datetime.year) // m_block == 2
280       m_datetime.year /= 10;
281     else
282     {
283       m_block = 1;
284       m_dirty = false;
285     }
286   }
287 }
288 
OnPrevious()289 void CGUIDialogNumeric::OnPrevious()
290 {
291   if (m_block)
292     m_block--;
293   m_dirty = false;
294 }
295 
OnNext()296 void CGUIDialogNumeric::OnNext()
297 {
298   if (m_mode == INPUT_IP_ADDRESS && m_block==0 && m_ip[0]==0)
299     return;
300 
301   if (m_block < m_lastblock)
302     m_block++;
303   m_dirty = false;
304   if (m_mode == INPUT_DATE)
305     VerifyDate(m_block == 2);
306 }
307 
FrameMove()308 void CGUIDialogNumeric::FrameMove()
309 {
310   std::string strLabel;
311   unsigned int start = 0;
312   unsigned int end = 0;
313   if (m_mode == INPUT_PASSWORD)
314     strLabel.assign(m_number.length(), '*');
315   else if (m_mode == INPUT_NUMBER)
316     strLabel = m_number;
317   else if (m_mode == INPUT_TIME)
318   { // format up the time
319     strLabel = StringUtils::Format("%2d:%02d", m_datetime.hour, m_datetime.minute);
320     start = m_block * 3;
321     end = m_block * 3 + 2;
322   }
323   else if (m_mode == INPUT_TIME_SECONDS)
324   { // format up the time
325     strLabel =
326         StringUtils::Format("%2d:%02d:%02d", m_datetime.hour, m_datetime.minute, m_datetime.second);
327     start = m_block * 3;
328     end = m_block * 3 + 2;
329   }
330   else if (m_mode == INPUT_DATE)
331   { // format up the date
332     strLabel =
333         StringUtils::Format("%2d/%2d/%4d", m_datetime.day, m_datetime.month, m_datetime.year);
334     start = m_block * 3;
335     end = m_block * 3 + 2;
336     if (m_block == 2)
337       end = m_block * 3 + 4;
338   }
339   else if (m_mode == INPUT_IP_ADDRESS)
340   { // format up the date
341     strLabel = StringUtils::Format("%3d.%3d.%3d.%3d", m_ip[0], m_ip[1], m_ip[2], m_ip[3]);
342     start = m_block * 4;
343     end = m_block * 4 + 3;
344   }
345   CGUILabelControl *pLabel = dynamic_cast<CGUILabelControl *>(GetControl(CONTROL_INPUT_LABEL));
346   if (pLabel)
347   {
348     pLabel->SetLabel(strLabel);
349     pLabel->SetHighlight(start, end);
350   }
351   CGUIDialog::FrameMove();
352 }
353 
OnNumber(uint32_t num)354 void CGUIDialogNumeric::OnNumber(uint32_t num)
355 {
356   ResetAutoClose();
357 
358   switch (m_mode)
359   {
360   case INPUT_NUMBER:
361   case INPUT_PASSWORD:
362     m_number += num + '0';
363     break;
364   case INPUT_TIME:
365     HandleInputTime(num);
366     break;
367   case INPUT_TIME_SECONDS:
368     HandleInputSeconds(num);
369     break;
370   case INPUT_DATE:
371     HandleInputDate(num);
372     break;
373   case INPUT_IP_ADDRESS:
374     HandleInputIP(num);
375     break;
376   }
377 }
378 
SetMode(INPUT_MODE mode,const KODI::TIME::SystemTime & initial)379 void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const KODI::TIME::SystemTime& initial)
380 {
381   m_mode = mode;
382   m_block = 0;
383   m_lastblock = 0;
384   if (m_mode == INPUT_TIME || m_mode == INPUT_TIME_SECONDS || m_mode == INPUT_DATE)
385   {
386     m_datetime = initial;
387     m_lastblock = (m_mode != INPUT_TIME) ? 2 : 1;
388   }
389 }
390 
SetMode(INPUT_MODE mode,const std::string & initial)391 void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const std::string &initial)
392 {
393   m_mode = mode;
394   m_block = 0;
395   m_lastblock = 0;
396   if (m_mode == INPUT_TIME || m_mode == INPUT_TIME_SECONDS || m_mode == INPUT_DATE)
397   {
398     CDateTime dateTime;
399     if (m_mode == INPUT_TIME || m_mode == INPUT_TIME_SECONDS)
400     {
401       // check if we have a pure number
402       if (initial.find_first_not_of("0123456789") == std::string::npos)
403       {
404         long seconds = strtol(initial.c_str(), nullptr, 10);
405         dateTime = seconds;
406       }
407       else
408       {
409         std::string tmp = initial;
410         // if we are handling seconds and if the string only contains
411         // "mm:ss" we need to add dummy "hh:" to get "hh:mm:ss"
412         if (m_mode == INPUT_TIME_SECONDS && tmp.length() <= 5)
413           tmp = "00:" + tmp;
414         dateTime.SetFromDBTime(tmp);
415       }
416     }
417     else if (m_mode == INPUT_DATE)
418     {
419       std::string tmp = initial;
420       StringUtils::Replace(tmp, '/', '.');
421       dateTime.SetFromDBDate(tmp);
422     }
423 
424     if (!dateTime.IsValid())
425       return;
426 
427     dateTime.GetAsSystemTime(m_datetime);
428     m_lastblock = (m_mode == INPUT_DATE) ? 2 : 1;
429   }
430   else if (m_mode == INPUT_IP_ADDRESS)
431   {
432     m_lastblock = 3;
433     auto blocks = StringUtils::Split(initial, '.');
434     if (blocks.size() != 4)
435       return;
436 
437     for (size_t i = 0; i < blocks.size(); ++i)
438     {
439       if (blocks[i].length() > 3)
440         return;
441 
442       m_ip[i] = static_cast<uint8_t>(atoi(blocks[i].c_str()));
443     }
444   }
445   else if (m_mode == INPUT_NUMBER || m_mode == INPUT_PASSWORD)
446     m_number = initial;
447 }
448 
GetOutput() const449 KODI::TIME::SystemTime CGUIDialogNumeric::GetOutput() const
450 {
451   assert(m_mode == INPUT_TIME || m_mode == INPUT_TIME_SECONDS || m_mode == INPUT_DATE);
452   return m_datetime;
453 }
454 
GetOutputString() const455 std::string CGUIDialogNumeric::GetOutputString() const
456 {
457   switch (m_mode)
458   {
459   case INPUT_DATE:
460     return StringUtils::Format("%02i/%02i/%04i", m_datetime.day, m_datetime.month, m_datetime.year);
461   case INPUT_TIME:
462     return StringUtils::Format("%i:%02i", m_datetime.hour, m_datetime.minute);
463   case INPUT_TIME_SECONDS:
464     return StringUtils::Format("%i:%02i:%02i", m_datetime.hour, m_datetime.minute,
465                                m_datetime.second);
466   case INPUT_IP_ADDRESS:
467     return StringUtils::Format("%d.%d.%d.%d", m_ip[0], m_ip[1], m_ip[2], m_ip[3]);
468   case INPUT_NUMBER:
469   case INPUT_PASSWORD:
470     return m_number;
471   }
472 
473   //should never get here
474   return std::string();
475 }
476 
ShowAndGetSeconds(std::string & timeString,const std::string & heading)477 bool CGUIDialogNumeric::ShowAndGetSeconds(std::string &timeString, const std::string &heading)
478 {
479   CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
480   if (!pDialog) return false;
481   int seconds = StringUtils::TimeStringToSeconds(timeString);
482   KODI::TIME::SystemTime time = {0};
483   time.hour = seconds / 3600;
484   time.minute = (seconds - time.hour * 3600) / 60;
485   time.second = seconds - time.hour * 3600 - time.minute * 60;
486   pDialog->SetMode(INPUT_TIME_SECONDS, time);
487   pDialog->SetHeading(heading);
488   pDialog->Open();
489   if (!pDialog->IsConfirmed() || pDialog->IsCanceled())
490     return false;
491   time = pDialog->GetOutput();
492   seconds = time.hour * 3600 + time.minute * 60 + time.second;
493   timeString = StringUtils::SecondsToTimeString(seconds);
494   return true;
495 }
496 
ShowAndGetTime(KODI::TIME::SystemTime & time,const std::string & heading)497 bool CGUIDialogNumeric::ShowAndGetTime(KODI::TIME::SystemTime& time, const std::string& heading)
498 {
499   CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
500   if (!pDialog) return false;
501   pDialog->SetMode(INPUT_TIME, time);
502   pDialog->SetHeading(heading);
503   pDialog->Open();
504   if (!pDialog->IsConfirmed() || pDialog->IsCanceled())
505     return false;
506   time = pDialog->GetOutput();
507   return true;
508 }
509 
ShowAndGetDate(KODI::TIME::SystemTime & date,const std::string & heading)510 bool CGUIDialogNumeric::ShowAndGetDate(KODI::TIME::SystemTime& date, const std::string& heading)
511 {
512   CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
513   if (!pDialog) return false;
514   pDialog->SetMode(INPUT_DATE, date);
515   pDialog->SetHeading(heading);
516   pDialog->Open();
517   if (!pDialog->IsConfirmed() || pDialog->IsCanceled())
518     return false;
519   date = pDialog->GetOutput();
520   return true;
521 }
522 
ShowAndGetIPAddress(std::string & IPAddress,const std::string & heading)523 bool CGUIDialogNumeric::ShowAndGetIPAddress(std::string &IPAddress, const std::string &heading)
524 {
525   CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
526   if (!pDialog) return false;
527   pDialog->SetMode(INPUT_IP_ADDRESS, IPAddress);
528   pDialog->SetHeading(heading);
529   pDialog->Open();
530   if (!pDialog->IsConfirmed() || pDialog->IsCanceled())
531     return false;
532   IPAddress = pDialog->GetOutputString();
533   return true;
534 }
535 
ShowAndGetNumber(std::string & strInput,const std::string & strHeading,unsigned int iAutoCloseTimeoutMs,bool bSetHidden)536 bool CGUIDialogNumeric::ShowAndGetNumber(std::string& strInput, const std::string &strHeading, unsigned int iAutoCloseTimeoutMs /* = 0 */, bool bSetHidden /* = false */)
537 {
538   // Prompt user for password input
539   CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
540   pDialog->SetHeading( strHeading );
541 
542   if (bSetHidden)
543     pDialog->SetMode(INPUT_PASSWORD, strInput);
544   else
545     pDialog->SetMode(INPUT_NUMBER, strInput);
546   if (iAutoCloseTimeoutMs)
547     pDialog->SetAutoClose(iAutoCloseTimeoutMs);
548 
549   pDialog->Open();
550 
551   if (!pDialog->IsAutoClosed() && (!pDialog->IsConfirmed() || pDialog->IsCanceled()))
552     return false;
553   strInput = pDialog->GetOutputString();
554   return true;
555 }
556 
557 // \brief Show numeric keypad twice to get and confirm a user-entered password string.
558 // \param strNewPassword String to preload into the keyboard accumulator. Overwritten with user input if return=true.
559 // \return true if successful display and user input entry/re-entry. false if unsuccessful display, no user input, or canceled editing.
ShowAndVerifyNewPassword(std::string & strNewPassword)560 bool CGUIDialogNumeric::ShowAndVerifyNewPassword(std::string& strNewPassword)
561 {
562   // Prompt user for password input
563   std::string strUserInput;
564   InputVerificationResult ret = ShowAndVerifyInput(strUserInput, g_localizeStrings.Get(12340), false);
565   if (ret != InputVerificationResult::SUCCESS)
566   {
567     if (ret == InputVerificationResult::FAILED)
568     {
569       // Show error to user saying the password entry was blank
570       HELPERS::ShowOKDialogText(CVariant{12357}, CVariant{12358}); // Password is empty/blank
571     }
572     return false;
573   }
574 
575   if (strUserInput.empty())
576     // user canceled out
577     return false;
578 
579   // Prompt again for password input, this time sending previous input as the password to verify
580   ret = ShowAndVerifyInput(strUserInput, g_localizeStrings.Get(12341), true);
581   if (ret != InputVerificationResult::SUCCESS)
582   {
583     if (ret == InputVerificationResult::FAILED)
584     {
585       // Show error to user saying the password re-entry failed
586       HELPERS::ShowOKDialogText(CVariant{12357}, CVariant{12344}); // Password do not match
587     }
588     return false;
589   }
590 
591   // password entry and re-entry succeeded
592   strNewPassword = strUserInput;
593   return true;
594 }
595 
596 // \brief Show numeric keypad and verify user input against strPassword.
597 // \param strPassword Value to compare against user input.
598 // \param strHeading String shown on dialog title. Converts to localized string if contains a positive integer.
599 // \param iRetries If greater than 0, shows "Incorrect password, %d retries left" on dialog line 2, else line 2 is blank.
600 // \return 0 if successful display and user input. 1 if unsuccessful input. -1 if no user input or canceled editing.
ShowAndVerifyPassword(std::string & strPassword,const std::string & strHeading,int iRetries)601 int CGUIDialogNumeric::ShowAndVerifyPassword(std::string& strPassword, const std::string& strHeading, int iRetries)
602 {
603   std::string strTempHeading = strHeading;
604   if (iRetries > 0)
605   {
606     // Show a string telling user they have iRetries retries left
607     strTempHeading = StringUtils::Format("%s. %s %i %s", strHeading.c_str(), g_localizeStrings.Get(12342).c_str(), iRetries, g_localizeStrings.Get(12343).c_str());
608   }
609 
610   // make a copy of strPassword to prevent from overwriting it later
611   std::string strPassTemp = strPassword;
612   InputVerificationResult ret = ShowAndVerifyInput(strPassTemp, strTempHeading, true);
613   if (ret == InputVerificationResult::SUCCESS)
614     return 0;   // user entered correct password
615 
616   if (ret == InputVerificationResult::CANCELED)
617     return -1;   // user canceled out
618 
619   return 1; // user must have entered an incorrect password
620 }
621 
622 // \brief Show numeric keypad and verify user input against strToVerify.
623 // \param strToVerify Value to compare against user input.
624 // \param dlgHeading String shown on dialog title.
625 // \param bVerifyInput If set as true we verify the users input versus strToVerify.
626 // \return the result of the check (success, failed, or canceled by user).
ShowAndVerifyInput(std::string & strToVerify,const std::string & dlgHeading,bool bVerifyInput)627 InputVerificationResult CGUIDialogNumeric::ShowAndVerifyInput(std::string& strToVerify, const std::string& dlgHeading, bool bVerifyInput)
628 {
629   // Prompt user for password input
630   CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
631   pDialog->SetHeading(dlgHeading);
632 
633   std::string strInput;
634   if (!bVerifyInput)
635     strInput = strToVerify;
636 
637   pDialog->SetMode(INPUT_PASSWORD, strInput);
638   pDialog->Open();
639 
640   strInput = pDialog->GetOutputString();
641 
642   if (!pDialog->IsConfirmed() || pDialog->IsCanceled())
643   {
644     // user canceled out
645     strToVerify = "";
646     return InputVerificationResult::CANCELED;
647   }
648 
649   const std::string md5pword2 = CDigest::Calculate(CDigest::Type::MD5, strInput);
650 
651   if (!bVerifyInput)
652   {
653     strToVerify = md5pword2;
654     return InputVerificationResult::SUCCESS;
655   }
656 
657   return StringUtils::EqualsNoCase(strToVerify, md5pword2) ? InputVerificationResult::SUCCESS : InputVerificationResult::FAILED;
658 }
659 
IsConfirmed() const660 bool CGUIDialogNumeric::IsConfirmed() const
661 {
662   return m_bConfirmed;
663 }
664 
IsCanceled() const665 bool CGUIDialogNumeric::IsCanceled() const
666 {
667   return m_bCanceled;
668 }
669 
SetHeading(const std::string & strHeading)670 void CGUIDialogNumeric::SetHeading(const std::string& strHeading)
671 {
672   Initialize();
673   CGUIMessage msg(GUI_MSG_LABEL_SET, GetID(), CONTROL_HEADING_LABEL);
674   msg.SetLabel(strHeading);
675   OnMessage(msg);
676 }
677 
VerifyDate(bool checkYear)678 void CGUIDialogNumeric::VerifyDate(bool checkYear)
679 {
680   if (m_datetime.day == 0)
681     m_datetime.day = 1;
682   if (m_datetime.month == 0)
683     m_datetime.month = 1;
684   // check for number of days in the month
685   if (m_datetime.day == 31)
686   {
687     if (m_datetime.month == 4 || m_datetime.month == 6 || m_datetime.month == 9 ||
688         m_datetime.month == 11)
689       m_datetime.day = 30;
690   }
691   if (m_datetime.month == 2 && m_datetime.day > 28)
692   {
693     m_datetime.day = 29; // max in february.
694     if (checkYear)
695     {
696       // leap years occur when the year is divisible by 4 but not by 100, or the year is divisible by 400
697       // thus they don't occur, if the year has a remainder when divided by 4, or when the year is divisible by 100 but not by 400
698       if ((m_datetime.year % 4) || (!(m_datetime.year % 100) && (m_datetime.year % 400)))
699         m_datetime.day = 28;
700     }
701   }
702 }
703 
OnOK()704 void CGUIDialogNumeric::OnOK()
705 {
706   m_bConfirmed = true;
707   m_bCanceled = false;
708   Close();
709 }
710 
OnCancel()711 void CGUIDialogNumeric::OnCancel()
712 {
713   m_bConfirmed = false;
714   m_bCanceled = true;
715   Close();
716 }
717 
HandleInputIP(uint32_t num)718 void CGUIDialogNumeric::HandleInputIP(uint32_t num)
719 {
720   if (m_dirty && ((m_ip[m_block] < 25) || (m_ip[m_block] == 25 && num < 6) || !(m_block == 0 && num == 0)))
721   {
722     m_ip[m_block] *= 10;
723     m_ip[m_block] += num;
724   }
725   else
726     m_ip[m_block] = num;
727 
728   if (m_ip[m_block] > 25 || (m_ip[m_block] == 0 && num == 0))
729   {
730     ++m_block;
731     if (m_block > 3)
732       m_block = 0;
733     m_dirty = false;
734   }
735   else
736     m_dirty = true;
737 }
738 
HandleInputDate(uint32_t num)739 void CGUIDialogNumeric::HandleInputDate(uint32_t num)
740 {
741   if (m_block == 0) // day of month
742   {
743     if (m_dirty && (m_datetime.day < 3 || num < 2))
744     {
745       m_datetime.day *= 10;
746       m_datetime.day += num;
747     }
748     else
749       m_datetime.day = num;
750 
751     if (m_datetime.day > 3)
752     {
753       m_block = 1;             // move to months
754       m_dirty = false;
755     }
756     else
757       m_dirty = true;
758   }
759   else if (m_block == 1)  // months
760   {
761     if (m_dirty && num < 3)
762     {
763       m_datetime.month *= 10;
764       m_datetime.month += num;
765     }
766     else
767       m_datetime.month = num;
768 
769     if (m_datetime.month > 1)
770     {
771       VerifyDate(false);
772       m_block = 2;             // move to year
773       m_dirty = false;
774     }
775     else
776       m_dirty = true;
777   }
778   else // year
779   {
780     if (m_dirty && m_datetime.year < 1000) // have taken input
781     {
782       m_datetime.year *= 10;
783       m_datetime.year += num;
784     }
785     else
786       m_datetime.year = num;
787 
788     if (m_datetime.year > 1000)
789     {
790       VerifyDate(true);
791       m_block = 0;        // move to day of month
792       m_dirty = false;
793     }
794     else
795       m_dirty = true;
796   }
797 }
798 
HandleInputSeconds(uint32_t num)799 void CGUIDialogNumeric::HandleInputSeconds(uint32_t num)
800 {
801   if (m_block == 0) // hour
802   {
803     if (m_dirty) // have input the first digit
804     {
805       m_datetime.hour *= 10;
806       m_datetime.hour += num;
807       m_block = 1;             // move to minutes - allows up to 99 hours
808       m_dirty = false;
809     }
810     else  // this is the first digit
811     {
812       m_datetime.hour = num;
813       m_dirty = true;
814     }
815   }
816   else if (m_block == 1) // minute
817   {
818     if (m_dirty) // have input the first digit
819     {
820       m_datetime.minute *= 10;
821       m_datetime.minute += num;
822       m_block = 2;             // move to seconds - allows up to 99 minutes
823       m_dirty = false;
824     }
825     else  // this is the first digit
826     {
827       m_datetime.minute = num;
828       if (num > 5)
829       {
830         m_block = 2;           // move to seconds
831         m_dirty = false;
832       }
833       else
834         m_dirty = true;
835     }
836   }
837   else  // seconds
838   {
839     if (m_dirty) // have input the first digit
840     {
841       m_datetime.second *= 10;
842       m_datetime.second += num;
843       m_block = 0;             // move to hours
844       m_dirty = false;
845     }
846     else  // this is the first digit
847     {
848       m_datetime.second = num;
849       if (num > 5)
850       {
851         m_block = 0;           // move to hours
852         m_dirty = false;
853       }
854       else
855         m_dirty = true;
856     }
857   }
858 }
859 
HandleInputTime(uint32_t num)860 void CGUIDialogNumeric::HandleInputTime(uint32_t num)
861 {
862   if (m_block == 0) // hour
863   {
864     if (m_dirty) // have input the first digit
865     {
866       if (m_datetime.hour < 2 || num < 4)
867       {
868         m_datetime.hour *= 10;
869         m_datetime.hour += num;
870       }
871       else
872         m_datetime.hour = num;
873 
874       m_block = 1;             // move to minutes
875       m_dirty = false;
876     }
877     else  // this is the first digit
878     {
879       m_datetime.hour = num;
880 
881       if (num > 2)
882       {
883         m_block = 1;             // move to minutes
884         m_dirty = false;
885       }
886       else
887         m_dirty = true;
888     }
889   }
890   else  // minute
891   {
892     if (m_dirty) // have input the first digit
893     {
894       m_datetime.minute *= 10;
895       m_datetime.minute += num;
896       m_block = 0;             // move to hours
897       m_dirty = false;
898     }
899     else  // this is the first digit
900     {
901       m_datetime.minute = num;
902 
903       if (num > 5)
904       {
905         m_block = 0;           // move to hours
906         m_dirty = false;
907       }
908       else
909         m_dirty = true;
910     }
911   }
912 }
913 
914