TSCP: Store paragraph signature RDF in the paragraph

Change-Id: Ic44e3238bd1c35445bc23d0cc1de07aa2bf4512c
Reviewed-on: https://gerrit.libreoffice.org/44243
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
diff --git a/sw/source/core/edit/edfcol.cxx b/sw/source/core/edit/edfcol.cxx
index 2b32ccf..2486c3f 100644
--- a/sw/source/core/edit/edfcol.cxx
+++ b/sw/source/core/edit/edfcol.cxx
@@ -95,9 +95,12 @@ namespace
{
static const OUString MetaFilename("tscp/bails.rdf");
static const OUString MetaNS("urn:bails");
static const OUString ParagraphSignatureRDFName = "urn:bails:loext:paragraph:signature";
static const OUString ParagraphSignatureDateRDFName = "urn:bails:loext:paragraph:signature:date";
static const OUString ParagraphSignatureUsageRDFName = "urn:bails:loext:paragraph:signature:usage";
static const OUString ParagraphSignatureRDFNamespace = "urn:bails:loext:paragraph:signature:";
static const OUString ParagraphSignatureIdRDFName = "urn:bails:loext:paragraph:signature:id";
static const OUString ParagraphSignatureDigestRDFName = ":digest";
static const OUString ParagraphSignatureDateRDFName = ":date";
static const OUString ParagraphSignatureUsageRDFName = ":usage";
static const OUString ParagraphSignatureLastIdRDFName = "urn:bails:loext:paragraph:signature:lastid";
static const OUString ParagraphClassificationNameRDFName = "urn:bails:loext:paragraph:classification:name";
static const OUString ParagraphClassificationValueRDFName = "urn:bails:loext:paragraph:classification:value";
static const OUString MetadataFieldServiceName = "com.sun.star.text.textfield.MetadataField";
@@ -275,29 +278,68 @@ std::pair<OUString, OUString> lcl_getFieldRDF(const uno::Reference<frame::XModel
bool lcl_IsParagraphSignatureField(const uno::Reference<frame::XModel>& xModel,
                                   const uno::Reference<css::text::XTextField>& xField)
{
    return lcl_getFieldRDF(xModel, xField, ParagraphSignatureRDFName).first == ParagraphSignatureRDFName;
    return (lcl_getFieldRDF(xModel, xField, ParagraphSignatureIdRDFName).first == ParagraphSignatureIdRDFName);
}

struct SignatureDescr
{
    OUString msSignature;
    OUString msUsage;
    OUString msDate;
    OUString msId;

    bool isValid() const { return !msSignature.isEmpty(); }
};

SignatureDescr lcl_getSignatureDescr(const uno::Reference<frame::XModel>& xModel,
                                     const uno::Reference<css::text::XTextContent>& xParagraph,
                                     const OUString& sFieldId)
{
    SignatureDescr aDescr;
    aDescr.msId = sFieldId;

    const css::uno::Reference<css::rdf::XResource> xParaSubject(xParagraph, uno::UNO_QUERY);
    const std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xParaSubject);
    const auto itSig = aStatements.find(ParagraphSignatureRDFNamespace + sFieldId + ParagraphSignatureDigestRDFName);
    aDescr.msSignature = (itSig != aStatements.end() ? itSig->second : OUString());

    const auto itDate = aStatements.find(ParagraphSignatureRDFNamespace + sFieldId + ParagraphSignatureDateRDFName);
    aDescr.msDate = (itDate != aStatements.end() ? itDate->second : OUString());

    const auto itUsage = aStatements.find(ParagraphSignatureRDFNamespace + sFieldId + ParagraphSignatureUsageRDFName);
    aDescr.msUsage = (itUsage != aStatements.end() ? itUsage->second : OUString());

    return aDescr;
}

SignatureDescr lcl_getSignatureDescr(const uno::Reference<frame::XModel>& xModel,
                                     const uno::Reference<css::text::XTextContent>& xParagraph,
                                     const uno::Reference<css::text::XTextField>& xField)
{
    const css::uno::Reference<css::rdf::XResource> xFieldSubject(xField, uno::UNO_QUERY);
    std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xFieldSubject);
    const auto itId = aStatements.find(ParagraphSignatureIdRDFName);
    return (itId != aStatements.end() ? lcl_getSignatureDescr(xModel, xParagraph, itId->second) : SignatureDescr());
}

