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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <services.h>
21 
22 #include <cppuhelper/implbase.hxx>
23 #include <cppuhelper/supportsservice.hxx>
24 #include <tools/urlobj.hxx>
25 #include <rtl/ref.hxx>
26 #include <rtl/ustrbuf.hxx>
27 
28 #include <com/sun/star/util/XURLTransformer.hpp>
29 #include <com/sun/star/util/URL.hpp>
30 #include <com/sun/star/lang/XServiceInfo.hpp>
31 #include <com/sun/star/uno/XComponentContext.hpp>
32 
33 namespace {
34 
35 class URLTransformer : public ::cppu::WeakImplHelper< css::util::XURLTransformer, css::lang::XServiceInfo>
36 {
37 public:
38     URLTransformer() {}
39 
40     virtual OUString SAL_CALL getImplementationName() override
41     {
42         return "com.sun.star.comp.framework.URLTransformer";
43     }
44 
45     virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
46     {
47         return cppu::supportsService(this, ServiceName);
48     }
49 
50     virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
51     {
52         return {"com.sun.star.util.URLTransformer"};
53     }
54 
55     virtual sal_Bool SAL_CALL parseStrict( css::util::URL& aURL ) override;
56 
57     virtual sal_Bool SAL_CALL parseSmart( css::util::URL& aURL, const OUString& sSmartProtocol ) override;
58 
59     virtual sal_Bool SAL_CALL assemble( css::util::URL& aURL ) override;
60 
61     virtual OUString SAL_CALL getPresentation( const css::util::URL& aURL, sal_Bool bWithPassword ) override;
62 };
63 
64 void lcl_ParserHelper(INetURLObject& _rParser, css::util::URL& _rURL,bool _bUseIntern)
65 {
66     // Get all information about this URL.
67     _rURL.Protocol  = INetURLObject::GetScheme( _rParser.GetProtocol() );
68     _rURL.User      = _rParser.GetUser  ( INetURLObject::DecodeMechanism::WithCharset );
69     _rURL.Password  = _rParser.GetPass  ( INetURLObject::DecodeMechanism::WithCharset );
70     _rURL.Server        = _rParser.GetHost  ( INetURLObject::DecodeMechanism::WithCharset );
71     _rURL.Port      = static_cast<sal_Int16>(_rParser.GetPort());
72 
73     sal_Int32 nCount = _rParser.getSegmentCount( false );
74     if ( nCount > 0 )
75     {
76         // Don't add last segment as it is the name!
77         --nCount;
78 
79         OUStringBuffer aPath;
80         for ( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ )
81         {
82             aPath.append( '/');
83             aPath.append( _rParser.getName( nIndex, false, INetURLObject::DecodeMechanism::NONE ));
84         }
85 
86         if ( nCount > 0 )
87             aPath.append( '/' ); // final slash!
88 
89         _rURL.Path = aPath.makeStringAndClear();
90         _rURL.Name = _rParser.getName( INetURLObject::LAST_SEGMENT, false, INetURLObject::DecodeMechanism::NONE );
91     }
92     else
93     {
94         _rURL.Path       = _rParser.GetURLPath( INetURLObject::DecodeMechanism::NONE           );
95         _rURL.Name = _rParser.GetLastName();
96     }
97 
98     _rURL.Arguments  = _rParser.GetParam();
99     _rURL.Mark      = _rParser.GetMark( INetURLObject::DecodeMechanism::WithCharset );
100 
101     // INetURLObject supports only an intelligent method of parsing URL's. So write
102     // back Complete to have a valid encoded URL in all cases!
103     _rURL.Complete  = _rParser.GetMainURL( INetURLObject::DecodeMechanism::NONE           );
104     if ( _bUseIntern )
105         _rURL.Complete   = _rURL.Complete.intern();
106 
107     _rParser.SetMark    ( OUString() );
108     _rParser.SetParam( OUString() );
109 
110     _rURL.Main       = _rParser.GetMainURL( INetURLObject::DecodeMechanism::NONE           );
111 }
112 
113 //  XURLTransformer
114 sal_Bool SAL_CALL URLTransformer::parseStrict( css::util::URL& aURL )
115 {
116     // Safe impossible cases.
117     if ( aURL.Complete.isEmpty() )
118     {
119         return false;
120     }
121     // Try to extract the protocol
122     sal_Int32 nURLIndex = aURL.Complete.indexOf( ':' );
123     OUString aProtocol;
124     if ( nURLIndex > 1 )
125     {
126         aProtocol = aURL.Complete.copy( 0, nURLIndex+1 );
127 
128         // If INetURLObject knows this protocol let it parse
129         if ( INetURLObject::CompareProtocolScheme( aProtocol ) != INetProtocol::NotValid )
130         {
131             // Initialize parser with given URL.
132             INetURLObject aParser( aURL.Complete );
133 
134             // Get all information about this URL.
135             INetProtocol eINetProt = aParser.GetProtocol();
136             if ( eINetProt == INetProtocol::NotValid )
137             {
138                 return false;
139             }
140             else if ( !aParser.HasError() )
141             {
142                 lcl_ParserHelper(aParser,aURL,false);
143                 // Return "URL is parsed".
144                 return true;
145             }
146         }
147         else
148         {
149             // Minimal support for unknown protocols. This is mandatory to support the "Protocol Handlers" implemented
150             // in framework!
151             aURL.Protocol   = aProtocol;
152             aURL.Main       = aURL.Complete;
153             aURL.Path       = aURL.Complete.copy( nURLIndex+1 );
154 
155             // Return "URL is parsed".
156             return true;
157         }
158     }
159 
160     return false;
161 }
162 
163 //  XURLTransformer
164 
165 sal_Bool SAL_CALL URLTransformer::parseSmart( css::util::URL& aURL,
166                                                 const   OUString&    sSmartProtocol  )
167 {
168     // Safe impossible cases.
169     if ( aURL.Complete.isEmpty() )
170     {
171         return false;
172     }
173 
174     // Initialize parser with given URL.
175     INetURLObject aParser;
176 
177     aParser.SetSmartProtocol( INetURLObject::CompareProtocolScheme( sSmartProtocol ));
178     bool bOk = aParser.SetSmartURL( aURL.Complete );
179     if ( bOk )
180     {
181         lcl_ParserHelper(aParser,aURL,true);
182         // Return "URL is parsed".
183         return true;
184     }
185     else
186     {
187         // Minimal support for unknown protocols. This is mandatory to support the "Protocol Handlers" implemented
188         // in framework!
189         if ( INetURLObject::CompareProtocolScheme( sSmartProtocol ) == INetProtocol::NotValid )
190         {
191             // Try to extract the protocol
192             sal_Int32 nIndex = aURL.Complete.indexOf( ':' );
193             OUString aProtocol;
194             if ( nIndex > 1 )
195             {
196                 aProtocol = aURL.Complete.copy( 0, nIndex+1 );
197 
198                 // If INetURLObject knows this protocol something is wrong as detected before =>
199                 // give up and return false!
200                 if ( INetURLObject::CompareProtocolScheme( aProtocol ) != INetProtocol::NotValid )
201                     return false;
202                 else
203                     aURL.Protocol = aProtocol;
204             }
205             else
206                 return false;
207 
208             aURL.Main = aURL.Complete;
209             aURL.Path = aURL.Complete.copy( nIndex+1 );
210             return true;
211         }
212         else
213             return false;
214     }
215 }
216 
217 //  XURLTransformer
218 sal_Bool SAL_CALL URLTransformer::assemble( css::util::URL& aURL )
219 {
220     // Initialize parser.
221     INetURLObject aParser;
222 
223     if ( INetURLObject::CompareProtocolScheme( aURL.Protocol ) != INetProtocol::NotValid )
224     {
225         OUStringBuffer aCompletePath( aURL.Path );
226 
227         // Concat the name if it is provided, just support a final slash
228         if ( !aURL.Name.isEmpty() )
229         {
230             sal_Int32 nIndex = aURL.Path.lastIndexOf( '/' );
231             if ( nIndex == ( aURL.Path.getLength() -1 ))
232                 aCompletePath.append( aURL.Name );
233             else
234             {
235                 aCompletePath.append( '/' );
236                 aCompletePath.append( aURL.Name );
237             }
238         }
239 
240         bool bResult = aParser.ConcatData(
241                             INetURLObject::CompareProtocolScheme( aURL.Protocol )   ,
242                              aURL.User                                              ,
243                             aURL.Password                                           ,
244                             aURL.Server                                             ,
245                              aURL.Port                                              ,
246                             aCompletePath.makeStringAndClear()                          );
247 
248         if ( !bResult )
249             return false;
250 
251         // First parse URL WITHOUT ...
252         aURL.Main = aParser.GetMainURL( INetURLObject::DecodeMechanism::NONE );
253         // ...and then WITH parameter and mark.
254         aParser.SetParam( aURL.Arguments);
255         aParser.SetMark ( aURL.Mark, INetURLObject::EncodeMechanism::All );
256         aURL.Complete = aParser.GetMainURL( INetURLObject::DecodeMechanism::NONE );
257 
258         // Return "URL is assembled".
259         return true;
260     }
261     else if ( !aURL.Protocol.isEmpty() )
262     {
263         // Minimal support for unknown protocols
264         OUStringBuffer aBuffer( aURL.Protocol );
265         aBuffer.append( aURL.Path );
266         aURL.Complete   = aBuffer.makeStringAndClear();
267         aURL.Main       = aURL.Complete;
268         return true;
269     }
270 
271     return false;
272 }
273 
274 //  XURLTransformer
275 
276 OUString SAL_CALL URLTransformer::getPresentation( const css::util::URL& aURL,
277                                                             sal_Bool    bWithPassword   )
278 {
279     // Safe impossible cases.
280     if  ( aURL.Complete.isEmpty() )
281     {
282         return OUString();
283     }
284 
285     // Check given URL
286     css::util::URL aTestURL = aURL;
287     bool bParseResult = parseSmart( aTestURL, aTestURL.Protocol );
288     if ( bParseResult )
289     {
290         if ( !bWithPassword && !aTestURL.Password.isEmpty() )
291         {
292             // Exchange password text with other placeholder string
293             aTestURL.Password = "<******>";
294             assemble( aTestURL );
295         }
296 
297         // Convert internal URLs to "praesentation"-URLs!
298         OUString sPraesentationURL;
299         INetURLObject::translateToExternal( aTestURL.Complete, sPraesentationURL, INetURLObject::DecodeMechanism::Unambiguous );
300 
301         return sPraesentationURL;
302     }
303     else
304         return OUString();
305 }
306 
307 }
308 
309 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
310 com_sun_star_comp_framework_URLTransformer_get_implementation(
311     css::uno::XComponentContext *,
312     css::uno::Sequence<css::uno::Any> const &)
313 {
314     return cppu::acquire(new URLTransformer());
315 }
316 
317 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
318