/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sfx2.hxx"
// includes --------------------------------------------------------------
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertyAccess.hpp>
#include <com/sun/star/frame/XFrame.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/util/XURLTransformer.hpp>
#include <com/sun/star/system/SystemMailProvider.hpp>
#include <com/sun/star/system/MailClientFlags.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/container/XContainerQuery.hpp>
#include <com/sun/star/util/XModifiable.hpp>
#include <com/sun/star/frame/XModuleManager.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/security/CertificateValidity.hpp>
#include <com/sun/star/security/DocumentSignatureInformation.hpp>
#include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/frame/XDispatch.hpp>
#include <com/sun/star/frame/XStatusListener.hpp>
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
#include <com/sun/star/document/XExporter.hpp>
#include <rtl/textenc.h>
#include <rtl/uri.h>
#include <rtl/uri.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/msgbox.hxx>

#include <sfx2/mailmodelapi.hxx>
#include "sfxtypes.hxx"
#include "sfx2/sfxresid.hxx"
#include <sfx2/sfxsids.hrc>
#include "dialog.hrc"

#include <unotools/tempfile.hxx>
#include <unotools/configitem.hxx>
#include <ucbhelper/content.hxx>
#include <tools/urlobj.hxx>
#include <unotools/useroptions.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/extract.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/sequenceasvector.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <comphelper/mediadescriptor.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <vcl/svapp.hxx>
#include <cppuhelper/implbase1.hxx>

// --------------------------------------------------------------
using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;

using ::com::sun::star::system::SystemMailProvider;
using ::com::sun::star::system::XMailClient;
using ::com::sun::star::system::XMailMessage;
using ::com::sun::star::system::XSystemMailProvider;
using rtl::OUString;

namespace MailClientFlags = ::com::sun::star::system::MailClientFlags;
namespace css = ::com::sun::star;


// - class PrepareListener_Impl ------------------------------------------
class PrepareListener_Impl : public ::cppu::WeakImplHelper1< css::frame::XStatusListener >
{
    bool m_bState;
public:
        PrepareListener_Impl();
        virtual ~PrepareListener_Impl();

        // css.frame.XStatusListener
        virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& aEvent)
          throw(css::uno::RuntimeException);

        // css.lang.XEventListener
        virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent)
          throw(css::uno::RuntimeException);

        bool IsSet() const {return m_bState;}
};

/*-- 25.08.2010 14:32:49---------------------------------------------------

  -----------------------------------------------------------------------*/
PrepareListener_Impl::PrepareListener_Impl() :
    m_bState( false )
{
}
/*-- 25.08.2010 14:32:51---------------------------------------------------

  -----------------------------------------------------------------------*/
PrepareListener_Impl::~PrepareListener_Impl()
{
}
/*-- 25.08.2010 14:32:51---------------------------------------------------

  -----------------------------------------------------------------------*/
void PrepareListener_Impl::statusChanged(const css::frame::FeatureStateEvent& rEvent) throw(css::uno::RuntimeException)
{
    if( rEvent.IsEnabled )
        rEvent.State >>= m_bState;
    else
        m_bState = sal_False;
}
/*-- 25.08.2010 14:32:52---------------------------------------------------

  -----------------------------------------------------------------------*/
void PrepareListener_Impl::disposing(const css::lang::EventObject& /*rEvent*/) throw(css::uno::RuntimeException)
{
}

// class AddressList_Impl ------------------------------------------------

typedef String* AddressItemPtr_Impl;
DECLARE_LIST( AddressList_Impl, AddressItemPtr_Impl )

// class SfxMailModel -----------------------------------------------

static const char       PDF_DOCUMENT_TYPE[]   = "pdf_Portable_Document_Format";
static const sal_uInt32 PDF_DOCUMENT_TYPE_LEN = 28;

void SfxMailModel::ClearList( AddressList_Impl* pList )
{
    if ( pList )
    {
        sal_uIntPtr i, nCount = pList->Count();
        for ( i = 0; i < nCount; ++i )
            delete pList->GetObject(i);
        pList->Clear();
    }
}

void SfxMailModel::MakeValueList( AddressList_Impl* pList, String& rValueList )
{
    rValueList.Erase();
    if ( pList )
    {
        sal_uIntPtr i, nCount = pList->Count();
        for ( i = 0; i < nCount; ++i )
        {
            if ( rValueList.Len() > 0 )
                rValueList += ',';
            rValueList += *pList->GetObject(i);
        }
    }
}