/// Validate and return validation result and signature field display text.
std::pair<bool, OUString>
lcl_MakeParagraphSignatureFieldText(const uno::Reference<frame::XModel>& xModel,
                                    const uno::Reference<css::text::XTextContent>& xParagraph,
                                    const uno::Reference<css::text::XTextField>& xField,
                                    const OString& utf8Text)
{
    OUString msg = SwResId(STR_INVALID_SIGNATURE);
    bool valid = false;

    const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
    std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xSubject);

    const auto it = aStatements.find(ParagraphSignatureRDFName);
    if (it != aStatements.end())
    const SignatureDescr aDescr = lcl_getSignatureDescr(xModel, xParagraph, xField);
    if (aDescr.isValid())
    {
        const sal_Char* pData = utf8Text.getStr();
        const std::vector<unsigned char> data(pData, pData + utf8Text.getLength());

        OString encSignature;
        if (it->second.convertToString(&encSignature, RTL_TEXTENCODING_UTF8, 0))
        if (aDescr.msSignature.convertToString(&encSignature, RTL_TEXTENCODING_UTF8, 0))
        {
            const std::vector<unsigned char> sig(svl::crypto::DecodeHexString(encSignature));
            SignatureInformation aInfo(0);
@@ -305,13 +347,8 @@ lcl_MakeParagraphSignatureFieldText(const uno::Reference<frame::XModel>& xModel,
            valid = valid && aInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;

            msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.ouSubject + ", ";

            const auto itDate = aStatements.find(ParagraphSignatureDateRDFName);
            msg += (itDate != aStatements.end() ? itDate->second : OUString());

            const auto itUsage = aStatements.find(ParagraphSignatureUsageRDFName);
            msg += (itUsage != aStatements.end() ? (" (" + itUsage->second + "): ") : OUString(": "));

            msg += aDescr.msDate;
            msg += (!aDescr.msUsage.isEmpty() ? (" (" + aDescr.msUsage + "): ") : OUString(": "));
            msg += (valid ? SwResId(STR_VALID) : SwResId(STR_INVALID));
        }
    }
@@ -319,9 +356,20 @@ lcl_MakeParagraphSignatureFieldText(const uno::Reference<frame::XModel>& xModel,
    return std::make_pair(valid, msg);
}

/// Generate the next valid ID for the a new signature on this paragraph.
OUString lcl_getNextSignatureId(const uno::Reference<frame::XModel>& xModel,
                                const uno::Reference<text::XTextContent>& xParagraph)
{
    const css::uno::Reference<css::rdf::XResource> xParaSubject(xParagraph, uno::UNO_QUERY);
    std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xParaSubject);
    const auto it = aStatements.find(ParagraphSignatureLastIdRDFName);
    return OUString::number(it != aStatements.end() ? it->second.toInt32() + 1 : 1);
}


/// Creates and inserts Paragraph Signature Metadata field and creates the RDF entry
uno::Reference<text::XTextField> lcl_InsertParagraphSignature(const uno::Reference<frame::XModel>& xModel,
                                                              const uno::Reference<text::XTextContent>& xParent,
                                                              const uno::Reference<text::XTextContent>& xParagraph,
                                                              const OUString& signature,
                                                              const OUString& usage)
{
@@ -329,16 +377,15 @@ uno::Reference<text::XTextField> lcl_InsertParagraphSignature(const uno::Referen
    auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);

    // Add the signature at the end.
    xField->attach(xParent->getAnchor()->getEnd());
    xField->attach(xParagraph->getAnchor()->getEnd());

    const OUString sId = lcl_getNextSignatureId(xModel, xParagraph);

    const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureRDFName, signature);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureUsageRDFName, usage);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureIdRDFName, sId);

    // First convert the UTC UNIX timestamp to a tools::DateTime.
    // First convert the UTC UNIX timestamp to a tools::DateTime then to local time.
    DateTime aDateTime = DateTime::CreateFromUnixTime(time(nullptr));

    // Then convert to a local UNO DateTime.
    aDateTime.ConvertToLocalTime();
    OUStringBuffer rBuffer;
    rBuffer.append((sal_Int32)aDateTime.GetYear());
