cui/Library_cui.mk                      |    3 --
 cui/source/dialogs/passwdomdlg.cxx      |   13 +++++---
 cui/source/inc/PasswordStrength.hxx     |   28 ------------------
 cui/source/util/PasswordStrength.cxx    |   48 --------------------------------
 include/sal/log-areas.dox               |    2 -
 include/sfx2/passwd.hxx                 |    2 +
 include/svl/PasswordHelper.hxx          |   25 ++++++++++++++++
 sc/source/ui/inc/protectiondlg.hxx      |    1 
 sc/source/ui/miscdlgs/protectiondlg.cxx |    9 +++++-
 sc/uiconfig/scalc/ui/protectsheetdlg.ui |   25 ++++++++++++----
 sfx2/source/dialog/passwd.cxx           |   21 +++++++++++++-
 sfx2/uiconfig/ui/password.ui            |   34 ++++++++++++++++++++--
 svl/Library_svl.mk                      |    2 +
 svl/source/misc/PasswordHelper.cxx      |   48 ++++++++++++++++++++++++++++++++
 uui/source/masterpasscrtdlg.cxx         |    7 ++++
 uui/source/masterpasscrtdlg.hxx         |    1 
 uui/uiconfig/ui/setmasterpassworddlg.ui |   23 +++++++++++----
 17 files changed, 188 insertions(+), 104 deletions(-)

New commits:
commit 861ebce9ca52cbe87121879f159d8ec0cb572755
Author:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
AuthorDate: Wed Nov 8 09:20:34 2023 +0300
Commit:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
CommitDate: Wed Nov 15 19:48:49 2023 +0100

    tdf#157518: add password strength bar to protectsheetdlg.ui
    
    Change-Id: Iedd6793446c2c339cc17d32a28c6b195dfb250e7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159372
    Tested-by: Jenkins
    Reviewed-by: Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>

diff --git a/sc/source/ui/inc/protectiondlg.hxx 
b/sc/source/ui/inc/protectiondlg.hxx
index d36c8a820fad..e59fb3accf77 100644
--- a/sc/source/ui/inc/protectiondlg.hxx
+++ b/sc/source/ui/inc/protectiondlg.hxx
@@ -51,6 +51,7 @@ private:
     std::unique_ptr<weld::Container> m_xOptions;
     std::unique_ptr<weld::Entry> m_xPassword1Edit;
     std::unique_ptr<weld::Entry> m_xPassword2Edit;
+    std::unique_ptr<weld::LevelBar> m_xPasswordStrengthBar;
     std::unique_ptr<weld::TreeView> m_xOptionsListBox;
     std::unique_ptr<weld::Button> m_xBtnOk;
     std::unique_ptr<weld::Label> m_xProtected;
diff --git a/sc/source/ui/miscdlgs/protectiondlg.cxx 
b/sc/source/ui/miscdlgs/protectiondlg.cxx
index 920e7bfc4888..1353f5f154cc 100644
--- a/sc/source/ui/miscdlgs/protectiondlg.cxx
+++ b/sc/source/ui/miscdlgs/protectiondlg.cxx
@@ -19,6 +19,7 @@
 
 #include <protectiondlg.hxx>
 #include <tabprotection.hxx>
+#include <svl/PasswordHelper.hxx>
 
 #include <vector>
 
@@ -43,6 +44,7 @@ ScTableProtectionDlg::ScTableProtectionDlg(weld::Window* 
pParent)
     , m_xOptions(m_xBuilder->weld_container("options"))
     , m_xPassword1Edit(m_xBuilder->weld_entry("password1"))
     , m_xPassword2Edit(m_xBuilder->weld_entry("password2"))
+    , m_xPasswordStrengthBar(m_xBuilder->weld_level_bar("passwordbar"))
     , m_xOptionsListBox(m_xBuilder->weld_tree_view("checklist"))
     , m_xBtnOk(m_xBuilder->weld_button("ok"))
     , m_xProtected(m_xBuilder->weld_label("protected"))
