/**************************************************************
 * 
 * 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_extensions.hxx"

#include "soaprequest.hxx"
#include "errormail.hxx"
#include "config.hxx"
#include <boost/shared_ptr.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/ucb/XSimpleFileAccess.hpp>
#include <rtl/strbuf.hxx>
#include <rtl/ustring.hxx>


using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::io;
using boost::shared_ptr;
using com::sun::star::io::XOutputStream;
using com::sun::star::ucb::XSimpleFileAccess;
using rtl::OUString;
using rtl::OString;
using rtl::OStringBuffer;


namespace
{
    static unsigned long asUlong(sal_Int8 input)
    {
        return *reinterpret_cast<unsigned char *>(&input);
    };

    static Sequence<sal_Int8> base64_encode(const Sequence<sal_Int8>& input)
    {
        static const char base64_tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        Sequence<sal_Int8> result(4);
        unsigned long value = asUlong(input[0]) << 16;
        if(input.getLength() > 1) value |= asUlong(input[1]) << 8;
        if(input.getLength() > 2) value |= asUlong(input[2]);

        result[0] = static_cast<sal_Int8>(base64_tab[(value >> 18) & 0x3F]);
        result[1] = static_cast<sal_Int8>(base64_tab[(value >> 12) & 0x3F]);
        result[2] = static_cast<sal_Int8>('=');
        result[3] = static_cast<sal_Int8>('=');

        if (input.getLength() > 1)
        {
            result[2] = base64_tab[(value >> 6) & 0x3F];
            if (input.getLength() > 2)
                 result[3] = base64_tab[(value >> 0) & 0x3F];
        }
        return result;
    };

    static OString replaceAll(const OString& str, sal_Char old, const OString& replacement)
    {
        OStringBuffer result;
        sal_Int32 idx = 0;
        do {
            result.append(str.getToken(0, old, idx));
            if(idx>=0) result.append(replacement);
        } while(idx >= 0);
        return result.makeStringAndClear();
    };

    static OString xmlEncode(const OString& input)
    {
        OString result = replaceAll(input, '&', OString("&amp;"));
        result = replaceAll(result, '<', OString("&lt;"));
        return replaceAll(result, '>', OString("&gt;"));
    }

    static shared_ptr<Sequence<sal_Int8> > createSequenceFromString(const OString& str)
    {
        const sal_Int32 size = str.getLength();
        shared_ptr<Sequence<sal_Int8> > result(new Sequence<sal_Int8>(size));
        for(sal_Int32 idx=0; idx < size; idx++)
            (*result)[idx] = str[idx];
        return result;
    };

    static void writeString(const Reference<XOutputStream>& target, const OString& str)
    {
        shared_ptr<Sequence<sal_Int8> > seq = createSequenceFromString(str);
        target->writeBytes(*seq);
    };

    static void writeFile(const Reference<XMultiServiceFactory>& sf, const Reference<XOutputStream>& target, const OUString& fileurl)
    {
        Reference<XSimpleFileAccess> file_access(
            sf->createInstance(OUString::createFromAscii("com.sun.star.ucb.SimpleFileAccess")),
            UNO_QUERY);
        Reference<XInputStream> file = file_access->openFileRead(fileurl);
        const sal_Int32 bufsize = 3;
        sal_Int32 bytes_read;
        Sequence<sal_Int8> buf(bufsize);
        do
        {
            bytes_read = file->readBytes(buf, bufsize);
            if(bytes_read < buf.getLength()) buf.realloc(bytes_read);
            if(bytes_read) target->writeBytes(base64_encode(buf));
        } while(bytes_read == bufsize);
    };

    static const OString SOAP_START(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
        "xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"\n"
        "xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"\n"
        "xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\"\n"
        "xmlns:rds=\"urn:ReportDataService\"\n"
        "xmlns:apache=\"http://xml.apache.org/xml-soap\"\n"
        "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
        "<SOAP-ENV:Body>\n"
        "<rds:submitReport>\n");
    static const OString SOAP_ITEMS_START("<hash xsi:type=\"apache:Map\">\n");
    static const OString SOAP_ITEMS_END("</hash>\n");
    static const OString SOAP_END(
        "</rds:submitReport>\n"
        "</SOAP-ENV:Body>\n"
        "</SOAP-ENV:Envelope>\n");
    static const OString SOAP_ITEM_END("]]></value></item>\n");

    static const OString getSoapOfficeversion(const Reference<XMultiServiceFactory>& sf)
    {
        return ::rtl::OUStringToOString(oooimprovement::Config(sf).getCompleteProductname(), RTL_TEXTENCODING_ASCII_US);
    };

    static const OString getSoapSoapId(const Reference<XMultiServiceFactory>& sf, const OString& soap_id)
    {
        OStringBuffer buf;
        buf.append("<body xsi:type=\"xsd:string\">");
        buf.append(xmlEncode(soap_id)).append("\n");
        buf.append(xmlEncode(getSoapOfficeversion(sf))).append("\n");
        buf.append("</body>\n");
        return buf.makeStringAndClear();
    };

    static const OString getSoapItemStart(const OString& key)
    {
        OStringBuffer buf =
            "<item>\n"
            "<key xsi:type=\"xsd:string\">" + key + "</key>\n"
            "<value xsi:type=\"xsd:string\"><![CDATA[";
        return buf.makeStringAndClear();
    };
}

namespace oooimprovement
{
    SoapRequest::SoapRequest(const Reference<XMultiServiceFactory>& sf, const OUString& soap_id, const OUString& logfile)
        : m_ServiceFactory(sf)
        , m_SoapId(soap_id)
        , m_Logfile(logfile)
    {}

     void SoapRequest::writeTo(const Reference<XOutputStream>& target) const
    {
        writeString(target, SOAP_START);
            writeString(
                target,
                getSoapSoapId(m_ServiceFactory, rtl::OUStringToOString(m_SoapId, RTL_TEXTENCODING_ASCII_US)));
            writeString(target, SOAP_ITEMS_START);
                writeString(target, getSoapItemStart("reportmail.xml"));
                    writeString(target, Errormail(m_ServiceFactory).getXml());
                writeString(target, SOAP_ITEM_END);
                writeString(target, getSoapItemStart("data.zip"));
                    writeFile(m_ServiceFactory, target, m_Logfile);
                writeString(target, SOAP_ITEM_END);
            writeString(target, SOAP_ITEMS_END);
        writeString(target, SOAP_END);
    }
}