@@ -351,7 +398,12 @@ uno::Reference<text::XTextField> lcl_InsertParagraphSignature(const uno::Referen
        rBuffer.append('0');
    rBuffer.append((sal_Int32)aDateTime.GetDay());

    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureDateRDFName, rBuffer.makeStringAndClear());
    // Now set the RDF on the paragraph, since that's what is preserved in .doc(x).
    const css::uno::Reference<css::rdf::XResource> xParaSubject(xParagraph, uno::UNO_QUERY);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, ParagraphSignatureLastIdRDFName, sId);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, ParagraphSignatureRDFNamespace + sId + ParagraphSignatureDigestRDFName, signature);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, ParagraphSignatureRDFNamespace + sId + ParagraphSignatureUsageRDFName, usage);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, ParagraphSignatureRDFNamespace + sId + ParagraphSignatureDateRDFName, rBuffer.makeStringAndClear());

    return xField;
}
@@ -359,6 +411,7 @@ uno::Reference<text::XTextField> lcl_InsertParagraphSignature(const uno::Referen
/// Updates the signature field text if changed and returns true only iff updated.
bool lcl_UpdateParagraphSignatureField(SwDoc* pDoc,
                                       const uno::Reference<frame::XModel>& xModel,
                                       const uno::Reference<css::text::XTextContent>& xParagraph,
                                       const uno::Reference<css::text::XTextField>& xField,
                                       const OString& utf8Text)
{
@@ -369,7 +422,7 @@ bool lcl_UpdateParagraphSignatureField(SwDoc* pDoc,
            pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
        });

    const std::pair<bool, OUString> res = lcl_MakeParagraphSignatureFieldText(xModel, xField, utf8Text);
    const std::pair<bool, OUString> res = lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text);
    uno::Reference<css::text::XTextRange> xText(xField, uno::UNO_QUERY);
    const OUString curText = xText->getString();
    if (curText != res.second)
@@ -463,13 +516,15 @@ bool lcl_UpdateParagraphClassificationField(SwDoc* pDoc,
        });

    uno::Reference<text::XTextField> xField = lcl_InsertParagraphClassification(xModel, xTextNode);

    css::uno::Reference<css::rdf::XResource> xFieldSubject(xField, uno::UNO_QUERY);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, sKey, sValue);
    css::uno::Reference<css::rdf::XResource> xNodeSubject(xTextNode, uno::UNO_QUERY);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, sKey, sValue);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphClassificationNameRDFName, sKey);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphClassificationValueRDFName, sValue);

    css::uno::Reference<css::rdf::XResource> xNodeSubject(xTextNode, uno::UNO_QUERY);
    SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, sKey, sValue);

    uno::Reference<css::text::XTextRange> xText(xField, uno::UNO_QUERY);
    const OUString curDisplayText = xText->getString();
    if (curDisplayText != sDisplayText)
@@ -488,6 +543,20 @@ void lcl_ValidateParagraphSignatures(SwDoc* pDoc, const uno::Reference<text::XTe
        return;

    uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();

    // Check if the paragraph is signed.
    try
    {
        const css::uno::Reference<css::rdf::XResource> xParaSubject(xParagraph, uno::UNO_QUERY);
        const std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xParaSubject);
        if (aStatements.find(ParagraphSignatureLastIdRDFName) == aStatements.end())
            return;
    }
    catch (const ::css::uno::Exception&)
    {
        return;
    }

    uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
    if (!xTextPortionEnumerationAccess.is())
        return;
@@ -522,9 +591,9 @@ void lcl_ValidateParagraphSignatures(SwDoc* pDoc, const uno::Reference<text::XTe

        if (updateDontRemove)
        {
            lcl_UpdateParagraphSignatureField(pDoc, xModel, xField, utf8Text);
            lcl_UpdateParagraphSignatureField(pDoc, xModel, xParagraph, xField, utf8Text);
        }
        else if (!lcl_MakeParagraphSignatureFieldText(xModel, xField, utf8Text).first)
        else if (!lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text).first)
        {
            pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
            SwUndoParagraphSigning* pUndo = new SwUndoParagraphSigning(pDoc, xField, xParagraph, false);
@@ -1112,23 +1181,11 @@ void SwEditShell::ApplyParagraphClassification(std::vector<svx::ClassificationRe
    }
}

std::vector<svx::ClassificationResult> SwEditShell::CollectParagraphClassification()
std::vector<svx::ClassificationResult> lcl_CollectParagraphClassification(const uno::Reference<frame::XModel>& xModel, const uno::Reference<text::XTextContent>& xParagraph)
{
    std::vector<svx::ClassificationResult> aResult;

    SwDocShell* pDocShell = GetDoc()->GetDocShell();
    if (!pDocShell)
        return aResult;

    SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
    if (pNode == nullptr)
        return aResult;

    uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
    uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
    uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);

    uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParent, uno::UNO_QUERY);
    uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
    if (!xTextPortionEnumerationAccess.is())
        return aResult;

