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