@@ -144,9 +146,14 @@ IMPL_LINK_NOARG(ScTableProtectionDlg, OKHdl, 
weld::Button&, void)
     m_xDialog->response(RET_OK);
 }
 
-IMPL_LINK_NOARG(ScTableProtectionDlg, PasswordModifyHdl, weld::Entry&, void)
+IMPL_LINK(ScTableProtectionDlg, PasswordModifyHdl, weld::Entry&, rEntry, void)
 {
     OUString aPass1 = m_xPassword1Edit->get_text();
+    if (&rEntry == m_xPassword1Edit.get())
+    {
+        m_xPasswordStrengthBar->set_percentage(
+            SvPasswordHelper::GetPasswordStrengthPercentage(aPass1));
+    }
     OUString aPass2 = m_xPassword2Edit->get_text();
     m_xBtnOk->set_sensitive(aPass1 == aPass2);
 }
diff --git a/sc/uiconfig/scalc/ui/protectsheetdlg.ui 
b/sc/uiconfig/scalc/ui/protectsheetdlg.ui
index 547af8e8d34e..83f1a1af012a 100644
--- a/sc/uiconfig/scalc/ui/protectsheetdlg.ui
+++ b/sc/uiconfig/scalc/ui/protectsheetdlg.ui
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.36.0 -->
+<!-- Generated with glade 3.38.2 -->
 <interface domain="sc">
   <requires lib="gtk+" version="3.20"/>
   <object class="GtkTreeStore" id="liststore1">
@@ -117,7 +117,7 @@
                   </packing>
                 </child>
                 <child>
-                  <!-- n-columns=1 n-rows=1 -->
+                  <!-- n-columns=2 n-rows=3 -->
                   <object class="GtkGrid" id="passwords">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
@@ -150,8 +150,8 @@
                         <property name="xalign">1</property>
                       </object>
                       <packing>
-                        <property name="left_attach">0</property>
-                        <property name="top_attach">1</property>
+                        <property name="left-attach">0</property>
+                        <property name="top-attach">2</property>
                       </packing>
                     </child>
                     <child>
@@ -182,10 +182,23 @@
                         <property name="input_purpose">password</property>
                       </object>
                       <packing>
-                        <property name="left_attach">1</property>
-                        <property name="top_attach">1</property>
+                        <property name="left-attach">1</property>
+                        <property name="top-attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLevelBar" id="passwordbar">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                      </object>
+                      <packing>
+                        <property name="left-attach">1</property>
+                        <property name="top-attach">1</property>
                       </packing>
                     </child>
+                    <child>
+                      <placeholder/>
+                    </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
commit e8fc5d7fcab6b283ec0655b1d7cab5bf28fde240
Author:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
AuthorDate: Wed Nov 8 08:43:41 2023 +0300
Commit:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
CommitDate: Wed Nov 15 19:48:44 2023 +0100

    tdf#157518: add password strength bar to sfx2/ui/password.ui
    
    Change-Id: I181b71c6072a61651b33835f76e8deebb5f8d58a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159371
    Tested-by: Jenkins
    Reviewed-by: Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>

diff --git a/include/sfx2/passwd.hxx b/include/sfx2/passwd.hxx
index 98c70787ca0e..bc5e478bbf03 100644
--- a/include/sfx2/passwd.hxx
+++ b/include/sfx2/passwd.hxx
@@ -50,12 +50,14 @@ private:
     std::unique_ptr<weld::Entry> m_xUserED;
     std::unique_ptr<weld::Label> m_xPassword1FT;
     std::unique_ptr<weld::Entry> m_xPassword1ED;
+    std::unique_ptr<weld::LevelBar> m_xPassword1StrengthBar;
     std::unique_ptr<weld::Label> m_xConfirm1FT;
     std::unique_ptr<weld::Entry> m_xConfirm1ED;
 
     std::unique_ptr<weld::Frame> m_xPassword2Box;
     std::unique_ptr<weld::Label> m_xPassword2FT;
     std::unique_ptr<weld::Entry> m_xPassword2ED;
