1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <officecfg/Office/Common.hxx>
11 #include <osl/file.hxx>
12 #include <osl/security.hxx>
13 #include <osl/thread.h>
14 #include <tools/diagnose_ex.h>
15 #include "certpath.hxx"
16 
17 #include <com/sun/star/mozilla/MozillaBootstrap.hpp>
18 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
19 #include <com/sun/star/ui/dialogs/FolderPicker.hpp>
20 #include <comphelper/processfactory.hxx>
21 
22 using namespace ::com::sun::star;
23 
CertPathDialog(weld::Window * pParent)24 CertPathDialog::CertPathDialog(weld::Window* pParent)
25     : GenericDialogController(pParent, "cui/ui/certdialog.ui", "CertDialog")
26     , m_xManualButton(m_xBuilder->weld_button("add"))
27     , m_xOKButton(m_xBuilder->weld_button("ok"))
28     , m_xCertPathList(m_xBuilder->weld_tree_view("paths"))
29 {
30     // these are just used to get translated strings
31     m_sAddDialogText = m_xBuilder->weld_label("certdir")->get_label();
32     m_sManualLabel = m_xBuilder->weld_label("manual")->get_label();
33 
34     m_xCertPathList->set_size_request(m_xCertPathList->get_approximate_digit_width() * 70,
35                                       m_xCertPathList->get_height_rows(6));
36 
37     std::vector<int> aWidths;
38     aWidths.push_back(m_xCertPathList->get_checkbox_column_width());
39     aWidths.push_back(m_xCertPathList->get_approximate_digit_width() * 20);
40     m_xCertPathList->set_column_fixed_widths(aWidths);
41 
42     std::vector<int> aRadioColumns;
43     aRadioColumns.push_back(0);
44     m_xCertPathList->set_toggle_columns_as_radio(aRadioColumns);
45 
46     m_xCertPathList->connect_toggled(LINK(this, CertPathDialog, CheckHdl_Impl));
47     m_xManualButton->connect_clicked( LINK( this, CertPathDialog, ManualHdl_Impl ) );
48     m_xOKButton->connect_clicked( LINK( this, CertPathDialog, OKHdl_Impl ) );
49 
50     try
51     {
52         // In the reverse order of preference for the default selected profile
53         mozilla::MozillaProductType const productTypes[3] = {
54             mozilla::MozillaProductType_Thunderbird,
55             mozilla::MozillaProductType_Firefox,
56             mozilla::MozillaProductType_Mozilla };
57         const char* const productNames[3] = {
58             "thunderbird",
59             "firefox",
60             "mozilla" };
61         bool bSelected = false;
62 
63         uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap = mozilla::MozillaBootstrap::create( comphelper::getProcessComponentContext() );
64 
65         for (sal_Int32 i = 0; i < sal_Int32(SAL_N_ELEMENTS(productTypes)); ++i)
66         {
67             sal_Int32 nProfileCount = xMozillaBootstrap->getProfileCount(productTypes[i]);
68             if (nProfileCount <= 0)
69                 continue;
70             OUString sDefaultProfile = xMozillaBootstrap->getDefaultProfile(productTypes[i]);
71             uno::Sequence<OUString> aProfileList(nProfileCount);
72 #ifndef NDEBUG
73             sal_Int32 nListLen =
74 #endif
75                  xMozillaBootstrap->getProfileList(productTypes[i], aProfileList);
76             assert((nProfileCount == nListLen) && (nListLen == aProfileList.getLength()));
77 
78             for (const auto& sProfileName : std::as_const(aProfileList))
79             {
80                 OUString sEntry = OUString::createFromAscii(productNames[i]) + ":" + sProfileName;
81                 OUString sProfilePath = xMozillaBootstrap->getProfilePath(productTypes[i], sProfileName);
82                 const bool bSelectDefaultProfile = !bSelected && sProfileName == sDefaultProfile;
83                 AddCertPath(sEntry, sProfilePath, bSelectDefaultProfile);
84                 if (bSelectDefaultProfile)
85                     bSelected = true;
86             }
87         }
88     }
89     catch (const uno::Exception&)
90     {
91     }
92 
93     try
94     {
95         AddManualCertPath(officecfg::Office::Common::Security::Scripting::CertDir::get().get_value_or(OUString()));
96         if (m_sManualPath.isEmpty())
97             AddManualCertPath(officecfg::Office::Common::Security::Scripting::ManualCertDir::get(), false);
98     }
99     catch (const uno::Exception &)
100     {
101         TOOLS_WARN_EXCEPTION("cui.options", "CertPathDialog::CertPathDialog()");
102     }
103 
104     const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
105     if (pEnv)
106         AddCertPath("$MOZILLA_CERTIFICATE_FOLDER", OUString(pEnv, strlen(pEnv), osl_getThreadTextEncoding()));
107 }
108 
AddManualCertPath(const OUString & sUserSetCertPath,bool bSelect)109 void CertPathDialog::AddManualCertPath(const OUString& sUserSetCertPath, bool bSelect)
110 {
111     if (sUserSetCertPath.isEmpty())
112         return;
113 
114     // check existence
115     ::osl::DirectoryItem aUserPathItem;
116     OUString sUserSetCertURLPath;
117     osl::FileBase::getFileURLFromSystemPath(sUserSetCertPath, sUserSetCertURLPath);
118     if (::osl::FileBase::E_None == ::osl::DirectoryItem::get(sUserSetCertURLPath, aUserPathItem))
119     {
120         ::osl::FileStatus aStatus( osl_FileStatus_Mask_Validate );
121         if (::osl::FileBase::E_None == aUserPathItem.getFileStatus(aStatus))
122             // the cert path exists
123             AddCertPath(m_sManualLabel, sUserSetCertPath, bSelect);
124     }
125 }
126 
IMPL_LINK_NOARG(CertPathDialog,OKHdl_Impl,weld::Button &,void)127 IMPL_LINK_NOARG(CertPathDialog, OKHdl_Impl, weld::Button&, void)
128 {
129     try
130     {
131         std::shared_ptr< comphelper::ConfigurationChanges > batch(
132             comphelper::ConfigurationChanges::create());
133         officecfg::Office::Common::Security::Scripting::CertDir::set(
134             getDirectory(), batch);
135         officecfg::Office::Common::Security::Scripting::ManualCertDir::set(m_sManualPath, batch);
136         batch->commit();
137     }
138     catch (const uno::Exception &)
139     {
140         TOOLS_WARN_EXCEPTION("cui.options", "CertPathDialog::OKHdl_Impl()");
141     }
142 
143     m_xDialog->response(RET_OK);
144 }
145 
getDirectory() const146 OUString CertPathDialog::getDirectory() const
147 {
148     int nEntry = m_xCertPathList->get_selected_index();
149     if (nEntry == -1)
150         return OUString();
151     return m_xCertPathList->get_id(nEntry);
152 }
153 
~CertPathDialog()154 CertPathDialog::~CertPathDialog()
155 {
156 }
157 
IMPL_LINK(CertPathDialog,CheckHdl_Impl,const row_col &,rRowCol,void)158 IMPL_LINK(CertPathDialog, CheckHdl_Impl, const row_col&, rRowCol, void)
159 {
160     HandleEntryChecked(rRowCol.first);
161 }
162 
HandleEntryChecked(int nRow)163 void CertPathDialog::HandleEntryChecked(int nRow)
164 {
165     const bool bChecked = m_xCertPathList->get_toggle(nRow, 0) == TRISTATE_TRUE;
166     if (bChecked)
167     {
168         // we have radio button behavior -> so uncheck the other entries
169         m_xCertPathList->select(nRow);
170         const int nCount = m_xCertPathList->n_children();
171         for (int i = 0; i < nCount; ++i)
172         {
173             if (i != nRow)
174                 m_xCertPathList->set_toggle(i, TRISTATE_FALSE, 0);
175         }
176     }
177 }
178 
AddCertPath(const OUString & rProfile,const OUString & rPath,const bool bSelect)179 void CertPathDialog::AddCertPath(const OUString &rProfile, const OUString &rPath, const bool bSelect)
180 {
181     int nRow = -1;
182     for (int i = 0, nCount = m_xCertPathList->n_children(); i < nCount; ++i)
183     {
184         OUString sCertPath = m_xCertPathList->get_id(i);
185         //already exists, just select the original one
186         if (sCertPath == rPath)
187         {
188             const bool bWantSelected = bSelect || m_xCertPathList->get_toggle(i, 0);
189             m_xCertPathList->set_toggle(i, bWantSelected ? TRISTATE_TRUE : TRISTATE_FALSE, 0);
190             HandleEntryChecked(i);
191             return;
192         }
193         else if (m_xCertPathList->get_text(i, 1) == rProfile)
194             nRow = i;
195     }
196 
197     if (m_sManualLabel == rProfile)
198         m_sManualPath = rPath;
199 
200     if (nRow < 0)
201     {
202         m_xCertPathList->append();
203         nRow = m_xCertPathList->n_children() - 1;
204     }
205     m_xCertPathList->set_toggle(nRow, bSelect ? TRISTATE_TRUE : TRISTATE_FALSE, 0);
206     m_xCertPathList->set_text(nRow, rProfile, 1);
207     m_xCertPathList->set_text(nRow, rPath, 2);
208     m_xCertPathList->set_id(nRow, rPath);
209     HandleEntryChecked(nRow);
210 }
211 
IMPL_LINK_NOARG(CertPathDialog,ManualHdl_Impl,weld::Button &,void)212 IMPL_LINK_NOARG(CertPathDialog, ManualHdl_Impl, weld::Button&, void)
213 {
214     try
215     {
216         uno::Reference<ui::dialogs::XFolderPicker2> xFolderPicker = ui::dialogs::FolderPicker::create(comphelper::getProcessComponentContext());
217 
218         OUString sURL;
219         if (!m_sManualPath.isEmpty())
220             osl::FileBase::getFileURLFromSystemPath(m_sManualPath, sURL);
221         if (sURL.isEmpty())
222             osl::Security().getHomeDir(sURL);
223         xFolderPicker->setDisplayDirectory(sURL);
224         xFolderPicker->setDescription(m_sAddDialogText);
225 
226         if (xFolderPicker->execute() == ui::dialogs::ExecutableDialogResults::OK)
227         {
228             sURL = xFolderPicker->getDirectory();
229             OUString aPath;
230             if (osl::FileBase::E_None == osl::FileBase::getSystemPathFromFileURL(sURL, aPath))
231                 AddCertPath(m_sManualLabel, aPath);
232         }
233     }
234     catch (const uno::Exception &)
235     {
236         TOOLS_WARN_EXCEPTION("cui.options", "");
237     }
238 }
239 
240 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
241