xref: /trunk/main/extensions/source/update/check/updateprotocol.cxx (revision 9d1a529d5e69cdb76097a23891d8b0f9baa85789)
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
getBootstrapData(uno::Sequence<::rtl::OUString> & rRepositoryList,::rtl::OUString & rBuildID,::rtl::OUString & rInstallSetID)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( "${$OOO_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( "${$OOO_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
checkForUpdates(UpdateInfo & o_rUpdateInfo,uno::Reference<uno::XComponentContext> const & rxContext,uno::Reference<task::XInteractionHandler> const & rxInteractionHandler,const uno::Reference<deployment::XUpdateInformationProvider> & rUpdateInfoProvider)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                 // xNodeList may be null if selectNodeList() above threw and was swallowed
167                 sal_Int32 i, imax = xNodeList.is() ? xNodeList->getLength() : 0;
168                 for( i = 0; i < imax; ++i )
169                 {
170                     uno::Reference< xml::dom::XNode > xNode2( xNodeList->item(i) );
171 
172                     if( xNode2.is() )
173                     {
174                         uno::Reference< xml::dom::XElement > xParent(xNode2->getParentNode(), uno::UNO_QUERY_THROW);
175                         rtl::OUString aType = xParent->getAttribute(UNISTRING("type"));
176                         bool bIsDirect = ( sal_False == aType.equalsIgnoreAsciiCaseAscii("text/html") );
177 
178                         o_rUpdateInfo.Sources.push_back( DownloadSource(bIsDirect, xNode2->getNodeValue()) );
179                     }
180                 }
181 
182                 uno::Reference< xml::dom::XNode > xNode2;
183                 try {
184                     xNode2 = xXPath->selectSingleNode(xNode, aXPathExpression
185                         + UNISTRING("/inst:version/text()"));
186                 } catch (css::xml::xpath::XPathException &) {
187                     // ignore
188                 }
189 
190                 if( xNode2.is() )
191                     o_rUpdateInfo.Version = xNode2->getNodeValue();
192 
193                 try {
194                     xNode2 = xXPath->selectSingleNode(xNode, aXPathExpression
195                         + UNISTRING("/inst:buildid/text()"));
196                 } catch (css::xml::xpath::XPathException &) {
197                     // ignore
198                 }
199 
200                 if( xNode2.is() )
201                     o_rUpdateInfo.BuildId = xNode2->getNodeValue();
202 
203                 o_rUpdateInfo.Description = aEntry.Description;
204 
205                 // Release Notes
206                 // reset, so a swallowed exception below does not leave us
207                 // iterating the previous (sources) node list
208                 xNodeList.clear();
209                 try {
210                     xNodeList = xXPath->selectNodeList(xNode, aXPathExpression
211                         + UNISTRING("/inst:relnote"));
212                 } catch (css::xml::xpath::XPathException &) {
213                     // ignore
214                 }
215                 imax = xNodeList.is() ? xNodeList->getLength() : 0;
216                 for( i = 0; i < imax; ++i )
217                 {
218                     uno::Reference< xml::dom::XElement > xRelNote(xNodeList->item(i), uno::UNO_QUERY);
219                     if( xRelNote.is() )
220                     {
221                         sal_Int32 pos = xRelNote->getAttribute(UNISTRING("pos")).toInt32();
222 
223                         ReleaseNote aRelNote((sal_uInt8) pos, xRelNote->getAttribute(UNISTRING("src")));
224 
225                         if( xRelNote->hasAttribute(UNISTRING("src2")) )
226                         {
227                             pos = xRelNote->getAttribute(UNISTRING("pos2")).toInt32();
228                             aRelNote.Pos2 = (sal_Int8) pos;
229                             aRelNote.URL2 = xRelNote->getAttribute(UNISTRING("src2"));
230                         }
231 
232                         o_rUpdateInfo.ReleaseNotes.push_back(aRelNote);
233                     }
234                 }
235 /*
236                 o_rUpdateInfo.ReleaseNotes.push_back(
237                     ReleaseNote(1, UNISTRING("http://qa.openoffice.org/tests/online_update_test.html"))
238                 );
239 */
240 
241                 if( o_rUpdateInfo.Sources.size() > 0 )
242                     return true;
243             }
244         }
245     }
246     catch( ... )
247     {
248         return false;
249     }
250 
251     return true;
252 }
253 
254 //------------------------------------------------------------------------------
storeExtensionUpdateInfos(const uno::Reference<uno::XComponentContext> & rxContext,const uno::Sequence<uno::Sequence<rtl::OUString>> & rUpdateInfos)255 bool storeExtensionUpdateInfos( const uno::Reference< uno::XComponentContext > & rxContext,
256                                 const uno::Sequence< uno::Sequence< rtl::OUString > > &rUpdateInfos )
257 {
258     bool bNotify = false;
259 
260     if ( rUpdateInfos.hasElements() )
261     {
262         rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( rxContext );
263 
264         for ( sal_Int32 i = rUpdateInfos.getLength() - 1; i >= 0; i-- )
265         {
266             bNotify |= aConfig->storeExtensionVersion( rUpdateInfos[i][0], rUpdateInfos[i][1] );
267         }
268     }
269     return bNotify;
270 }
271 
272 //------------------------------------------------------------------------------
273 // Returns 'true' if there are updates for any extension
274 
checkForExtensionUpdates(const uno::Reference<uno::XComponentContext> & rxContext)275 bool checkForExtensionUpdates( const uno::Reference< uno::XComponentContext > & rxContext )
276 {
277     uno::Sequence< uno::Sequence< rtl::OUString > > aUpdateList;
278 
279     uno::Reference< deployment::XPackageInformationProvider > xInfoProvider;
280     try
281     {
282         uno::Any aValue( rxContext->getValueByName(
283                 UNISTRING( "/singletons/com.sun.star.deployment.PackageInformationProvider" ) ) );
284         OSL_VERIFY( aValue >>= xInfoProvider );
285     }
286     catch( const uno::Exception& )
287     {
288         OSL_ENSURE( false, "checkForExtensionUpdates: could not create the PackageInformationProvider!" );
289     }
290 
291     if ( !xInfoProvider.is() ) return false;
292 
293     aUpdateList = xInfoProvider->isUpdateAvailable( ::rtl::OUString() );
294     bool bNotify = storeExtensionUpdateInfos( rxContext, aUpdateList );
295 
296     return bNotify;
297 }
298 
299 //------------------------------------------------------------------------------
300 // Returns 'true' if there are any pending updates for any extension (offline check)
301 
checkForPendingUpdates(const uno::Reference<uno::XComponentContext> & rxContext)302 bool checkForPendingUpdates( const uno::Reference< uno::XComponentContext > & rxContext )
303 {
304     uno::Sequence< uno::Sequence< rtl::OUString > > aExtensionList;
305     uno::Reference< deployment::XPackageInformationProvider > xInfoProvider;
306     try
307     {
308         uno::Any aValue( rxContext->getValueByName(
309                 UNISTRING( "/singletons/com.sun.star.deployment.PackageInformationProvider" ) ) );
310         OSL_VERIFY( aValue >>= xInfoProvider );
311     }
312     catch( const uno::Exception& )
313     {
314         OSL_ENSURE( false, "checkForExtensionUpdates: could not create the PackageInformationProvider!" );
315     }
316 
317     if ( !xInfoProvider.is() ) return false;
318 
319     bool bPendingUpdateFound = false;
320 
321     aExtensionList = xInfoProvider->getExtensionList();
322     if ( aExtensionList.hasElements() )
323     {
324         rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( rxContext );
325 
326         for ( sal_Int32 i = aExtensionList.getLength() - 1; i >= 0; i-- )
327         {
328             bPendingUpdateFound = aConfig->checkExtensionVersion( aExtensionList[i][0], aExtensionList[i][1] );
329             if ( bPendingUpdateFound )
330                 break;
331         }
332     }
333 
334     return bPendingUpdateFound;
335 }
336