+    std::unique_ptr<weld::LevelBar> m_xPassword2StrengthBar;
     std::unique_ptr<weld::Label> m_xConfirm2FT;
     std::unique_ptr<weld::Entry> m_xConfirm2ED;
 
diff --git a/sfx2/source/dialog/passwd.cxx b/sfx2/source/dialog/passwd.cxx
index 51f3abd84c0c..a69d0aef352a 100644
--- a/sfx2/source/dialog/passwd.cxx
+++ b/sfx2/source/dialog/passwd.cxx
@@ -20,6 +20,7 @@
 #include <sfx2/passwd.hxx>
 #include <sfx2/sfxresid.hxx>
 #include <sfx2/strings.hrc>
+#include <svl/PasswordHelper.hxx>
 #include <rtl/ustrbuf.hxx>
 #include <vcl/svapp.hxx>
 #include <vcl/weld.hxx>
@@ -31,10 +32,22 @@ IMPL_LINK_NOARG(SfxPasswordDialog, EditModifyHdl, 
weld::Entry&, void)
 
 void SfxPasswordDialog::ModifyHdl()
 {
-    bool bEnable = m_xPassword1ED->get_text().getLength() >= mnMinLen;
+    OUString aPassword1Text = m_xPassword1ED->get_text();
+    bool bEnable = aPassword1Text.getLength() >= mnMinLen;
     if (m_xPassword2ED->get_visible())
         bEnable = (bEnable && (m_xPassword2ED->get_text().getLength() >= 
mnMinLen));
     m_xOKBtn->set_sensitive(bEnable);
+
+    if (m_xConfirm1ED->get_visible())
+    {
+        m_xPassword1StrengthBar->set_percentage(
+            SvPasswordHelper::GetPasswordStrengthPercentage(aPassword1Text));
+    }
+    if (m_xConfirm2ED->get_visible())
+    {
+        m_xPassword2StrengthBar->set_percentage(
+            
SvPasswordHelper::GetPasswordStrengthPercentage(m_xPassword2ED->get_text()));
+    }
 }
 
 IMPL_LINK(SfxPasswordDialog, InsertTextHdl, OUString&, rTest, bool)
