1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_extensions.hxx"
26 #include <com/sun/star/xml/xpath/XXPathAPI.hpp>
27 
28 #include "updateprotocol.hxx"
29 #include "updatecheckconfig.hxx"
30 
31 #ifndef _COM_SUN_STAR_DEPLOYMENT_UPDATEINFORMATINENTRY_HPP_
32 #include <com/sun/star/deployment/UpdateInformationEntry.hpp>
33 #endif
34 #include <com/sun/star/deployment/XPackageInformationProvider.hpp>
35 
36 
37 #include <rtl/ref.hxx>
38 #include <rtl/uri.hxx>
39 #include <rtl/strbuf.hxx>
40 #include <rtl/ustrbuf.hxx>
41 #include <rtl/bootstrap.hxx>
42 #include <osl/process.h>
43 
44 #include <cppuhelper/implbase1.hxx>
45 
46 namespace css = com::sun::star ;
47 namespace container = css::container ;
48 namespace deployment = css::deployment ;
49 namespace lang = css::lang ;
50 namespace uno = css::uno ;
51 namespace task = css::task ;
52 namespace xml = css::xml ;
53 
54 #define UNISTRING(s) rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(s))
55 
56 //------------------------------------------------------------------------------
57 
58 static bool
59 getBootstrapData(
60     uno::Sequence< ::rtl::OUString > & rRepositoryList,
61     ::rtl::OUString & rBuildID,
62     ::rtl::OUString & rInstallSetID)
63 {
64     rBuildID = UNISTRING( "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("version") ":ProductBuildid}" );
65     rtl::Bootstrap::expandMacros( rBuildID );
66     if ( ! rBuildID.getLength() )
67         return false;
68 
69     rInstallSetID = UNISTRING( "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("version") ":UpdateID}" );
70     rtl::Bootstrap::expandMacros( rInstallSetID );
71     if ( ! rInstallSetID.getLength() )
72         return false;
73 
74     rtl::OUString aValue( UNISTRING( "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("version") ":UpdateURL}" ) );
75     rtl::Bootstrap::expandMacros( aValue );
76 
77     if( aValue.getLength() > 0 )
78     {
79         rRepositoryList.realloc(1);
80         rRepositoryList[0] = aValue;
81     }
82 
83     return true;
84 }
85 
86 //------------------------------------------------------------------------------
87 
88 // Returns 'true' if successfully connected to the update server
89 bool
90 checkForUpdates(
91     UpdateInfo& o_rUpdateInfo,
92     uno::Reference< uno::XComponentContext > const & rxContext,
93     uno::Reference< task::XInteractionHandler > const & rxInteractionHandler,
94     const uno::Reference< deployment::XUpdateInformationProvider >& rUpdateInfoProvider)
95 {
96     OSL_TRACE("checking for updates ..\n");
97 
98     ::rtl::OUString myArch;
99     ::rtl::OUString myOS;
100 
101     rtl::Bootstrap::get(UNISTRING("_OS"), myOS);
102     rtl::Bootstrap::get(UNISTRING("_ARCH"), myArch);
103 
104     uno::Sequence< ::rtl::OUString > aRepositoryList;
105     ::rtl::OUString aBuildID;
106     ::rtl::OUString aInstallSetID;
107 
108     if( ! ( getBootstrapData(aRepositoryList, aBuildID, aInstallSetID) && (aRepositoryList.getLength() > 0) ) )
109         return false;
110 
111     if( !rxContext.is() )
112         throw uno::RuntimeException(
113             UNISTRING( "checkForUpdates: empty component context" ), uno::Reference< uno::XInterface >() );
114 
115     OSL_ASSERT( rxContext->getServiceManager().is() );
116 
117     // XPath implementation
118     uno::Reference< xml::xpath::XXPathAPI > xXPath(
119         rxContext->getServiceManager()->createInstanceWithContext( UNISTRING( "com.sun.star.xml.xpath.XPathAPI" ), rxContext ),
120         uno::UNO_QUERY_THROW);
121 
122     xXPath->registerNS( UNISTRING("inst"), UNISTRING("http://installation.openoffice.org/description") );
123 
124     if( rxInteractionHandler.is() )
125         rUpdateInfoProvider->setInteractionHandler(rxInteractionHandler);
126 
127     try
128     {
129 		uno::Reference< container::XEnumeration > aUpdateInfoEnumeration =
130             rUpdateInfoProvider->getUpdateInformationEnumeration( aRepositoryList, aInstallSetID );
131 
132         if ( !aUpdateInfoEnumeration.is() )
133             return false; // something went wrong ..
134 
135         rtl::OUStringBuffer aBuffer;
136         aBuffer.appendAscii("/child::inst:description[inst:os=\'");
137         aBuffer.append( myOS );
138         aBuffer.appendAscii("\' and inst:arch=\'");
139         aBuffer.append( myArch );
140         aBuffer.appendAscii("\' and inst:buildid>");
141         aBuffer.append( aBuildID );
142         aBuffer.appendAscii("]");
143 
144         rtl::OUString aXPathExpression = aBuffer.makeStringAndClear();
145 
146         while( aUpdateInfoEnumeration->hasMoreElements() )
147         {
148             deployment::UpdateInformationEntry aEntry;
149 
150             if( aUpdateInfoEnumeration->nextElement() >>= aEntry )
151             {
152                 uno::Reference< xml::dom::XNode > xNode( aEntry.UpdateDocument.get() );
153                 uno::Reference< xml::dom::XNodeList > xNodeList;
154                 try {
155                     xNodeList = xXPath->selectNodeList(xNode, aXPathExpression
156                         + UNISTRING("/inst:update/attribute::src"));
157                 } catch (css::xml::xpath::XPathException &) {
158                     // ignore
159                 }
160 
161 /*
162                 o_rUpdateInfo.Sources.push_back( DownloadSource(true,
163                     UNISTRING("http://openoffice.bouncer.osuosl.org/?product=OpenOffice.org&os=solarissparcwjre&lang=en-US&version=2.2.1") ) );
164 */
165 
166                 sal_Int32 i, imax = xNodeList->getLength();
167                 for( i = 0; i < imax; ++i )
168                 {
169                     uno::Reference< xml::dom::XNode > xNode2( xNodeList->item(i) );
170 
171                     if( xNode2.is() )
172                     {
173                         uno::Reference< xml::dom::XElement > xParent(xNode2->getParentNode(), uno::UNO_QUERY_THROW);
174                         rtl::OUString aType = xParent->getAttribute(UNISTRING("type"));
175                         bool bIsDirect = ( sal_False == aType.equalsIgnoreAsciiCaseAscii("text/html") );
176 
177                         o_rUpdateInfo.Sources.push_back( DownloadSource(bIsDirect, xNode2->getNodeValue()) );
178                     }
179                 }
180 
181                 uno::Reference< xml::dom::XNode > xNode2;
182                 try {
183                     xNode2 = xXPath->selectSingleNode(xNode, aXPathExpression
184                         + UNISTRING("/inst:version/text()"));
185                 } catch (css::xml::xpath::XPathException &) {
186                     // ignore
187                 }
188 
189                 if( xNode2.is() )
190                     o_rUpdateInfo.Version = xNode2->getNodeValue();
191 
192                 try {
193                     xNode2 = xXPath->selectSingleNode(xNode, aXPathExpression
194                         + UNISTRING("/inst:buildid/text()"));
195                 } catch (css::xml::xpath::XPathException &) {
196                     // ignore
197                 }
198 
199                 if( xNode2.is() )
200                     o_rUpdateInfo.BuildId = xNode2->getNodeValue();
201 
202                 o_rUpdateInfo.Description = aEntry.Description;
203 
204                 // Release Notes
205                 try {
206                     xNodeList = xXPath->selectNodeList(xNode, aXPathExpression
207                         + UNISTRING("/inst:relnote"));
208                 } catch (css::xml::xpath::XPathException &) {
209                     // ignore
210                 }
211                 imax = xNodeList->getLength();
212                 for( i = 0; i < imax; ++i )
213                 {
214                     uno::Reference< xml::dom::XElement > xRelNote(xNodeList->item(i), uno::UNO_QUERY);
215                     if( xRelNote.is() )
216                     {
217                         sal_Int32 pos = xRelNote->getAttribute(UNISTRING("pos")).toInt32();
218 
219                         ReleaseNote aRelNote((sal_uInt8) pos, xRelNote->getAttribute(UNISTRING("src")));
220 
221                         if( xRelNote->hasAttribute(UNISTRING("src2")) )
222                         {
223                             pos = xRelNote->getAttribute(UNISTRING("pos2")).toInt32();
224                             aRelNote.Pos2 = (sal_Int8) pos;
225                             aRelNote.URL2 = xRelNote->getAttribute(UNISTRING("src2"));
226                         }
227 
228                         o_rUpdateInfo.ReleaseNotes.push_back(aRelNote);
229                     }
230                 }
231 /*
232                 o_rUpdateInfo.ReleaseNotes.push_back(
233                     ReleaseNote(1, UNISTRING("http://qa.openoffice.org/tests/online_update_test.html"))
234                 );
235 */
236 
237                 if( o_rUpdateInfo.Sources.size() > 0 )
238                     return true;
239             }
240         }
241     }
242     catch( ... )
243     {
244         return false;
245     }
246 
247     return true;
248 }
249 
250 //------------------------------------------------------------------------------
251 bool storeExtensionUpdateInfos( const uno::Reference< uno::XComponentContext > & rxContext,
252                                 const uno::Sequence< uno::Sequence< rtl::OUString > > &rUpdateInfos )
253 {
254     bool bNotify = false;
255 
256     if ( rUpdateInfos.hasElements() )
257     {
258         rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( rxContext );
259 
260         for ( sal_Int32 i = rUpdateInfos.getLength() - 1; i >= 0; i-- )
261         {
262             bNotify |= aConfig->storeExtensionVersion( rUpdateInfos[i][0], rUpdateInfos[i][1] );
263         }
264     }
265     return bNotify;
266 }
267 
268 //------------------------------------------------------------------------------
269 // Returns 'true' if there are updates for any extension
270 
271 bool checkForExtensionUpdates( const uno::Reference< uno::XComponentContext > & rxContext )
272 {
273     uno::Sequence< uno::Sequence< rtl::OUString > > aUpdateList;
274 
275     uno::Reference< deployment::XPackageInformationProvider > xInfoProvider;
276     try
277     {
278         uno::Any aValue( rxContext->getValueByName(
279                 UNISTRING( "/singletons/com.sun.star.deployment.PackageInformationProvider" ) ) );
280         OSL_VERIFY( aValue >>= xInfoProvider );
281     }
282     catch( const uno::Exception& )
283     {
284         OSL_ENSURE( false, "checkForExtensionUpdates: could not create the PackageInformationProvider!" );
285     }
286 
287     if ( !xInfoProvider.is() ) return false;
288 
289     aUpdateList = xInfoProvider->isUpdateAvailable( ::rtl::OUString() );
290     bool bNotify = storeExtensionUpdateInfos( rxContext, aUpdateList );
291 
292     return bNotify;
293 }
294 
295 //------------------------------------------------------------------------------
296 // Returns 'true' if there are any pending updates for any extension (offline check)
297 
298 bool checkForPendingUpdates( const uno::Reference< uno::XComponentContext > & rxContext )
299 {
300     uno::Sequence< uno::Sequence< rtl::OUString > > aExtensionList;
301     uno::Reference< deployment::XPackageInformationProvider > xInfoProvider;
302     try
303     {
304         uno::Any aValue( rxContext->getValueByName(
305                 UNISTRING( "/singletons/com.sun.star.deployment.PackageInformationProvider" ) ) );
306         OSL_VERIFY( aValue >>= xInfoProvider );
307     }
308     catch( const uno::Exception& )
309     {
310         OSL_ENSURE( false, "checkForExtensionUpdates: could not create the PackageInformationProvider!" );
311     }
312 
313     if ( !xInfoProvider.is() ) return false;
314 
315     bool bPendingUpdateFound = false;
316 
317     aExtensionList = xInfoProvider->getExtensionList();
318     if ( aExtensionList.hasElements() )
319     {
320         rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( rxContext );
321 
322         for ( sal_Int32 i = aExtensionList.getLength() - 1; i >= 0; i-- )
323         {
324             bPendingUpdateFound = aConfig->checkExtensionVersion( aExtensionList[i][0], aExtensionList[i][1] );
325             if ( bPendingUpdateFound )
326                 break;
327         }
328     }
329 
330     return bPendingUpdateFound;
331 }
332