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 <com/sun/star/lang/XMultiServiceFactory.hpp>
11 #include <com/sun/star/task/InteractionHandler.hpp>
12 #include <com/sun/star/task/PasswordContainer.hpp>
13 #include <com/sun/star/task/XPasswordContainer2.hpp>
14 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
15 #include <com/sun/star/ucb/XContentAccess.hpp>
16 #include <com/sun/star/sdbc/XResultSet.hpp>
17 #include <com/sun/star/sdbc/XRow.hpp>
18 
19 #include <comphelper/processfactory.hxx>
20 #include <officecfg/Office/Common.hxx>
21 #include <rtl/uri.hxx>
22 #include <ucbhelper/content.hxx>
23 #include <ucbhelper/commandenvironment.hxx>
24 #include <toolkit/helper/vclunohelper.hxx>
25 
26 #include <svtools/PlaceEditDialog.hxx>
27 #include <svtools/ServerDetailsControls.hxx>
28 
29 #include <config_oauth2.h>
30 
31 using namespace std;
32 using namespace com::sun::star::lang;
33 using namespace com::sun::star::sdbc;
34 using namespace com::sun::star::task;
35 using namespace com::sun::star::ucb;
36 using namespace com::sun::star::uno;
37 
38 DetailsContainer::DetailsContainer(PlaceEditDialog* pDialog)
39     : m_pDialog(pDialog)
40 {
41     m_pDialog->m_xEDPort->connect_output(LINK(this, DetailsContainer, FormatPortHdl));
42 }
43 
44 //format without thousand separator
45 IMPL_STATIC_LINK(DetailsContainer, FormatPortHdl, weld::SpinButton&, rSpinButton, void)
46 {
47     rSpinButton.set_text(OUString::number(rSpinButton.get_value()));
48 }
49 
50 DetailsContainer::~DetailsContainer( )
51 {
52 }
53 
54 void DetailsContainer::set_visible( bool )
55 {
56     m_pDialog->m_xDetailsGrid->set_sensitive(true);
57 
58     m_pDialog->m_xEDHost->connect_changed( LINK( this, DetailsContainer, ValueChangeHdl ) );
59     m_pDialog->m_xEDPort->connect_changed( LINK( this, DetailsContainer, ValueChangeHdl ) );
60     m_pDialog->m_xEDRoot->connect_changed( LINK( this, DetailsContainer, ValueChangeHdl ) );
61 }
62 
63 INetURLObject DetailsContainer::getUrl( )
64 {
65     // Don't use that class directly: make it smarter by subclassing it.
66     return INetURLObject( );
67 }
68 
69 bool DetailsContainer::setUrl( const INetURLObject& )
70 {
71     // That class doesn't contain any logic... it defers the dirty work
72     // to the sub classes.
73     return false;
74 }
75 
76 void DetailsContainer::notifyChange( )
77 {
78     m_aChangeHdl.Call( this );
79 }
80 
81 IMPL_LINK_NOARG( DetailsContainer, ValueChangeHdl, weld::Entry&, void )
82 {
83     notifyChange( );
84 }
85 
86 HostDetailsContainer::HostDetailsContainer(PlaceEditDialog* pDialog, sal_uInt16 nPort, const OUString& sScheme) :
87     DetailsContainer( pDialog ),
88     m_nDefaultPort( nPort ),
89     m_sScheme( sScheme )
90 {
91     set_visible( false );
92 }
93 
94 void HostDetailsContainer::set_visible( bool bShow )
95 {
96     m_pDialog->m_xFTHost->set_visible( bShow );
97     m_pDialog->m_xHostBox->set_visible( bShow );
98     m_pDialog->m_xEDRoot->set_visible( bShow );
99     m_pDialog->m_xFTRoot->set_visible( bShow );
100 
101     DetailsContainer::set_visible( bShow );
102 
103     if ( bShow )
104     {
105         if (m_pDialog->m_xEDPort->get_value() == 0)
106             m_pDialog->m_xEDPort->set_value( m_nDefaultPort );
107         m_pDialog->m_xEDHost->set_text( m_sHost );
108     }
109     else
110         m_pDialog->m_xEDPort->set_value( 0 );
111 }
112 
113 INetURLObject HostDetailsContainer::getUrl( )
114 {
115     OUString sHost = m_pDialog->m_xEDHost->get_text().trim();
116     sal_Int64 nPort = m_pDialog->m_xEDPort->get_value();
117     OUString sPath = m_pDialog->m_xEDRoot->get_text().trim();
118 
119     OUString sUrl;
120     if ( !sHost.isEmpty( ) )
121     {
122         sUrl = m_sScheme + "://" + sHost;
123         if ( nPort != m_nDefaultPort )
124             sUrl += ":" + OUString::number( nPort );
125         if ( !sPath.isEmpty( ) )
126             if ( sPath.indexOf( '/' ) != 0 )
127                 sUrl += "/";
128         sUrl += sPath;
129     }
130 
131     return INetURLObject( sUrl );
132 }
133 
134 bool HostDetailsContainer::setUrl( const INetURLObject& rUrl )
135 {
136     bool bSuccess = verifyScheme( INetURLObject::GetScheme( rUrl.GetProtocol( ) ) );
137 
138     if ( bSuccess )
139     {
140         m_sHost = rUrl.GetHost( );
141         m_pDialog->m_xEDHost->set_text( rUrl.GetHost( ) );
142         m_pDialog->m_xEDPort->set_value( rUrl.GetPort( ) );
143         m_pDialog->m_xEDRoot->set_text( rUrl.GetURLPath() );
144     }
145 
146     return bSuccess;
147 }
148 
149 bool HostDetailsContainer::verifyScheme( const OUString& sScheme )
150 {
151     return sScheme == ( m_sScheme + "://" );
152 }
153 
154 DavDetailsContainer::DavDetailsContainer(PlaceEditDialog* pBuilder)
155     : HostDetailsContainer(pBuilder, 80, "http")
156 {
157     m_pDialog->m_xCBDavs->connect_toggled(LINK(this, DavDetailsContainer, ToggledDavsHdl));
158 
159     set_visible( false );
160 }
161 
162 void DavDetailsContainer::set_visible( bool bShow )
163 {
164     HostDetailsContainer::set_visible( bShow );
165 
166     if ( !bShow )
167         m_pDialog->m_xCBDavs->set_active(false);
168 
169     m_pDialog->m_xCBDavs->set_visible(bShow);
170 }
171 
172 bool DavDetailsContainer::verifyScheme( const OUString& rScheme )
173 {
174     bool bValid = false;
175     if ( rScheme == "http://" )
176     {
177         bValid = true;
178         m_pDialog->m_xCBDavs->set_active(false);
179     }
180     else if ( rScheme == "https://" )
181     {
182         bValid = true;
183         m_pDialog->m_xCBDavs->set_active(true);
184     }
185     return bValid;
186 }
187 
188 IMPL_LINK( DavDetailsContainer, ToggledDavsHdl, weld::ToggleButton&, rCheckBox, void )
189 {
190     // Change default port if needed
191     bool bCheckedDavs = rCheckBox.get_active();
192     if ( m_pDialog->m_xEDPort->get_value() == 80 && bCheckedDavs )
193         m_pDialog->m_xEDPort->set_value( 443 );
194     else if ( m_pDialog->m_xEDPort->get_value() == 443 && !bCheckedDavs )
195         m_pDialog->m_xEDPort->set_value( 80 );
196 
197     OUString sScheme( "http" );
198     if ( bCheckedDavs )
199         sScheme = "https";
200     setScheme( sScheme );
201 
202     notifyChange( );
203 }
204 
205 SmbDetailsContainer::SmbDetailsContainer(PlaceEditDialog* pDialog)
206     : DetailsContainer(pDialog)
207 {
208     m_pDialog->m_xEDShare->connect_changed( LINK( this, DetailsContainer, ValueChangeHdl ) );
209 
210     set_visible( false );
211 }
212 
213 INetURLObject SmbDetailsContainer::getUrl( )
214 {
215     OUString sHost = m_pDialog->m_xEDHost->get_text().trim( );
216     OUString sShare = m_pDialog->m_xEDShare->get_text().trim( );
217     OUString sPath = m_pDialog->m_xEDRoot->get_text().trim( );
218 
219     OUString sUrl;
220     if ( !sHost.isEmpty( ) )
221     {
222         sUrl = "smb://" + sHost + "/";
223         if ( !sShare.isEmpty( ) )
224             sUrl += sShare;
225         if ( !sPath.isEmpty( ) )
226             if ( sPath.indexOf( '/' ) != 0 )
227                 sUrl += "/";
228         sUrl += sPath;
229     }
230 
231     return INetURLObject( sUrl );
232 }
233 
234 bool SmbDetailsContainer::setUrl( const INetURLObject& rUrl )
235 {
236     bool bSuccess =  rUrl.GetProtocol() == INetProtocol::Smb;
237 
238     if ( bSuccess )
239     {
240         OUString sShare = rUrl.getName( 0 );
241         OUString sFullPath = rUrl.GetURLPath( );
242         OUString sPath;
243         if ( sFullPath.getLength( ) > sShare.getLength( ) )
244         {
245             sal_Int32 nPos = sShare.getLength( );
246             if ( nPos != 0 )
247                 ++nPos;
248             sPath = sFullPath.copy( nPos );
249         }
250 
251         m_sHost = rUrl.GetHost( );
252         m_pDialog->m_xEDHost->set_text( m_sHost );
253         m_pDialog->m_xEDShare->set_text( sShare );
254         m_pDialog->m_xEDRoot->set_text( sPath );
255     }
256 
257     return bSuccess;
258 }
259 
260 void SmbDetailsContainer::set_visible( bool bShow )
261 {
262     m_pDialog->m_xEDShare->set_visible( bShow );
263     m_pDialog->m_xFTShare->set_visible( bShow );
264     m_pDialog->m_xEDRoot->set_visible( bShow );
265     m_pDialog->m_xFTRoot->set_visible( bShow );
266 
267     m_pDialog->m_xFTHost->set_visible( bShow );
268     m_pDialog->m_xHostBox->set_visible( bShow );
269     m_pDialog->m_xEDPort->set_sensitive( !bShow );
270     m_pDialog->m_xFTPort->set_sensitive( !bShow );
271 
272     if ( bShow )
273         m_pDialog->m_xEDHost->set_text( m_sHost );
274 }
275 
276 CmisDetailsContainer::CmisDetailsContainer(PlaceEditDialog* pParentDialog, OUString const & sBinding) :
277     DetailsContainer( pParentDialog ),
278     m_sUsername( ),
279     m_xCmdEnv( ),
280     m_aRepoIds( ),
281     m_sRepoId( ),
282     m_sBinding( sBinding ),
283     m_xParentDialog(pParentDialog->getDialog()->GetXWindow())
284 {
285     Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
286     Reference< XInteractionHandler > xGlobalInteractionHandler(
287         InteractionHandler::createWithParent(xContext, m_xParentDialog), UNO_QUERY);
288     m_xCmdEnv = new ucbhelper::CommandEnvironment( xGlobalInteractionHandler, Reference< XProgressHandler >() );
289 
290     set_visible( false );
291 }
292 
293 void CmisDetailsContainer::set_visible( bool bShow )
294 {
295     m_pDialog->m_xLBRepository->connect_changed( LINK( this, CmisDetailsContainer, SelectRepoHdl ) );
296     m_pDialog->m_xBTRepoRefresh->connect_clicked( LINK( this, CmisDetailsContainer, RefreshReposHdl ) );
297 
298     m_pDialog->m_xEDHost->set_text( m_sBinding );
299 
300     if( ( m_sBinding == GDRIVE_BASE_URL )
301             || m_sBinding.startsWith( ALFRESCO_CLOUD_BASE_URL )
302             || ( m_sBinding == ONEDRIVE_BASE_URL ) )
303     {
304         m_pDialog->m_xFTHost->hide();
305         m_pDialog->m_xHostBox->hide();
306         m_pDialog->m_xFTRepository->hide();
307         m_pDialog->m_xRepositoryBox->hide();
308         m_pDialog->m_xEDRoot->hide();
309         m_pDialog->m_xFTRoot->hide();
310     }
311     else
312     {
313         m_pDialog->m_xFTHost->set_visible( bShow );
314         m_pDialog->m_xHostBox->set_visible( bShow );
315         m_pDialog->m_xFTRepository->set_visible( bShow );
316         m_pDialog->m_xRepositoryBox->set_visible( bShow );
317         m_pDialog->m_xEDRoot->set_visible( bShow );
318         m_pDialog->m_xFTRoot->set_visible( bShow );
319     }
320 
321     DetailsContainer::set_visible( bShow );
322     m_pDialog->m_xEDPort->set_sensitive( !bShow );
323     m_pDialog->m_xFTPort->set_sensitive( !bShow );
324 }
325 
326 INetURLObject CmisDetailsContainer::getUrl( )
327 {
328     OUString sBindingUrl = m_pDialog->m_xEDHost->get_text().trim();
329     OUString sPath = m_pDialog->m_xEDRoot->get_text().trim();
330 
331     bool bSkip = true;
332     if( ( m_sBinding == GDRIVE_BASE_URL )
333             || m_sBinding.startsWith( ALFRESCO_CLOUD_BASE_URL )
334             || ( m_sBinding == ONEDRIVE_BASE_URL ) )
335     {
336         bSkip = m_sUsername.isEmpty();
337     }
338     else
339     {
340         bSkip = m_sRepoId.isEmpty();
341     }
342 
343     OUString sUrl;
344     if ( !sBindingUrl.isEmpty( ) && !bSkip )
345     {
346         OUString sEncodedBinding = rtl::Uri::encode(
347                 sBindingUrl + "#" + m_sRepoId,
348                 rtl_UriCharClassRelSegment,
349                 rtl_UriEncodeKeepEscapes,
350                 RTL_TEXTENCODING_UTF8 );
351         sUrl = "vnd.libreoffice.cmis://" + sEncodedBinding;
352     }
353     sUrl += sPath;
354 
355     return INetURLObject( sUrl );
356 }
357 
358 bool CmisDetailsContainer::setUrl( const INetURLObject& rUrl )
359 {
360     bool bSuccess =  rUrl.GetProtocol() == INetProtocol::Cmis;
361 
362     if ( bSuccess )
363     {
364         OUString sDecodedHost = rUrl.GetHost( INetURLObject::DecodeMechanism::WithCharset );
365         INetURLObject aHostUrl( sDecodedHost );
366         m_sBinding = aHostUrl.GetURLNoMark( );
367         m_sRepoId = aHostUrl.GetMark( );
368 
369         m_pDialog->m_xEDHost->set_text( m_sBinding );
370         m_pDialog->m_xEDRoot->set_text( rUrl.GetURLPath() );
371     }
372     return bSuccess;
373 }
374 
375 void CmisDetailsContainer::setUsername( const OUString& rUsername )
376 {
377     m_sUsername = rUsername;
378 }
379 
380 void CmisDetailsContainer::setPassword( const OUString& rPass )
381 {
382     m_sPassword = rPass;
383 }
384 
385 void CmisDetailsContainer::selectRepository( )
386 {
387     // Get the repo ID and call the Change listener
388     const int nPos = m_pDialog->m_xLBRepository->get_active();
389     if( static_cast<size_t>(nPos) < m_aRepoIds.size() )
390     {
391         m_sRepoId = m_aRepoIds[nPos];
392         notifyChange( );
393     }
394 }
395 
396 IMPL_LINK_NOARG( CmisDetailsContainer, RefreshReposHdl, weld::Button&, void  )
397 {
398     Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
399     Reference< XPasswordContainer2 > xMasterPasswd = PasswordContainer::create( xContext );
400 
401 
402     OUString sBindingUrl = m_pDialog->m_xEDHost->get_text().trim( );
403 
404     OUString sEncodedUsername = "";
405 
406     if ( !m_sUsername.isEmpty( ) )
407     {
408         sEncodedUsername = rtl::Uri::encode(m_sUsername,
409                                             rtl_UriCharClassUserinfo,
410                                             rtl_UriEncodeKeepEscapes,
411                                             RTL_TEXTENCODING_UTF8 );
412         sEncodedUsername += "@";
413     }
414 
415     // Clean the listbox
416     m_pDialog->m_xLBRepository->clear();
417     m_aRepoIds.clear();
418 
419     // Compute the URL
420     OUString sUrl;
421     if ( !sBindingUrl.isEmpty( ) )
422     {
423         OUString sEncodedBinding = rtl::Uri::encode(
424                 sBindingUrl,
425                 rtl_UriCharClassRelSegment,
426                 rtl_UriEncodeKeepEscapes,
427                 RTL_TEXTENCODING_UTF8 );
428         sUrl = "vnd.libreoffice.cmis://" + sEncodedUsername + sEncodedBinding;
429     }
430 
431     // temporary remember the password
432     try
433     {
434         if( !sUrl.isEmpty() && !m_sUsername.isEmpty() && !m_sPassword.isEmpty() )
435         {
436             Reference< XInteractionHandler > xInteractionHandler(
437                 InteractionHandler::createWithParent(xContext, m_xParentDialog),
438                 UNO_QUERY );
439 
440             Sequence<OUString> aPasswd { m_sPassword };
441 
442             xMasterPasswd->add(
443                 sUrl, m_sUsername, aPasswd, xInteractionHandler );
444         }
445     }
446     catch( const Exception& )
447     {}
448 
449     // Get the Content
450     ::ucbhelper::Content aCnt( sUrl, m_xCmdEnv, comphelper::getProcessComponentContext() );
451     Sequence<OUString> aProps { "Title" };
452 
453     try
454     {
455         Reference< XResultSet > xResultSet( aCnt.createCursor( aProps ), UNO_QUERY_THROW );
456         Reference< XContentAccess > xAccess( xResultSet, UNO_QUERY_THROW );
457         while ( xResultSet->next() )
458         {
459             OUString sURL = xAccess->queryContentIdentifierString( );
460             INetURLObject aURL( sURL );
461             OUString sId = aURL.GetURLPath( INetURLObject::DecodeMechanism::WithCharset );
462             sId = sId.copy( 1 );
463             m_aRepoIds.push_back( sId );
464 
465             Reference< XRow > xRow( xResultSet, UNO_QUERY );
466             OUString sName = xRow->getString( 1 );
467             m_pDialog->m_xLBRepository->append_text(sName);
468         }
469     }
470     catch ( const Exception& )
471     {
472     }
473 
474     // Auto-select the first one
475     if (m_pDialog->m_xLBRepository->get_count() > 0)
476     {
477         m_pDialog->m_xLBRepository->set_active(0);
478         selectRepository( );
479     }
480 
481     // remove temporary password
482     try
483     {
484         xMasterPasswd->remove( sUrl, m_sUsername );
485     }
486     catch( const Exception& )
487     {}
488 }
489 
490 IMPL_LINK_NOARG( CmisDetailsContainer, SelectRepoHdl, weld::ComboBox&, void )
491 {
492     selectRepository( );
493 }
494 
495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
496