sal_Bool HasDocumentValidSignature( const css::uno::Reference< css::frame::XModel >& xModel )
{
    try
    {
        css::uno::Reference< css::beans::XPropertySet > xPropSet( xModel, css::uno::UNO_QUERY );
        if ( xPropSet.is() )
        {
            Any a = xPropSet->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HasValidSignatures" )));
            sal_Bool bReturn = sal_Bool();
            if ( a >>= bReturn )
                return bReturn;
        }
    }
    catch ( css::uno::RuntimeException& )
    {
        throw;
    }
    catch ( css::uno::Exception& )
    {
    }

    return sal_False;
}

SfxMailModel::SaveResult SfxMailModel::ShowFilterOptionsDialog(
    uno::Reference< lang::XMultiServiceFactory > xSMGR,
    uno::Reference< frame::XModel > xModel,
    const rtl::OUString& rFilterName,
    const rtl::OUString& rType,
    bool bModified,
    sal_Int32& rNumArgs,
    ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rArgs )
{
    SaveResult eRet( SAVE_ERROR );

    try
    {
        uno::Sequence < beans::PropertyValue > aProps;
            ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess > xFilterCFG =
                uno::Reference< container::XNameAccess >(
                    xSMGR->createInstance(
                        ::rtl::OUString::createFromAscii( "com.sun.star.document.FilterFactory" ) ), uno::UNO_QUERY );
        css::uno::Reference< css::util::XModifiable > xModifiable( xModel, css::uno::UNO_QUERY );

        if ( !xFilterCFG.is() )
            return eRet;

        uno::Any aAny = xFilterCFG->getByName( rFilterName );

        if ( aAny >>= aProps )
        {
            sal_Int32 nPropertyCount = aProps.getLength();
            for( sal_Int32 nProperty=0; nProperty < nPropertyCount; ++nProperty )
            {
                if( aProps[nProperty].Name.equals( ::rtl::OUString::createFromAscii( "UIComponent" )) )
                {
                    ::rtl::OUString aServiceName;
                    aProps[nProperty].Value >>= aServiceName;
                    if( aServiceName.getLength() )
                    {
                        uno::Reference< ui::dialogs::XExecutableDialog > xFilterDialog(
                            xSMGR->createInstance( aServiceName ), uno::UNO_QUERY );
                        uno::Reference< beans::XPropertyAccess > xFilterProperties(
                            xFilterDialog, uno::UNO_QUERY );

                        if( xFilterDialog.is() && xFilterProperties.is() )
                        {
                            uno::Sequence< beans::PropertyValue > aPropsForDialog(1);
                            uno::Reference< document::XExporter > xExporter( xFilterDialog, uno::UNO_QUERY );

                            if ( rType.equalsAsciiL( PDF_DOCUMENT_TYPE, PDF_DOCUMENT_TYPE_LEN ))
                            {
                                //add an internal property, used to tell the dialog we want to set a different
                                //string for the ok button
                                //used in filter/source/pdf/impdialog.cxx
                                String aOkSendText( SfxResId( STR_PDF_EXPORT_SEND ));

                                uno::Sequence< beans::PropertyValue > aFilterDataValue(1);
                                aFilterDataValue[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "_OkButtonString" ));
                                aFilterDataValue[0].Value = css::uno::makeAny( ::rtl::OUString( aOkSendText ));

                                //add to the filterdata property, the only one the PDF export filter dialog will care for
                                aPropsForDialog[0].Name =  ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterData" ));
                                aPropsForDialog[0].Value = css::uno::makeAny( aFilterDataValue );

                                //when executing the dialog will merge the persistent FilterData properties
                                xFilterProperties->setPropertyValues( aPropsForDialog );
                            }

                            if( xExporter.is() )
                                xExporter->setSourceDocument(
                                    uno::Reference< lang::XComponent >( xModel, uno::UNO_QUERY ) );

                            if( xFilterDialog->execute() )
                            {
                                //get the filter data
                                uno::Sequence< beans::PropertyValue > aPropsFromDialog = xFilterProperties->getPropertyValues();

                                //add them to the args
                                for ( sal_Int32 nInd = 0; nInd < aPropsFromDialog.getLength(); nInd++ )
                                {
                                    if( aPropsFromDialog[ nInd ].Name.equals( ::rtl::OUString::createFromAscii( "FilterData" ) ) )
                                    {
                                        //found the filterdata, add to the storing argument
                                        rArgs.realloc( ++rNumArgs );
                                        rArgs[rNumArgs-1].Name = aPropsFromDialog[ nInd ].Name;
                                        rArgs[rNumArgs-1].Value = aPropsFromDialog[ nInd ].Value;
                                        break;
                                    }
                                }
                                eRet = SAVE_SUCCESSFULL;
                            }
                            else
                            {
                                // cancel from dialog, then do not send
                                // If the model is not modified, it could be modified by the dispatch calls.
                                // Therefore set back to modified = false. This should not hurt if we call
                                // on a non-modified model.
                                if ( !bModified )
                                {
                                    try
                                    {
                                        xModifiable->setModified( sal_False );
                                    }
                                    catch( com::sun::star::beans::PropertyVetoException& )
                                    {
                                    }
                                }
                                eRet = SAVE_CANCELLED;
                            }
                        }
                        break;
                    }
                }
            }
        }
    }
    catch( css::uno::RuntimeException& )
    {
        throw;
    }
    catch( uno::Exception& )
    {
    }

    return eRet;
}

