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