@@ -1178,6 +1235,23 @@ std::vector<svx::ClassificationResult> SwEditShell::CollectParagraphClassificati
    return aResult;
}

std::vector<svx::ClassificationResult> SwEditShell::CollectParagraphClassification()
{
    std::vector<svx::ClassificationResult> aResult;

    SwDocShell* pDocShell = GetDoc()->GetDocShell();
    if (!pDocShell)
        return aResult;

    SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
    if (pNode == nullptr)
        return aResult;

    uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
    uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
    return lcl_CollectParagraphClassification(xModel, xParent);
}

sal_Int16 lcl_GetAngle(const drawing::HomogenMatrix3& rMatrix)
{
    basegfx::B2DHomMatrix aTransformation;
@@ -1494,8 +1568,9 @@ SwUndoParagraphSigning::SwUndoParagraphSigning(SwDoc* pDoc,
    // Save the metadata and field content to undo/redo.
    uno::Reference<frame::XModel> xModel = m_pDoc->GetDocShell()->GetBaseModel();
    const css::uno::Reference<css::rdf::XResource> xSubject(m_xField, uno::UNO_QUERY);

    std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
    const auto it = aStatements.find(ParagraphSignatureRDFName);
    const auto it = aStatements.find(ParagraphSignatureIdRDFName);
    if (it != aStatements.end())
        m_signature = it->second;

@@ -1579,8 +1654,8 @@ void SwEditShell::SignParagraph()
        return;

    // 1. Get the text (without fields).
    const uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
    const OString utf8Text = lcl_getParagraphBodyText(xParent);
    const uno::Reference<text::XTextContent> xParagraph = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
    const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
    if (utf8Text.isEmpty())
        return;

@@ -1624,11 +1699,11 @@ void SwEditShell::SignParagraph()
    GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);

    const uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
    uno::Reference<css::text::XTextField> xField = lcl_InsertParagraphSignature(xModel, xParent, signature, aUsage);
    uno::Reference<css::text::XTextField> xField = lcl_InsertParagraphSignature(xModel, xParagraph, signature, aUsage);

    lcl_UpdateParagraphSignatureField(GetDoc(), xModel, xField, utf8Text);
    lcl_UpdateParagraphSignatureField(GetDoc(), xModel, xParagraph, xField, utf8Text);

    SwUndoParagraphSigning* pUndo = new SwUndoParagraphSigning(GetDoc(), xField, xParent, true);
    SwUndoParagraphSigning* pUndo = new SwUndoParagraphSigning(GetDoc(), xField, xParagraph, true);
    GetDoc()->GetIDocumentUndoRedo().AppendUndo(pUndo);

    GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
@@ -1698,7 +1773,7 @@ uno::Reference<text::XTextField> lcl_GetParagraphMetadataFieldAtIndex(const SwDo
                const css::uno::Reference<css::rdf::XResource> xSubject(pMeta->MakeUnoObject(), uno::UNO_QUERY);
                uno::Reference<frame::XModel> xModel = pDocSh->GetBaseModel();
                const std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
                if (aStatements.find(ParagraphSignatureRDFName) != aStatements.end() ||
                if (aStatements.find(ParagraphSignatureIdRDFName) != aStatements.end() ||
                    aStatements.find(ParagraphClassificationNameRDFName) != aStatements.end())
                {
                    xTextField = uno::Reference<text::XTextField>(xSubject, uno::UNO_QUERY);
diff --git a/sw/source/core/edit/edws.cxx b/sw/source/core/edit/edws.cxx
index 01dc694..73f8c6a 100644
--- a/sw/source/core/edit/edws.cxx
+++ b/sw/source/core/edit/edws.cxx
@@ -53,10 +53,7 @@ SwEditShell::SwEditShell( SwDoc& rDoc, vcl::Window *pWindow, const SwViewOption 

    // Update the paragraph signatures.
    // Since this ctor is called only on creating/loading the doc, we validate once only.
    //FIXME: This is taking too long for the scheduler's taste.
    // Need to do a quick check to see if a paragraph is signed or not,
    // but for that need paragraph RDF, which is in a later patch.
    // ValidateAllParagraphSignatures(true);
    ValidateAllParagraphSignatures(true);
}

SwEditShell::~SwEditShell() // USED