sal_Int32 SfxMailModel::GetCount() const
{
    return maAttachedDocuments.size();
}

sal_Bool SfxMailModel::IsEmpty() const
{
    return maAttachedDocuments.empty();
}

SfxMailModel::SaveResult SfxMailModel::SaveDocumentAsFormat(
    const rtl::OUString& aSaveFileName,
    const css::uno::Reference< css::uno::XInterface >& xFrameOrModel,
    const rtl::OUString& rType,
    rtl::OUString& rFileNamePath )
{
    SaveResult  eRet( SAVE_ERROR );
    bool        bSendAsPDF = (rType.equalsAsciiL( PDF_DOCUMENT_TYPE, PDF_DOCUMENT_TYPE_LEN ));

    css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR  = ::comphelper::getProcessServiceFactory();
    if (!xSMGR.is())
        return eRet;

    const rtl::OUString aModuleManager( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.ModuleManager" ));
    css::uno::Reference< css::frame::XModuleManager > xModuleManager( xSMGR->createInstance( aModuleManager ), css::uno::UNO_QUERY_THROW );
    if ( !xModuleManager.is() )
        return eRet;

    rtl::OUString aModule;
    try
    {
         aModule = xModuleManager->identify( xFrameOrModel );
    }
    catch ( css::uno::RuntimeException& )
    {
        throw;
    }
    catch ( css::uno::Exception& )
    {
    }

    css::uno::Reference< css::frame::XFrame > xFrame( xFrameOrModel, css::uno::UNO_QUERY );
    css::uno::Reference< css::frame::XModel > xModel( xFrameOrModel, css::uno::UNO_QUERY );
    if ( xFrame.is() )
    {
        css::uno::Reference< css::frame::XController > xController = xFrame->getController();
        if ( xController.is() )
            xModel = xController->getModel();
    }

    // We need at least a valid module name and model reference
    if (( aModule.getLength() > 0 ) && xModel.is() )
    {
        bool bModified( false );
        bool bHasLocation( false );
        bool bStoreTo( false );

        css::uno::Reference< css::util::XModifiable > xModifiable( xModel, css::uno::UNO_QUERY );
        css::uno::Reference< css::frame::XStorable > xStorable( xModel, css::uno::UNO_QUERY );

        if ( xModifiable.is() )
            bModified = xModifiable->isModified();
        if ( xStorable.is() )
        {
            rtl::OUString aLocation = xStorable->getLocation();
            INetURLObject aFileObj( aLocation );

            bool bPrivateProtocol = ( aFileObj.GetProtocol() == INET_PROT_PRIV_SOFFICE );

            bHasLocation = ( aLocation.getLength() > 0 ) && !bPrivateProtocol;
            OSL_ASSERT( !bPrivateProtocol );
        }
        if ( rType.getLength() > 0 )
            bStoreTo = true;

        if ( xStorable.is() )
        {
            rtl::OUString aFilterName;
            rtl::OUString aTypeName( rType );
            rtl::OUString aFileName;
            rtl::OUString aExtension;

            css::uno::Reference< css::container::XContainerQuery > xContainerQuery(
                xSMGR->createInstance( rtl::OUString(
                    RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.document.FilterFactory" ))),
                    css::uno::UNO_QUERY );

            if ( bStoreTo )
            {
                // Retrieve filter from type
                css::uno::Sequence< css::beans::NamedValue > aQuery( bSendAsPDF ? 3 : 2 );
                aQuery[0].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Type" ));
                aQuery[0].Value = css::uno::makeAny( aTypeName );
                aQuery[1].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DocumentService" ));
                aQuery[1].Value = css::uno::makeAny( aModule );
                if( bSendAsPDF )
                {
                    // #i91419#
                    // FIXME: we want just an export filter. However currently we need
                    // exact flag value as detailed in the filter configuration to get it
                    // this seems to be a bug
                    // without flags we get an import filter here, which is also unwanted
                    aQuery[2].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Flags" ));
                    aQuery[2].Value = css::uno::makeAny( sal_Int32(0x80042) ); // EXPORT ALIEN 3RDPARTY
                }

                css::uno::Reference< css::container::XEnumeration > xEnumeration =
                    xContainerQuery->createSubSetEnumerationByProperties( aQuery );

                if ( xEnumeration->hasMoreElements() )
                {
                    ::comphelper::SequenceAsHashMap aFilterPropsHM( xEnumeration->nextElement() );
                    aFilterName = aFilterPropsHM.getUnpackedValueOrDefault(
                                                ::rtl::OUString::createFromAscii( "Name" ),
                                                ::rtl::OUString() );
                }

                if ( bHasLocation )
                {
                    // Retrieve filter from media descriptor
                    ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
                    rtl::OUString aOrgFilterName = aMediaDescrPropsHM.getUnpackedValueOrDefault(
                                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" )),
                                    ::rtl::OUString() );
                    if ( aOrgFilterName == aFilterName )
                    {
                        // We should save the document in the original format. Therefore this
                        // is not a storeTo operation. To support signing in this case, reset
                        // bStoreTo flag.
                        bStoreTo = false;
                    }
                }
            }
            else
            {
                if ( bHasLocation )
                {
                    // Retrieve filter from media descriptor
                    ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
                    aFilterName = aMediaDescrPropsHM.getUnpackedValueOrDefault(
                                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" )),
                                    ::rtl::OUString() );
                }

                if ( !bHasLocation || ( aFilterName.getLength() == 0 ))
                {
                    // Retrieve the user defined default filter
                    css::uno::Reference< css::container::XNameAccess > xNameAccess( xModuleManager, css::uno::UNO_QUERY );
                    try
                    {
                        ::comphelper::SequenceAsHashMap aFilterPropsHM( xNameAccess->getByName( aModule ) );
                        aFilterName = aFilterPropsHM.getUnpackedValueOrDefault(
                                                    ::rtl::OUString::createFromAscii( "ooSetupFactoryDefaultFilter" ),
                                                    ::rtl::OUString() );
                        css::uno::Reference< css::container::XNameAccess > xNameAccess2(
                            xContainerQuery, css::uno::UNO_QUERY );
                        if ( xNameAccess2.is() )
                        {
                            ::comphelper::SequenceAsHashMap aFilterPropsHM2( xNameAccess2->getByName( aFilterName ) );
                            aTypeName = aFilterPropsHM2.getUnpackedValueOrDefault(
                                                        ::rtl::OUString::createFromAscii( "Type" ),
                                                        ::rtl::OUString() );
                        }
                    }
                    catch ( css::container::NoSuchElementException& )
                    {
                    }
                    catch ( css::beans::UnknownPropertyException& )
                    {
                    }
                }
            }

            // No filter found => error
            // No type and no location => error
            if (( aFilterName.getLength() == 0 ) ||
                (( aTypeName.getLength() == 0 ) && !bHasLocation ))
                return eRet;

            // Determine filen name and extension
            if ( bHasLocation && !bStoreTo )
            {
                INetURLObject aFileObj( xStorable->getLocation() );
                aExtension = (rtl::OUString)aFileObj.getExtension();
            }
            else
            {
                css::uno::Reference< container::XNameAccess > xTypeDetection(
                    xSMGR->createInstance( ::rtl::OUString(
                        RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.document.TypeDetection" ))),
                    css::uno::UNO_QUERY );


                if ( xTypeDetection.is() )
                {
                    try
                    {
                        ::comphelper::SequenceAsHashMap aTypeNamePropsHM( xTypeDetection->getByName( aTypeName ) );
                        uno::Sequence< ::rtl::OUString > aExtensions = aTypeNamePropsHM.getUnpackedValueOrDefault(
                                                        ::rtl::OUString::createFromAscii( "Extensions" ),
                                                        ::uno::Sequence< ::rtl::OUString >() );
                        if ( aExtensions.getLength() )
                            aExtension = aExtensions[0];
                    }
                    catch ( css::container::NoSuchElementException& )
                    {
                    }
                }
            }

            // Use provided save file name. If empty determine file name
            aFileName = aSaveFileName;
            if ( aFileName.getLength() == 0 )
            {
                if ( !bHasLocation )
                {
                    // Create a noname file name with the correct extension
                    const rtl::OUString aNoNameFileName( RTL_CONSTASCII_USTRINGPARAM( "noname" ));
                    aFileName = aNoNameFileName;
                }
                else
                {
                    // Determine file name from model
                    INetURLObject aFileObj( xStorable->getLocation() );
                    aFileName = aFileObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::NO_DECODE );
                }
            }

            // No file name => error
            if ( aFileName.getLength() == 0 )
                return eRet;

            OSL_ASSERT( aFilterName.getLength() > 0 );
            OSL_ASSERT( aFileName.getLength() > 0 );

            // Creates a temporary directory to store a predefined file into it.
            // This makes it possible to store the file for "send document as e-mail"
            // with the original file name. We cannot use the original file as
            // some mail programs need exclusive access.
            ::utl::TempFile aTempDir( NULL, sal_True );

            INetURLObject aFilePathObj( aTempDir.GetURL() );
            aFilePathObj.insertName( aFileName );
            aFilePathObj.setExtension( aExtension );

            rtl::OUString aFileURL = aFilePathObj.GetMainURL( INetURLObject::NO_DECODE );

            sal_Int32 nNumArgs(0);
            const rtl::OUString aPasswordPropName( RTL_CONSTASCII_USTRINGPARAM( "Password" ));
            css::uno::Sequence< css::beans::PropertyValue > aArgs( ++nNumArgs );
            aArgs[nNumArgs-1].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" ));
            aArgs[nNumArgs-1].Value = css::uno::makeAny( aFilterName );

            ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
            rtl::OUString aPassword = aMediaDescrPropsHM.getUnpackedValueOrDefault(
                                            aPasswordPropName,
                                            ::rtl::OUString() );
            if ( aPassword.getLength() > 0 )
            {
                aArgs.realloc( ++nNumArgs );
                aArgs[nNumArgs-1].Name = aPasswordPropName;
                aArgs[nNumArgs-1].Value = css::uno::makeAny( aPassword );
            }

            bool bNeedsPreparation = false;
            css::util::URL aPrepareURL;
            css::uno::Reference< css::frame::XDispatch > xPrepareDispatch;
            css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( xFrame, css::uno::UNO_QUERY );
            css::uno::Reference< css::util::XURLTransformer > xURLTransformer(
                        xSMGR->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                        css::uno::UNO_QUERY );
            if( !bSendAsPDF )
            {
                try
                {
                    // check if the document needs to be prepared for sending as mail (embedding of links, removal of invisible content)

                    if ( xURLTransformer.is() )
                    {
                        aPrepareURL.Complete = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:PrepareMailExport" ));
                        xURLTransformer->parseStrict( aPrepareURL );
                    }

                    if ( xDispatchProvider.is() )
                    {
                        xPrepareDispatch = css::uno::Reference< css::frame::XDispatch >(
                            xDispatchProvider->queryDispatch( aPrepareURL, ::rtl::OUString(), 0 ));
                        if ( xPrepareDispatch.is() )
                        {
                                PrepareListener_Impl* pPrepareListener;
                                uno::Reference< css::frame::XStatusListener > xStatusListener = pPrepareListener = new PrepareListener_Impl;
                                xPrepareDispatch->addStatusListener( xStatusListener, aPrepareURL );
                                bNeedsPreparation = pPrepareListener->IsSet();
                                xPrepareDispatch->removeStatusListener( xStatusListener, aPrepareURL );
                        }
                    }
                }
                catch ( css::uno::RuntimeException& )
                {
                    throw;
                }
                catch ( css::uno::Exception& )
                {
                }
            }

            if ( bModified || !bHasLocation || bStoreTo || bNeedsPreparation )
            {
                // Document is modified, is newly created or should be stored in a special format
                try
                {
                    if( bNeedsPreparation && xPrepareDispatch.is() )
                    {
                        if ( xPrepareDispatch.is() )
                        {
                            try
                            {
                                css::uno::Sequence< css::beans::PropertyValue > aDispatchArgs;
                                xPrepareDispatch->dispatch( aPrepareURL, aDispatchArgs );
                            }
                            catch ( css::uno::RuntimeException& )
                            {
                                throw;
                            }
                            catch ( css::uno::Exception& )
                            {
                            }
                        }
                    }

                    //check if this is the pdf otput filter (i#64555)
                    if( bSendAsPDF )
                    {
                        SaveResult eShowPDFFilterDialog = ShowFilterOptionsDialog(
                                                            xSMGR, xModel, aFilterName, rType, bModified, nNumArgs, aArgs );

                        // don't continue on dialog cancel or error
                        if ( eShowPDFFilterDialog != SAVE_SUCCESSFULL )
                            return eShowPDFFilterDialog;
                    }

                    xStorable->storeToURL( aFileURL, aArgs );
                    rFileNamePath = aFileURL;
                    eRet = SAVE_SUCCESSFULL;

                    if( !bSendAsPDF )
                    {
                        css::util::URL aURL;
                        // #i30432# notify that export is finished - the Writer may want to restore removed content
                        if ( xURLTransformer.is() )
                        {
                            aURL.Complete = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:MailExportFinished" ));
                            xURLTransformer->parseStrict( aURL );
                        }

                        if ( xDispatchProvider.is() )
                        {
                            css::uno::Reference< css::frame::XDispatch > xDispatch = css::uno::Reference< css::frame::XDispatch >(
                                xDispatchProvider->queryDispatch( aURL, ::rtl::OUString(), 0 ));
                            if ( xDispatch.is() )
                            {
                                try
                                {
                                    css::uno::Sequence< css::beans::PropertyValue > aDispatchArgs;
                                    xDispatch->dispatch( aURL, aDispatchArgs );
                                }
                                catch ( css::uno::RuntimeException& )
                                {
                                    throw;
                                }
                                catch ( css::uno::Exception& )
                                {
                                }
                            }
                        }
                    }
                    // If the model is not modified, it could be modified by the dispatch calls.
                    // Therefore set back to modified = false. This should not hurt if we call
                    // on a non-modified model.
                    if ( !bModified )
                    {
                        try
                        {
                            xModifiable->setModified( sal_False );
                        }
                        catch( com::sun::star::beans::PropertyVetoException& )
                        {
                        }
                    }
                }
                catch ( com::sun::star::io::IOException& )
                {
                    eRet = SAVE_ERROR;
                }
            }
            else
            {
                // We need 1:1 copy of the document to preserve an added signature.
                aArgs.realloc( ++nNumArgs );
                aArgs[nNumArgs-1].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CopyStreamIfPossible" ) );
                aArgs[nNumArgs-1].Value = css::uno::makeAny( (sal_Bool)sal_True );

                try
                {
                    xStorable->storeToURL( aFileURL, aArgs );
                    rFileNamePath = aFileURL;
                    eRet = SAVE_SUCCESSFULL;
                }
                catch ( com::sun::star::io::IOException& )
                {
                    eRet = SAVE_ERROR;
                }
            }
        }
    }

    return eRet;
}

SfxMailModel::SfxMailModel() :
    mpToList    ( NULL ),
    mpCcList    ( NULL ),
    mpBccList   ( NULL ),
    mePriority  ( PRIO_NORMAL ),
    mbLoadDone  ( sal_True )
{
}

SfxMailModel::~SfxMailModel()
{
    ClearList( mpToList );
    delete mpToList;
    ClearList( mpCcList );
    delete mpCcList;
    ClearList( mpBccList );
    delete mpBccList;
}

void SfxMailModel::AddAddress( const String& rAddress, AddressRole eRole )
{
    // don't add a empty address
    if ( rAddress.Len() > 0 )
    {
        AddressList_Impl* pList = NULL;
        if ( ROLE_TO == eRole )
        {
            if ( !mpToList )
                // create the list
                mpToList = new AddressList_Impl;
            pList = mpToList;
        }
        else if ( ROLE_CC == eRole )
        {
            if ( !mpCcList )
                // create the list
                mpCcList = new AddressList_Impl;
            pList = mpCcList;
        }
        else if ( ROLE_BCC == eRole )
        {
            if ( !mpBccList )
                // create the list
                mpBccList = new AddressList_Impl;
            pList = mpBccList;
        }
        else
        {
            DBG_ERRORFILE( "invalid address role" );
        }

        if ( pList )
        {
            // add address to list
            AddressItemPtr_Impl pAddress = new String( rAddress );
            pList->Insert( pAddress, LIST_APPEND );
        }
    }
}

SfxMailModel::SendMailResult SfxMailModel::AttachDocument(
    const ::rtl::OUString& sDocumentType,
    const css::uno::Reference< css::uno::XInterface >& xFrameOrModel,
    const ::rtl::OUString& sAttachmentTitle )
{
    rtl::OUString sFileName;

    SaveResult eSaveResult = SaveDocumentAsFormat( sAttachmentTitle, xFrameOrModel, sDocumentType, sFileName );
    if ( eSaveResult == SAVE_SUCCESSFULL && ( sFileName.getLength() > 0 ) )
        maAttachedDocuments.push_back(sFileName);
    return eSaveResult == SAVE_SUCCESSFULL ? SEND_MAIL_OK : SEND_MAIL_ERROR;
}

SfxMailModel::SendMailResult SfxMailModel::Send( const css::uno::Reference< css::frame::XFrame >& xFrame )
{
    OSL_ENSURE(!maAttachedDocuments.empty(),"No document added!");
    SendMailResult  eResult = SEND_MAIL_ERROR;
    if ( !maAttachedDocuments.empty() )
    {
        css::uno::Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
        if ( xContext.is() )
        {
            css::uno::Reference< XSystemMailProvider > xSystemMailProvider( SystemMailProvider::create( xContext ) );

            if ( xSystemMailProvider.is() )
            {
                css::uno::Reference< XMailClient > xMailClient = xSystemMailProvider->queryMailClient();

                if ( !xMailClient.is() )
                {
                    // no mail client support => message box!
                    return SEND_MAIL_ERROR;
                }

                // we have a simple mail client
                css::uno::Reference< XMailMessage > xMailMessage = xMailClient->createMailMessage();
                if ( xMailMessage.is() )
                {
                    sal_Int32 nSendFlags = MailClientFlags::DEFAULTS;
                    if ( maFromAddress.Len() == 0 )
                    {
                        // from address not set, try figure out users e-mail address
                        CreateFromAddress_Impl( maFromAddress );
                    }
                    xMailMessage->setOriginator( maFromAddress );

                    sal_Int32 nToCount      = mpToList ? mpToList->Count() : 0;
                    sal_Int32 nCcCount      = mpCcList ? mpCcList->Count() : 0;
                    sal_Int32 nCcSeqCount   = nCcCount;

                    // set recipient (only one) for this simple mail server!!
                    if ( nToCount > 1 )
                    {
                        nCcSeqCount = nToCount - 1 + nCcCount;
                        xMailMessage->setRecipient( *mpToList->GetObject( 0 ));
                        nSendFlags = MailClientFlags::NO_USER_INTERFACE;
                    }
                    else if ( nToCount == 1 )
                    {
                        xMailMessage->setRecipient( *mpToList->GetObject( 0 ));
                        nSendFlags = MailClientFlags::NO_USER_INTERFACE;
                    }

                    // all other recipient must be handled with CC recipients!
                    if ( nCcSeqCount > 0 )
                    {
                        sal_Int32               nIndex = 0;
                        Sequence< OUString >    aCcRecipientSeq;

                        aCcRecipientSeq.realloc( nCcSeqCount );
                        if ( nCcSeqCount > nCcCount )
                        {
                            for ( sal_Int32 i = 1; i < nToCount; ++i )
                            {
                                aCcRecipientSeq[nIndex++] = *mpToList->GetObject(i);
                            }
                        }

                        for ( sal_Int32 i = 0; i < nCcCount; i++ )
                        {
                            aCcRecipientSeq[nIndex++] = *mpCcList->GetObject(i);
                        }
                        xMailMessage->setCcRecipient( aCcRecipientSeq );
                    }

                    sal_Int32 nBccCount = mpBccList ? mpBccList->Count() : 0;
                    if ( nBccCount > 0 )
                    {
                        Sequence< OUString > aBccRecipientSeq( nBccCount );
                        for ( sal_Int32 i = 0; i < nBccCount; ++i )
                        {
                            aBccRecipientSeq[i] = *mpBccList->GetObject(i);
                        }
                        xMailMessage->setBccRecipient( aBccRecipientSeq );
                    }

                    Sequence< OUString > aAttachmentSeq(&(maAttachedDocuments[0]),maAttachedDocuments.size());

                    xMailMessage->setSubject( maSubject );
                    xMailMessage->setAttachement( aAttachmentSeq );

                    sal_Bool bSend( sal_False );
                    try
                    {
                        xMailClient->sendMailMessage( xMailMessage, nSendFlags );
                        bSend = sal_True;
                    }
                    catch ( IllegalArgumentException& )
                    {
                    }
                    catch ( Exception& )
                    {
                    }

                    if ( bSend == sal_False )
                    {
                        css::uno::Reference< css::awt::XWindow > xParentWindow = xFrame->getContainerWindow();

                        ::vos::OGuard aGuard( Application::GetSolarMutex() );
                        Window* pParentWindow = VCLUnoHelper::GetWindow( xParentWindow );

                        ErrorBox aBox( pParentWindow, SfxResId( RID_ERRBOX_MAIL_CONFIG ));
                        aBox.Execute();
                        eResult = SEND_MAIL_CANCELLED;
                    }
                    else
                        eResult = SEND_MAIL_OK;
                }
            }
        }
    }
    else
        eResult = SEND_MAIL_CANCELLED;

    return eResult;
}

SfxMailModel::SendMailResult SfxMailModel::SaveAndSend( const css::uno::Reference< css::frame::XFrame >& xFrame, const rtl::OUString& rTypeName )
{
    SaveResult      eSaveResult;
    SendMailResult  eResult = SEND_MAIL_ERROR;
    rtl::OUString   aFileName;

    eSaveResult = SaveDocumentAsFormat( rtl::OUString(), xFrame, rTypeName, aFileName );

    if ( eSaveResult == SAVE_SUCCESSFULL )
    {
        maAttachedDocuments.push_back( aFileName );
        return Send( xFrame );
    }
    else if ( eSaveResult == SAVE_CANCELLED )
        eResult = SEND_MAIL_CANCELLED;

    return eResult;
}

// functions -------------------------------------------------------------

sal_Bool CreateFromAddress_Impl( String& rFrom )

/*  [Beschreibung]

    Diese Funktion versucht mit Hilfe des IniManagers eine From-Adresse
    zu erzeugen. daf"ur werden die Felder 'Vorname', 'Name' und 'EMail'
    aus der Applikations-Ini-Datei ausgelesen. Sollten diese Felder
    nicht gesetzt sein, wird FALSE zur"uckgegeben.

    [R"uckgabewert]

    sal_True:   Adresse konnte erzeugt werden.
    sal_False:  Adresse konnte nicht erzeugt werden.
*/

{
    SvtUserOptions aUserCFG;
    String aName        = aUserCFG.GetLastName  ();
    String aFirstName   = aUserCFG.GetFirstName ();
    if ( aFirstName.Len() || aName.Len() )
    {
        if ( aFirstName.Len() )
        {
            rFrom = TRIM( aFirstName );

            if ( aName.Len() )
                rFrom += ' ';
        }
        rFrom += TRIM( aName );
        // unerlaubte Zeichen entfernen
        rFrom.EraseAllChars( '<' );
        rFrom.EraseAllChars( '>' );
        rFrom.EraseAllChars( '@' );
    }
    String aEmailName = aUserCFG.GetEmail();

    // unerlaubte Zeichen entfernen
    aEmailName.EraseAllChars( '<' );
    aEmailName.EraseAllChars( '>' );

    if ( aEmailName.Len() )
    {
        if ( rFrom.Len() )
            rFrom += ' ';
        ( ( rFrom += '<' ) += TRIM( aEmailName ) ) += '>';
    }
    else
        rFrom.Erase();
    return ( rFrom.Len() > 0 );
}