@@ -100,11 +113,13 @@ SfxPasswordDialog::SfxPasswordDialog(weld::Widget* 
pParent, const OUString* pGro
     , m_xUserED(m_xBuilder->weld_entry("usered"))
     , m_xPassword1FT(m_xBuilder->weld_label("pass1ft"))
     , m_xPassword1ED(m_xBuilder->weld_entry("pass1ed"))
+    , m_xPassword1StrengthBar(m_xBuilder->weld_level_bar("pass1bar"))
     , m_xConfirm1FT(m_xBuilder->weld_label("confirm1ft"))
     , m_xConfirm1ED(m_xBuilder->weld_entry("confirm1ed"))
     , m_xPassword2Box(m_xBuilder->weld_frame("password2frame"))
     , m_xPassword2FT(m_xBuilder->weld_label("pass2ft"))
     , m_xPassword2ED(m_xBuilder->weld_entry("pass2ed"))
+    , m_xPassword2StrengthBar(m_xBuilder->weld_level_bar("pass2bar"))
     , m_xConfirm2FT(m_xBuilder->weld_label("confirm2ft"))
     , m_xConfirm2ED(m_xBuilder->weld_entry("confirm2ed"))
     , m_xMinLengthFT(m_xBuilder->weld_label("minlenft"))
@@ -177,6 +192,7 @@ void SfxPasswordDialog::PreRun()
     m_xUserED->hide();
     m_xConfirm1FT->hide();
     m_xConfirm1ED->hide();
+    m_xPassword1StrengthBar->hide();
     m_xPassword1FT->hide();
     m_xPassword2Box->hide();
     m_xPassword2FT->hide();
@@ -184,6 +200,7 @@ void SfxPasswordDialog::PreRun()
     m_xPassword2FT->hide();
     m_xConfirm2FT->hide();
     m_xConfirm2ED->hide();
+    m_xPassword2StrengthBar->hide();
 
     if (mnExtras != SfxShowExtras::NONE)
         m_xPassword1FT->show();
@@ -196,6 +213,7 @@ void SfxPasswordDialog::PreRun()
     {
         m_xConfirm1FT->show();
         m_xConfirm1ED->show();
+        m_xPassword1StrengthBar->show();
     }
     if (mnExtras & SfxShowExtras::PASSWORD2)
     {
@@ -207,6 +225,7 @@ void SfxPasswordDialog::PreRun()
     {
         m_xConfirm2FT->show();
         m_xConfirm2ED->show();
+        m_xPassword2StrengthBar->show();
     }
 }
 
diff --git a/sfx2/uiconfig/ui/password.ui b/sfx2/uiconfig/ui/password.ui
index d17b8d9077e3..0eabdd68682b 100644
--- a/sfx2/uiconfig/ui/password.ui
+++ b/sfx2/uiconfig/ui/password.ui
@@ -87,7 +87,7 @@
                 <property name="label-xalign">0</property>
                 <property name="shadow-type">none</property>
                 <child>
-                  <!-- n-columns=2 n-rows=3 -->
+                  <!-- n-columns=2 n-rows=4 -->
                   <object class="GtkGrid" id="grid1">
                     <property name="visible">True</property>
                     <property name="can-focus">False</property>
@@ -134,7 +134,7 @@
                       </object>
                       <packing>
                         <property name="left-attach">0</property>
-                        <property name="top-attach">2</property>
+                        <property name="top-attach">3</property>
                       </packing>
                     </child>
                     <child>
@@ -186,11 +186,24 @@
                           </object>
                         </child>
                       </object>
+                      <packing>
+                        <property name="left-attach">1</property>
+                        <property name="top-attach">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLevelBar" id="pass1bar">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                      </object>
                       <packing>
                         <property name="left-attach">1</property>
                         <property name="top-attach">2</property>
                       </packing>
                     </child>
+                    <child>
+                      <placeholder/>
+                    </child>
                   </object>
                 </child>
                 <child type="label">
@@ -217,7 +230,7 @@
                 <property name="label-xalign">0</property>
                 <property name="shadow-type">none</property>
                 <child>
-                  <!-- n-columns=2 n-rows=2 -->
+                  <!-- n-columns=2 n-rows=3 -->
                   <object class="GtkGrid" id="grid2">
                     <property name="visible">True</property>
                     <property name="can-focus">False</property>
@@ -250,7 +263,7 @@
                       </object>
                       <packing>
                         <property name="left-attach">0</property>
-                        <property name="top-attach">1</property>
+                        <property name="top-attach">2</property>
                       </packing>
                     </child>
                     <child>
@@ -288,11 +301,24 @@
                           </object>
                         </child>
                       </object>
+                      <packing>
+                        <property name="left-attach">1</property>
+                        <property name="top-attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLevelBar" id="pass2bar">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                      </object>
                       <packing>
                         <property name="left-attach">1</property>
                         <property name="top-attach">1</property>
                       </packing>
                     </child>
+                    <child>
+                      <placeholder/>
+                    </child>
                   </object>
                 </child>
                 <child type="label">
commit cdcff8c34144e883eca9dc6e1a85968ed34909c2
Author:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
AuthorDate: Wed Nov 8 07:36:52 2023 +0300
Commit:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
CommitDate: Wed Nov 15 19:48:38 2023 +0100

    tdf#157518: add password strength meter to setmasterpassworddlg
    
    Moves PasswordStrength bits that provide utility functions from cui to svl,
    merging them with PasswordHelper there.
    
    Adds password strength bar for the set master password dialog.
    (accessible via Tools -> Options -> LibreOffice -> Security -> Passwords 
for Web
    Connections)
    
    Change-Id: I8dc1090a041f8388c2e139beb1d0d9a0beb8acb0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159370
    Tested-by: Jenkins
    Reviewed-by: Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>

diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk
index 4a41c10baa2c..e122d2b734c4 100644
--- a/cui/Library_cui.mk
+++ b/cui/Library_cui.mk
@@ -71,13 +71,11 @@ $(eval $(call gb_Library_use_externals,cui,\
        boost_headers \
        $(call gb_Helper_optional,OPENCL,\
                clew) \
-       icui18n \
     icuuc \
     icu_headers \
     libxml2 \
     orcus-parser \
     orcus \
-       zxcvbn-c \
     zxing \
 ))
 
@@ -244,7 +242,6 @@ $(eval $(call gb_Library_add_exception_objects,cui,\
     cui/source/tabpages/tptrans \
     cui/source/tabpages/transfrm \
     cui/source/util/FontFeatures \
-    cui/source/util/PasswordStrength \
     cui/source/dialogs/widgettestdlg \
 ))
 
diff --git a/cui/source/dialogs/passwdomdlg.cxx 
b/cui/source/dialogs/passwdomdlg.cxx
index 189738b45e3c..a7e95a29f3b4 100644
--- a/cui/source/dialogs/passwdomdlg.cxx
+++ b/cui/source/dialogs/passwdomdlg.cxx
@@ -18,10 +18,10 @@
  */
 
 #include <sfx2/objsh.hxx>
+#include <svl/PasswordHelper.hxx>
 #include <vcl/svapp.hxx>
 #include <officecfg/Office/Common.hxx>
 #include <passwdomdlg.hxx>
-#include <PasswordStrength.hxx>
 #include <strings.hrc>
 #include <dialmgr.hxx>
 
@@ -41,14 +41,16 @@ IMPL_LINK_NOARG(PasswordToOpenModifyDialog, OkBtnClickHdl, 
weld::Button&, void)
     {
         if (m_oPasswordPolicy)
         {
-            if 
(!passwordCompliesPolicy(m_xPasswdToOpenED->get_text().toUtf8().getStr()))
+            if 
(!SvPasswordHelper::PasswordMeetsPolicy(m_xPasswdToOpenED->get_text(),
+                                                       m_oPasswordPolicy))
             {
                 m_xPasswdToOpenED->grab_focus();
                 return;
             }
 
             if (m_xOpenReadonlyCB->get_active()
-                && 
!passwordCompliesPolicy(m_xPasswdToModifyED->get_text().toUtf8().getStr()))
+                && 
!SvPasswordHelper::PasswordMeetsPolicy(m_xPasswdToModifyED->get_text(),
+                                                          m_oPasswordPolicy))
             {
                 m_xPasswdToModifyED->grab_focus();
                 return;
@@ -115,7 +117,8 @@ IMPL_LINK(PasswordToOpenModifyDialog, ChangeHdl, 
weld::Entry&, rEntry, void)
     }
     assert(pIndicator);
 
-    bool bPasswordMeetsPolicy = 
passwordCompliesPolicy(aPasswordText.toUtf8().getStr());
+    bool bPasswordMeetsPolicy
+        = SvPasswordHelper::PasswordMeetsPolicy(aPasswordText, 
m_oPasswordPolicy);
     if (pLevelBar)
     {
         rEntry.set_message_type(bPasswordMeetsPolicy ? 
weld::EntryMessageType::Normal
@@ -126,7 +129,7 @@ IMPL_LINK(PasswordToOpenModifyDialog, ChangeHdl, 
weld::Entry&, rEntry, void)
     // if password doesn't meet policy cap the percentage at 70%
     if (pLevelBar)
         pLevelBar->set_percentage(
-            
std::min(getPasswordStrengthPercentage(aPasswordText.toUtf8().getStr()),
+            
std::min(SvPasswordHelper::GetPasswordStrengthPercentage(aPasswordText),
                      bPasswordMeetsPolicy ? std::numeric_limits<double>::max() 
: 70.0));
 
     if (m_nMaxPasswdLen)
diff --git a/cui/source/inc/PasswordStrength.hxx 
b/cui/source/inc/PasswordStrength.hxx
deleted file mode 100644
index e8c9e86a83cb..000000000000
--- a/cui/source/inc/PasswordStrength.hxx
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-/** Get password strength percentage
-
-    Maps the received password entropy bits to password strength percentage.
-    0 bits      -> 0%
-    >= 112 bits -> 100%
-
-    @param pPassword null terminated password string.
-    @returns Password strength percentage in the range [0.0, 100.0]
-*/
-double getPasswordStrengthPercentage(const char* pPassword);
-
-/** Checks if the password meets the password policies
-
-    @param pPassword null terminated password string.
-    @returns true if password meets the policy or there is no policy enforced.
-*/
-bool passwordCompliesPolicy(const char* pPassword);
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/cui/source/util/PasswordStrength.cxx 
b/cui/source/util/PasswordStrength.cxx
deleted file mode 100644
index 678ad2f74e76..000000000000
--- a/cui/source/util/PasswordStrength.cxx
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#include <PasswordStrength.hxx>
-#include <officecfg/Office/Common.hxx>
-#include <unicode/regex.h>
-#include <unicode/unistr.h>
-#include <unicode/errorcode.h>
-#include <sal/log.hxx>
-#include <zxcvbn.h>
-
-double getPasswordStrengthPercentage(const char* pPassword)
-{
-    static constexpr double fMaxStrengthBits = 112.0;
-    return std::min(100.0, ZxcvbnMatch(pPassword, nullptr, nullptr) * 100.0 / 
fMaxStrengthBits);
-}
-
-bool passwordCompliesPolicy(const char* pPassword)
-{
-    std::optional<OUString> oPasswordPolicy
-        = 
officecfg::Office::Common::Security::Scripting::PasswordPolicy::get();
-    if (oPasswordPolicy)
-    {
-        icu::ErrorCode aStatus;
-        icu::UnicodeString sPassword(pPassword);
-        icu::UnicodeString sRegex(oPasswordPolicy->getStr());
-        icu::RegexMatcher aRegexMatcher(sRegex, sPassword, 0, aStatus);
-
-        if (aRegexMatcher.matches(aStatus))
-            return true;
-
-        if (aStatus.isFailure())
-        {
-            SAL_INFO("cui.util", "Password policy regular expression failed 
with error: "
-                                     << aStatus.errorName());
-        }
-        return false;
-    }
-    return true;
-}
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 0b3907e08610..0315b006f333 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -128,7 +128,6 @@ certain functionality.
 @li @c cui.factory
 @li @c cui.options
 @li @c cui.tabpages
-@li @c cui.util
 
 @section drawinglayer
 
@@ -388,6 +387,7 @@ certain functionality.
 @li @c svl
 @li @c svl.crypto
 @li @c svl.items
+@li @c svl.misc
 @li @c svl.numbers
 
 @section svtools
diff --git a/include/svl/PasswordHelper.hxx b/include/svl/PasswordHelper.hxx
index aa7e55867343..405b3962aab6 100644
--- a/include/svl/PasswordHelper.hxx
+++ b/include/svl/PasswordHelper.hxx
@@ -23,6 +23,7 @@
 #include <sal/config.h>
 
 #include <string_view>
+#include <optional>
 
 #include <svl/svldllapi.h>
 #include <sal/types.h>
@@ -48,6 +49,30 @@ public:
     ODF 1.2, requiring UTF-8 encoding.
     */
     SVL_DLLPUBLIC static bool     CompareHashPassword(const 
css::uno::Sequence<sal_Int8>& rOldPassHash, std::u16string_view sNewPass);
+
+    /** Get password strength percentage
+
+    Maps the calculated password entropy bit amount to password strength 
percentage:
+    0 bits      -> 0%
+    >= 112 bits -> 100%
+
+    @param pPassword null terminated password string.
+    @returns Password strength percentage in the range [0.0, 100.0]
+    */
+    SVL_DLLPUBLIC static double GetPasswordStrengthPercentage(const char* 
pPassword);
+    SVL_DLLPUBLIC static double GetPasswordStrengthPercentage(const OUString& 
aPassword);
+
+    /** Checks if the password meets the password policies
+
+    @param pPassword null terminated password string.
+    @param oPasswordPolicy Regular expression string that defines the password 
policy.
+
+    @returns true if password meets the policy or there is no policy enforced.
+    */
+    SVL_DLLPUBLIC static bool PasswordMeetsPolicy(const char* pPassword,
+                                                  const 
std::optional<OUString>& oPasswordPolicy);
+    SVL_DLLPUBLIC static bool PasswordMeetsPolicy(const OUString& aPassword,
+                                                  const 
std::optional<OUString>& oPasswordPolicy);
 };
 
 #endif
diff --git a/svl/Library_svl.mk b/svl/Library_svl.mk
index aee540d56514..56923d712ced 100644
--- a/svl/Library_svl.mk
+++ b/svl/Library_svl.mk
@@ -25,9 +25,11 @@ $(eval $(call gb_Library_use_externals,svl,\
         curl) \
     dtoa \
     icu_headers \
+    icui18n \
     icuuc \
     mdds_headers \
     libxml2 \
+    zxcvbn-c \
 ))
 
 $(eval $(call gb_Library_set_componentfile,svl,svl/util/svl,services))
diff --git a/svl/source/misc/PasswordHelper.cxx 
b/svl/source/misc/PasswordHelper.cxx
index 41d7bf1ea68b..cfae72f64937 100644
--- a/svl/source/misc/PasswordHelper.cxx
+++ b/svl/source/misc/PasswordHelper.cxx
@@ -22,6 +22,11 @@
 #include <comphelper/hash.hxx>
 #include <rtl/digest.h>
 #include <memory>
+#include <unicode/regex.h>
+#include <unicode/unistr.h>
+#include <unicode/errorcode.h>
+#include <zxcvbn.h>
+#include <sal/log.hxx>
 
 using namespace com::sun::star;
 
@@ -129,4 +134,47 @@ bool SvPasswordHelper::CompareHashPassword(const 
uno::Sequence<sal_Int8>& rOldPa
     return bResult;
 }
 
+double SvPasswordHelper::GetPasswordStrengthPercentage(const char* pPassword)
+{
+    // Entropy bits corresponding to 100% password strength
+    static constexpr double fMaxPassStrengthEntorpyBits = 112.0;
+    return std::min(100.0,
+                    ZxcvbnMatch(pPassword, nullptr, nullptr) * 100.0 / 
fMaxPassStrengthEntorpyBits);
+}
+
+double SvPasswordHelper::GetPasswordStrengthPercentage(const OUString& 
aPassword)
+{
+    OString aPasswordUtf8 = aPassword.toUtf8();
+    return GetPasswordStrengthPercentage(aPasswordUtf8.getStr());
+}
+
+bool SvPasswordHelper::PasswordMeetsPolicy(const char* pPassword,
+                                           const std::optional<OUString>& 
oPasswordPolicy)
+{
+    if (oPasswordPolicy)
+    {
+        icu::ErrorCode aStatus;
+        icu::UnicodeString sPassword(pPassword);
+        icu::UnicodeString sRegex(oPasswordPolicy->getStr());
+        icu::RegexMatcher aRegexMatcher(sRegex, sPassword, 0, aStatus);
+
+        if (aRegexMatcher.matches(aStatus))
+            return true;
+
+        SAL_WARN_IF(
+            aStatus.isFailure(), "svl.misc",
+            "Password policy regular expression failed with error: " << 
aStatus.errorName());
+
+        return false;
+    }
+    return true;
+}
+
+bool SvPasswordHelper::PasswordMeetsPolicy(const OUString& aPassword,
+                                           const std::optional<OUString>& 
oPasswordPolicy)
+{
+    OString aPasswordUtf8 = aPassword.toUtf8();
+    return PasswordMeetsPolicy(aPasswordUtf8.getStr(), oPasswordPolicy);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/uui/source/masterpasscrtdlg.cxx b/uui/source/masterpasscrtdlg.cxx
index 4801bec28f85..2216ea5a1d48 100644
--- a/uui/source/masterpasscrtdlg.cxx
+++ b/uui/source/masterpasscrtdlg.cxx
@@ -21,6 +21,7 @@
 #include <vcl/svapp.hxx>
 #include <vcl/weld.hxx>
 #include <strings.hrc>
+#include <svl/PasswordHelper.hxx>
 #include "masterpasscrtdlg.hxx"
 
 // 
MasterPasswordCreateDialog---------------------------------------------------
@@ -28,7 +29,10 @@
 
 IMPL_LINK_NOARG(MasterPasswordCreateDialog, EditHdl_Impl, weld::Entry&, void)
 {
-    m_xOKBtn->set_sensitive(m_xEDMasterPasswordCrt->get_text().getLength() >= 
1);
+    OUString aPasswordText = m_xEDMasterPasswordCrt->get_text();
+    m_xOKBtn->set_sensitive(aPasswordText.getLength() >= 1);
+    m_xPasswdStrengthBar->set_percentage(
+        SvPasswordHelper::GetPasswordStrengthPercentage(aPasswordText));
 }
 
 IMPL_LINK_NOARG(MasterPasswordCreateDialog, OKHdl_Impl, weld::Button&, void)
@@ -55,6 +59,7 @@ 
MasterPasswordCreateDialog::MasterPasswordCreateDialog(weld::Window* pParent, co
     , m_xEDMasterPasswordCrt(m_xBuilder->weld_entry("password1"))
     , m_xEDMasterPasswordRepeat(m_xBuilder->weld_entry("password2"))
     , m_xOKBtn(m_xBuilder->weld_button("ok"))
+    , m_xPasswdStrengthBar(m_xBuilder->weld_level_bar("password1levelbar"))
 {
     m_xOKBtn->set_sensitive(false);
     m_xOKBtn->connect_clicked( LINK( this, MasterPasswordCreateDialog, 
OKHdl_Impl ) );
diff --git a/uui/source/masterpasscrtdlg.hxx b/uui/source/masterpasscrtdlg.hxx
index a8695fc1ed27..f7f01cda79cc 100644
--- a/uui/source/masterpasscrtdlg.hxx
+++ b/uui/source/masterpasscrtdlg.hxx
@@ -29,6 +29,7 @@ private:
     std::unique_ptr<weld::Entry> m_xEDMasterPasswordCrt;
     std::unique_ptr<weld::Entry> m_xEDMasterPasswordRepeat;
     std::unique_ptr<weld::Button> m_xOKBtn;
+    std::unique_ptr<weld::LevelBar> m_xPasswdStrengthBar;
 
     DECL_LINK(OKHdl_Impl, weld::Button&, void);
     DECL_LINK(EditHdl_Impl, weld::Entry&, void);
diff --git a/uui/uiconfig/ui/setmasterpassworddlg.ui 
b/uui/uiconfig/ui/setmasterpassworddlg.ui
index 0402b4de7b35..52cec48eb30a 100644
--- a/uui/uiconfig/ui/setmasterpassworddlg.ui
+++ b/uui/uiconfig/ui/setmasterpassworddlg.ui
@@ -129,19 +129,30 @@
                 <property name="position">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkLevelBar" id="password1levelbar">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
             <child>
               <object class="GtkLabel" id="label3">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
+                <property name="can-focus">False</property>
                 <property name="label" translatable="yes" 
context="setmasterpassworddlg|label3">_Reenter password:</property>
-                <property name="use_underline">True</property>
-                <property name="mnemonic_widget">password2</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">password2</property>
                 <property name="xalign">0</property>
               </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">True</property>
-                <property name="position">3</property>
+                <property name="position">4</property>
               </packing>
             </child>
             <child>
@@ -161,7 +172,7 @@
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">True</property>
-                <property name="position">4</property>
+                <property name="position">5</property>
               </packing>
             </child>
             <child>
@@ -180,7 +191,7 @@
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">True</property>
-                <property name="position">5</property>
+                <property name="position">6</property>
               </packing>
             </child>
           </object>

Reply via email to