/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* MSWorksCalcImportFilter: Sets up the filter, and calls DocumentCollector * to do the actual filtering * * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include "WPFTEncodingDialog.hxx" #include "WPFTResMgr.hxx" #include "MSWorksCalcImportFilter.hxx" #include "strings.hrc" #include using namespace ::com::sun::star; using uno::Sequence; using uno::XInterface; using uno::Exception; using uno::RuntimeException; using uno::XComponentContext; namespace MSWorksCalcImportFilterInternal { /// returns the list of stream name present in a folder uno::Reference getResultSet(const css::uno::Reference &xPackageContent) try { if (xPackageContent.is()) { ucbhelper::Content packageContent(xPackageContent, uno::Reference(), comphelper::getProcessComponentContext()); uno::Sequence lPropNames { "Title" }; uno::Reference xResultSet(packageContent.createCursor(lPropNames, ucbhelper::INCLUDE_DOCUMENTS_ONLY)); return xResultSet; } return uno::Reference(); } catch (...) { SAL_WARN("writerperfect", "ignoring Exception in MSWorksCalcImportFilterInternal:getResultSet"); return uno::Reference(); } /** internal class used to create a structured RVNGInputStream from a list of path and their short names */ class FolderStream: public librevenge::RVNGInputStream { public: //! constructor explicit FolderStream(const css::uno::Reference &xContent) : librevenge::RVNGInputStream(), m_xContent(xContent), m_nameToPathMap() { } //! add a file void addFile(rtl::OUString const &path, std::string const &shortName) { m_nameToPathMap[shortName]=path; } /**! reads numbytes data. * \return a pointer to the read elements */ const unsigned char *read(unsigned long, unsigned long &) override { return nullptr; } //! returns actual offset position long tell() override { return 0; } /*! \brief seeks to a offset position, from actual, beginning or ending position * \return 0 if ok */ int seek(long, librevenge::RVNG_SEEK_TYPE) override { return 1; } //! returns true if we are at the end of the section/file bool isEnd() override { return true; } /** returns true if the stream is ole \sa returns always false*/ bool isStructured() override { return true; } /** returns the number of sub streams. \sa returns always 2*/ unsigned subStreamCount() override { return unsigned(m_nameToPathMap.size()); } /** returns the ith sub streams name */ const char *subStreamName(unsigned id) override { std::map::const_iterator it=m_nameToPathMap.begin(); for (unsigned i=0; ifirst.c_str(); } /** returns true if a substream with name exists */ bool existsSubStream(const char *name) override { return name && m_nameToPathMap.find(name)!= m_nameToPathMap.end(); } /** return a new stream for a ole zone */ librevenge::RVNGInputStream *getSubStreamByName(const char *name) override { if (m_nameToPathMap.find(name)== m_nameToPathMap.end() || !m_xContent.is()) return nullptr; try { const uno::Reference xResultSet=getResultSet(m_xContent); if (xResultSet.is() && xResultSet->first()) { const uno::Reference xContentAccess(xResultSet, uno::UNO_QUERY_THROW); const uno::Reference xRow(xResultSet, uno::UNO_QUERY_THROW); OUString lPath=m_nameToPathMap.find(name)->second; do { const rtl::OUString aTitle(xRow->getString(1)); if (aTitle != lPath) continue; const uno::Reference xSubContent(xContentAccess->queryContent()); ucbhelper::Content aSubContent(xSubContent, uno::Reference(), comphelper::getProcessComponentContext()); uno::Reference xInputStream = aSubContent.openStream(); if (xInputStream.is()) return new writerperfect::WPXSvInputStream(xInputStream); break; } while (xResultSet->next()); } } catch (...) { SAL_WARN("writerperfect", "ignoring Exception in MSWorksCalcImportFilterInternal::FolderStream::getSubStreamByName"); } return nullptr; } /** return a new stream for a ole zone */ librevenge::RVNGInputStream *getSubStreamById(unsigned id) override { char const *name=subStreamName(id); return name ? getSubStreamByName(name) : nullptr; } private: /// the main container uno::Reference m_xContent; /// the map short name to path std::map m_nameToPathMap; FolderStream(const FolderStream &) = delete; FolderStream &operator=(const FolderStream &) = delete; }; } //////////////////////////////////////////////////////////// bool MSWorksCalcImportFilter::doImportDocument(librevenge::RVNGInputStream &rInput, OdsGenerator &rGenerator, utl::MediaDescriptor &) { libwps::WPSKind kind = libwps::WPS_TEXT; libwps::WPSCreator creator; bool needEncoding; const libwps::WPSConfidence confidence = libwps::WPSDocument::isFileFormatSupported(&rInput, kind, creator, needEncoding); if ((kind != libwps::WPS_SPREADSHEET && kind != libwps::WPS_DATABASE) || (confidence == libwps::WPS_CONFIDENCE_NONE)) return false; std::string fileEncoding; if (needEncoding) { OUString title, encoding; if (creator == libwps::WPS_MSWORKS) { title=WpResId(STR_ENCODING_DIALOG_TITLE_MSWORKS); encoding="CP850"; } else if (creator == libwps::WPS_LOTUS) { title=WpResId(STR_ENCODING_DIALOG_TITLE_LOTUS); encoding="CP437"; } else if (creator == libwps::WPS_SYMPHONY) { title=WpResId(STR_ENCODING_DIALOG_TITLE_SYMPHONY); encoding="CP437"; } else { title=WpResId(STR_ENCODING_DIALOG_TITLE_QUATTROPRO); encoding="CP437"; } try { const ScopedVclPtrInstance pDlg(title, encoding); if (pDlg->Execute() == RET_OK) { if (!pDlg->GetEncoding().isEmpty()) fileEncoding=pDlg->GetEncoding().toUtf8().getStr(); } // we can fail because we are in headless mode, the user has cancelled conversion, ... else if (pDlg->hasUserCalledCancel()) return false; } catch (...) { SAL_WARN("writerperfect", "ignoring Exception in MSWorksCalcImportFilter::doImportDocument"); } } OString aUtf8Passwd; if (confidence==libwps::WPS_CONFIDENCE_SUPPORTED_ENCRYPTION) { // try to ask for a password try { ScopedVclPtrInstance< SfxPasswordDialog > aPasswdDlg(nullptr); aPasswdDlg->SetMinLen(1); if (!aPasswdDlg->Execute()) return false; OUString aPasswd = aPasswdDlg->GetPassword(); aUtf8Passwd = OUStringToOString(aPasswd, RTL_TEXTENCODING_UTF8); } catch (...) { return false; } } return libwps::WPS_OK == libwps::WPSDocument::parse(&rInput, &rGenerator, confidence==libwps::WPS_CONFIDENCE_SUPPORTED_ENCRYPTION ? aUtf8Passwd.getStr() : nullptr, fileEncoding.c_str()); } //XExtendedFilterDetection sal_Bool MSWorksCalcImportFilter::filter(const css::uno::Sequence< css::beans::PropertyValue > &rDescriptor) { OUString sUrl; css::uno::Reference < css::io::XInputStream > xInputStream; css::uno::Reference < ucb::XContent > xContent; sal_Int32 nLength = rDescriptor.getLength(); const css::beans::PropertyValue *pValue = rDescriptor.getConstArray(); for (sal_Int32 i = 0 ; i < nLength; i++) { if (pValue[i].Name == "InputStream") pValue[i].Value >>= xInputStream; else if (pValue[i].Name == "UCBContent") pValue[i].Value >>= xContent; else if (pValue[i].Name == "FileName" || pValue[i].Name == "URL") pValue[i].Value >>= sUrl; } if (!getXContext().is() || !xInputStream.is()) { OSL_ASSERT(false); return false; } // An XML import service: what we push sax messages to.. css::uno::Reference < css::xml::sax::XDocumentHandler > xInternalHandler (getXContext()->getServiceManager()->createInstanceWithContext (writerperfect::DocumentHandlerFor::name(), getXContext()), css::uno::UNO_QUERY_THROW); // The XImporter sets up an empty target document for XDocumentHandler to write to.. css::uno::Reference < css::document::XImporter > xImporter(xInternalHandler, css::uno::UNO_QUERY); xImporter->setTargetDocument(getTargetDocument()); // OO Graphics Handler: abstract class to handle document SAX messages, concrete implementation here // writes to in-memory target doc writerperfect::DocumentHandler aHandler(xInternalHandler); writerperfect::WPXSvInputStream input(xInputStream); OdsGenerator exporter; exporter.addDocumentHandler(&aHandler, ODF_FLAT_XML); this->doRegisterHandlers(exporter); utl::MediaDescriptor aDescriptor(rDescriptor); try { // time to check if the file is a WK3 file and a FM3 file is // present bool checkForFM3=false; if (input.seek(0, librevenge::RVNG_SEEK_SET)==0 && xContent.is() && INetURLObject(sUrl).getExtension().equalsIgnoreAsciiCase("WK3")) { // check if the file header corresponds to a .wk3 file unsigned long numBytesRead; const unsigned char *data=input.read(6, numBytesRead); if (data && numBytesRead==6 && data[0]==0 && data[1]==0 && data[2]==0x1a && data[3]==0 && data[4]<2 && data[5]==0x10) checkForFM3=true; } if (checkForFM3) { // check if the format file exists const css::uno::Reference < container::XChild > xChild(xContent, uno::UNO_QUERY); if (xChild.is()) { rtl::OUString sWM3Name; rtl::OUString sFM3Name; const css::uno::Reference < ucb::XContent > xPackageContent(xChild->getParent(), uno::UNO_QUERY); uno::Reference xResultSet=MSWorksCalcImportFilterInternal::getResultSet(xPackageContent); if (xResultSet.is() && xResultSet->first()) { const uno::Reference xContentAccess(xResultSet, uno::UNO_QUERY_THROW); const uno::Reference xRow(xResultSet, uno::UNO_QUERY_THROW); INetURLObject aTmpUrl(sUrl); sWM3Name = aTmpUrl.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset); aTmpUrl.setExtension("FM3"); const rtl::OUString &sTestFM3Name = aTmpUrl.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset); do { const rtl::OUString &aTitle(xRow->getString(1)); if (aTitle.equalsIgnoreAsciiCase(sTestFM3Name)) sFM3Name = aTitle; } while (xResultSet->next() && sFM3Name.isEmpty()); } if (!sFM3Name.isEmpty()) { MSWorksCalcImportFilterInternal::FolderStream structuredInput(xPackageContent); structuredInput.addFile(sWM3Name,"WK3"); structuredInput.addFile(sFM3Name,"FM3"); libwps::WPSKind kind = libwps::WPS_TEXT; libwps::WPSCreator creator; bool needEncoding; const libwps::WPSConfidence confidence = libwps::WPSDocument::isFileFormatSupported(&structuredInput, kind, creator, needEncoding); if (confidence!=libwps::WPS_CONFIDENCE_NONE) return this->doImportDocument(structuredInput, exporter, aDescriptor); } } } } catch (...) { } return this->doImportDocument(input, exporter, aDescriptor); } bool MSWorksCalcImportFilter::doDetectFormat(librevenge::RVNGInputStream &rInput, OUString &rTypeName) { libwps::WPSKind kind = libwps::WPS_TEXT; libwps::WPSCreator creator; bool needEncoding; const libwps::WPSConfidence confidence = libwps::WPSDocument::isFileFormatSupported(&rInput, kind, creator, needEncoding); if ((kind == libwps::WPS_SPREADSHEET || kind == libwps::WPS_DATABASE) && confidence != libwps::WPS_CONFIDENCE_NONE) { if (creator == libwps::WPS_MSWORKS) { rTypeName = "calc_MS_Works_Document"; return true; } if (creator == libwps::WPS_LOTUS || creator == libwps::WPS_SYMPHONY) { rTypeName = "calc_WPS_Lotus_Document"; return true; } if (creator == libwps::WPS_QUATTRO_PRO) { rTypeName = "calc_WPS_QPro_Document"; return true; } } return false; } void MSWorksCalcImportFilter::doRegisterHandlers(OdsGenerator &) { } // XServiceInfo OUString SAL_CALL MSWorksCalcImportFilter::getImplementationName() { return OUString("com.sun.star.comp.Calc.MSWorksCalcImportFilter"); } sal_Bool SAL_CALL MSWorksCalcImportFilter::supportsService(const OUString &rServiceName) { return cppu::supportsService(this, rServiceName); } Sequence< OUString > SAL_CALL MSWorksCalcImportFilter::getSupportedServiceNames() { Sequence < OUString > aRet(2); OUString *pArray = aRet.getArray(); pArray[0] = "com.sun.star.document.ImportFilter"; pArray[1] = "com.sun.star.document.ExtendedTypeDetection"; return aRet; } extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *SAL_CALL com_sun_star_comp_Calc_MSWorksCalcImportFilter_get_implementation( css::uno::XComponentContext *const context, const css::uno::Sequence &) { return cppu::acquire(new MSWorksCalcImportFilter(context)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */