1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include "precompiled_sfx2.hxx" 29 30 #include <sfx2/DocumentMetadataAccess.hxx> 31 32 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> 33 #include <com/sun/star/beans/XPropertySet.hpp> 34 #include <com/sun/star/embed/ElementModes.hpp> 35 #include <com/sun/star/embed/XStorage.hpp> 36 #include <com/sun/star/embed/XTransactedObject.hpp> 37 #include <com/sun/star/task/ErrorCodeIOException.hpp> 38 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp> 39 #include <com/sun/star/rdf/FileFormat.hpp> 40 #include <com/sun/star/rdf/URIs.hpp> 41 #include <com/sun/star/rdf/Statement.hpp> 42 #include <com/sun/star/rdf/Literal.hpp> 43 #include <com/sun/star/rdf/URI.hpp> 44 #include <com/sun/star/rdf/Repository.hpp> 45 46 #include <rtl/uuid.h> 47 #include <rtl/ustrbuf.hxx> 48 #include <rtl/uri.hxx> 49 #include <rtl/bootstrap.hxx> 50 51 #include <comphelper/interaction.hxx> 52 #include <comphelper/makesequence.hxx> 53 #include <comphelper/mediadescriptor.hxx> 54 #include <comphelper/sequenceasvector.hxx> 55 #include <comphelper/storagehelper.hxx> 56 57 #include <sfx2/docfile.hxx> 58 #include <sfx2/XmlIdRegistry.hxx> 59 60 #include <libxml/tree.h> // for xmlValidateNCName 61 62 #include <boost/bind.hpp> 63 #include <boost/shared_array.hpp> 64 #include <boost/tuple/tuple.hpp> 65 66 #include <vector> 67 #include <set> 68 #include <map> 69 #include <functional> 70 #include <algorithm> 71 72 #include <unotools/ucbhelper.hxx> 73 #include <com/sun/star/uri/XUriReference.hpp> 74 #include <com/sun/star/uri/XUriReferenceFactory.hpp> 75 #include <com/sun/star/uri/XVndSunStarPkgUrlReferenceFactory.hpp> 76 77 78 /* 79 Note: in the context of this implementation, all rdf.QueryExceptions and 80 rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such. 81 82 This implementation assumes that it is only used with ODF documents, not mere 83 ODF packages. In other words, we enforce that metadata files must not be 84 called reserved names. 85 */ 86 87 using namespace ::com::sun::star; 88 89 namespace sfx2 { 90 91 92 bool isValidNCName(::rtl::OUString const & i_rIdref) 93 { 94 const ::rtl::OString id( 95 ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) ); 96 return !(xmlValidateNCName( 97 reinterpret_cast<const unsigned char*>(id.getStr()), 0)); 98 } 99 100 //////////////////////////////////////////////////////////////////////////// 101 102 static const char s_content [] = "content.xml"; 103 static const char s_styles [] = "styles.xml"; 104 static const char s_meta [] = "meta.xml"; 105 static const char s_settings[] = "settings.xml"; 106 static const char s_manifest[] = "manifest.rdf"; 107 static const char s_rdfxml [] = "application/rdf+xml"; 108 static const char s_odfmime [] = "application/vnd.oasis.opendocument."; 109 110 //////////////////////////////////////////////////////////////////////////// 111 112 static bool isContentFile(::rtl::OUString const & i_rPath) 113 { 114 return i_rPath.equalsAscii(s_content); 115 } 116 117 static bool isStylesFile (::rtl::OUString const & i_rPath) 118 { 119 return i_rPath.equalsAscii(s_styles); 120 } 121 122 static bool isReservedFile(::rtl::OUString const & i_rPath) 123 { 124 return isContentFile(i_rPath) 125 || isStylesFile(i_rPath) 126 || i_rPath.equalsAscii(s_meta) 127 || i_rPath.equalsAscii(s_settings); 128 } 129 130 //////////////////////////////////////////////////////////////////////////// 131 132 uno::Reference<rdf::XURI> createBaseURI( 133 uno::Reference<uno::XComponentContext> const & i_xContext, 134 uno::Reference<embed::XStorage> const & i_xStorage, 135 ::rtl::OUString const & i_rPkgURI, ::rtl::OUString const & i_rSubDocument) 136 { 137 if (!i_xContext.is() || !i_xStorage.is() || !i_rPkgURI.getLength()) { 138 throw uno::RuntimeException(); 139 } 140 141 // #i108078# workaround non-hierarchical vnd.sun.star.expand URIs 142 // this really should be done somewhere else, not here. 143 ::rtl::OUString pkgURI(i_rPkgURI); 144 if (pkgURI.matchIgnoreAsciiCaseAsciiL( 145 RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:"))) 146 { 147 // expand it here (makeAbsolute requires hierarchical URI) 148 pkgURI = pkgURI.copy( RTL_CONSTASCII_LENGTH("vnd.sun.star.expand:") ); 149 if (pkgURI.getLength() != 0) { 150 pkgURI = ::rtl::Uri::decode( 151 pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8); 152 if (pkgURI.getLength() == 0) { 153 throw uno::RuntimeException(); 154 } 155 ::rtl::Bootstrap::expandMacros(pkgURI); 156 } 157 } 158 159 const uno::Reference<lang::XMultiComponentFactory> xServiceFactory( 160 i_xContext->getServiceManager(), uno::UNO_SET_THROW); 161 const uno::Reference<uri::XUriReferenceFactory> xUriFactory( 162 xServiceFactory->createInstanceWithContext( 163 ::rtl::OUString::createFromAscii( 164 "com.sun.star.uri.UriReferenceFactory"), i_xContext), 165 uno::UNO_QUERY_THROW); 166 uno::Reference< uri::XUriReference > xBaseURI; 167 168 const uno::Reference< uri::XUriReference > xPkgURI( 169 xUriFactory->parse(pkgURI), uno::UNO_SET_THROW ); 170 xPkgURI->clearFragment(); 171 172 // need to know whether the storage is a FileSystemStorage 173 // XServiceInfo would be better, but it is not implemented 174 // if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) ) 175 if (true) { 176 xBaseURI.set( xPkgURI, uno::UNO_SET_THROW ); 177 #if 0 178 } else { 179 const uno::Reference<uri::XVndSunStarPkgUrlReferenceFactory> 180 xPkgUriFactory( xServiceFactory->createInstanceWithContext( 181 ::rtl::OUString::createFromAscii( 182 "com.sun.star.uri.VndSunStarPkgUrlReferenceFactory"), 183 i_xContext), 184 uno::UNO_QUERY_THROW); 185 xBaseURI.set( xPkgUriFactory->createVndSunStarPkgUrlReference(xPkgURI), 186 uno::UNO_SET_THROW ); 187 #endif 188 } 189 ::rtl::OUStringBuffer buf; 190 if (!xBaseURI->getUriReference().endsWithAsciiL("/", 1)) 191 { 192 const sal_Int32 count( xBaseURI->getPathSegmentCount() ); 193 if (count > 0) 194 { 195 const ::rtl::OUString last( xBaseURI->getPathSegment(count - 1) ); 196 buf.append(last); 197 } 198 buf.append(static_cast<sal_Unicode>('/')); 199 } 200 if (i_rSubDocument.getLength()) 201 { 202 buf.append(i_rSubDocument); 203 buf.append(static_cast<sal_Unicode>('/')); 204 } 205 const ::rtl::OUString Path(buf.makeStringAndClear()); 206 if (Path.getLength()) 207 { 208 const uno::Reference< uri::XUriReference > xPathURI( 209 xUriFactory->parse(Path), uno::UNO_SET_THROW ); 210 xBaseURI.set( 211 xUriFactory->makeAbsolute(xBaseURI, xPathURI, 212 true, uri::RelativeUriExcessParentSegments_ERROR), 213 uno::UNO_SET_THROW); 214 } 215 216 return rdf::URI::create(i_xContext, xBaseURI->getUriReference()); 217 } 218 219 //////////////////////////////////////////////////////////////////////////// 220 221 struct DocumentMetadataAccess_Impl 222 { 223 // note: these are all initialized in constructor, and loadFromStorage 224 const uno::Reference<uno::XComponentContext> m_xContext; 225 const IXmlIdRegistrySupplier & m_rXmlIdRegistrySupplier; 226 uno::Reference<rdf::XURI> m_xBaseURI; 227 uno::Reference<rdf::XRepository> m_xRepository; 228 uno::Reference<rdf::XNamedGraph> m_xManifest; 229 DocumentMetadataAccess_Impl( 230 uno::Reference<uno::XComponentContext> const& i_xContext, 231 IXmlIdRegistrySupplier const & i_rRegistrySupplier) 232 : m_xContext(i_xContext) 233 , m_rXmlIdRegistrySupplier(i_rRegistrySupplier) 234 , m_xBaseURI() 235 , m_xRepository() 236 , m_xManifest() 237 { 238 OSL_ENSURE(m_xContext.is(), "context null"); 239 } 240 }; 241 242 // this is... a hack. 243 template<sal_Int16 Constant> 244 /*static*/ uno::Reference<rdf::XURI> 245 getURI(uno::Reference< uno::XComponentContext > const & i_xContext) 246 { 247 static uno::Reference< rdf::XURI > xURI( 248 rdf::URI::createKnown(i_xContext, Constant), uno::UNO_QUERY_THROW); 249 return xURI; 250 } 251 252 253 /** would storing the file to a XStorage succeed? */ 254 static bool isFileNameValid(const ::rtl::OUString & i_rFileName) 255 { 256 if (i_rFileName.getLength() <= 0) return false; 257 if (i_rFileName[0] == '/') return false; // no absolute paths! 258 sal_Int32 idx(0); 259 do { 260 const ::rtl::OUString segment( 261 i_rFileName.getToken(0, static_cast<sal_Unicode> ('/'), idx) ); 262 if (!segment.getLength() || // no empty segments 263 segment.equalsAscii(".") || // no . segments 264 segment.equalsAscii("..") || // no .. segments 265 !::comphelper::OStorageHelper::IsValidZipEntryFileName( 266 segment, sal_False)) // no invalid characters 267 return false; 268 } while (idx >= 0); 269 return true; 270 } 271 272 /** split a uri hierarchy into first segment and rest */ 273 static bool 274 splitPath(::rtl::OUString const & i_rPath, 275 ::rtl::OUString & o_rDir, ::rtl::OUString& o_rRest) 276 { 277 const sal_Int32 idx(i_rPath.indexOf(static_cast<sal_Unicode>('/'))); 278 if (idx < 0 || idx >= i_rPath.getLength()) { 279 o_rDir = ::rtl::OUString(); 280 o_rRest = i_rPath; 281 return true; 282 } else if (idx == 0 || idx == i_rPath.getLength() - 1) { 283 // input must not start or end with '/' 284 return false; 285 } else { 286 o_rDir = (i_rPath.copy(0, idx)); 287 o_rRest = (i_rPath.copy(idx+1)); 288 return true; 289 } 290 } 291 292 static bool 293 splitXmlId(::rtl::OUString const & i_XmlId, 294 ::rtl::OUString & o_StreamName, ::rtl::OUString& o_Idref ) 295 { 296 const sal_Int32 idx(i_XmlId.indexOf(static_cast<sal_Unicode>('#'))); 297 if ((idx <= 0) || (idx >= i_XmlId.getLength() - 1)) { 298 return false; 299 } else { 300 o_StreamName = (i_XmlId.copy(0, idx)); 301 o_Idref = (i_XmlId.copy(idx+1)); 302 return isValidXmlId(o_StreamName, o_Idref); 303 } 304 } 305 306 //////////////////////////////////////////////////////////////////////////// 307 308 static uno::Reference<rdf::XURI> 309 getURIForStream(struct DocumentMetadataAccess_Impl& i_rImpl, 310 ::rtl::OUString const& i_rPath) 311 { 312 const uno::Reference<rdf::XURI> xURI( 313 rdf::URI::createNS( i_rImpl.m_xContext, 314 i_rImpl.m_xBaseURI->getStringValue(), i_rPath), 315 uno::UNO_SET_THROW); 316 return xURI; 317 } 318 319 /** add statements declaring i_xResource to be a file of type i_xType with 320 path i_rPath to manifest, with optional additional types i_pTypes */ 321 static void 322 addFile(struct DocumentMetadataAccess_Impl & i_rImpl, 323 uno::Reference<rdf::XURI> const& i_xType, 324 ::rtl::OUString const & i_rPath, 325 const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes = 0) 326 { 327 try { 328 const uno::Reference<rdf::XURI> xURI( getURIForStream( 329 i_rImpl, i_rPath) ); 330 331 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(), 332 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), 333 xURI.get()); 334 i_rImpl.m_xManifest->addStatement(xURI.get(), 335 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 336 i_xType.get()); 337 if (i_pTypes) { 338 for (sal_Int32 i = 0; i < i_pTypes->getLength(); ++i) { 339 i_rImpl.m_xManifest->addStatement(xURI.get(), 340 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 341 (*i_pTypes)[i].get()); 342 } 343 } 344 } catch (uno::RuntimeException &) { 345 throw; 346 } catch (uno::Exception & e) { 347 throw lang::WrappedTargetRuntimeException( 348 ::rtl::OUString::createFromAscii( 349 "addFile: exception"), /*this*/0, uno::makeAny(e)); 350 } 351 } 352 353 /** add content.xml or styles.xml to manifest */ 354 static bool 355 addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl, 356 const ::rtl::OUString & i_rPath) 357 { 358 uno::Reference<rdf::XURI> xType; 359 if (isContentFile(i_rPath)) { 360 xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext)); 361 } else if (isStylesFile(i_rPath)) { 362 xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext)); 363 } else { 364 return false; 365 } 366 addFile(i_rImpl, xType.get(), i_rPath); 367 return true; 368 } 369 370 /** add metadata file to manifest */ 371 static void 372 addMetadataFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl, 373 const ::rtl::OUString & i_rPath, 374 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes) 375 { 376 addFile(i_rImpl, 377 getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext), 378 i_rPath, &i_rTypes); 379 } 380 381 /** remove a file from the manifest */ 382 static void 383 removeFile(struct DocumentMetadataAccess_Impl & i_rImpl, 384 uno::Reference<rdf::XURI> const& i_xPart) 385 { 386 if (!i_xPart.is()) throw uno::RuntimeException(); 387 try { 388 i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI.get(), 389 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), 390 i_xPart.get()); 391 i_rImpl.m_xManifest->removeStatements(i_xPart.get(), 392 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 0); 393 } catch (uno::RuntimeException &) { 394 throw; 395 } catch (uno::Exception & e) { 396 throw lang::WrappedTargetRuntimeException( 397 ::rtl::OUString::createFromAscii("removeFile: exception"), 398 0, uno::makeAny(e)); 399 } 400 } 401 402 static ::std::vector< uno::Reference< rdf::XURI > > 403 getAllParts(struct DocumentMetadataAccess_Impl & i_rImpl) 404 { 405 ::std::vector< uno::Reference< rdf::XURI > > ret; 406 try { 407 const uno::Reference<container::XEnumeration> xEnum( 408 i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI.get(), 409 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), 0), 410 uno::UNO_SET_THROW); 411 while (xEnum->hasMoreElements()) { 412 rdf::Statement stmt; 413 if (!(xEnum->nextElement() >>= stmt)) { 414 throw uno::RuntimeException(); 415 } 416 const uno::Reference<rdf::XURI> xPart(stmt.Object, 417 uno::UNO_QUERY); 418 if (!xPart.is()) continue; 419 ret.push_back(xPart); 420 } 421 return ret; 422 } catch (uno::RuntimeException &) { 423 throw; 424 } catch (uno::Exception & e) { 425 throw lang::WrappedTargetRuntimeException( 426 ::rtl::OUString::createFromAscii("getAllParts: exception"), 427 0, uno::makeAny(e)); 428 } 429 } 430 431 static bool 432 isPartOfType(struct DocumentMetadataAccess_Impl & i_rImpl, 433 uno::Reference<rdf::XURI> const & i_xPart, 434 uno::Reference<rdf::XURI> const & i_xType) 435 { 436 if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException(); 437 try { 438 const uno::Reference<container::XEnumeration> xEnum( 439 i_rImpl.m_xManifest->getStatements(i_xPart.get(), 440 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 441 i_xType.get()), 442 uno::UNO_SET_THROW); 443 return (xEnum->hasMoreElements()); 444 } catch (uno::RuntimeException &) { 445 throw; 446 } catch (uno::Exception & e) { 447 throw lang::WrappedTargetRuntimeException( 448 ::rtl::OUString::createFromAscii("isPartOfType: exception"), 449 0, uno::makeAny(e)); 450 } 451 } 452 453 //////////////////////////////////////////////////////////////////////////// 454 455 static ucb::InteractiveAugmentedIOException 456 mkException( ::rtl::OUString const & i_rMessage, 457 ucb::IOErrorCode const i_ErrorCode, 458 ::rtl::OUString const & i_rUri, ::rtl::OUString const & i_rResource) 459 { 460 ucb::InteractiveAugmentedIOException iaioe; 461 iaioe.Message = i_rMessage; 462 iaioe.Classification = task::InteractionClassification_ERROR; 463 iaioe.Code = i_ErrorCode; 464 465 const beans::PropertyValue uriProp(::rtl::OUString::createFromAscii("Uri"), 466 -1, uno::makeAny(i_rUri), static_cast<beans::PropertyState>(0)); 467 const beans::PropertyValue rnProp( 468 ::rtl::OUString::createFromAscii("ResourceName"), 469 -1, uno::makeAny(i_rResource), static_cast<beans::PropertyState>(0)); 470 iaioe.Arguments = ::comphelper::makeSequence( 471 uno::makeAny(uriProp), uno::makeAny(rnProp)); 472 return iaioe; 473 } 474 475 /** error handling policy. 476 <p>If a handler is given, ask it how to proceed: 477 <ul><li>(default:) cancel import, raise exception</li> 478 <li>ignore the error and continue</li> 479 <li>retry the action that led to the error</li></ul></p> 480 N.B.: must not be called before DMA is fully initalized! 481 @returns true iff caller should retry 482 */ 483 static bool 484 handleError( ucb::InteractiveAugmentedIOException const & i_rException, 485 const uno::Reference<task::XInteractionHandler> & i_xHandler) 486 { 487 if (!i_xHandler.is()) { 488 throw lang::WrappedTargetException(::rtl::OUString::createFromAscii( 489 "DocumentMetadataAccess::loadMetadataFromStorage: exception"), 490 /* *this*/ 0, uno::makeAny(i_rException)); 491 } 492 493 ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest( 494 new ::comphelper::OInteractionRequest(uno::makeAny(i_rException)) ); 495 ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry( 496 new ::comphelper::OInteractionRetry ); 497 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( 498 new ::comphelper::OInteractionApprove ); 499 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( 500 new ::comphelper::OInteractionAbort ); 501 /* this does not seem to work 502 if (i_rException.Code != ucb::IOErrorCode_WRONG_FORMAT) { 503 pRequest->addContinuation( pRetry.get() ); 504 } 505 */ 506 pRequest->addContinuation( pApprove.get() ); 507 pRequest->addContinuation( pAbort.get() ); 508 // actually call the handler 509 i_xHandler->handle( pRequest.get() ); 510 if (pRetry->wasSelected()) { 511 return true; 512 } else if (pApprove->wasSelected()) { 513 return false; 514 } else { 515 OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?"); 516 throw lang::WrappedTargetException(::rtl::OUString::createFromAscii( 517 "DocumentMetadataAccess::loadMetadataFromStorage: exception"), 518 /* *this*/ 0, uno::makeAny(i_rException)); 519 } 520 } 521 522 /** check if storage has content.xml/styles.xml; 523 e.g. ODB files seem to only have content.xml */ 524 static void 525 collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage, 526 ::rtl::OUString i_Path, 527 std::set< ::rtl::OUString > & o_rFiles) 528 { 529 static ::rtl::OUString content(::rtl::OUString::createFromAscii(s_content)); 530 static ::rtl::OUString styles (::rtl::OUString::createFromAscii(s_styles )); 531 try { 532 if (i_xStorage->hasByName(content) && 533 i_xStorage->isStreamElement(content)) 534 { 535 o_rFiles.insert(i_Path + content); 536 } 537 if (i_xStorage->hasByName(styles) && 538 i_xStorage->isStreamElement(styles)) 539 { 540 o_rFiles.insert(i_Path + styles); 541 } 542 } catch (uno::Exception &) { 543 OSL_TRACE("collectFilesFromStorage: exception?"); 544 } 545 } 546 547 /** import a metadata file into repository */ 548 static void 549 readStream(struct DocumentMetadataAccess_Impl & i_rImpl, 550 uno::Reference< embed::XStorage > const & i_xStorage, 551 ::rtl::OUString const & i_rPath, 552 ::rtl::OUString const & i_rBaseURI) 553 { 554 ::rtl::OUString dir; 555 ::rtl::OUString rest; 556 try { 557 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException(); 558 if (dir.equalsAscii("")) { 559 if (i_xStorage->isStreamElement(i_rPath)) { 560 const uno::Reference<io::XStream> xStream( 561 i_xStorage->openStreamElement(i_rPath, 562 embed::ElementModes::READ), uno::UNO_SET_THROW); 563 const uno::Reference<io::XInputStream> xInStream( 564 xStream->getInputStream(), uno::UNO_SET_THROW ); 565 const uno::Reference<rdf::XURI> xBaseURI( 566 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI)); 567 const uno::Reference<rdf::XURI> xURI( 568 rdf::URI::createNS(i_rImpl.m_xContext, 569 i_rBaseURI, i_rPath)); 570 i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML, 571 xInStream, xURI, xBaseURI); 572 } else { 573 throw mkException(::rtl::OUString::createFromAscii( 574 "readStream: is not a stream"), 575 ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath); 576 } 577 } else { 578 if (i_xStorage->isStorageElement(dir)) { 579 const uno::Reference<embed::XStorage> xDir( 580 i_xStorage->openStorageElement(dir, 581 embed::ElementModes::READ)); 582 const uno::Reference< beans::XPropertySet > xDirProps(xDir, 583 uno::UNO_QUERY_THROW); 584 try { 585 ::rtl::OUString mimeType; 586 xDirProps->getPropertyValue( 587 ::comphelper::MediaDescriptor::PROP_MEDIATYPE() ) 588 >>= mimeType; 589 if (mimeType.matchAsciiL(s_odfmime, sizeof(s_odfmime) - 1)) 590 { 591 OSL_TRACE("readStream: " 592 "refusing to recurse into embedded document"); 593 return; 594 } 595 } catch (uno::Exception &) { } 596 ::rtl::OUStringBuffer buf(i_rBaseURI); 597 buf.append(dir).append(static_cast<sal_Unicode>('/')); 598 readStream(i_rImpl, xDir, rest, buf.makeStringAndClear() ); 599 } else { 600 throw mkException(::rtl::OUString::createFromAscii( 601 "readStream: is not a directory"), 602 ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir); 603 } 604 } 605 } catch (container::NoSuchElementException & e) { 606 throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH, 607 i_rBaseURI + i_rPath, i_rPath); 608 } catch (io::IOException & e) { 609 throw mkException(e.Message, ucb::IOErrorCode_CANT_READ, 610 i_rBaseURI + i_rPath, i_rPath); 611 } catch (rdf::ParseException & e) { 612 throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT, 613 i_rBaseURI + i_rPath, i_rPath); 614 } 615 } 616 617 /** import a metadata file into repository */ 618 static void 619 importFile(struct DocumentMetadataAccess_Impl & i_rImpl, 620 uno::Reference<embed::XStorage> const & i_xStorage, 621 ::rtl::OUString const & i_rBaseURI, 622 uno::Reference<task::XInteractionHandler> const & i_xHandler, 623 ::rtl::OUString i_rPath) 624 { 625 retry: 626 try { 627 readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI); 628 } catch (ucb::InteractiveAugmentedIOException & e) { 629 if (handleError(e, i_xHandler)) goto retry; 630 } catch (uno::RuntimeException &) { 631 throw; 632 } catch (uno::Exception & e) { 633 throw lang::WrappedTargetRuntimeException( 634 ::rtl::OUString::createFromAscii("importFile: exception"), 635 0, uno::makeAny(e)); 636 } 637 } 638 639 /** actually write a metadata file to the storage */ 640 static void 641 exportStream(struct DocumentMetadataAccess_Impl & i_rImpl, 642 uno::Reference< embed::XStorage > const & i_xStorage, 643 uno::Reference<rdf::XURI> const & i_xGraphName, 644 ::rtl::OUString const & i_rFileName, 645 ::rtl::OUString const & i_rBaseURI) 646 { 647 const uno::Reference<io::XStream> xStream( 648 i_xStorage->openStreamElement(i_rFileName, 649 embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE), 650 uno::UNO_SET_THROW); 651 const uno::Reference< beans::XPropertySet > xStreamProps(xStream, 652 uno::UNO_QUERY); 653 if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage 654 xStreamProps->setPropertyValue( 655 ::rtl::OUString::createFromAscii("MediaType"), 656 uno::makeAny(::rtl::OUString::createFromAscii(s_rdfxml))); 657 } 658 const uno::Reference<io::XOutputStream> xOutStream( 659 xStream->getOutputStream(), uno::UNO_SET_THROW ); 660 const uno::Reference<rdf::XURI> xBaseURI( 661 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI)); 662 i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML, 663 xOutStream, i_xGraphName, xBaseURI); 664 } 665 666 /** write a metadata file to the storage */ 667 static void 668 writeStream(struct DocumentMetadataAccess_Impl & i_rImpl, 669 uno::Reference< embed::XStorage > const & i_xStorage, 670 uno::Reference<rdf::XURI> const & i_xGraphName, 671 ::rtl::OUString const & i_rPath, 672 ::rtl::OUString const & i_rBaseURI) 673 { 674 ::rtl::OUString dir; 675 ::rtl::OUString rest; 676 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException(); 677 try { 678 if (dir.equalsAscii("")) { 679 exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath, 680 i_rBaseURI); 681 } else { 682 const uno::Reference<embed::XStorage> xDir( 683 i_xStorage->openStorageElement(dir, 684 embed::ElementModes::WRITE)); 685 const uno::Reference< beans::XPropertySet > xDirProps(xDir, 686 uno::UNO_QUERY_THROW); 687 try { 688 ::rtl::OUString mimeType; 689 xDirProps->getPropertyValue( 690 ::comphelper::MediaDescriptor::PROP_MEDIATYPE() ) 691 >>= mimeType; 692 if (mimeType.matchAsciiL(s_odfmime, sizeof(s_odfmime) - 1)) { 693 OSL_TRACE("writeStream: " 694 "refusing to recurse into embedded document"); 695 return; 696 } 697 } catch (uno::Exception &) { } 698 ::rtl::OUStringBuffer buf(i_rBaseURI); 699 buf.append(dir).append(static_cast<sal_Unicode>('/')); 700 writeStream(i_rImpl, xDir, i_xGraphName, rest, 701 buf.makeStringAndClear()); 702 uno::Reference<embed::XTransactedObject> const xTransaction( 703 xDir, uno::UNO_QUERY); 704 if (xTransaction.is()) { 705 xTransaction->commit(); 706 } 707 } 708 } catch (uno::RuntimeException &) { 709 throw; 710 } catch (io::IOException &) { 711 throw; 712 } 713 } 714 715 static void 716 initLoading(struct DocumentMetadataAccess_Impl & i_rImpl, 717 const uno::Reference< embed::XStorage > & i_xStorage, 718 const uno::Reference<rdf::XURI> & i_xBaseURI, 719 const uno::Reference<task::XInteractionHandler> & i_xHandler) 720 { 721 retry: 722 // clear old data 723 i_rImpl.m_xManifest.clear(); 724 // init BaseURI 725 i_rImpl.m_xBaseURI = i_xBaseURI; 726 727 // create repository 728 i_rImpl.m_xRepository.clear(); 729 i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext), 730 uno::UNO_SET_THROW); 731 732 const ::rtl::OUString manifest ( 733 ::rtl::OUString::createFromAscii(s_manifest)); 734 const ::rtl::OUString baseURI( i_xBaseURI->getStringValue() ); 735 // try to delay raising errors until after initialization is done 736 uno::Any rterr; 737 ucb::InteractiveAugmentedIOException iaioe; 738 bool err(false); 739 740 const uno::Reference <rdf::XURI> xManifest( 741 getURIForStream(i_rImpl, manifest)); 742 try { 743 readStream(i_rImpl, i_xStorage, manifest, baseURI); 744 } catch (ucb::InteractiveAugmentedIOException & e) { 745 // no manifest.rdf: this is not an error in ODF < 1.2 746 if (!(ucb::IOErrorCode_NOT_EXISTING_PATH == e.Code)) { 747 iaioe = e; 748 err = true; 749 } 750 } catch (uno::Exception & e) { 751 rterr <<= e; 752 } 753 754 // init manifest graph 755 const uno::Reference<rdf::XNamedGraph> xManifestGraph( 756 i_rImpl.m_xRepository->getGraph(xManifest)); 757 i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph : 758 i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW); 759 const uno::Reference<container::XEnumeration> xEnum( 760 i_rImpl.m_xManifest->getStatements(0, 761 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 762 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get())); 763 764 // document statement 765 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(), 766 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 767 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get()); 768 769 OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null"); 770 OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null"); 771 OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null"); 772 773 if (rterr.hasValue()) { 774 throw lang::WrappedTargetRuntimeException( 775 ::rtl::OUString::createFromAscii( 776 "DocumentMetadataAccess::loadMetadataFromStorage: " 777 "exception"), 0, rterr); 778 } 779 780 if (err) { 781 if (handleError(iaioe, i_xHandler)) goto retry; 782 } 783 } 784 785 /** init Impl struct */ 786 static void init(struct DocumentMetadataAccess_Impl & i_rImpl) 787 { 788 try { 789 790 i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph( 791 getURIForStream(i_rImpl, 792 ::rtl::OUString::createFromAscii(s_manifest))), 793 uno::UNO_SET_THROW); 794 795 // insert the document statement 796 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(), 797 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 798 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get()); 799 } catch (uno::Exception & e) { 800 throw lang::WrappedTargetRuntimeException( 801 ::rtl::OUString::createFromAscii("init: unexpected exception"), 0, 802 uno::makeAny(e)); 803 } 804 805 // add top-level content files 806 if (!addContentOrStylesFileImpl(i_rImpl, 807 ::rtl::OUString::createFromAscii(s_content))) { 808 throw uno::RuntimeException(); 809 } 810 if (!addContentOrStylesFileImpl(i_rImpl, 811 ::rtl::OUString::createFromAscii(s_styles))) { 812 throw uno::RuntimeException(); 813 } 814 } 815 816 817 //////////////////////////////////////////////////////////////////////////// 818 819 DocumentMetadataAccess::DocumentMetadataAccess( 820 uno::Reference< uno::XComponentContext > const & i_xContext, 821 const IXmlIdRegistrySupplier & i_rRegistrySupplier) 822 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier)) 823 { 824 // no initalization: must call loadFrom... 825 } 826 827 DocumentMetadataAccess::DocumentMetadataAccess( 828 uno::Reference< uno::XComponentContext > const & i_xContext, 829 const IXmlIdRegistrySupplier & i_rRegistrySupplier, 830 ::rtl::OUString const & i_rURI) 831 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier)) 832 { 833 OSL_ENSURE(i_rURI.getLength(), "DMA::DMA: no URI given!"); 834 OSL_ENSURE(i_rURI.endsWithAsciiL("/", 1), "DMA::DMA: URI without / given!"); 835 if (!i_rURI.endsWithAsciiL("/", 1)) throw uno::RuntimeException(); 836 m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI)); 837 m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext), 838 uno::UNO_SET_THROW); 839 840 // init repository 841 init(*m_pImpl); 842 843 OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null"); 844 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null"); 845 OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null"); 846 } 847 848 DocumentMetadataAccess::~DocumentMetadataAccess() 849 { 850 } 851 852 853 // ::com::sun::star::rdf::XRepositorySupplier: 854 uno::Reference< rdf::XRepository > SAL_CALL 855 DocumentMetadataAccess::getRDFRepository() throw (uno::RuntimeException) 856 { 857 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized"); 858 return m_pImpl->m_xRepository; 859 } 860 861 // ::com::sun::star::rdf::XNode: 862 ::rtl::OUString SAL_CALL 863 DocumentMetadataAccess::getStringValue() throw (uno::RuntimeException) 864 { 865 return m_pImpl->m_xBaseURI->getStringValue(); 866 } 867 868 // ::com::sun::star::rdf::XURI: 869 ::rtl::OUString SAL_CALL 870 DocumentMetadataAccess::getNamespace() throw (uno::RuntimeException) 871 { 872 return m_pImpl->m_xBaseURI->getNamespace(); 873 } 874 875 ::rtl::OUString SAL_CALL 876 DocumentMetadataAccess::getLocalName() throw (uno::RuntimeException) 877 { 878 return m_pImpl->m_xBaseURI->getLocalName(); 879 } 880 881 // ::com::sun::star::rdf::XDocumentMetadataAccess: 882 uno::Reference< rdf::XMetadatable > SAL_CALL 883 DocumentMetadataAccess::getElementByMetadataReference( 884 const ::com::sun::star::beans::StringPair & i_rReference) 885 throw (uno::RuntimeException) 886 { 887 const IXmlIdRegistry * pReg( 888 m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() ); 889 if (!pReg) { 890 throw uno::RuntimeException(::rtl::OUString::createFromAscii( 891 "DocumentMetadataAccess::getElementByXmlId: no registry"), *this); 892 } 893 return pReg->GetElementByMetadataReference(i_rReference); 894 } 895 896 uno::Reference< rdf::XMetadatable > SAL_CALL 897 DocumentMetadataAccess::getElementByURI( 898 const uno::Reference< rdf::XURI > & i_xURI ) 899 throw (uno::RuntimeException, lang::IllegalArgumentException) 900 { 901 if (!i_xURI.is()) { 902 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 903 "DocumentMetadataAccess::getElementByURI: URI is null"), *this, 0); 904 } 905 906 const ::rtl::OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() ); 907 const ::rtl::OUString name( i_xURI->getStringValue() ); 908 if (!name.match(baseURI)) { 909 return 0; 910 } 911 const ::rtl::OUString relName( name.copy(baseURI.getLength()) ); 912 ::rtl::OUString path; 913 ::rtl::OUString idref; 914 if (!splitXmlId(relName, path, idref)) { 915 return 0; 916 } 917 918 return getElementByMetadataReference( beans::StringPair(path, idref) ); 919 } 920 921 922 uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL 923 DocumentMetadataAccess::getMetadataGraphsWithType( 924 const uno::Reference<rdf::XURI> & i_xType) 925 throw (uno::RuntimeException, lang::IllegalArgumentException) 926 { 927 if (!i_xType.is()) { 928 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 929 "DocumentMetadataAccess::getMetadataGraphsWithType: " 930 "type is null"), *this, 0); 931 } 932 933 ::comphelper::SequenceAsVector< uno::Reference< rdf::XURI > > ret; 934 const ::std::vector< uno::Reference< rdf::XURI > > parts( 935 getAllParts(*m_pImpl) ); 936 ::std::remove_copy_if(parts.begin(), parts.end(), 937 ::std::back_inserter(ret), 938 ::boost::bind( 939 ::std::logical_not<bool>(), 940 ::boost::bind(&isPartOfType, ::boost::ref(*m_pImpl), _1, i_xType) )); 941 return ret.getAsConstList(); 942 } 943 944 uno::Reference<rdf::XURI> SAL_CALL 945 DocumentMetadataAccess::addMetadataFile(const ::rtl::OUString & i_rFileName, 946 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes) 947 throw (uno::RuntimeException, lang::IllegalArgumentException, 948 container::ElementExistException) 949 { 950 if (!isFileNameValid(i_rFileName)) { 951 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 952 "DocumentMetadataAccess::addMetadataFile: invalid FileName"), 953 *this, 0); 954 } 955 if (isReservedFile(i_rFileName)) { 956 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 957 "DocumentMetadataAccess::addMetadataFile:" 958 "invalid FileName: reserved"), *this, 0); 959 } 960 for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) { 961 if (!i_rTypes[i].is()) { 962 throw lang::IllegalArgumentException( 963 ::rtl::OUString::createFromAscii( 964 "DocumentMetadataAccess::addMetadataFile: " 965 "null type"), *this, 2); 966 } 967 } 968 969 const uno::Reference<rdf::XURI> xGraphName( 970 getURIForStream(*m_pImpl, i_rFileName) ); 971 972 try { 973 m_pImpl->m_xRepository->createGraph(xGraphName); 974 } catch (rdf::RepositoryException & e) { 975 throw lang::WrappedTargetRuntimeException( 976 ::rtl::OUString::createFromAscii( 977 "DocumentMetadataAccess::addMetadataFile: exception"), 978 *this, uno::makeAny(e)); 979 // note: all other exceptions are propagated 980 } 981 982 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes); 983 return xGraphName; 984 } 985 986 uno::Reference<rdf::XURI> SAL_CALL 987 DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format, 988 const uno::Reference< io::XInputStream > & i_xInStream, 989 const ::rtl::OUString & i_rFileName, 990 const uno::Reference< rdf::XURI > & i_xBaseURI, 991 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes) 992 throw (uno::RuntimeException, lang::IllegalArgumentException, 993 datatransfer::UnsupportedFlavorException, 994 container::ElementExistException, rdf::ParseException, io::IOException) 995 { 996 if (!isFileNameValid(i_rFileName)) { 997 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 998 "DocumentMetadataAccess::importMetadataFile: invalid FileName"), 999 *this, 0); 1000 } 1001 if (isReservedFile(i_rFileName)) { 1002 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1003 "DocumentMetadataAccess::importMetadataFile:" 1004 "invalid FileName: reserved"), *this, 0); 1005 } 1006 for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) { 1007 if (!i_rTypes[i].is()) { 1008 throw lang::IllegalArgumentException( 1009 ::rtl::OUString::createFromAscii( 1010 "DocumentMetadataAccess::importMetadataFile: null type"), 1011 *this, 5); 1012 } 1013 } 1014 1015 const uno::Reference<rdf::XURI> xGraphName( 1016 getURIForStream(*m_pImpl, i_rFileName) ); 1017 1018 try { 1019 m_pImpl->m_xRepository->importGraph( 1020 i_Format, i_xInStream, xGraphName, i_xBaseURI); 1021 } catch (rdf::RepositoryException & e) { 1022 throw lang::WrappedTargetRuntimeException( 1023 ::rtl::OUString::createFromAscii( 1024 "DocumentMetadataAccess::importMetadataFile: " 1025 "RepositoryException"), *this, uno::makeAny(e)); 1026 // note: all other exceptions are propagated 1027 } 1028 1029 // add to manifest 1030 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes); 1031 return xGraphName; 1032 } 1033 1034 void SAL_CALL 1035 DocumentMetadataAccess::removeMetadataFile( 1036 const uno::Reference< rdf::XURI > & i_xGraphName) 1037 throw (uno::RuntimeException, lang::IllegalArgumentException, 1038 container::NoSuchElementException) 1039 { 1040 try { 1041 m_pImpl->m_xRepository->destroyGraph(i_xGraphName); 1042 } catch (rdf::RepositoryException & e) { 1043 throw lang::WrappedTargetRuntimeException( 1044 ::rtl::OUString::createFromAscii( 1045 "DocumentMetadataAccess::removeMetadataFile: " 1046 "RepositoryException"), *this, uno::makeAny(e)); 1047 // note: all other exceptions are propagated 1048 } 1049 1050 // remove file from manifest 1051 removeFile(*m_pImpl, i_xGraphName.get()); 1052 } 1053 1054 void SAL_CALL 1055 DocumentMetadataAccess::addContentOrStylesFile( 1056 const ::rtl::OUString & i_rFileName) 1057 throw (uno::RuntimeException, lang::IllegalArgumentException, 1058 container::ElementExistException) 1059 { 1060 if (!isFileNameValid(i_rFileName)) { 1061 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1062 "DocumentMetadataAccess::addContentOrStylesFile: " 1063 "invalid FileName"), *this, 0); 1064 } 1065 1066 if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) { 1067 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1068 "DocumentMetadataAccess::addContentOrStylesFile: " 1069 "invalid FileName: must end with content.xml or styles.xml"), 1070 *this, 0); 1071 } 1072 } 1073 1074 void SAL_CALL 1075 DocumentMetadataAccess::removeContentOrStylesFile( 1076 const ::rtl::OUString & i_rFileName) 1077 throw (uno::RuntimeException, lang::IllegalArgumentException, 1078 container::NoSuchElementException) 1079 { 1080 if (!isFileNameValid(i_rFileName)) { 1081 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1082 "DocumentMetadataAccess::removeContentOrStylesFile: " 1083 "invalid FileName"), *this, 0); 1084 } 1085 1086 try { 1087 const uno::Reference<rdf::XURI> xPart( 1088 getURIForStream(*m_pImpl, i_rFileName) ); 1089 const uno::Reference<container::XEnumeration> xEnum( 1090 m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI.get(), 1091 getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext), 1092 xPart.get()), 1093 uno::UNO_SET_THROW); 1094 if (!xEnum->hasMoreElements()) { 1095 throw container::NoSuchElementException( 1096 ::rtl::OUString::createFromAscii( 1097 "DocumentMetadataAccess::removeContentOrStylesFile: " 1098 "cannot find stream in manifest graph: ") + i_rFileName, 1099 *this); 1100 } 1101 1102 // remove file from manifest 1103 removeFile(*m_pImpl, xPart); 1104 1105 } catch (uno::RuntimeException &) { 1106 throw; 1107 } catch (uno::Exception & e) { 1108 throw lang::WrappedTargetRuntimeException( 1109 ::rtl::OUString::createFromAscii( 1110 "DocumentMetadataAccess::removeContentOrStylesFile: exception"), 1111 *this, uno::makeAny(e)); 1112 } 1113 } 1114 1115 void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage( 1116 const uno::Reference< embed::XStorage > & i_xStorage, 1117 const uno::Reference<rdf::XURI> & i_xBaseURI, 1118 const uno::Reference<task::XInteractionHandler> & i_xHandler) 1119 throw (uno::RuntimeException, lang::IllegalArgumentException, 1120 lang::WrappedTargetException) 1121 { 1122 if (!i_xStorage.is()) { 1123 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1124 "DocumentMetadataAccess::loadMetadataFromStorage: " 1125 "storage is null"), *this, 0); 1126 } 1127 if (!i_xBaseURI.is()) { 1128 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1129 "DocumentMetadataAccess::loadMetadataFromStorage: " 1130 "base URI is null"), *this, 1); 1131 } 1132 const ::rtl::OUString baseURI( i_xBaseURI->getStringValue()); 1133 if (baseURI.indexOf('#') >= 0) { 1134 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1135 "DocumentMetadataAccess::loadMetadataFromStorage: " 1136 "base URI not absolute"), *this, 1); 1137 } 1138 if (!baseURI.getLength() || !baseURI.endsWithAsciiL("/", 1)) { 1139 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1140 "DocumentMetadataAccess::loadMetadataFromStorage: " 1141 "base URI does not end with slash"), *this, 1); 1142 } 1143 1144 initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler); 1145 1146 std::set< ::rtl::OUString > StgFiles; 1147 collectFilesFromStorage(i_xStorage, 1148 ::rtl::OUString::createFromAscii(""), StgFiles); 1149 1150 std::vector< ::rtl::OUString > MfstMetadataFiles; 1151 1152 try { 1153 const ::std::vector< uno::Reference< rdf::XURI > > parts( 1154 getAllParts(*m_pImpl) ); 1155 const uno::Reference<rdf::XURI> xContentFile( 1156 getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext)); 1157 const uno::Reference<rdf::XURI> xStylesFile( 1158 getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext)); 1159 const uno::Reference<rdf::XURI> xMetadataFile( 1160 getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext)); 1161 const sal_Int32 len( baseURI.getLength() ); 1162 const ::rtl::OUString manifest ( 1163 ::rtl::OUString::createFromAscii(s_manifest)); 1164 for (::std::vector< uno::Reference< rdf::XURI > >::const_iterator it 1165 = parts.begin(); 1166 it != parts.end(); ++it) { 1167 const ::rtl::OUString name((*it)->getStringValue()); 1168 if (!name.match(baseURI)) { 1169 OSL_TRACE("loadMetadataFromStorage: graph not in document: %s", 1170 ::rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8) 1171 .getStr()); 1172 continue; 1173 } 1174 const ::rtl::OUString relName( name.copy(len) ); 1175 if (relName == manifest) { 1176 OSL_TRACE("loadMetadataFromStorage: " 1177 "found ourselves a recursive manifest!"); 1178 continue; 1179 } 1180 // remove found items from StgFiles 1181 StgFiles.erase(relName); 1182 if (isContentFile(relName)) { 1183 if (!isPartOfType(*m_pImpl, *it, xContentFile)) { 1184 const uno::Reference <rdf::XURI> xName( 1185 getURIForStream(*m_pImpl, relName) ); 1186 // add missing type statement 1187 m_pImpl->m_xManifest->addStatement(xName.get(), 1188 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext), 1189 xContentFile.get()); 1190 } 1191 } else if (isStylesFile(relName)) { 1192 if (!isPartOfType(*m_pImpl, *it, xStylesFile)) { 1193 const uno::Reference <rdf::XURI> xName( 1194 getURIForStream(*m_pImpl, relName) ); 1195 // add missing type statement 1196 m_pImpl->m_xManifest->addStatement(xName.get(), 1197 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext), 1198 xStylesFile.get()); 1199 } 1200 } else if (isReservedFile(relName)) { 1201 OSL_TRACE("loadMetadataFromStorage: " 1202 "reserved file name in manifest"); 1203 } else { 1204 if (isPartOfType(*m_pImpl, *it, xMetadataFile)) { 1205 MfstMetadataFiles.push_back(relName); 1206 } 1207 // do not add statement for MetadataFile; it could be 1208 // something else! just ignore it... 1209 } 1210 } 1211 } catch (uno::RuntimeException &) { 1212 throw; 1213 } catch (uno::Exception & e) { 1214 throw lang::WrappedTargetRuntimeException( 1215 ::rtl::OUString::createFromAscii( 1216 "DocumentMetadataAccess::loadMetadataFromStorage: " 1217 "exception"), *this, uno::makeAny(e)); 1218 } 1219 1220 std::for_each(StgFiles.begin(), StgFiles.end(), 1221 boost::bind(addContentOrStylesFileImpl, boost::ref(*m_pImpl), _1)); 1222 1223 std::for_each(MfstMetadataFiles.begin(), MfstMetadataFiles.end(), 1224 boost::bind(importFile, boost::ref(*m_pImpl), 1225 i_xStorage, baseURI, i_xHandler, _1)); 1226 } 1227 1228 void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage( 1229 const uno::Reference< embed::XStorage > & i_xStorage) 1230 throw (uno::RuntimeException, lang::IllegalArgumentException, 1231 lang::WrappedTargetException) 1232 { 1233 if (!i_xStorage.is()) { 1234 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1235 "DocumentMetadataAccess::storeMetadataToStorage: " 1236 "storage is null"), *this, 0); 1237 } 1238 1239 // export manifest 1240 const ::rtl::OUString manifest ( 1241 ::rtl::OUString::createFromAscii(s_manifest)); 1242 const uno::Reference <rdf::XURI> xManifest( 1243 getURIForStream(*m_pImpl, manifest) ); 1244 const ::rtl::OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() ); 1245 try { 1246 writeStream(*m_pImpl, i_xStorage, xManifest, manifest, baseURI); 1247 } catch (uno::RuntimeException &) { 1248 throw; 1249 } catch (io::IOException & e) { 1250 throw lang::WrappedTargetException( ::rtl::OUString::createFromAscii( 1251 "storeMetadataToStorage: IO exception"), *this, uno::makeAny(e)); 1252 } catch (uno::Exception & e) { 1253 throw lang::WrappedTargetRuntimeException( 1254 ::rtl::OUString::createFromAscii( 1255 "storeMetadataToStorage: exception"), *this, uno::makeAny(e)); 1256 } 1257 1258 // export metadata streams 1259 try { 1260 const uno::Sequence<uno::Reference<rdf::XURI> > graphs( 1261 m_pImpl->m_xRepository->getGraphNames()); 1262 const sal_Int32 len( baseURI.getLength() ); 1263 for (sal_Int32 i = 0; i < graphs.getLength(); ++i) { 1264 const uno::Reference<rdf::XURI> xName(graphs[i]); 1265 const ::rtl::OUString name(xName->getStringValue()); 1266 if (!name.match(baseURI)) { 1267 OSL_TRACE("storeMetadataToStorage: graph not in document: %s", 1268 ::rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8) 1269 .getStr()); 1270 continue; 1271 } 1272 const ::rtl::OUString relName( name.copy(len) ); 1273 if (relName == manifest) { 1274 continue; 1275 } 1276 if (!isFileNameValid(relName) || isReservedFile(relName)) { 1277 OSL_TRACE("storeMetadataToStorage: invalid file name: %s", 1278 ::rtl::OUStringToOString(relName, RTL_TEXTENCODING_UTF8) 1279 .getStr()); 1280 continue; 1281 } 1282 try { 1283 writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI); 1284 } catch (uno::RuntimeException &) { 1285 throw; 1286 } catch (io::IOException & e) { 1287 throw lang::WrappedTargetException( 1288 ::rtl::OUString::createFromAscii( 1289 "storeMetadataToStorage: IO exception"), 1290 *this, uno::makeAny(e)); 1291 } catch (uno::Exception & e) { 1292 throw lang::WrappedTargetRuntimeException( 1293 ::rtl::OUString::createFromAscii( 1294 "storeMetadataToStorage: exception"), 1295 *this, uno::makeAny(e)); 1296 } 1297 } 1298 } catch (rdf::RepositoryException & e) { 1299 throw lang::WrappedTargetRuntimeException( 1300 ::rtl::OUString::createFromAscii( 1301 "storeMetadataToStorage: exception"), *this, uno::makeAny(e)); 1302 } 1303 } 1304 1305 void SAL_CALL 1306 DocumentMetadataAccess::loadMetadataFromMedium( 1307 const uno::Sequence< beans::PropertyValue > & i_rMedium) 1308 throw (uno::RuntimeException, lang::IllegalArgumentException, 1309 lang::WrappedTargetException) 1310 { 1311 uno::Reference<io::XInputStream> xIn; 1312 ::comphelper::MediaDescriptor md(i_rMedium); 1313 ::rtl::OUString URL; 1314 md[ ::comphelper::MediaDescriptor::PROP_URL() ] >>= URL; 1315 ::rtl::OUString BaseURL; 1316 md[ ::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL() ] >>= BaseURL; 1317 if (md.addInputStream()) { 1318 md[ ::comphelper::MediaDescriptor::PROP_INPUTSTREAM() ] >>= xIn; 1319 } 1320 if (!xIn.is() && URL.equalsAscii("")) { 1321 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1322 "DocumentMetadataAccess::loadMetadataFromMedium: " 1323 "inalid medium: no URL, no input stream"), *this, 0); 1324 } 1325 uno::Reference<embed::XStorage> xStorage; 1326 try { 1327 const uno::Reference<lang::XMultiServiceFactory> xMsf ( 1328 m_pImpl->m_xContext->getServiceManager(), uno::UNO_QUERY_THROW); 1329 if (xIn.is()) { 1330 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream( 1331 xIn, xMsf); 1332 } else { // fallback to url 1333 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2( 1334 URL, embed::ElementModes::READ, xMsf); 1335 } 1336 } catch (uno::RuntimeException &) { 1337 throw; 1338 } catch (io::IOException &) { 1339 throw; 1340 } catch (uno::Exception & e) { 1341 throw lang::WrappedTargetException( 1342 ::rtl::OUString::createFromAscii( 1343 "DocumentMetadataAccess::loadMetadataFromMedium: " 1344 "exception"), *this, uno::makeAny(e)); 1345 } 1346 if (!xStorage.is()) { 1347 throw uno::RuntimeException(::rtl::OUString::createFromAscii( 1348 "DocumentMetadataAccess::loadMetadataFromMedium: " 1349 "cannot get Storage"), *this); 1350 } 1351 uno::Reference<rdf::XURI> xBaseURI; 1352 try { 1353 xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, BaseURL); 1354 } catch (uno::Exception &) { 1355 // fall back to URL 1356 try { 1357 xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, URL); 1358 } catch (uno::Exception &) { 1359 OSL_ENSURE(false, "cannot create base URI"); 1360 } 1361 } 1362 uno::Reference<task::XInteractionHandler> xIH; 1363 md[ ::comphelper::MediaDescriptor::PROP_INTERACTIONHANDLER() ] >>= xIH; 1364 loadMetadataFromStorage(xStorage, xBaseURI, xIH); 1365 } 1366 1367 void SAL_CALL 1368 DocumentMetadataAccess::storeMetadataToMedium( 1369 const uno::Sequence< beans::PropertyValue > & i_rMedium) 1370 throw (uno::RuntimeException, lang::IllegalArgumentException, 1371 lang::WrappedTargetException) 1372 { 1373 ::comphelper::MediaDescriptor md(i_rMedium); 1374 ::rtl::OUString URL; 1375 md[ ::comphelper::MediaDescriptor::PROP_URL() ] >>= URL; 1376 if (URL.equalsAscii("")) { 1377 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1378 "DocumentMetadataAccess::storeMetadataToMedium: " 1379 "invalid medium: no URL"), *this, 0); 1380 } 1381 1382 SfxMedium aMedium(i_rMedium); 1383 uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage()); 1384 1385 bool sfx(false); 1386 if (xStorage.is()) { 1387 sfx = true; 1388 } else { 1389 const uno::Reference<lang::XMultiServiceFactory> xMsf ( 1390 m_pImpl->m_xContext->getServiceManager(), uno::UNO_QUERY_THROW); 1391 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2( 1392 URL, embed::ElementModes::WRITE, xMsf); 1393 } 1394 1395 if (!xStorage.is()) { 1396 throw uno::RuntimeException(::rtl::OUString::createFromAscii( 1397 "DocumentMetadataAccess::storeMetadataToMedium: " 1398 "cannot get Storage"), *this); 1399 } 1400 // set MIME type of the storage 1401 ::comphelper::MediaDescriptor::const_iterator iter 1402 = md.find(::comphelper::MediaDescriptor::PROP_MEDIATYPE()); 1403 if (iter != md.end()) { 1404 uno::Reference< beans::XPropertySet > xProps(xStorage, 1405 uno::UNO_QUERY_THROW); 1406 try { 1407 // this is NOT supported in FileSystemStorage 1408 xProps->setPropertyValue( 1409 ::comphelper::MediaDescriptor::PROP_MEDIATYPE(), 1410 iter->second); 1411 } catch (uno::Exception &) { } 1412 } 1413 storeMetadataToStorage(xStorage); 1414 1415 if (sfx) { 1416 const sal_Bool bOk = aMedium.Commit(); 1417 aMedium.Close(); 1418 if ( !bOk ) { 1419 sal_uInt32 nError = aMedium.GetError(); 1420 if ( nError == ERRCODE_NONE ) { 1421 nError = ERRCODE_IO_GENERAL; 1422 } 1423 task::ErrorCodeIOException ex( ::rtl::OUString(), 1424 uno::Reference< uno::XInterface >(), nError); 1425 throw lang::WrappedTargetException(::rtl::OUString(), *this, 1426 uno::makeAny(ex)); 1427 } 1428 } 1429 } 1430 1431 } // namespace sfx2 1432 1433