1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <config_folders.h>
11 
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 
16 #ifdef IOS
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <unicode/udata.h>
20 #include <unicode/ucnv.h>
21 #include <premac.h>
22 #import <Foundation/Foundation.h>
23 #import <CoreGraphics/CoreGraphics.h>
24 #include <postmac.h>
25 #endif
26 
27 #ifdef ANDROID
28 #include <osl/detail/android-bootstrap.h>
29 #endif
30 
31 #include <algorithm>
32 #include <memory>
33 #include <iostream>
34 #include <boost/property_tree/json_parser.hpp>
35 #include <boost/algorithm/string.hpp>
36 
37 #include <LibreOfficeKit/LibreOfficeKit.h>
38 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
39 
40 #include <sal/log.hxx>
41 #include <vcl/errinf.hxx>
42 #include <vcl/lok.hxx>
43 #include <osl/file.hxx>
44 #include <osl/process.h>
45 #include <osl/thread.h>
46 #include <rtl/bootstrap.hxx>
47 #include <rtl/strbuf.hxx>
48 #include <rtl/uri.hxx>
49 #include <cppuhelper/bootstrap.hxx>
50 #include <comphelper/base64.hxx>
51 #include <comphelper/dispatchcommand.hxx>
52 #include <comphelper/lok.hxx>
53 #include <comphelper/processfactory.hxx>
54 #include <comphelper/string.hxx>
55 #include <comphelper/profilezone.hxx>
56 #include <comphelper/propertysequence.hxx>
57 #include <comphelper/scopeguard.hxx>
58 #include <comphelper/threadpool.hxx>
59 
60 #include <com/sun/star/beans/XPropertySet.hpp>
61 #include <com/sun/star/container/XNameAccess.hpp>
62 #include <com/sun/star/frame/Desktop.hpp>
63 #include <com/sun/star/frame/DispatchResultEvent.hpp>
64 #include <com/sun/star/frame/DispatchResultState.hpp>
65 #include <com/sun/star/frame/XDispatchProvider.hpp>
66 #include <com/sun/star/frame/XDispatchResultListener.hpp>
67 #include <com/sun/star/frame/XSynchronousDispatch.hpp>
68 #include <com/sun/star/frame/XStorable.hpp>
69 #include <com/sun/star/lang/Locale.hpp>
70 #include <com/sun/star/lang/XComponent.hpp>
71 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
72 #include <com/sun/star/reflection/theCoreReflection.hpp>
73 #include <com/sun/star/reflection/XIdlClass.hpp>
74 #include <com/sun/star/reflection/XIdlReflection.hpp>
75 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
76 #include <com/sun/star/ucb/XContentProvider.hpp>
77 #include <com/sun/star/ucb/XUniversalContentBroker.hpp>
78 #include <com/sun/star/util/URLTransformer.hpp>
79 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
80 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
81 #include <com/sun/star/datatransfer/XTransferable2.hpp>
82 #include <com/sun/star/text/TextContentAnchorType.hpp>
83 #include <com/sun/star/document/XRedlinesSupplier.hpp>
84 #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
85 
86 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
87 #include <com/sun/star/xml/crypto/XSEInitializer.hpp>
88 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
89 #include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
90 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
91 #include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
92 #include <com/sun/star/security/XCertificate.hpp>
93 
94 #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
95 #include <com/sun/star/linguistic2/XSpellChecker.hpp>
96 #include <com/sun/star/i18n/ScriptType.hpp>
97 #include <com/sun/star/lang/DisposedException.hpp>
98 
99 #include <editeng/fontitem.hxx>
100 #include <editeng/flstitem.hxx>
101 #include <sfx2/app.hxx>
102 #include <sfx2/objsh.hxx>
103 #include <sfx2/viewsh.hxx>
104 #include <sfx2/viewfrm.hxx>
105 #include <sfx2/msgpool.hxx>
106 #include <sfx2/dispatch.hxx>
107 #include <sfx2/lokcharthelper.hxx>
108 #include <sfx2/DocumentSigner.hxx>
109 #include <svx/dialmgr.hxx>
110 #include <svx/dialogs.hrc>
111 #include <svx/strings.hrc>
112 #include <svx/ruler.hxx>
113 #include <svx/svdview.hxx>
114 #include <svx/svxids.hrc>
115 #include <svx/ucsubset.hxx>
116 #include <vcl/vclevent.hxx>
117 #include <vcl/GestureEvent.hxx>
118 #include <vcl/svapp.hxx>
119 #include <unotools/resmgr.hxx>
120 #include <tools/fract.hxx>
121 #include <svtools/ctrltool.hxx>
122 #include <svtools/langtab.hxx>
123 #include <vcl/floatwin.hxx>
124 #include <vcl/fontcharmap.hxx>
125 #include <vcl/graphicfilter.hxx>
126 #include <vcl/ptrstyle.hxx>
127 #include <vcl/sysdata.hxx>
128 #include <vcl/virdev.hxx>
129 #include <vcl/ImageTree.hxx>
130 #include <vcl/ITiledRenderable.hxx>
131 #include <vcl/IDialogRenderable.hxx>
132 #include <vcl/dialog.hxx>
133 #include <unicode/uchar.h>
134 #include <unotools/configmgr.hxx>
135 #include <unotools/syslocaleoptions.hxx>
136 #include <unotools/mediadescriptor.hxx>
137 #include <unotools/pathoptions.hxx>
138 #include <unotools/tempfile.hxx>
139 #include <unotools/streamwrap.hxx>
140 #include <osl/module.hxx>
141 #include <comphelper/sequence.hxx>
142 #include <sfx2/sfxbasemodel.hxx>
143 #include <svl/undo.hxx>
144 #include <unotools/datetime.hxx>
145 #include <i18nlangtag/mslangid.hxx>
146 #include <i18nlangtag/languagetag.hxx>
147 #include <vcl/builder.hxx>
148 #include <vcl/abstdlg.hxx>
149 #include <tools/diagnose_ex.h>
150 #include <vcl/uitest/uiobject.hxx>
151 
152 #include <app.hxx>
153 
154 #include "../app/cmdlineargs.hxx"
155 // We also need to hackily be able to start the main libreoffice thread:
156 #include "../app/sofficemain.h"
157 #include "../app/officeipcthread.hxx"
158 #include <lib/init.hxx>
159 
160 #include "lokinteractionhandler.hxx"
161 #include "lokclipboard.hxx"
162 #include <officecfg/Office/Impress.hxx>
163 
164 using namespace css;
165 using namespace vcl;
166 using namespace desktop;
167 using namespace utl;
168 
169 static LibLibreOffice_Impl *gImpl = nullptr;
170 static std::weak_ptr< LibreOfficeKitClass > gOfficeClass;
171 static std::weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass;
172 
SetLastExceptionMsg(const OUString & s=OUString ())173 static void SetLastExceptionMsg(const OUString& s = OUString())
174 {
175     SAL_WARN_IF(!s.isEmpty(), "lok", "lok exception '" + s + "'");
176     if (gImpl)
177         gImpl->maLastExceptionMsg = s;
178 }
179 
180 struct ExtensionMap
181 {
182     const char *extn;
183     const char *filterName;
184 };
185 
186 static const ExtensionMap aWriterExtensionMap[] =
187 {
188     { "doc",   "MS Word 97" },
189     { "docm",  "MS Word 2007 XML VBA" },
190     { "docx",  "MS Word 2007 XML" },
191     { "fodt",  "OpenDocument Text Flat XML" },
192     { "html",  "HTML (StarWriter)" },
193     { "odt",   "writer8" },
194     { "ott",   "writer8_template" },
195     { "pdf",   "writer_pdf_Export" },
196     { "epub",  "EPUB" },
197     { "rtf",   "Rich Text Format" },
198     { "txt",   "Text" },
199     { "xhtml", "XHTML Writer File" },
200     { "png",   "writer_png_Export" },
201     { nullptr, nullptr }
202 };
203 
204 static const ExtensionMap aCalcExtensionMap[] =
205 {
206     { "csv",   "Text - txt - csv (StarCalc)" },
207     { "fods",  "OpenDocument Spreadsheet Flat XML" },
208     { "html",  "HTML (StarCalc)" },
209     { "ods",   "calc8" },
210     { "ots",   "calc8_template" },
211     { "pdf",   "calc_pdf_Export" },
212     { "xhtml", "XHTML Calc File" },
213     { "xls",   "MS Excel 97" },
214     { "xlsm",  "Calc MS Excel 2007 VBA XML" },
215     { "xlsx",  "Calc MS Excel 2007 XML" },
216     { "png",   "calc_png_Export" },
217     { nullptr, nullptr }
218 };
219 
220 static const ExtensionMap aImpressExtensionMap[] =
221 {
222     { "fodp",  "OpenDocument Presentation Flat XML" },
223     { "html",  "impress_html_Export" },
224     { "odg",   "impress8_draw" },
225     { "odp",   "impress8" },
226     { "otp",   "impress8_template" },
227     { "pdf",   "impress_pdf_Export" },
228     { "potm",  "Impress MS PowerPoint 2007 XML Template" },
229     { "pot",   "MS PowerPoint 97 Vorlage" },
230     { "pptm",  "Impress MS PowerPoint 2007 XML VBA" },
231     { "pptx",  "Impress MS PowerPoint 2007 XML" },
232     { "pps",   "MS PowerPoint 97 Autoplay" },
233     { "ppt",   "MS PowerPoint 97" },
234     { "svg",   "impress_svg_Export" },
235     { "swf",   "impress_flash_Export" },
236     { "xhtml", "XHTML Impress File" },
237     { "png",   "impress_png_Export"},
238     { nullptr, nullptr }
239 };
240 
241 static const ExtensionMap aDrawExtensionMap[] =
242 {
243     { "fodg",  "draw_ODG_FlatXML" },
244     { "html",  "draw_html_Export" },
245     { "odg",   "draw8" },
246     { "pdf",   "draw_pdf_Export" },
247     { "svg",   "draw_svg_Export" },
248     { "swf",   "draw_flash_Export" },
249     { "xhtml", "XHTML Draw File" },
250     { "png",   "draw_png_Export"},
251     { nullptr, nullptr }
252 };
253 
getUString(const char * pString)254 static OUString getUString(const char* pString)
255 {
256     if (pString == nullptr)
257         return OUString();
258 
259     OString sString(pString, strlen(pString));
260     return OStringToOUString(sString, RTL_TEXTENCODING_UTF8);
261 }
262 
263 // Tolerate embedded \0s etc.
convertOString(const OString & rStr)264 static char *convertOString(const OString &rStr)
265 {
266     char* pMemory = static_cast<char*>(malloc(rStr.getLength() + 1));
267     assert(pMemory); // don't tolerate failed allocations.
268     memcpy(pMemory, rStr.getStr(), rStr.getLength() + 1);
269     return pMemory;
270 }
271 
convertOUString(const OUString & aStr)272 static char *convertOUString(const OUString &aStr)
273 {
274     return convertOString(OUStringToOString(aStr, RTL_TEXTENCODING_UTF8));
275 }
276 
277 /// Try to convert a relative URL to an absolute one, unless it already looks like a URL.
getAbsoluteURL(const char * pURL)278 static OUString getAbsoluteURL(const char* pURL)
279 {
280     OUString aURL(getUString(pURL));
281     if (aURL.isEmpty())
282         return aURL;
283 
284     // convert relative paths to absolute ones
285     OUString aWorkingDir;
286     osl_getProcessWorkingDir(&aWorkingDir.pData);
287     if (!aWorkingDir.endsWith("/"))
288         aWorkingDir += "/";
289 
290     try
291     {
292         return rtl::Uri::convertRelToAbs(aWorkingDir, aURL);
293     }
294     catch (const rtl::MalformedUriException &)
295     {
296     }
297 
298     return OUString();
299 }
300 
jsonToUnoAny(const boost::property_tree::ptree & aTree)301 static uno::Any jsonToUnoAny(const boost::property_tree::ptree& aTree)
302 {
303     uno::Any aAny;
304     uno::Any aValue;
305     sal_Int32 nFields;
306     uno::TypeClass aTypeClass;
307     uno::Reference< reflection::XIdlField > aField;
308     boost::property_tree::ptree aNodeNull, aNodeValue, aNodeField;
309     const std::string& rType = aTree.get<std::string>("type", "");
310     const std::string& rValue = aTree.get<std::string>("value", "");
311     uno::Sequence< uno::Reference< reflection::XIdlField > > aFields;
312     uno::Reference< reflection:: XIdlClass > xIdlClass =
313         css::reflection::theCoreReflection::get(comphelper::getProcessComponentContext())->forName(OUString::fromUtf8(rType.c_str()));
314     if (xIdlClass.is())
315     {
316         aTypeClass = xIdlClass->getTypeClass();
317         xIdlClass->createObject(aAny);
318         aFields = xIdlClass->getFields();
319         nFields = aFields.getLength();
320         aNodeValue = aTree.get_child("value", aNodeNull);
321         if (nFields > 0 && aNodeValue != aNodeNull)
322         {
323             for (sal_Int32 itField = 0; itField < nFields; ++itField)
324             {
325                 aField = aFields[itField];
326                 aNodeField = aNodeValue.get_child(aField->getName().toUtf8().getStr(), aNodeNull);
327                 if (aNodeField != aNodeNull)
328                 {
329                     aValue = jsonToUnoAny(aNodeField);
330                     aField->set(aAny, aValue);
331                 }
332             }
333         }
334         else if (!rValue.empty())
335         {
336             if (aTypeClass == uno::TypeClass_VOID)
337                 aAny.clear();
338             else if (aTypeClass == uno::TypeClass_BYTE)
339                 aAny <<= static_cast<sal_Int8>(OString(rValue.c_str()).toInt32());
340             else if (aTypeClass == uno::TypeClass_BOOLEAN)
341                 aAny <<= OString(rValue.c_str()).toBoolean();
342             else if (aTypeClass == uno::TypeClass_SHORT)
343                 aAny <<= static_cast<sal_Int16>(OString(rValue.c_str()).toInt32());
344             else if (aTypeClass == uno::TypeClass_UNSIGNED_SHORT)
345                 aAny <<= static_cast<sal_uInt16>(OString(rValue.c_str()).toUInt32());
346             else if (aTypeClass == uno::TypeClass_LONG)
347                 aAny <<= OString(rValue.c_str()).toInt32();
348             else if (aTypeClass == uno::TypeClass_UNSIGNED_LONG)
349                 aAny <<= static_cast<sal_uInt32>(OString(rValue.c_str()).toInt32());
350             else if (aTypeClass == uno::TypeClass_FLOAT)
351                 aAny <<= OString(rValue.c_str()).toFloat();
352             else if (aTypeClass == uno::TypeClass_DOUBLE)
353                 aAny <<= OString(rValue.c_str()).toDouble();
354             else if (aTypeClass == uno::TypeClass_STRING)
355                 aAny <<= OUString::fromUtf8(rValue.c_str());
356         }
357     }
358     return aAny;
359 }
360 
jsonToPropertyValuesVector(const char * pJSON)361 std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char* pJSON)
362 {
363     std::vector<beans::PropertyValue> aArguments;
364     if (pJSON && pJSON[0] != '\0')
365     {
366         boost::property_tree::ptree aTree, aNodeNull, aNodeValue;
367         std::stringstream aStream(pJSON);
368         boost::property_tree::read_json(aStream, aTree);
369 
370         for (const auto& rPair : aTree)
371         {
372             const std::string& rType = rPair.second.get<std::string>("type", "");
373             const std::string& rValue = rPair.second.get<std::string>("value", "");
374 
375             beans::PropertyValue aValue;
376             aValue.Name = OUString::fromUtf8(rPair.first.c_str());
377             if (rType == "string")
378                 aValue.Value <<= OUString::fromUtf8(rValue.c_str());
379             else if (rType == "boolean")
380                 aValue.Value <<= OString(rValue.c_str()).toBoolean();
381             else if (rType == "float")
382                 aValue.Value <<= OString(rValue.c_str()).toFloat();
383             else if (rType == "long")
384                 aValue.Value <<= OString(rValue.c_str()).toInt32();
385             else if (rType == "short")
386                 aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32());
387             else if (rType == "unsigned short")
388                 aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32());
389             else if (rType == "int64")
390                 aValue.Value <<= OString(rValue.c_str()).toInt64();
391             else if (rType == "int32")
392                 aValue.Value <<= OString(rValue.c_str()).toInt32();
393             else if (rType == "int16")
394                 aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32());
395             else if (rType == "uint64")
396                 aValue.Value <<= OString(rValue.c_str()).toUInt64();
397             else if (rType == "uint32")
398                 aValue.Value <<= OString(rValue.c_str()).toUInt32();
399             else if (rType == "uint16")
400                 aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32());
401             else if (rType == "[]byte")
402             {
403                 aNodeValue = rPair.second.get_child("value", aNodeNull);
404                 if (aNodeValue != aNodeNull && aNodeValue.size() == 0)
405                 {
406                     uno::Sequence< sal_Int8 > aSeqByte(reinterpret_cast<const sal_Int8*>(rValue.c_str()), rValue.size());
407                     aValue.Value <<= aSeqByte;
408                 }
409             }
410             else if (rType == "[]any")
411             {
412                 aNodeValue = rPair.second.get_child("value", aNodeNull);
413                 if (aNodeValue != aNodeNull && !aNodeValue.empty())
414                 {
415                     sal_Int32 itSeq = 0;
416                     uno::Sequence< uno::Any > aSeq(aNodeValue.size());
417                     for (const auto& rSeqPair : aNodeValue)
418                         aSeq[itSeq++] = jsonToUnoAny(rSeqPair.second);
419                     aValue.Value <<= aSeq;
420                 }
421             }
422             else
423                 SAL_WARN("desktop.lib", "jsonToPropertyValuesVector: unhandled type '"<<rType<<"'");
424             aArguments.push_back(aValue);
425         }
426     }
427     return aArguments;
428 }
429 
430 
jsonToStringMap(const char * pJSON)431 static StringMap jsonToStringMap(const char* pJSON)
432 {
433     StringMap aArgs;
434     if (pJSON && pJSON[0] != '\0')
435     {
436         std::stringstream aStream(pJSON);
437         boost::property_tree::ptree aTree;
438         boost::property_tree::read_json(aStream, aTree);
439 
440         for (const auto& rPair : aTree)
441         {
442             aArgs[OUString::fromUtf8(rPair.first.c_str())] = OUString::fromUtf8(rPair.second.get_value<std::string>(".").c_str());
443         }
444     }
445     return aArgs;
446 }
447 
448 
unoAnyToPropertyTree(const uno::Any & anyItem)449 static boost::property_tree::ptree unoAnyToPropertyTree(const uno::Any& anyItem)
450 {
451     boost::property_tree::ptree aTree;
452     OUString aType = anyItem.getValueTypeName();
453     aTree.put("type", aType.toUtf8().getStr());
454 
455     if (aType == "string")
456         aTree.put("value", anyItem.get<OUString>().toUtf8().getStr());
457     else if (aType == "unsigned long")
458         aTree.put("value", OString::number(anyItem.get<sal_uInt32>()).getStr());
459     else if (aType == "long")
460         aTree.put("value", OString::number(anyItem.get<sal_Int32>()).getStr());
461     else if (aType == "[]any")
462     {
463         uno::Sequence<uno::Any> aSeq;
464         if (anyItem >>= aSeq)
465         {
466             boost::property_tree::ptree aSubTree;
467 
468             for (auto i = 0; i < aSeq.getLength(); ++i)
469             {
470                 aSubTree.add_child(OString::number(i).getStr(), unoAnyToPropertyTree(aSeq[i]));
471             }
472             aTree.add_child("value", aSubTree);
473         }
474     }
475 
476     // TODO: Add more as required
477 
478     return aTree;
479 }
480 
481 namespace desktop {
482 
Create(const std::string & rPayload)483 RectangleAndPart RectangleAndPart::Create(const std::string& rPayload)
484 {
485     RectangleAndPart aRet;
486     if (rPayload.compare(0, 5, "EMPTY") == 0) // payload starts with "EMPTY"
487     {
488         aRet.m_aRectangle = tools::Rectangle(0, 0, SfxLokHelper::MaxTwips, SfxLokHelper::MaxTwips);
489         if (comphelper::LibreOfficeKit::isPartInInvalidation())
490             aRet.m_nPart = std::stol(rPayload.substr(6));
491 
492         return aRet;
493     }
494 
495     std::istringstream aStream(rPayload);
496     long nLeft, nTop, nWidth, nHeight;
497     long nPart = INT_MIN;
498     char nComma;
499     if (comphelper::LibreOfficeKit::isPartInInvalidation())
500     {
501         aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight >> nComma >> nPart;
502     }
503     else
504     {
505         aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
506     }
507 
508     if (nWidth > 0 && nHeight > 0)
509     {
510         // The top-left corner starts at (0, 0).
511         // Anything negative is invalid.
512         if (nLeft < 0)
513         {
514             nWidth += nLeft;
515             nLeft = 0;
516         }
517 
518         if (nTop < 0)
519         {
520             nHeight += nTop;
521             nTop = 0;
522         }
523 
524         if (nWidth > 0 && nHeight > 0)
525         {
526             aRet.m_aRectangle = tools::Rectangle(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
527         }
528     }
529     // else leave empty rect.
530 
531     aRet.m_nPart = nPart;
532     return aRet;
533 }
534 
setRectangleAndPart(const std::string & payload)535 RectangleAndPart& CallbackFlushHandler::CallbackData::setRectangleAndPart(const std::string& payload)
536 {
537     setRectangleAndPart(RectangleAndPart::Create(payload));
538 
539     // Return reference to the cached object.
540     return boost::get<RectangleAndPart>(PayloadObject);
541 }
542 
setRectangleAndPart(const RectangleAndPart & rRectAndPart)543 void CallbackFlushHandler::CallbackData::setRectangleAndPart(const RectangleAndPart& rRectAndPart)
544 {
545     PayloadString = rRectAndPart.toString().getStr();
546     PayloadObject = rRectAndPart;
547 }
548 
getRectangleAndPart() const549 const RectangleAndPart& CallbackFlushHandler::CallbackData::getRectangleAndPart() const
550 {
551     assert(PayloadObject.which() == 1);
552     return boost::get<RectangleAndPart>(PayloadObject);
553 }
554 
setJson(const std::string & payload)555 boost::property_tree::ptree& CallbackFlushHandler::CallbackData::setJson(const std::string& payload)
556 {
557     boost::property_tree::ptree aTree;
558     std::stringstream aStream(payload);
559     boost::property_tree::read_json(aStream, aTree);
560 
561     // Let boost normalize the payload so it always matches the cache.
562     setJson(aTree);
563 
564     // Return reference to the cached object.
565     return boost::get<boost::property_tree::ptree>(PayloadObject);
566 }
567 
setJson(const boost::property_tree::ptree & rTree)568 void CallbackFlushHandler::CallbackData::setJson(const boost::property_tree::ptree& rTree)
569 {
570     std::stringstream aJSONStream;
571     constexpr bool bPretty = false; // Don't waste time and bloat logs.
572     boost::property_tree::write_json(aJSONStream, rTree, bPretty);
573     PayloadString = boost::trim_copy(aJSONStream.str());
574 
575     PayloadObject = rTree;
576 }
577 
getJson() const578 const boost::property_tree::ptree& CallbackFlushHandler::CallbackData::getJson() const
579 {
580     assert(PayloadObject.which() == 2);
581     return boost::get<boost::property_tree::ptree>(PayloadObject);
582 }
583 
validate() const584 bool CallbackFlushHandler::CallbackData::validate() const
585 {
586     switch (PayloadObject.which())
587     {
588         // Not cached.
589         case 0:
590             return true;
591 
592         // RectangleAndPart.
593         case 1:
594             return getRectangleAndPart().toString().getStr() == PayloadString;
595 
596         // Json.
597         case 2:
598         {
599             std::stringstream aJSONStream;
600             boost::property_tree::write_json(aJSONStream, getJson(), false);
601             const std::string aExpected = boost::trim_copy(aJSONStream.str());
602             return aExpected == PayloadString;
603         }
604 
605         default:
606             assert(!"Unknown variant type; please add an entry to validate.");
607     }
608 
609     return false;
610 }
611 
612 }
613 
614 namespace {
615 
lcl_isViewCallbackType(const int type)616 bool lcl_isViewCallbackType(const int type)
617 {
618     switch (type)
619     {
620         case LOK_CALLBACK_CELL_VIEW_CURSOR:
621         case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
622         case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
623         case LOK_CALLBACK_TEXT_VIEW_SELECTION:
624         case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
625             return true;
626 
627         default:
628             return false;
629     }
630 }
631 
lcl_getViewId(const std::string & payload)632 int lcl_getViewId(const std::string& payload)
633 {
634     // this is a cheap way how to get the viewId from a JSON message; proper
635     // parsing is terribly expensive, and we just need the viewId here
636     size_t viewIdPos = payload.find("viewId");
637     if (viewIdPos == std::string::npos)
638         return 0;
639 
640     size_t numberPos = payload.find(":", viewIdPos + 6);
641     if (numberPos == std::string::npos)
642         return 0;
643 
644     for (++numberPos; numberPos < payload.length(); ++numberPos)
645     {
646         if (payload[numberPos] == ',' || payload[numberPos] == '}' || (payload[numberPos] >= '0' && payload[numberPos] <= '9'))
647             break;
648     }
649 
650     if (numberPos < payload.length() && payload[numberPos] >= '0' && payload[numberPos] <= '9')
651         return strtol(payload.substr(numberPos).c_str(), nullptr, 10);
652 
653     return 0;
654 }
655 
lcl_getViewId(const desktop::CallbackFlushHandler::CallbackData & rCallbackData)656 int lcl_getViewId(const desktop::CallbackFlushHandler::CallbackData& rCallbackData)
657 {
658     if (rCallbackData.isCached())
659         return rCallbackData.getJson().get<int>("viewId");
660     return lcl_getViewId(rCallbackData.PayloadString);
661 }
662 
extractCertificate(const std::string & certificate)663 std::string extractCertificate(const std::string & certificate)
664 {
665     const std::string header("-----BEGIN CERTIFICATE-----");
666     const std::string footer("-----END CERTIFICATE-----");
667 
668     std::string result;
669 
670     size_t pos1 = certificate.find(header);
671     if (pos1 == std::string::npos)
672         return result;
673 
674     size_t pos2 = certificate.find(footer, pos1 + 1);
675     if (pos2 == std::string::npos)
676         return result;
677 
678     pos1 = pos1 + header.length();
679     pos2 = pos2 - pos1;
680 
681     return certificate.substr(pos1, pos2);
682 }
683 
extractPrivateKey(const std::string & privateKey)684 std::string extractPrivateKey(const std::string & privateKey)
685 {
686     const std::string header("-----BEGIN PRIVATE KEY-----");
687     const std::string footer("-----END PRIVATE KEY-----");
688 
689     std::string result;
690 
691     size_t pos1 = privateKey.find(header);
692     if (pos1 == std::string::npos)
693         return result;
694 
695     size_t pos2 = privateKey.find(footer, pos1 + 1);
696     if (pos2 == std::string::npos)
697         return result;
698 
699     pos1 = pos1 + header.length();
700     pos2 = pos2 - pos1;
701 
702     return privateKey.substr(pos1, pos2);
703 }
704 
705 }  // end anonymous namespace
706 
707 // Could be anonymous in principle, but for the unit testing purposes, we
708 // declare it in init.hxx.
extractParameter(OUString & rOptions,const OUString & rName)709 OUString desktop::extractParameter(OUString& rOptions, const OUString& rName)
710 {
711     OUString aValue;
712 
713     OUString aNameEquals(rName + "=");
714     OUString aCommaNameEquals("," + rName + "=");
715 
716     int nIndex = -1;
717     if (rOptions.startsWith(aNameEquals))
718     {
719         size_t nLen = aNameEquals.getLength();
720         int nComma = rOptions.indexOf(",", nLen);
721         if (nComma >= 0)
722         {
723             aValue = rOptions.copy(nLen, nComma - nLen);
724             rOptions = rOptions.copy(nComma + 1);
725         }
726         else
727         {
728             aValue = rOptions.copy(nLen);
729             rOptions.clear();
730         }
731     }
732     else if ((nIndex = rOptions.indexOf(aCommaNameEquals)) >= 0)
733     {
734         size_t nLen = aCommaNameEquals.getLength();
735         int nComma = rOptions.indexOf(",", nIndex + nLen);
736         if (nComma >= 0)
737         {
738             aValue = rOptions.copy(nIndex + nLen, nComma - nIndex - nLen);
739             rOptions = rOptions.copy(0, nIndex) + rOptions.copy(nComma);
740         }
741         else
742         {
743             aValue = rOptions.copy(nIndex + nLen);
744             rOptions = rOptions.copy(0, nIndex);
745         }
746     }
747 
748     return aValue;
749 }
750 
751 extern "C"
752 {
753 
754 static void doc_destroy(LibreOfficeKitDocument* pThis);
755 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions);
756 static int doc_getDocumentType(LibreOfficeKitDocument* pThis);
757 static int doc_getParts(LibreOfficeKitDocument* pThis);
758 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis);
759 static int doc_getPart(LibreOfficeKitDocument* pThis);
760 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart);
761 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect);
762 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate);
763 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart);
764 static void doc_setPartMode(LibreOfficeKitDocument* pThis, int nPartMode);
765 static void doc_paintTile(LibreOfficeKitDocument* pThis,
766                           unsigned char* pBuffer,
767                           const int nCanvasWidth, const int nCanvasHeight,
768                           const int nTilePosX, const int nTilePosY,
769                           const int nTileWidth, const int nTileHeight);
770 #ifdef IOS
771 static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
772                                      void* rCGContext,
773                                      const int nCanvasWidth, const int nCanvasHeight,
774                                      const int nTilePosX, const int nTilePosY,
775                                      const int nTileWidth, const int nTileHeight);
776 #endif
777 static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
778                               unsigned char* pBuffer,
779                               const int nPart,
780                               const int nCanvasWidth, const int nCanvasHeight,
781                               const int nTilePosX, const int nTilePosY,
782                               const int nTileWidth, const int nTileHeight);
783 static int doc_getTileMode(LibreOfficeKitDocument* pThis);
784 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
785                                 long* pWidth,
786                                 long* pHeight);
787 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
788                                        const char* pArguments);
789 
790 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
791                                 LibreOfficeKitCallback pCallback,
792                                 void* pData);
793 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis,
794                              int nType,
795                              int nCharCode,
796                              int nKeyCode);
797 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis,
798                                             unsigned nWindowId,
799                                             int nType,
800                                             const char* pText);
801 static void doc_removeTextContext(LibreOfficeKitDocument* pThis,
802                                   unsigned nLOKWindowId,
803                                   int nCharBefore,
804                                   int nCharAfter);
805 static void doc_sendDialogEvent(LibreOfficeKitDocument* pThis,
806                                unsigned nLOKWindowId,
807                                const char* pArguments);
808 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* pThis,
809                                    unsigned nLOKWindowId,
810                                    int nType,
811                                    int nCharCode,
812                                    int nKeyCode);
813 static void doc_postMouseEvent (LibreOfficeKitDocument* pThis,
814                                 int nType,
815                                 int nX,
816                                 int nY,
817                                 int nCount,
818                                 int nButtons,
819                                 int nModifier);
820 static void doc_postWindowMouseEvent (LibreOfficeKitDocument* pThis,
821                                       unsigned nLOKWindowId,
822                                       int nType,
823                                       int nX,
824                                       int nY,
825                                       int nCount,
826                                       int nButtons,
827                                       int nModifier);
828 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* pThis,
829                                       unsigned nLOKWindowId,
830                                       const char* pType,
831                                       int nX,
832                                       int nY,
833                                       int nOffset);
834 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis,
835                                const char* pCommand,
836                                const char* pArguments,
837                                bool bNotifyWhenFinished);
838 static void doc_setTextSelection (LibreOfficeKitDocument* pThis,
839                                   int nType,
840                                   int nX,
841                                   int nY);
842 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis,
843                                   const char* pMimeType,
844                                   char** pUsedMimeType);
845 static int doc_getSelectionType(LibreOfficeKitDocument* pThis);
846 static int doc_getClipboard (LibreOfficeKitDocument* pThis,
847                              const char **pMimeTypes,
848                              size_t      *pOutCount,
849                              char      ***pOutMimeTypes,
850                              size_t     **pOutSizes,
851                              char      ***pOutStreams);
852 static int doc_setClipboard (LibreOfficeKitDocument* pThis,
853                              const size_t   nInCount,
854                              const char   **pInMimeTypes,
855                              const size_t  *pInSizes,
856                              const char   **pInStreams);
857 static bool doc_paste(LibreOfficeKitDocument* pThis,
858                       const char* pMimeType,
859                       const char* pData,
860                       size_t nSize);
861 static void doc_setGraphicSelection (LibreOfficeKitDocument* pThis,
862                                   int nType,
863                                   int nX,
864                                   int nY);
865 static void doc_resetSelection (LibreOfficeKitDocument* pThis);
866 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand);
867 static void doc_setClientZoom(LibreOfficeKitDocument* pThis,
868                                     int nTilePixelWidth,
869                                     int nTilePixelHeight,
870                                     int nTileTwipWidth,
871                                     int nTileTwipHeight);
872 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight);
873 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden);
874 static int doc_createView(LibreOfficeKitDocument* pThis);
875 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, const char* pOptions);
876 static void doc_destroyView(LibreOfficeKitDocument* pThis, int nId);
877 static void doc_setView(LibreOfficeKitDocument* pThis, int nId);
878 static int doc_getView(LibreOfficeKitDocument* pThis);
879 static int doc_getViewsCount(LibreOfficeKitDocument* pThis);
880 static bool doc_getViewIds(LibreOfficeKitDocument* pThis, int* pArray, size_t nSize);
881 static void doc_setViewLanguage(LibreOfficeKitDocument* pThis, int nId, const char* language);
882 static unsigned char* doc_renderFontOrientation(LibreOfficeKitDocument* pThis,
883                           const char *pFontName,
884                           const char *pChar,
885                           int* pFontWidth,
886                           int* pFontHeight,
887                           int pOrientation);
888 static unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
889                           const char *pFontName,
890                           const char *pChar,
891                           int* pFontWidth,
892                           int* pFontHeight);
893 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart);
894 
895 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
896                             const int nX, const int nY,
897                             const int nWidth, const int nHeight);
898 
899 static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
900                                const int nX, const int nY,
901                                const int nWidth, const int nHeight,
902                                const double fDPIScale);
903 
904 static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
905                                    const int nX, const int nY,
906                                    const int nWidth, const int nHeight,
907                                    const double fDPIScale, int viewId);
908 
909 static void doc_postWindow(LibreOfficeKitDocument* pThis, unsigned
910  nLOKWindowId, int nAction, const char* pData);
911 
912 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart);
913 
914 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
915                                   const unsigned char* pCertificateBinary,
916                                   const int nCertificateBinarySize,
917                                   const unsigned char* pPrivateKeyBinary,
918                                   const int nPrivateKeyBinarySize);
919 
920 static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
921                                  const unsigned char* pCertificateBinary,
922                                  const int nCertificateBinarySize);
923 
924 static int doc_getSignatureState(LibreOfficeKitDocument* pThis);
925 
926 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput);
927 
928 static void doc_resizeWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
929                              const int nWidth, const int nHeight);
930 
931 } // extern "C"
932 
933 namespace {
getTiledRenderable(LibreOfficeKitDocument * pThis)934 ITiledRenderable* getTiledRenderable(LibreOfficeKitDocument* pThis)
935 {
936     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
937     return dynamic_cast<ITiledRenderable*>(pDocument->mxComponent.get());
938 }
939 
940 #ifndef IOS
941 
942 /*
943  * Unfortunately clipboard creation using UNO is insanely baroque.
944  * we also need to ensure that this works for the first view which
945  * has no clear 'createView' called for it (unfortunately).
946  */
forceSetClipboardForCurrentView(LibreOfficeKitDocument * pThis)947 rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis)
948 {
949     ITiledRenderable* pDoc = getTiledRenderable(pThis);
950     rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView());
951 
952     SAL_INFO("lok", "Set to clipboard for view " << xClip.get());
953     // FIXME: using a hammer here - should not be necessary if all tests used createView.
954     pDoc->setClipboard(uno::Reference<datatransfer::clipboard::XClipboard>(xClip->getXI(), UNO_QUERY));
955 
956     return xClip;
957 }
958 
959 #endif
960 
961 } // anonymous namespace
962 
LibLODocument_Impl(const uno::Reference<css::lang::XComponent> & xComponent)963 LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XComponent> &xComponent)
964     : mxComponent(xComponent)
965 {
966     if (!(m_pDocumentClass = gDocumentClass.lock()))
967     {
968         m_pDocumentClass.reset(new LibreOfficeKitDocumentClass);
969 
970         m_pDocumentClass->nSize = sizeof(LibreOfficeKitDocumentClass);
971 
972         m_pDocumentClass->destroy = doc_destroy;
973         m_pDocumentClass->saveAs = doc_saveAs;
974         m_pDocumentClass->getDocumentType = doc_getDocumentType;
975         m_pDocumentClass->getParts = doc_getParts;
976         m_pDocumentClass->getPartPageRectangles = doc_getPartPageRectangles;
977         m_pDocumentClass->getPart = doc_getPart;
978         m_pDocumentClass->setPart = doc_setPart;
979         m_pDocumentClass->selectPart = doc_selectPart;
980         m_pDocumentClass->moveSelectedParts = doc_moveSelectedParts;
981         m_pDocumentClass->getPartName = doc_getPartName;
982         m_pDocumentClass->setPartMode = doc_setPartMode;
983         m_pDocumentClass->paintTile = doc_paintTile;
984 #ifdef IOS
985         m_pDocumentClass->paintTileToCGContext = doc_paintTileToCGContext;
986 #endif
987         m_pDocumentClass->paintPartTile = doc_paintPartTile;
988         m_pDocumentClass->getTileMode = doc_getTileMode;
989         m_pDocumentClass->getDocumentSize = doc_getDocumentSize;
990         m_pDocumentClass->initializeForRendering = doc_initializeForRendering;
991         m_pDocumentClass->registerCallback = doc_registerCallback;
992         m_pDocumentClass->postKeyEvent = doc_postKeyEvent;
993         m_pDocumentClass->postWindowExtTextInputEvent = doc_postWindowExtTextInputEvent;
994         m_pDocumentClass->removeTextContext = doc_removeTextContext;
995         m_pDocumentClass->postWindowKeyEvent = doc_postWindowKeyEvent;
996         m_pDocumentClass->postMouseEvent = doc_postMouseEvent;
997         m_pDocumentClass->postWindowMouseEvent = doc_postWindowMouseEvent;
998         m_pDocumentClass->sendDialogEvent = doc_sendDialogEvent;
999         m_pDocumentClass->postUnoCommand = doc_postUnoCommand;
1000         m_pDocumentClass->setTextSelection = doc_setTextSelection;
1001         m_pDocumentClass->getTextSelection = doc_getTextSelection;
1002         m_pDocumentClass->getSelectionType = doc_getSelectionType;
1003         m_pDocumentClass->getClipboard = doc_getClipboard;
1004         m_pDocumentClass->setClipboard = doc_setClipboard;
1005         m_pDocumentClass->paste = doc_paste;
1006         m_pDocumentClass->setGraphicSelection = doc_setGraphicSelection;
1007         m_pDocumentClass->resetSelection = doc_resetSelection;
1008         m_pDocumentClass->getCommandValues = doc_getCommandValues;
1009         m_pDocumentClass->setClientZoom = doc_setClientZoom;
1010         m_pDocumentClass->setClientVisibleArea = doc_setClientVisibleArea;
1011         m_pDocumentClass->setOutlineState = doc_setOutlineState;
1012 
1013         m_pDocumentClass->createView = doc_createView;
1014         m_pDocumentClass->destroyView = doc_destroyView;
1015         m_pDocumentClass->setView = doc_setView;
1016         m_pDocumentClass->getView = doc_getView;
1017         m_pDocumentClass->getViewsCount = doc_getViewsCount;
1018         m_pDocumentClass->getViewIds = doc_getViewIds;
1019 
1020         m_pDocumentClass->renderFont = doc_renderFont;
1021         m_pDocumentClass->renderFontOrientation = doc_renderFontOrientation;
1022         m_pDocumentClass->getPartHash = doc_getPartHash;
1023 
1024         m_pDocumentClass->paintWindow = doc_paintWindow;
1025         m_pDocumentClass->paintWindowDPI = doc_paintWindowDPI;
1026         m_pDocumentClass->paintWindowForView = doc_paintWindowForView;
1027         m_pDocumentClass->postWindow = doc_postWindow;
1028         m_pDocumentClass->resizeWindow = doc_resizeWindow;
1029 
1030         m_pDocumentClass->setViewLanguage = doc_setViewLanguage;
1031 
1032         m_pDocumentClass->getPartInfo = doc_getPartInfo;
1033 
1034         m_pDocumentClass->insertCertificate = doc_insertCertificate;
1035         m_pDocumentClass->addCertificate = doc_addCertificate;
1036         m_pDocumentClass->getSignatureState = doc_getSignatureState;
1037 
1038         m_pDocumentClass->renderShapeSelection = doc_renderShapeSelection;
1039         m_pDocumentClass->postWindowGestureEvent = doc_postWindowGestureEvent;
1040 
1041         m_pDocumentClass->createViewWithOptions = doc_createViewWithOptions;
1042 
1043         gDocumentClass = m_pDocumentClass;
1044     }
1045     pClass = m_pDocumentClass.get();
1046 
1047 #ifndef IOS
1048     forceSetClipboardForCurrentView(this);
1049 #endif
1050 }
1051 
~LibLODocument_Impl()1052 LibLODocument_Impl::~LibLODocument_Impl()
1053 {
1054     try
1055     {
1056         mxComponent->dispose();
1057     }
1058     catch (const css::lang::DisposedException& rException)
1059     {
1060         SAL_WARN("lok", "failed to dispose document:" << rException.Message);
1061     }
1062 }
1063 
getGenerator()1064 static OUString getGenerator()
1065 {
1066     OUString sGenerator(
1067         Translate::ExpandVariables("%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION (%1)"));
1068     OUString os("$_OS");
1069     ::rtl::Bootstrap::expandMacros(os);
1070     return sGenerator.replaceFirst("%1", os);
1071 }
1072 
1073 extern "C" {
1074 
CallbackFlushHandler(LibreOfficeKitDocument * pDocument,LibreOfficeKitCallback pCallback,void * pData)1075 CallbackFlushHandler::CallbackFlushHandler(LibreOfficeKitDocument* pDocument, LibreOfficeKitCallback pCallback, void* pData)
1076     : Idle( "lokit timer callback" ),
1077       m_pDocument(pDocument),
1078       m_pCallback(pCallback),
1079       m_pData(pData),
1080       m_nDisableCallbacks(0),
1081       m_bEventLatch(false)
1082 {
1083     SetPriority(TaskPriority::POST_PAINT);
1084 
1085     // Add the states that are safe to skip duplicates on, even when
1086     // not consequent (i.e. do no emit them if unchanged from last).
1087     m_states.emplace(LOK_CALLBACK_TEXT_SELECTION, "NIL");
1088     m_states.emplace(LOK_CALLBACK_GRAPHIC_SELECTION, "NIL");
1089     m_states.emplace(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, "NIL");
1090     m_states.emplace(LOK_CALLBACK_STATE_CHANGED, "NIL");
1091     m_states.emplace(LOK_CALLBACK_MOUSE_POINTER, "NIL");
1092     m_states.emplace(LOK_CALLBACK_CELL_CURSOR, "NIL");
1093     m_states.emplace(LOK_CALLBACK_CELL_FORMULA, "NIL");
1094     m_states.emplace(LOK_CALLBACK_CELL_ADDRESS, "NIL");
1095     m_states.emplace(LOK_CALLBACK_CURSOR_VISIBLE, "NIL");
1096     m_states.emplace(LOK_CALLBACK_SET_PART, "NIL");
1097 
1098     Start();
1099 }
1100 
~CallbackFlushHandler()1101 CallbackFlushHandler::~CallbackFlushHandler()
1102 {
1103     Stop();
1104 }
1105 
callback(const int type,const char * payload,void * data)1106 void CallbackFlushHandler::callback(const int type, const char* payload, void* data)
1107 {
1108     CallbackFlushHandler* self = static_cast<CallbackFlushHandler*>(data);
1109     if (self)
1110     {
1111         self->queue(type, payload);
1112     }
1113 }
1114 
queue(const int type,const char * data)1115 void CallbackFlushHandler::queue(const int type, const char* data)
1116 {
1117     comphelper::ProfileZone aZone("CallbackFlushHander::queue");
1118 
1119     CallbackData aCallbackData(type, (data ? data : "(nil)"));
1120     const std::string& payload = aCallbackData.PayloadString;
1121     SAL_INFO("lok", "Queue: [" << type << "]: [" << payload << "] on " << m_queue.size() << " entries.");
1122 
1123     bool bIsChartActive = false;
1124     if (type == LOK_CALLBACK_GRAPHIC_SELECTION)
1125     {
1126         LokChartHelper aChartHelper(SfxViewShell::Current());
1127         bIsChartActive = aChartHelper.GetWindow() != nullptr;
1128     }
1129 
1130     if (callbacksDisabled() && !bIsChartActive)
1131     {
1132         // We drop notifications when this is set, except for important ones.
1133         // When we issue a complex command (such as .uno:InsertAnnotation)
1134         // there will be multiple notifications. On the first invalidation
1135         // we will start painting, but other events will get fired
1136         // while the complex command in question executes.
1137         // We don't want to suppress everything here on the wrong assumption
1138         // that no new events are fired during painting.
1139         if (type != LOK_CALLBACK_STATE_CHANGED &&
1140             type != LOK_CALLBACK_INVALIDATE_TILES &&
1141             type != LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1142             type != LOK_CALLBACK_CURSOR_VISIBLE &&
1143             type != LOK_CALLBACK_VIEW_CURSOR_VISIBLE &&
1144             type != LOK_CALLBACK_TEXT_SELECTION &&
1145             type != LOK_CALLBACK_REFERENCE_MARKS)
1146         {
1147             SAL_INFO("lok", "Skipping while painting [" << type << "]: [" << payload << "].");
1148             return;
1149         }
1150 
1151         // In Writer we drop all notifications during painting.
1152         if (doc_getDocumentType(m_pDocument) == LOK_DOCTYPE_TEXT)
1153             return;
1154     }
1155 
1156     // Suppress invalid payloads.
1157     if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1158         payload.find(", 0, 0, ") != std::string::npos)
1159     {
1160         // The cursor position is often the relative coordinates of the widget
1161         // issuing it, instead of the absolute one that we expect.
1162         // This is temporary however, and, once the control is created and initialized
1163         // correctly, it eventually emits the correct absolute coordinates.
1164         SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
1165         return;
1166     }
1167 
1168     std::unique_lock<std::mutex> lock(m_mutex);
1169 
1170     // drop duplicate callbacks for the listed types
1171     switch (type)
1172     {
1173         case LOK_CALLBACK_TEXT_SELECTION_START:
1174         case LOK_CALLBACK_TEXT_SELECTION_END:
1175         case LOK_CALLBACK_TEXT_SELECTION:
1176         case LOK_CALLBACK_GRAPHIC_SELECTION:
1177         case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1178         case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1179         case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1180         case LOK_CALLBACK_STATE_CHANGED:
1181         case LOK_CALLBACK_MOUSE_POINTER:
1182         case LOK_CALLBACK_CELL_CURSOR:
1183         case LOK_CALLBACK_CELL_VIEW_CURSOR:
1184         case LOK_CALLBACK_CELL_FORMULA:
1185         case LOK_CALLBACK_CELL_ADDRESS:
1186         case LOK_CALLBACK_CURSOR_VISIBLE:
1187         case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1188         case LOK_CALLBACK_SET_PART:
1189         case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1190         case LOK_CALLBACK_INVALIDATE_HEADER:
1191         case LOK_CALLBACK_WINDOW:
1192         {
1193             const auto& pos = std::find_if(m_queue.rbegin(), m_queue.rend(),
1194                     [type] (const queue_type::value_type& elem) { return (elem.Type == type); });
1195 
1196             if (pos != m_queue.rend() && pos->PayloadString == payload)
1197             {
1198                 SAL_INFO("lok", "Skipping queue duplicate [" << type << + "]: [" << payload << "].");
1199                 return;
1200             }
1201         }
1202         break;
1203     }
1204 
1205     if (type == LOK_CALLBACK_TEXT_SELECTION && payload.empty())
1206     {
1207         const auto& posStart = std::find_if(m_queue.rbegin(), m_queue.rend(),
1208                 [] (const queue_type::value_type& elem) { return (elem.Type == LOK_CALLBACK_TEXT_SELECTION_START); });
1209         if (posStart != m_queue.rend())
1210             posStart->PayloadString.clear();
1211 
1212         const auto& posEnd = std::find_if(m_queue.rbegin(), m_queue.rend(),
1213                 [] (const queue_type::value_type& elem) { return (elem.Type == LOK_CALLBACK_TEXT_SELECTION_END); });
1214         if (posEnd != m_queue.rend())
1215             posEnd->PayloadString.clear();
1216     }
1217 
1218     // When payload is empty discards any previous state.
1219     if (payload.empty())
1220     {
1221         switch (type)
1222         {
1223             case LOK_CALLBACK_TEXT_SELECTION_START:
1224             case LOK_CALLBACK_TEXT_SELECTION_END:
1225             case LOK_CALLBACK_TEXT_SELECTION:
1226             case LOK_CALLBACK_GRAPHIC_SELECTION:
1227             case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1228             case LOK_CALLBACK_INVALIDATE_TILES:
1229                 if (removeAll(
1230                         [type](const queue_type::value_type& elem) { return (elem.Type == type); }))
1231                     SAL_INFO("lok", "Removed dups of [" << type << "]: [" << payload << "].");
1232                 break;
1233         }
1234     }
1235     else
1236     {
1237         switch (type)
1238         {
1239             // These are safe to use the latest state and ignore previous
1240             // ones (if any) since the last overrides previous ones.
1241             case LOK_CALLBACK_TEXT_SELECTION_START:
1242             case LOK_CALLBACK_TEXT_SELECTION_END:
1243             case LOK_CALLBACK_TEXT_SELECTION:
1244             case LOK_CALLBACK_MOUSE_POINTER:
1245             case LOK_CALLBACK_CELL_CURSOR:
1246             case LOK_CALLBACK_CELL_FORMULA:
1247             case LOK_CALLBACK_CELL_ADDRESS:
1248             case LOK_CALLBACK_CURSOR_VISIBLE:
1249             case LOK_CALLBACK_SET_PART:
1250             case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
1251             case LOK_CALLBACK_RULER_UPDATE:
1252             {
1253                 if (removeAll(
1254                         [type](const queue_type::value_type& elem) { return (elem.Type == type); }))
1255                     SAL_INFO("lok", "Removed dups of [" << type << "]: [" << payload << "].");
1256             }
1257             break;
1258 
1259             // These are safe to use the latest state and ignore previous
1260             // ones (if any) since the last overrides previous ones,
1261             // but only if the view is the same.
1262             case LOK_CALLBACK_CELL_VIEW_CURSOR:
1263             case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1264             case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1265             case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1266             case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1267             case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1268             {
1269                 const int nViewId = lcl_getViewId(payload);
1270                 removeAll(
1271                     [type, nViewId] (const queue_type::value_type& elem) {
1272                         return (elem.Type == type && nViewId == lcl_getViewId(elem));
1273                     }
1274                 );
1275             }
1276             break;
1277 
1278             case LOK_CALLBACK_INVALIDATE_TILES:
1279                 if (processInvalidateTilesEvent(aCallbackData))
1280                     return;
1281             break;
1282 
1283             // State changes with same name override previous ones with a different value.
1284             // Ex. ".uno:PageStatus=Slide 20 of 83" overwrites any previous PageStatus.
1285             case LOK_CALLBACK_STATE_CHANGED:
1286             {
1287                 // Compare the state name=value and overwrite earlier entries with same name.
1288                 const auto pos = payload.find('=');
1289                 if (pos != std::string::npos)
1290                 {
1291                     const std::string name = payload.substr(0, pos + 1);
1292                     removeAll(
1293                         [type, &name] (const queue_type::value_type& elem) {
1294                             return (elem.Type == type) && (elem.PayloadString.compare(0, name.size(), name) == 0);
1295                         }
1296                     );
1297                 }
1298             }
1299             break;
1300 
1301             case LOK_CALLBACK_WINDOW:
1302                 if (processWindowEvent(aCallbackData))
1303                     return;
1304             break;
1305 
1306             case LOK_CALLBACK_GRAPHIC_SELECTION:
1307             {
1308                 // remove only selection ranges and 'EMPTY' messages
1309                 // always send 'INPLACE' and 'INPLACE EXIT' messages
1310                 removeAll([type, payload] (const queue_type::value_type& elem)
1311                     { return (elem.Type == type && elem.PayloadString[0] != 'I'); });
1312             }
1313             break;
1314         }
1315     }
1316 
1317     // Validate that the cached data and the payload string are identical.
1318     assert(aCallbackData.validate() && "Cached callback payload object and string mismatch!");
1319     m_queue.emplace_back(aCallbackData);
1320     SAL_INFO("lok", "Queued #" << (m_queue.size() - 1) <<
1321              " [" << type << "]: [" << payload << "] to have " << m_queue.size() << " entries.");
1322 
1323 #ifdef DBG_UTIL
1324     {
1325         // Dump the queue state and validate cached data.
1326         int i = 1;
1327         std::ostringstream oss;
1328         if (m_queue.empty())
1329             oss << "Empty";
1330         else
1331             oss << m_queue.size() << " items\n";
1332         for (const CallbackData& c : m_queue)
1333             oss << i++ << ": [" << c.Type << "] [" << c.PayloadString << "].\n";
1334         SAL_INFO("lok", "Current Queue: " << oss.str());
1335         for (const CallbackData& c : m_queue)
1336             assert(c.validate());
1337     }
1338 #endif
1339 
1340     lock.unlock();
1341     if (!IsActive())
1342     {
1343         Start();
1344     }
1345 }
1346 
processInvalidateTilesEvent(CallbackData & aCallbackData)1347 bool CallbackFlushHandler::processInvalidateTilesEvent(CallbackData& aCallbackData)
1348 {
1349     const std::string& payload = aCallbackData.PayloadString;
1350     const int type = aCallbackData.Type;
1351 
1352     RectangleAndPart& rcNew = aCallbackData.setRectangleAndPart(payload);
1353     if (rcNew.isEmpty())
1354     {
1355         SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
1356         return true;
1357     }
1358 
1359     // If we have to invalidate all tiles, we can skip any new tile invalidation.
1360     // Find the last INVALIDATE_TILES entry, if any to see if it's invalidate-all.
1361     const auto& pos
1362         = std::find_if(m_queue.rbegin(), m_queue.rend(), [](const queue_type::value_type& elem) {
1363               return (elem.Type == LOK_CALLBACK_INVALIDATE_TILES);
1364           });
1365     if (pos != m_queue.rend())
1366     {
1367         const RectangleAndPart& rcOld = pos->getRectangleAndPart();
1368         if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart))
1369         {
1370             SAL_INFO("lok", "Skipping queue [" << type << "]: [" << payload
1371                                                << "] since all tiles need to be invalidated.");
1372             return true;
1373         }
1374 
1375         if (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart)
1376         {
1377             // If fully overlapping.
1378             if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
1379             {
1380                 SAL_INFO("lok", "Skipping queue [" << type << "]: [" << payload
1381                                                    << "] since overlaps existing all-parts.");
1382                 return true;
1383             }
1384         }
1385     }
1386 
1387     if (rcNew.isInfinite())
1388     {
1389         SAL_INFO("lok", "Have Empty [" << type << "]: [" << payload
1390                                        << "] so removing all with part " << rcNew.m_nPart << ".");
1391         removeAll([&rcNew](const queue_type::value_type& elem) {
1392             if (elem.Type == LOK_CALLBACK_INVALIDATE_TILES)
1393             {
1394                 // Remove exiting if new is all-encompassing, or if of the same part.
1395                 const RectangleAndPart rcOld = RectangleAndPart::Create(elem.PayloadString);
1396                 return (rcNew.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart);
1397             }
1398 
1399             // Keep others.
1400             return false;
1401         });
1402     }
1403     else
1404     {
1405         const auto rcOrig = rcNew;
1406 
1407         SAL_INFO("lok", "Have [" << type << "]: [" << payload << "] so merging overlapping.");
1408         removeAll([&rcNew](const queue_type::value_type& elem) {
1409             if (elem.Type == LOK_CALLBACK_INVALIDATE_TILES)
1410             {
1411                 const RectangleAndPart& rcOld = elem.getRectangleAndPart();
1412                 if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 && rcOld.m_nPart != rcNew.m_nPart)
1413                 {
1414                     SAL_INFO("lok", "Nothing to merge between new: "
1415                                         << rcNew.toString() << ", and old: " << rcOld.toString());
1416                     return false;
1417                 }
1418 
1419                 if (rcNew.m_nPart == -1)
1420                 {
1421                     // Don't merge unless fully overlapped.
1422                     SAL_INFO("lok", "New " << rcNew.toString() << " has " << rcOld.toString()
1423                                            << "?");
1424                     if (rcNew.m_aRectangle.IsInside(rcOld.m_aRectangle))
1425                     {
1426                         SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
1427                                                << rcOld.toString() << ".");
1428                         return true;
1429                     }
1430                 }
1431                 else if (rcOld.m_nPart == -1)
1432                 {
1433                     // Don't merge unless fully overlapped.
1434                     SAL_INFO("lok", "Old " << rcOld.toString() << " has " << rcNew.toString()
1435                                            << "?");
1436                     if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
1437                     {
1438                         SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
1439                                                << rcOld.toString() << ".");
1440                         return true;
1441                     }
1442                 }
1443                 else
1444                 {
1445                     const tools::Rectangle rcOverlap
1446                         = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
1447                     const bool bOverlap = !rcOverlap.IsEmpty();
1448                     SAL_INFO("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString()
1449                                                << " => " << rcOverlap.toString()
1450                                                << " Overlap: " << bOverlap);
1451                     if (bOverlap)
1452                     {
1453                         rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
1454                         SAL_INFO("lok", "Merged: " << rcNew.toString());
1455                         return true;
1456                     }
1457                 }
1458             }
1459 
1460             // Keep others.
1461             return false;
1462         });
1463 
1464         if (rcNew.m_aRectangle != rcOrig.m_aRectangle)
1465         {
1466             SAL_INFO("lok", "Replacing: " << rcOrig.toString() << " by " << rcNew.toString());
1467             if (rcNew.m_aRectangle.GetWidth() < rcOrig.m_aRectangle.GetWidth()
1468                 || rcNew.m_aRectangle.GetHeight() < rcOrig.m_aRectangle.GetHeight())
1469             {
1470                 SAL_WARN("lok", "Error: merged rect smaller.");
1471             }
1472         }
1473     }
1474 
1475     aCallbackData.setRectangleAndPart(rcNew);
1476     // Queue this one.
1477     return false;
1478 }
1479 
processWindowEvent(CallbackData & aCallbackData)1480 bool CallbackFlushHandler::processWindowEvent(CallbackData& aCallbackData)
1481 {
1482     const std::string& payload = aCallbackData.PayloadString;
1483     const int type = aCallbackData.Type;
1484 
1485     boost::property_tree::ptree& aTree = aCallbackData.setJson(payload);
1486     const unsigned nLOKWindowId = aTree.get<unsigned>("id", 0);
1487     const std::string aAction = aTree.get<std::string>("action", "");
1488     if (aAction == "invalidate")
1489     {
1490         std::string aRectStr = aTree.get<std::string>("rectangle", "");
1491         // no 'rectangle' field => invalidate all of the window =>
1492         // remove all previous window part invalidations
1493         if (aRectStr.empty())
1494         {
1495             removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1496                 if (elem.Type == LOK_CALLBACK_WINDOW)
1497                 {
1498                     const boost::property_tree::ptree& aOldTree = elem.getJson();
1499                     if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)
1500                         && aOldTree.get<std::string>("action", "") == "invalidate")
1501                     {
1502                         return true;
1503                     }
1504                 }
1505                 return false;
1506             });
1507         }
1508         else
1509         {
1510             // if we have to invalidate all of the window, ignore
1511             // any part invalidation message
1512             const auto invAllExist = std::any_of(m_queue.rbegin(), m_queue.rend(),
1513                                         [&nLOKWindowId] (const queue_type::value_type& elem)
1514                                         {
1515                                             if (elem.Type != LOK_CALLBACK_WINDOW)
1516                                                 return false;
1517 
1518                                             const boost::property_tree::ptree& aOldTree = elem.getJson();
1519                                             return nLOKWindowId == aOldTree.get<unsigned>("id", 0)
1520                                                 && aOldTree.get<std::string>("action", "") == "invalidate"
1521                                                 && aOldTree.get<std::string>("rectangle", "").empty();
1522                                         });
1523 
1524             // we found a invalidate-all window callback
1525             if (invAllExist)
1526             {
1527                 SAL_INFO("lok.dialog", "Skipping queue ["
1528                                            << type << "]: [" << payload
1529                                            << "] since whole window needs to be invalidated.");
1530                 return true;
1531             }
1532 
1533             std::istringstream aRectStream(aRectStr);
1534             long nLeft, nTop, nWidth, nHeight;
1535             char nComma;
1536             aRectStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
1537             tools::Rectangle aNewRect(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
1538             bool currentIsRedundant = false;
1539             removeAll([&aNewRect, &nLOKWindowId,
1540                        &currentIsRedundant](const queue_type::value_type& elem) {
1541                 if (elem.Type != LOK_CALLBACK_WINDOW)
1542                     return false;
1543 
1544                 const boost::property_tree::ptree& aOldTree = elem.getJson();
1545                 if (aOldTree.get<std::string>("action", "") == "invalidate")
1546                 {
1547                     // Not possible that we encounter an empty rectangle here; we already handled this case above.
1548                     std::istringstream aOldRectStream(aOldTree.get<std::string>("rectangle", ""));
1549                     long nOldLeft, nOldTop, nOldWidth, nOldHeight;
1550                     char nOldComma;
1551                     aOldRectStream >> nOldLeft >> nOldComma >> nOldTop >> nOldComma >> nOldWidth
1552                         >> nOldComma >> nOldHeight;
1553                     const tools::Rectangle aOldRect = tools::Rectangle(
1554                         nOldLeft, nOldTop, nOldLeft + nOldWidth, nOldTop + nOldHeight);
1555 
1556                     if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1557                     {
1558                         if (aNewRect == aOldRect)
1559                         {
1560                             SAL_INFO("lok.dialog", "Duplicate rect [" << aNewRect.toString()
1561                                                                       << "]. Skipping new.");
1562                             // We have a rectangle in the queue already that makes the current Callback useless.
1563                             currentIsRedundant = true;
1564                             return false;
1565                         }
1566                         // new one engulfs the old one?
1567                         else if (aNewRect.IsInside(aOldRect))
1568                         {
1569                             SAL_INFO("lok.dialog",
1570                                      "New rect [" << aNewRect.toString() << "] engulfs old ["
1571                                                   << aOldRect.toString() << "]. Replacing old.");
1572                             return true;
1573                         }
1574                         // old one engulfs the new one?
1575                         else if (aOldRect.IsInside(aNewRect))
1576                         {
1577                             SAL_INFO("lok.dialog",
1578                                      "Old rect [" << aOldRect.toString() << "] engulfs new ["
1579                                                   << aNewRect.toString() << "]. Skipping new.");
1580                             // We have a rectangle in the queue already that makes the current Callback useless.
1581                             currentIsRedundant = true;
1582                             return false;
1583                         }
1584                         else
1585                         {
1586                             // Overlapping rects.
1587                             const tools::Rectangle aPreMergeRect = aNewRect;
1588                             aNewRect.Union(aOldRect);
1589                             SAL_INFO("lok.dialog", "Merging rects ["
1590                                                        << aPreMergeRect.toString() << "] & ["
1591                                                        << aOldRect.toString() << "] = ["
1592                                                        << aNewRect.toString()
1593                                                        << "]. Replacing old.");
1594                             return true;
1595                         }
1596                     }
1597                 }
1598 
1599                 // keep rest
1600                 return false;
1601             });
1602 
1603             // Do not enqueue if redundant.
1604             if (currentIsRedundant)
1605                 return true;
1606 
1607             aTree.put("rectangle", aNewRect.toString().getStr());
1608             aCallbackData.setJson(aTree);
1609             assert(aCallbackData.validate() && "Validation after setJson failed!");
1610         }
1611     }
1612     else if (aAction == "created")
1613     {
1614         // Remove all previous actions on same dialog, if we are creating it anew.
1615         removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1616             if (elem.Type == LOK_CALLBACK_WINDOW)
1617             {
1618                 const boost::property_tree::ptree& aOldTree = elem.getJson();
1619                 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1620                     return true;
1621             }
1622             return false;
1623         });
1624 
1625         VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
1626         if (!pWindow)
1627         {
1628             gImpl->maLastExceptionMsg = "Document doesn't support dialog rendering, or window not found.";
1629             return false;
1630         }
1631 
1632 #ifndef IOS
1633         auto xClip = forceSetClipboardForCurrentView(m_pDocument);
1634 
1635         uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(xClip.get());
1636         pWindow->SetClipboard(xClipboard);
1637 #endif
1638     }
1639     else if (aAction == "size_changed")
1640     {
1641         // A size change is practically re-creation of the window.
1642         // But at a minimum it's a full invalidation.
1643         removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1644             if (elem.Type == LOK_CALLBACK_WINDOW)
1645             {
1646                 const boost::property_tree::ptree& aOldTree = elem.getJson();
1647                 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1648                 {
1649                     const std::string aOldAction = aOldTree.get<std::string>("action", "");
1650                     if (aOldAction == "invalidate")
1651                         return true;
1652                 }
1653             }
1654             return false;
1655         });
1656     }
1657 
1658     // Queue this one.
1659     return false;
1660 }
1661 
Invoke()1662 void CallbackFlushHandler::Invoke()
1663 {
1664     comphelper::ProfileZone aZone("CallbackFlushHander::Invoke");
1665 
1666     if (m_pCallback && !m_bEventLatch)
1667     {
1668         std::scoped_lock<std::mutex> lock(m_mutex);
1669 
1670         SAL_INFO("lok", "Flushing " << m_queue.size() << " elements.");
1671         for (const auto& rCallbackData : m_queue)
1672         {
1673             const int type = rCallbackData.Type;
1674             const auto& payload = rCallbackData.PayloadString;
1675             const int viewId = lcl_isViewCallbackType(type) ? lcl_getViewId(rCallbackData) : -1;
1676 
1677             if (viewId == -1)
1678             {
1679                 const auto stateIt = m_states.find(type);
1680                 if (stateIt != m_states.end())
1681                 {
1682                     // If the state didn't change, it's safe to ignore.
1683                     if (stateIt->second == payload)
1684                     {
1685                         SAL_INFO("lok", "Skipping duplicate [" << type << "]: [" << payload << "].");
1686                         continue;
1687                     }
1688 
1689                     stateIt->second = payload;
1690                 }
1691             }
1692             else
1693             {
1694                 const auto statesIt = m_viewStates.find(viewId);
1695                 if (statesIt != m_viewStates.end())
1696                 {
1697                     auto& states = statesIt->second;
1698                     const auto stateIt = states.find(type);
1699                     if (stateIt != states.end())
1700                     {
1701                         // If the state didn't change, it's safe to ignore.
1702                         if (stateIt->second == payload)
1703                         {
1704                             SAL_INFO("lok", "Skipping view duplicate [" << type << ',' << viewId << "]: [" << payload << "].");
1705                             continue;
1706                         }
1707 
1708                         SAL_INFO("lok", "Replacing an element in view states [" << type << ',' << viewId << "]: [" << payload << "].");
1709                         stateIt->second = payload;
1710                     }
1711                     else
1712                     {
1713                         SAL_INFO("lok", "Inserted a new element in view states: [" << type << ',' << viewId << "]: [" << payload << "]");
1714                         states.emplace(type, payload);
1715 
1716                     }
1717                 }
1718             }
1719 
1720             m_pCallback(type, payload.c_str(), m_pData);
1721         }
1722 
1723         m_queue.clear();
1724     }
1725 }
1726 
removeAll(const std::function<bool (const CallbackFlushHandler::queue_type::value_type &)> & rTestFunc)1727 bool CallbackFlushHandler::removeAll(const std::function<bool (const CallbackFlushHandler::queue_type::value_type&)>& rTestFunc)
1728 {
1729     auto newEnd = std::remove_if(m_queue.begin(), m_queue.end(), rTestFunc);
1730     if (newEnd != m_queue.end())
1731     {
1732         m_queue.erase(newEnd, m_queue.end());
1733         return true;
1734     }
1735 
1736     return false;
1737 }
1738 
addViewStates(int viewId)1739 void CallbackFlushHandler::addViewStates(int viewId)
1740 {
1741     const auto& result = m_viewStates.emplace(viewId, decltype(m_viewStates)::mapped_type());
1742     if (!result.second && result.first != m_viewStates.end())
1743     {
1744         result.first->second.clear();
1745     }
1746 }
1747 
removeViewStates(int viewId)1748 void CallbackFlushHandler::removeViewStates(int viewId)
1749 {
1750     m_viewStates.erase(viewId);
1751 }
1752 
1753 
doc_destroy(LibreOfficeKitDocument * pThis)1754 static void doc_destroy(LibreOfficeKitDocument *pThis)
1755 {
1756     comphelper::ProfileZone aZone("doc_destroy");
1757 
1758     SolarMutexGuard aGuard;
1759 
1760     LOKClipboardFactory::releaseClipboardForView(-1);
1761 
1762     LibLODocument_Impl *pDocument = static_cast<LibLODocument_Impl*>(pThis);
1763     delete pDocument;
1764 }
1765 
1766 static void                    lo_destroy       (LibreOfficeKit* pThis);
1767 static int                     lo_initialize    (LibreOfficeKit* pThis, const char* pInstallPath, const char* pUserProfilePath);
1768 static LibreOfficeKitDocument* lo_documentLoad  (LibreOfficeKit* pThis, const char* pURL);
1769 static char *                  lo_getError      (LibreOfficeKit* pThis);
1770 static void                    lo_freeError     (char* pFree);
1771 static LibreOfficeKitDocument* lo_documentLoadWithOptions  (LibreOfficeKit* pThis,
1772                                                            const char* pURL,
1773                                                            const char* pOptions);
1774 static void                    lo_registerCallback (LibreOfficeKit* pThis,
1775                                                     LibreOfficeKitCallback pCallback,
1776                                                     void* pData);
1777 static char* lo_getFilterTypes(LibreOfficeKit* pThis);
1778 static void                    lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long features);
1779 static void                    lo_setDocumentPassword(LibreOfficeKit* pThis,
1780                                                        const char* pURL,
1781                                                        const char* pPassword);
1782 static char*                   lo_getVersionInfo(LibreOfficeKit* pThis);
1783 static int                     lo_runMacro      (LibreOfficeKit* pThis, const char* pURL);
1784 
1785 static bool lo_signDocument(LibreOfficeKit* pThis,
1786                                    const char* pUrl,
1787                                    const unsigned char* pCertificateBinary,
1788                                    const int nCertificateBinarySize,
1789                                    const unsigned char* pPrivateKeyBinary,
1790                                    const int nPrivateKeyBinarySize);
1791 
1792 static void lo_runLoop(LibreOfficeKit* pThis,
1793                        LibreOfficeKitPollCallback pPollCallback,
1794                        LibreOfficeKitWakeCallback pWakeCallback,
1795                        void* pData);
1796 
LibLibreOffice_Impl()1797 LibLibreOffice_Impl::LibLibreOffice_Impl()
1798     : m_pOfficeClass( gOfficeClass.lock() )
1799     , maThread(nullptr)
1800     , mpCallback(nullptr)
1801     , mpCallbackData(nullptr)
1802     , mOptionalFeatures(0)
1803 {
1804     if(!m_pOfficeClass) {
1805         m_pOfficeClass.reset(new LibreOfficeKitClass);
1806         m_pOfficeClass->nSize = sizeof(LibreOfficeKitClass);
1807 
1808         m_pOfficeClass->destroy = lo_destroy;
1809         m_pOfficeClass->documentLoad = lo_documentLoad;
1810         m_pOfficeClass->getError = lo_getError;
1811         m_pOfficeClass->freeError = lo_freeError;
1812         m_pOfficeClass->documentLoadWithOptions = lo_documentLoadWithOptions;
1813         m_pOfficeClass->registerCallback = lo_registerCallback;
1814         m_pOfficeClass->getFilterTypes = lo_getFilterTypes;
1815         m_pOfficeClass->setOptionalFeatures = lo_setOptionalFeatures;
1816         m_pOfficeClass->setDocumentPassword = lo_setDocumentPassword;
1817         m_pOfficeClass->getVersionInfo = lo_getVersionInfo;
1818         m_pOfficeClass->runMacro = lo_runMacro;
1819         m_pOfficeClass->signDocument = lo_signDocument;
1820         m_pOfficeClass->runLoop = lo_runLoop;
1821 
1822         gOfficeClass = m_pOfficeClass;
1823     }
1824 
1825     pClass = m_pOfficeClass.get();
1826 }
1827 
~LibLibreOffice_Impl()1828 LibLibreOffice_Impl::~LibLibreOffice_Impl()
1829 {
1830 }
1831 
1832 namespace
1833 {
1834 
1835 #ifdef IOS
paintTileToCGContext(ITiledRenderable * pDocument,void * rCGContext,const Size nCanvasSize,const int nTilePosX,const int nTilePosY,const int nTileWidth,const int nTileHeight)1836 void paintTileToCGContext(ITiledRenderable* pDocument,
1837                           void* rCGContext, const Size nCanvasSize,
1838                           const int nTilePosX, const int nTilePosY,
1839                           const int nTileWidth, const int nTileHeight)
1840 {
1841     SystemGraphicsData aData;
1842     aData.rCGContext = reinterpret_cast<CGContextRef>(rCGContext);
1843 
1844     ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::DEFAULT);
1845     pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
1846     pDevice->SetOutputSizePixel(nCanvasSize);
1847     pDocument->paintTile(*pDevice, nCanvasSize.Width(), nCanvasSize.Height(),
1848                     nTilePosX, nTilePosY, nTileWidth, nTileHeight);
1849 }
1850 
paintTileIOS(LibreOfficeKitDocument * pThis,unsigned char * pBuffer,const int nCanvasWidth,const int nCanvasHeight,const double fDPIScale,const int nTilePosX,const int nTilePosY,const int nTileWidth,const int nTileHeight)1851 void paintTileIOS(LibreOfficeKitDocument* pThis,
1852              unsigned char* pBuffer,
1853              const int nCanvasWidth, const int nCanvasHeight, const double fDPIScale,
1854              const int nTilePosX, const int nTilePosY,
1855              const int nTileWidth, const int nTileHeight)
1856 {
1857     CGContextRef pCGContext = CGBitmapContextCreate(pBuffer, nCanvasWidth, nCanvasHeight, 8,
1858                                                     nCanvasWidth * 4, CGColorSpaceCreateDeviceRGB(),
1859                                                     kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little);
1860 
1861     CGContextTranslateCTM(pCGContext, 0, nCanvasHeight);
1862     CGContextScaleCTM(pCGContext, fDPIScale, -fDPIScale);
1863 
1864     doc_paintTileToCGContext(pThis, (void*) pCGContext, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
1865 
1866     CGContextRelease(pCGContext);
1867 }
1868 #endif
1869 
1870 } // anonymous namespace
1871 
1872 // Wonder global state ...
1873 static uno::Reference<css::uno::XComponentContext> xContext;
1874 static uno::Reference<css::lang::XMultiServiceFactory> xSFactory;
1875 static uno::Reference<css::lang::XMultiComponentFactory> xFactory;
1876 
lo_documentLoad(LibreOfficeKit * pThis,const char * pURL)1877 static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char* pURL)
1878 {
1879     return lo_documentLoadWithOptions(pThis, pURL, nullptr);
1880 }
1881 
lo_documentLoadWithOptions(LibreOfficeKit * pThis,const char * pURL,const char * pOptions)1882 static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis, const char* pURL, const char* pOptions)
1883 {
1884     comphelper::ProfileZone aZone("lo_documentLoadWithOptions");
1885 
1886     SolarMutexGuard aGuard;
1887 
1888     LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
1889     pLib->maLastExceptionMsg.clear();
1890 
1891     OUString aURL(getAbsoluteURL(pURL));
1892     if (aURL.isEmpty())
1893     {
1894         pLib->maLastExceptionMsg = "Filename to load was not provided.";
1895         SAL_INFO("lok", "URL for load is empty");
1896         return nullptr;
1897     }
1898 
1899     pLib->maLastExceptionMsg.clear();
1900 
1901     if (!xContext.is())
1902     {
1903         pLib->maLastExceptionMsg = "ComponentContext is not available";
1904         SAL_INFO("lok", "ComponentContext is not available");
1905         return nullptr;
1906     }
1907 
1908     uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
1909 
1910     if (!xComponentLoader.is())
1911     {
1912         pLib->maLastExceptionMsg = "ComponentLoader is not available";
1913         SAL_INFO("lok", "ComponentLoader is not available");
1914         return nullptr;
1915     }
1916 
1917     try
1918     {
1919         // 'Language=...' is an option that LOK consumes by itself, and does
1920         // not pass it as a parameter to the filter
1921         OUString aOptions = getUString(pOptions);
1922         const OUString aLanguage = extractParameter(aOptions, "Language");
1923 
1924         if (!aLanguage.isEmpty())
1925         {
1926             // use with care - it sets it for the entire core, not just the
1927             // document
1928             SvtSysLocaleOptions aSysLocaleOptions;
1929             aSysLocaleOptions.SetLocaleConfigString(aLanguage);
1930             aSysLocaleOptions.SetUILocaleConfigString(aLanguage);
1931             // Set the LOK language tag, used for dialog tunneling.
1932             comphelper::LibreOfficeKit::setLanguageTag(aSysLocaleOptions.GetLanguageTag());
1933         }
1934 
1935         uno::Sequence<css::beans::PropertyValue> aFilterOptions(2);
1936         aFilterOptions[0] = css::beans::PropertyValue( "FilterOptions",
1937                                                        0,
1938                                                        uno::makeAny(aOptions),
1939                                                        beans::PropertyState_DIRECT_VALUE);
1940 
1941         rtl::Reference<LOKInteractionHandler> const pInteraction(
1942             new LOKInteractionHandler("load", pLib));
1943         auto const pair(pLib->mInteractionMap.insert(std::make_pair(aURL.toUtf8(), pInteraction)));
1944         comphelper::ScopeGuard const g([&] () {
1945                 if (pair.second)
1946                 {
1947                     pLib->mInteractionMap.erase(aURL.toUtf8());
1948                 }
1949             });
1950         uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
1951         aFilterOptions[1].Name = "InteractionHandler";
1952         aFilterOptions[1].Value <<= xInteraction;
1953 
1954         /* TODO
1955         sal_Int16 nMacroExecMode = document::MacroExecMode::USE_CONFIG;
1956         aFilterOptions[2].Name = "MacroExecutionMode";
1957         aFilterOptions[2].Value <<= nMacroExecMode;
1958 
1959         sal_Int16 nUpdateDoc = document::UpdateDocMode::ACCORDING_TO_CONFIG;
1960         aFilterOptions[3].Name = "UpdateDocMode";
1961         aFilterOptions[3].Value <<= nUpdateDoc;
1962         */
1963 
1964         uno::Reference<lang::XComponent> xComponent = xComponentLoader->loadComponentFromURL(
1965                                             aURL, "_blank", 0,
1966                                             aFilterOptions);
1967 
1968         assert(!xComponent.is() || pair.second); // concurrent loading of same URL ought to fail
1969 
1970         if (!xComponent.is())
1971         {
1972             pLib->maLastExceptionMsg = "loadComponentFromURL returned an empty reference";
1973             SAL_INFO("lok", "Document can't be loaded - " << pLib->maLastExceptionMsg);
1974             return nullptr;
1975         }
1976 
1977         LibLODocument_Impl* pDocument = new LibLODocument_Impl(xComponent);
1978         if (pLib->mpCallback)
1979         {
1980             int nState = doc_getSignatureState(pDocument);
1981             pLib->mpCallback(LOK_CALLBACK_SIGNATURE_STATUS, OString::number(nState).getStr(), pLib->mpCallbackData);
1982         }
1983         return pDocument;
1984     }
1985     catch (const uno::Exception& exception)
1986     {
1987         pLib->maLastExceptionMsg = exception.Message;
1988         TOOLS_INFO_EXCEPTION("lok", "Document can't be loaded");
1989     }
1990 
1991     return nullptr;
1992 }
1993 
lo_runMacro(LibreOfficeKit * pThis,const char * pURL)1994 static int lo_runMacro(LibreOfficeKit* pThis, const char *pURL)
1995 {
1996     comphelper::ProfileZone aZone("lo_runMacro");
1997 
1998     SolarMutexGuard aGuard;
1999 
2000     LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2001     pLib->maLastExceptionMsg.clear();
2002 
2003     OUString sURL( pURL, strlen(pURL), RTL_TEXTENCODING_UTF8 );
2004     if (sURL.isEmpty())
2005     {
2006         pLib->maLastExceptionMsg = "Macro to run was not provided.";
2007         SAL_INFO("lok", "Macro URL is empty");
2008         return false;
2009     }
2010 
2011     if (!sURL.startsWith("macro://"))
2012     {
2013         pLib->maLastExceptionMsg = "This doesn't look like macro URL";
2014         SAL_INFO("lok", "Macro URL is invalid");
2015         return false;
2016     }
2017 
2018     pLib->maLastExceptionMsg.clear();
2019 
2020     if (!xContext.is())
2021     {
2022         pLib->maLastExceptionMsg = "ComponentContext is not available";
2023         SAL_INFO("lok", "ComponentContext is not available");
2024         return false;
2025     }
2026 
2027     util::URL aURL;
2028     aURL.Complete = sURL;
2029 
2030     uno::Reference < util::XURLTransformer > xParser( util::URLTransformer::create( xContext ) );
2031 
2032     if( xParser.is() )
2033         xParser->parseStrict( aURL );
2034 
2035     uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
2036 
2037     if (!xComponentLoader.is())
2038     {
2039         pLib->maLastExceptionMsg = "ComponentLoader is not available";
2040         SAL_INFO("lok", "ComponentLoader is not available");
2041         return false;
2042     }
2043 
2044     xFactory = xContext->getServiceManager();
2045 
2046     if (xFactory.is())
2047     {
2048         uno::Reference<frame::XDispatchProvider> xDP;
2049         xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
2050         xDP.set( xSFactory->createInstance("com.sun.star.comp.sfx2.SfxMacroLoader"), uno::UNO_QUERY );
2051         uno::Reference<frame::XDispatch> xD = xDP->queryDispatch( aURL, OUString(), 0);
2052 
2053         if (!xD.is())
2054         {
2055             pLib->maLastExceptionMsg = "Macro loader is not available";
2056             SAL_INFO("lok", "Macro loader is not available");
2057             return false;
2058         }
2059 
2060         uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xD, uno::UNO_QUERY_THROW );
2061         uno::Sequence<css::beans::PropertyValue> aEmpty;
2062         css::beans::PropertyValue aErr;
2063         uno::Any aRet = xSyncDisp->dispatchWithReturnValue( aURL, aEmpty );
2064         aRet >>= aErr;
2065 
2066         if (aErr.Name == "ErrorCode")
2067         {
2068             sal_uInt32 nErrCode = 0; // ERRCODE_NONE
2069             aErr.Value >>= nErrCode;
2070 
2071             pLib->maLastExceptionMsg = "An error occurred running macro (error code: " + OUString::number( nErrCode ) + ")";
2072             SAL_INFO("lok", "Macro execution terminated with error code " << nErrCode);
2073 
2074             return false;
2075         }
2076 
2077         return true;
2078     }
2079 
2080     return false;
2081 }
2082 
lo_signDocument(LibreOfficeKit *,const char * pURL,const unsigned char * pCertificateBinary,const int nCertificateBinarySize,const unsigned char * pPrivateKeyBinary,const int nPrivateKeyBinarySize)2083 static bool lo_signDocument(LibreOfficeKit* /*pThis*/,
2084                             const char* pURL,
2085                             const unsigned char* pCertificateBinary,
2086                             const int nCertificateBinarySize,
2087                             const unsigned char* pPrivateKeyBinary,
2088                             const int nPrivateKeyBinarySize)
2089 {
2090     comphelper::ProfileZone aZone("lo_signDocument");
2091 
2092     OUString aURL(getAbsoluteURL(pURL));
2093     if (aURL.isEmpty())
2094        return false;
2095 
2096     if (!xContext.is())
2097         return false;
2098 
2099     uno::Sequence<sal_Int8> aCertificateSequence;
2100 
2101     std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
2102     std::string aCertificateBase64String = extractCertificate(aCertificateString);
2103     if (!aCertificateBase64String.empty())
2104     {
2105         OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
2106         comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
2107     }
2108     else
2109     {
2110         aCertificateSequence.realloc(nCertificateBinarySize);
2111         std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
2112     }
2113 
2114     uno::Sequence<sal_Int8> aPrivateKeySequence;
2115     std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeyBinarySize);
2116     std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
2117     if (!aPrivateKeyBase64String.empty())
2118     {
2119         OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String.c_str());
2120         comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
2121     }
2122     else
2123     {
2124         aPrivateKeySequence.realloc(nPrivateKeyBinarySize);
2125         std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeyBinarySize, aPrivateKeySequence.begin());
2126     }
2127 
2128     uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
2129     uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
2130     if (!xSecurityContext.is())
2131         return false;
2132 
2133     uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
2134     uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
2135 
2136     if (!xCertificateCreator.is())
2137         return false;
2138 
2139     uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
2140 
2141     if (!xCertificate.is())
2142         return false;
2143 
2144     sfx2::DocumentSigner aDocumentSigner(aURL);
2145     if (!aDocumentSigner.signDocument(xCertificate))
2146         return false;
2147 
2148     return true;
2149 }
2150 
lo_registerCallback(LibreOfficeKit * pThis,LibreOfficeKitCallback pCallback,void * pData)2151 static void lo_registerCallback (LibreOfficeKit* pThis,
2152                                  LibreOfficeKitCallback pCallback,
2153                                  void* pData)
2154 {
2155     SolarMutexGuard aGuard;
2156 
2157     LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2158     pLib->maLastExceptionMsg.clear();
2159 
2160     pLib->mpCallback = pCallback;
2161     pLib->mpCallbackData = pData;
2162 }
2163 
doc_saveAs(LibreOfficeKitDocument * pThis,const char * sUrl,const char * pFormat,const char * pFilterOptions)2164 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* sUrl, const char* pFormat, const char* pFilterOptions)
2165 {
2166     comphelper::ProfileZone aZone("doc_saveAs");
2167 
2168     SolarMutexGuard aGuard;
2169     SetLastExceptionMsg();
2170 
2171     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
2172 
2173     OUString sFormat = getUString(pFormat);
2174     OUString aURL(getAbsoluteURL(sUrl));
2175     if (aURL.isEmpty())
2176     {
2177         SetLastExceptionMsg("Filename to save to was not provided.");
2178         SAL_INFO("lok", "URL for save is empty");
2179         return false;
2180     }
2181 
2182     try
2183     {
2184         const ExtensionMap* pMap;
2185 
2186         switch (doc_getDocumentType(pThis))
2187         {
2188         case LOK_DOCTYPE_SPREADSHEET:
2189             pMap = aCalcExtensionMap;
2190             break;
2191         case LOK_DOCTYPE_PRESENTATION:
2192             pMap = aImpressExtensionMap;
2193             break;
2194         case LOK_DOCTYPE_DRAWING:
2195             pMap = aDrawExtensionMap;
2196             break;
2197         case LOK_DOCTYPE_TEXT:
2198             pMap = aWriterExtensionMap;
2199             break;
2200         case LOK_DOCTYPE_OTHER:
2201         default:
2202             SAL_INFO("lok", "Can't save document - unsupported document type.");
2203             return false;
2204         }
2205 
2206         if (pFormat == nullptr)
2207         {
2208             // sniff from the extension
2209             sal_Int32 idx = aURL.lastIndexOf(".");
2210             if( idx > 0 )
2211             {
2212                 sFormat = aURL.copy( idx + 1 );
2213             }
2214             else
2215             {
2216                 SetLastExceptionMsg("input filename without a suffix");
2217                 return false;
2218             }
2219         }
2220 
2221         OUString aFilterName;
2222         for (sal_Int32 i = 0; pMap[i].extn; ++i)
2223         {
2224             if (sFormat.equalsIgnoreAsciiCaseAscii(pMap[i].extn))
2225             {
2226                 aFilterName = getUString(pMap[i].filterName);
2227                 break;
2228             }
2229         }
2230         if (aFilterName.isEmpty())
2231         {
2232             SetLastExceptionMsg("no output filter found for provided suffix");
2233             return false;
2234         }
2235 
2236         OUString aFilterOptions = getUString(pFilterOptions);
2237 
2238         // Check if watermark for pdf is passed by filteroptions...
2239         // It is not a real filter option so it must be filtered out.
2240         OUString watermarkText, sFullSheetPreview;
2241         int aIndex = -1;
2242         if ((aIndex = aFilterOptions.indexOf(",Watermark=")) >= 0)
2243         {
2244             int bIndex = aFilterOptions.indexOf("WATERMARKEND");
2245             watermarkText = aFilterOptions.copy(aIndex+11, bIndex-(aIndex+11));
2246 
2247             OUString temp = aFilterOptions.copy(0, aIndex);
2248             aFilterOptions = temp + aFilterOptions.copy(bIndex+12);
2249         }
2250 
2251         aIndex = -1;
2252         if ((aIndex = aFilterOptions.indexOf(",FullSheetPreview=")) >= 0)
2253         {
2254             int bIndex = aFilterOptions.indexOf("FULLSHEETPREVEND");
2255             sFullSheetPreview = aFilterOptions.copy(aIndex+18, bIndex-(aIndex+18));
2256 
2257             OUString temp = aFilterOptions.copy(0, aIndex);
2258             aFilterOptions = temp + aFilterOptions.copy(bIndex+16);
2259         }
2260 
2261         bool bFullSheetPreview = sFullSheetPreview == "true";
2262 
2263         // 'TakeOwnership' == this is a 'real' SaveAs (that is, the document
2264         // gets a new name).  When this is not provided, the meaning of
2265         // saveAs() is more like save-a-copy, which allows saving to any
2266         // random format like PDF or PNG.
2267         // It is not a real filter option, so we have to filter it out.
2268         const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(aFilterOptions);
2269         std::vector<OUString> aFilteredOptionVec;
2270         bool bTakeOwnership = false;
2271         MediaDescriptor aSaveMediaDescriptor;
2272         for (const auto& rOption : aOptionSeq)
2273         {
2274             if (rOption == "TakeOwnership")
2275                 bTakeOwnership = true;
2276             else if (rOption == "NoFileSync")
2277                 aSaveMediaDescriptor["NoFileSync"] <<= true;
2278             else
2279                 aFilteredOptionVec.push_back(rOption);
2280         }
2281 
2282         aSaveMediaDescriptor["Overwrite"] <<= true;
2283         aSaveMediaDescriptor["FilterName"] <<= aFilterName;
2284 
2285         auto aFilteredOptionSeq = comphelper::containerToSequence<OUString>(aFilteredOptionVec);
2286         aFilterOptions = comphelper::string::convertCommaSeparated(aFilteredOptionSeq);
2287         aSaveMediaDescriptor[MediaDescriptor::PROP_FILTEROPTIONS()] <<= aFilterOptions;
2288 
2289         if(!watermarkText.isEmpty() || bFullSheetPreview)
2290         {
2291             uno::Sequence< beans::PropertyValue > aFilterData( static_cast<int>(bFullSheetPreview) + static_cast<int>(!watermarkText.isEmpty()) );
2292 
2293             if (!watermarkText.isEmpty())
2294             {
2295                 aFilterData[ 0 ].Name = "TiledWatermark";
2296                 aFilterData[ 0 ].Value <<= watermarkText;
2297             }
2298 
2299             if (bFullSheetPreview)
2300             {
2301                 int nOptIndex = static_cast<int>(!watermarkText.isEmpty());
2302 
2303                 aFilterData[ nOptIndex ].Name = "SinglePageSheets";
2304                 aFilterData[ nOptIndex ].Value <<= true;
2305             }
2306 
2307             aSaveMediaDescriptor["FilterData"] <<= aFilterData;
2308         }
2309 
2310         // add interaction handler too
2311         if (gImpl)
2312         {
2313             // gImpl does not have to exist when running from a unit test
2314             rtl::Reference<LOKInteractionHandler> const pInteraction(
2315                     new LOKInteractionHandler("saveas", gImpl, pDocument));
2316             uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
2317 
2318             aSaveMediaDescriptor[MediaDescriptor::PROP_INTERACTIONHANDLER()] <<= xInteraction;
2319         }
2320 
2321         uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
2322 
2323         if (bTakeOwnership)
2324             xStorable->storeAsURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
2325         else
2326             xStorable->storeToURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
2327 
2328         return true;
2329     }
2330     catch (const uno::Exception& exception)
2331     {
2332         SetLastExceptionMsg("exception: " + exception.Message);
2333     }
2334     return false;
2335 }
2336 
doc_iniUnoCommands()2337 static void doc_iniUnoCommands ()
2338 {
2339     SolarMutexGuard aGuard;
2340     SetLastExceptionMsg();
2341 
2342     OUString sUnoCommands[] =
2343     {
2344         OUString(".uno:AlignLeft"),
2345         OUString(".uno:AlignHorizontalCenter"),
2346         OUString(".uno:AlignRight"),
2347         OUString(".uno:BackColor"),
2348         OUString(".uno:BackgroundColor"),
2349         OUString(".uno:TableCellBackgroundColor"),
2350         OUString(".uno:Bold"),
2351         OUString(".uno:CenterPara"),
2352         OUString(".uno:CharBackColor"),
2353         OUString(".uno:CharBackgroundExt"),
2354         OUString(".uno:CharFontName"),
2355         OUString(".uno:Color"),
2356         OUString(".uno:ControlCodes"),
2357         OUString(".uno:DecrementIndent"),
2358         OUString(".uno:DefaultBullet"),
2359         OUString(".uno:DefaultNumbering"),
2360         OUString(".uno:FontColor"),
2361         OUString(".uno:FontHeight"),
2362         OUString(".uno:IncrementIndent"),
2363         OUString(".uno:Italic"),
2364         OUString(".uno:JustifyPara"),
2365         OUString(".uno:OutlineFont"),
2366         OUString(".uno:LeftPara"),
2367         OUString(".uno:LanguageStatus"),
2368         OUString(".uno:RightPara"),
2369         OUString(".uno:Shadowed"),
2370         OUString(".uno:SubScript"),
2371         OUString(".uno:SuperScript"),
2372         OUString(".uno:Strikeout"),
2373         OUString(".uno:StyleApply"),
2374         OUString(".uno:Underline"),
2375         OUString(".uno:ModifiedStatus"),
2376         OUString(".uno:Undo"),
2377         OUString(".uno:Redo"),
2378         OUString(".uno:InsertPage"),
2379         OUString(".uno:DeletePage"),
2380         OUString(".uno:DuplicatePage"),
2381         OUString(".uno:Cut"),
2382         OUString(".uno:Copy"),
2383         OUString(".uno:Paste"),
2384         OUString(".uno:SelectAll"),
2385         OUString(".uno:InsertAnnotation"),
2386         OUString(".uno:DeleteAnnotation"),
2387         OUString(".uno:ReplyComment"),
2388         OUString(".uno:ResolveComment"),
2389         OUString(".uno:InsertRowsBefore"),
2390         OUString(".uno:InsertRowsAfter"),
2391         OUString(".uno:InsertColumnsBefore"),
2392         OUString(".uno:InsertColumnsAfter"),
2393         OUString(".uno:DeleteRows"),
2394         OUString(".uno:DeleteColumns"),
2395         OUString(".uno:DeleteTable"),
2396         OUString(".uno:SelectTable"),
2397         OUString(".uno:EntireRow"),
2398         OUString(".uno:EntireColumn"),
2399         OUString(".uno:EntireCell"),
2400         OUString(".uno:AssignLayout"),
2401         OUString(".uno:StatusDocPos"),
2402         OUString(".uno:RowColSelCount"),
2403         OUString(".uno:StatusPageStyle"),
2404         OUString(".uno:InsertMode"),
2405         OUString(".uno:SpellOnline"),
2406         OUString(".uno:StatusSelectionMode"),
2407         OUString(".uno:StateTableCell"),
2408         OUString(".uno:StatusBarFunc"),
2409         OUString(".uno:StatePageNumber"),
2410         OUString(".uno:StateWordCount"),
2411         OUString(".uno:SelectionMode"),
2412         OUString(".uno:PageStatus"),
2413         OUString(".uno:LayoutStatus"),
2414         OUString(".uno:Context"),
2415         OUString(".uno:WrapText"),
2416         OUString(".uno:ToggleMergeCells"),
2417         OUString(".uno:NumberFormatCurrency"),
2418         OUString(".uno:NumberFormatPercent"),
2419         OUString(".uno:NumberFormatDate"),
2420         OUString(".uno:SortAscending"),
2421         OUString(".uno:SortDescending"),
2422         OUString(".uno:TrackChanges"),
2423         OUString(".uno:ShowTrackedChanges"),
2424         OUString(".uno:NextTrackedChange"),
2425         OUString(".uno:PreviousTrackedChange"),
2426         OUString(".uno:AcceptAllTrackedChanges"),
2427         OUString(".uno:RejectAllTrackedChanges"),
2428         OUString(".uno:TableDialog"),
2429         OUString(".uno:FormatCellDialog"),
2430         OUString(".uno:FontDialog"),
2431         OUString(".uno:ParagraphDialog"),
2432         OUString(".uno:OutlineBullet"),
2433         OUString(".uno:InsertIndexesEntry"),
2434         OUString(".uno:DocumentRepair"),
2435         OUString(".uno:TransformDialog"),
2436         OUString(".uno:InsertPageHeader"),
2437         OUString(".uno:InsertPageFooter"),
2438         OUString(".uno:OnlineAutoFormat"),
2439         OUString(".uno:InsertSymbol"),
2440         OUString(".uno:EditRegion"),
2441         OUString(".uno:ThesaurusDialog")
2442     };
2443 
2444     util::URL aCommandURL;
2445     SfxViewShell* pViewShell = SfxViewShell::Current();
2446     SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
2447 
2448     // check if Frame-Controller were created.
2449     if (!pViewFrame)
2450     {
2451         SAL_WARN("lok", "iniUnoCommands: No Frame-Controller created.");
2452         return;
2453     }
2454 
2455     if (!xContext.is())
2456         xContext = comphelper::getProcessComponentContext();
2457     if (!xContext.is())
2458     {
2459         SAL_WARN("lok", "iniUnoCommands: Component context is not available");
2460         return;
2461     }
2462 
2463     SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
2464     uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(xContext));
2465 
2466     for (const auto & sUnoCommand : sUnoCommands)
2467     {
2468         aCommandURL.Complete = sUnoCommand;
2469         xParser->parseStrict(aCommandURL);
2470 
2471         // when null, this command is not supported by the given component
2472         // (like eg. Calc does not have ".uno:DefaultBullet" etc.)
2473         if (const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path))
2474         {
2475             // Initialize slot to dispatch .uno: Command.
2476             pViewFrame->GetBindings().GetDispatch(pSlot, aCommandURL, false);
2477         }
2478     }
2479 }
2480 
doc_getDocumentType(LibreOfficeKitDocument * pThis)2481 static int doc_getDocumentType (LibreOfficeKitDocument* pThis)
2482 {
2483     comphelper::ProfileZone aZone("doc_getDocumentType");
2484 
2485     SolarMutexGuard aGuard;
2486     SetLastExceptionMsg();
2487 
2488     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
2489 
2490     try
2491     {
2492         uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW);
2493 
2494         if (xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
2495         {
2496             return LOK_DOCTYPE_SPREADSHEET;
2497         }
2498         else if (xDocument->supportsService("com.sun.star.presentation.PresentationDocument"))
2499         {
2500             return LOK_DOCTYPE_PRESENTATION;
2501         }
2502         else if (xDocument->supportsService("com.sun.star.drawing.DrawingDocument"))
2503         {
2504             return LOK_DOCTYPE_DRAWING;
2505         }
2506         else if (xDocument->supportsService("com.sun.star.text.TextDocument") || xDocument->supportsService("com.sun.star.text.WebDocument"))
2507         {
2508             return LOK_DOCTYPE_TEXT;
2509         }
2510         else
2511         {
2512             SetLastExceptionMsg("unknown document type");
2513         }
2514     }
2515     catch (const uno::Exception& exception)
2516     {
2517         SetLastExceptionMsg("exception: " + exception.Message);
2518     }
2519     return LOK_DOCTYPE_OTHER;
2520 }
2521 
doc_getParts(LibreOfficeKitDocument * pThis)2522 static int doc_getParts (LibreOfficeKitDocument* pThis)
2523 {
2524     comphelper::ProfileZone aZone("doc_getParts");
2525 
2526     SolarMutexGuard aGuard;
2527 
2528     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2529     if (!pDoc)
2530     {
2531         SetLastExceptionMsg("Document doesn't support tiled rendering");
2532         return 0;
2533     }
2534 
2535     return pDoc->getParts();
2536 }
2537 
doc_getPart(LibreOfficeKitDocument * pThis)2538 static int doc_getPart (LibreOfficeKitDocument* pThis)
2539 {
2540     comphelper::ProfileZone aZone("doc_getPart");
2541 
2542     SolarMutexGuard aGuard;
2543     SetLastExceptionMsg();
2544 
2545     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2546     if (!pDoc)
2547     {
2548         SetLastExceptionMsg("Document doesn't support tiled rendering");
2549         return 0;
2550     }
2551 
2552     return pDoc->getPart();
2553 }
2554 
doc_setPart(LibreOfficeKitDocument * pThis,int nPart)2555 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart)
2556 {
2557     comphelper::ProfileZone aZone("doc_setPart");
2558 
2559     SolarMutexGuard aGuard;
2560     SetLastExceptionMsg();
2561 
2562     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2563     if (!pDoc)
2564     {
2565         SetLastExceptionMsg("Document doesn't support tiled rendering");
2566         return;
2567     }
2568 
2569     pDoc->setPart( nPart );
2570 }
2571 
doc_getPartInfo(LibreOfficeKitDocument * pThis,int nPart)2572 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart)
2573 {
2574     comphelper::ProfileZone aZone("doc_getPartInfo");
2575 
2576     SolarMutexGuard aGuard;
2577     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2578     if (!pDoc)
2579     {
2580         SetLastExceptionMsg("Document doesn't support tiled rendering");
2581         return nullptr;
2582     }
2583 
2584     return convertOUString(pDoc->getPartInfo(nPart));
2585 }
2586 
doc_selectPart(LibreOfficeKitDocument * pThis,int nPart,int nSelect)2587 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect)
2588 {
2589     SolarMutexGuard aGuard;
2590     if (gImpl)
2591         gImpl->maLastExceptionMsg.clear();
2592 
2593     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2594     if (!pDoc)
2595     {
2596         gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
2597         return;
2598     }
2599 
2600     pDoc->selectPart( nPart, nSelect );
2601 }
2602 
doc_moveSelectedParts(LibreOfficeKitDocument * pThis,int nPosition,bool bDuplicate)2603 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate)
2604 {
2605     SolarMutexGuard aGuard;
2606     if (gImpl)
2607         gImpl->maLastExceptionMsg.clear();
2608 
2609     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2610     if (!pDoc)
2611     {
2612         gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
2613         return;
2614     }
2615 
2616     pDoc->moveSelectedParts(nPosition, bDuplicate);
2617 }
2618 
doc_getPartPageRectangles(LibreOfficeKitDocument * pThis)2619 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis)
2620 {
2621     comphelper::ProfileZone aZone("doc_getPartPageRectangles");
2622 
2623     SolarMutexGuard aGuard;
2624     SetLastExceptionMsg();
2625 
2626     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2627     if (!pDoc)
2628     {
2629         SetLastExceptionMsg("Document doesn't support tiled rendering");
2630         return nullptr;
2631     }
2632 
2633     return convertOUString(pDoc->getPartPageRectangles());
2634 }
2635 
doc_getPartName(LibreOfficeKitDocument * pThis,int nPart)2636 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart)
2637 {
2638     comphelper::ProfileZone aZone("doc_getPartName");
2639 
2640     SolarMutexGuard aGuard;
2641     SetLastExceptionMsg();
2642 
2643     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2644     if (!pDoc)
2645     {
2646         SetLastExceptionMsg("Document doesn't support tiled rendering");
2647         return nullptr;
2648     }
2649 
2650     return convertOUString(pDoc->getPartName(nPart));
2651 }
2652 
doc_getPartHash(LibreOfficeKitDocument * pThis,int nPart)2653 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart)
2654 {
2655     comphelper::ProfileZone aZone("doc_getPartHash");
2656 
2657     SolarMutexGuard aGuard;
2658     SetLastExceptionMsg();
2659 
2660     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2661     if (!pDoc)
2662     {
2663         SetLastExceptionMsg("Document doesn't support tiled rendering");
2664         return nullptr;
2665     }
2666 
2667     return convertOUString(pDoc->getPartHash(nPart));
2668 }
2669 
doc_setPartMode(LibreOfficeKitDocument * pThis,int nPartMode)2670 static void doc_setPartMode(LibreOfficeKitDocument* pThis,
2671                             int nPartMode)
2672 {
2673     comphelper::ProfileZone aZone("doc_setPartMode");
2674 
2675     SolarMutexGuard aGuard;
2676     SetLastExceptionMsg();
2677 
2678     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2679     if (!pDoc)
2680     {
2681         SetLastExceptionMsg("Document doesn't support tiled rendering");
2682         return;
2683     }
2684 
2685 
2686     int nCurrentPart = pDoc->getPart();
2687 
2688     pDoc->setPartMode(nPartMode);
2689 
2690     // We need to make sure the internal state is updated, just changing the mode
2691     // might not update the relevant shells (i.e. impress will keep rendering the
2692     // previous mode unless we do this).
2693     // TODO: we might want to do this within the relevant components rather than
2694     // here, but that's also dependent on how we implement embedded object
2695     // rendering I guess?
2696     // TODO: we could be clever and e.g. set to 0 when we change to/from
2697     // embedded object mode, and not when changing between slide/notes/combined
2698     // modes?
2699     if ( nCurrentPart < pDoc->getParts() )
2700     {
2701         pDoc->setPart( nCurrentPart );
2702     }
2703     else
2704     {
2705         pDoc->setPart( 0 );
2706     }
2707 }
2708 
2709 #if defined(ANDROID)
2710 /// For the distinction if the LOK is used for the 'old' (JNI-based) or the
2711 /// 'new' (loolwsd-based) app.  Default to the 'new', ie. not used from JNI as
2712 /// implemented in sal/android/libreofficekit-jni.c.
2713 bool android_lok_from_jni = false;
2714 #endif
2715 
doc_paintTile(LibreOfficeKitDocument * pThis,unsigned char * pBuffer,const int nCanvasWidth,const int nCanvasHeight,const int nTilePosX,const int nTilePosY,const int nTileWidth,const int nTileHeight)2716 static void doc_paintTile(LibreOfficeKitDocument* pThis,
2717                           unsigned char* pBuffer,
2718                           const int nCanvasWidth, const int nCanvasHeight,
2719                           const int nTilePosX, const int nTilePosY,
2720                           const int nTileWidth, const int nTileHeight)
2721 {
2722     comphelper::ProfileZone aZone("doc_paintTile");
2723 
2724     SolarMutexGuard aGuard;
2725     SetLastExceptionMsg();
2726 
2727     SAL_INFO( "lok.tiledrendering", "paintTile: painting [" << nTileWidth << "x" << nTileHeight <<
2728               "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
2729               nCanvasWidth << "x" << nCanvasHeight << "]px" );
2730 
2731     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2732     if (!pDoc)
2733     {
2734         SetLastExceptionMsg("Document doesn't support tiled rendering");
2735         return;
2736     }
2737 
2738 #if defined(UNX) && !defined(MACOSX) && !defined(ENABLE_HEADLESS)
2739 
2740     // Painting of zoomed or HiDPI spreadsheets is special, we actually draw everything at 100%,
2741     // and only set cairo's (or CoreGraphic's, in the iOS case) scale factor accordingly, so that
2742     // everything is painted bigger or smaller. This is different to what Calc's internal scaling
2743     // would do - because that one is trying to fit the lines between cells to integer multiples of
2744     // pixels.
2745     comphelper::ScopeGuard dpiScaleGuard([]() { comphelper::LibreOfficeKit::setDPIScale(1.0); });
2746 
2747 #if defined(IOS)
2748     double fDPIScaleX = 1.0;
2749     paintTileIOS(pThis, pBuffer, nCanvasWidth, nCanvasHeight, fDPIScaleX, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2750 #else
2751     ScopedVclPtrInstance< VirtualDevice > pDevice(DeviceFormat::DEFAULT);
2752 
2753 #if defined(ANDROID)
2754     if (!android_lok_from_jni)
2755 #endif
2756     {
2757         // Set background to transparent by default.
2758         // [Unless it is the 'old' (JNI-based) Android app - no idea why it
2759         // needs avoiding this.]
2760         pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2761     }
2762 
2763     pDevice->SetOutputSizePixelScaleOffsetAndBuffer(
2764                 Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(),
2765                 pBuffer);
2766 
2767     pDoc->paintTile(*pDevice, nCanvasWidth, nCanvasHeight,
2768                     nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2769 
2770     static bool bDebug = getenv("LOK_DEBUG_TILES") != nullptr;
2771     if (bDebug)
2772     {
2773         // Draw a small red rectangle in the top left corner so that it's easy to see where a new tile begins.
2774         tools::Rectangle aRect(0, 0, 5, 5);
2775         aRect = pDevice->PixelToLogic(aRect);
2776         pDevice->Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
2777         pDevice->SetFillColor(COL_LIGHTRED);
2778         pDevice->SetLineColor();
2779         pDevice->DrawRect(aRect);
2780         pDevice->Pop();
2781     }
2782 #endif
2783 
2784 #else
2785     (void) pBuffer;
2786 #endif
2787 }
2788 
2789 #ifdef IOS
2790 
2791 // This function is separate only to be used by LibreOfficeLight. If that app can be retired, this
2792 // function's code can be inlined.
doc_paintTileToCGContext(LibreOfficeKitDocument * pThis,void * rCGContext,const int nCanvasWidth,const int nCanvasHeight,const int nTilePosX,const int nTilePosY,const int nTileWidth,const int nTileHeight)2793 static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
2794                                      void* rCGContext,
2795                                      const int nCanvasWidth, const int nCanvasHeight,
2796                                      const int nTilePosX, const int nTilePosY,
2797                                      const int nTileWidth, const int nTileHeight)
2798 {
2799     SolarMutexGuard aGuard;
2800     SetLastExceptionMsg();
2801 
2802     SAL_INFO( "lok.tiledrendering", "paintTileToCGContext: painting [" << nTileWidth << "x" << nTileHeight <<
2803               "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
2804               nCanvasWidth << "x" << nCanvasHeight << "]px" );
2805 
2806     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2807     if (!pDoc)
2808     {
2809         SetLastExceptionMsg("Document doesn't support tiled rendering");
2810         return;
2811     }
2812 
2813     Size aCanvasSize(nCanvasWidth, nCanvasHeight);
2814     paintTileToCGContext(pDoc, rCGContext, aCanvasSize, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2815 }
2816 
2817 #endif
2818 
doc_paintPartTile(LibreOfficeKitDocument * pThis,unsigned char * pBuffer,const int nPart,const int nCanvasWidth,const int nCanvasHeight,const int nTilePosX,const int nTilePosY,const int nTileWidth,const int nTileHeight)2819 static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
2820                               unsigned char* pBuffer,
2821                               const int nPart,
2822                               const int nCanvasWidth, const int nCanvasHeight,
2823                               const int nTilePosX, const int nTilePosY,
2824                               const int nTileWidth, const int nTileHeight)
2825 {
2826     comphelper::ProfileZone aZone("doc_paintPartTile");
2827 
2828     SolarMutexGuard aGuard;
2829     SetLastExceptionMsg();
2830 
2831     SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " ["
2832                << nTileWidth << "x" << nTileHeight << "]@("
2833                << nTilePosX << ", " << nTilePosY << ") to ["
2834                << nCanvasWidth << "x" << nCanvasHeight << "]px" );
2835 
2836     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
2837     int nOrigViewId = doc_getView(pThis);
2838 
2839     if (nOrigViewId < 0)
2840     {
2841         // tile painting always needs a SfxViewShell::Current(), but actually
2842         // it does not really matter which one - all of them should paint the
2843         // same thing.
2844         int viewCount = doc_getViewsCount(pThis);
2845         if (viewCount == 0)
2846             return;
2847 
2848         std::vector<int> viewIds(viewCount);
2849         doc_getViewIds(pThis, viewIds.data(), viewCount);
2850 
2851         nOrigViewId = viewIds[0];
2852         doc_setView(pThis, nOrigViewId);
2853     }
2854 
2855     // Disable callbacks while we are painting.
2856     if (nOrigViewId >= 0)
2857     {
2858         const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
2859         if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
2860             handlerIt->second->disableCallbacks();
2861     }
2862 
2863     try
2864     {
2865         // Text documents have a single coordinate system; don't change part.
2866         int nOrigPart = 0;
2867         const bool isText = (doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT);
2868         int nViewId = nOrigViewId;
2869         if (!isText)
2870         {
2871             // Check if just switching to another view is enough, that has
2872             // less side-effects.
2873             if (nPart != doc_getPart(pThis))
2874             {
2875                 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
2876                 while (pViewShell)
2877                 {
2878                     if (pViewShell->getPart() == nPart)
2879                     {
2880                         nViewId = static_cast<sal_Int32>(pViewShell->GetViewShellId());
2881                         doc_setView(pThis, nViewId);
2882                         break;
2883                     }
2884                     pViewShell = SfxViewShell::GetNext(*pViewShell);
2885                 }
2886             }
2887 
2888             nOrigPart = doc_getPart(pThis);
2889             if (nPart != nOrigPart)
2890             {
2891                 doc_setPart(pThis, nPart);
2892             }
2893         }
2894 
2895         doc_paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2896 
2897         if (!isText && nPart != nOrigPart)
2898         {
2899             doc_setPart(pThis, nOrigPart);
2900         }
2901         if (!isText && nViewId != nOrigViewId)
2902         {
2903             doc_setView(pThis, nOrigViewId);
2904         }
2905     }
2906     catch (const std::exception&)
2907     {
2908         // Nothing to do but restore the PartTilePainting flag.
2909     }
2910 
2911     if (nOrigViewId >= 0)
2912     {
2913         const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
2914         if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
2915             handlerIt->second->enableCallbacks();
2916     }
2917 }
2918 
doc_getTileMode(SAL_UNUSED_PARAMETER LibreOfficeKitDocument *)2919 static int doc_getTileMode(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
2920 {
2921     SetLastExceptionMsg();
2922     return LOK_TILEMODE_BGRA;
2923 }
2924 
doc_getDocumentSize(LibreOfficeKitDocument * pThis,long * pWidth,long * pHeight)2925 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
2926                                 long* pWidth,
2927                                 long* pHeight)
2928 {
2929     comphelper::ProfileZone aZone("doc_getDocumentSize");
2930 
2931     SolarMutexGuard aGuard;
2932     SetLastExceptionMsg();
2933 
2934     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2935     if (pDoc)
2936     {
2937         Size aDocumentSize = pDoc->getDocumentSize();
2938         *pWidth = aDocumentSize.Width();
2939         *pHeight = aDocumentSize.Height();
2940     }
2941     else
2942     {
2943         SetLastExceptionMsg("Document doesn't support tiled rendering");
2944     }
2945 }
2946 
doc_initializeForRendering(LibreOfficeKitDocument * pThis,const char * pArguments)2947 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
2948                                        const char* pArguments)
2949 {
2950     comphelper::ProfileZone aZone("doc_initializeForRendering");
2951 
2952     SolarMutexGuard aGuard;
2953     SetLastExceptionMsg();
2954 
2955     ITiledRenderable* pDoc = getTiledRenderable(pThis);
2956     if (pDoc)
2957     {
2958         doc_iniUnoCommands();
2959         pDoc->initializeForTiledRendering(
2960                 comphelper::containerToSequence(jsonToPropertyValuesVector(pArguments)));
2961     }
2962 }
2963 
doc_registerCallback(LibreOfficeKitDocument * pThis,LibreOfficeKitCallback pCallback,void * pData)2964 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
2965                                  LibreOfficeKitCallback pCallback,
2966                                  void* pData)
2967 {
2968     SolarMutexGuard aGuard;
2969     SetLastExceptionMsg();
2970 
2971     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
2972 
2973     int nView = SfxLokHelper::getView();
2974     if (nView < 0)
2975         return;
2976 
2977     if (pCallback != nullptr)
2978     {
2979         size_t nId = nView;
2980         for (auto& pair : pDocument->mpCallbackFlushHandlers)
2981         {
2982             if (pair.first == nId)
2983                 continue;
2984 
2985             pair.second->addViewStates(nView);
2986         }
2987     }
2988     else
2989     {
2990         size_t nId = nView;
2991         for (auto& pair : pDocument->mpCallbackFlushHandlers)
2992         {
2993             if (pair.first == nId)
2994                 continue;
2995 
2996             pair.second->removeViewStates(nView);
2997         }
2998     }
2999 
3000     pDocument->mpCallbackFlushHandlers[nView].reset(new CallbackFlushHandler(pThis, pCallback, pData));
3001 
3002     if (pCallback != nullptr)
3003     {
3004         size_t nId = nView;
3005         for (const auto& pair : pDocument->mpCallbackFlushHandlers)
3006         {
3007             if (pair.first == nId)
3008                 continue;
3009 
3010             pDocument->mpCallbackFlushHandlers[nView]->addViewStates(pair.first);
3011         }
3012     }
3013 
3014     if (SfxViewShell* pViewShell = SfxViewShell::Current())
3015     {
3016         pViewShell->registerLibreOfficeKitViewCallback(CallbackFlushHandler::callback, pDocument->mpCallbackFlushHandlers[nView].get());
3017     }
3018 }
3019 
3020 /// Returns the JSON representation of all the comments in the document
getPostIts(LibreOfficeKitDocument * pThis)3021 static char* getPostIts(LibreOfficeKitDocument* pThis)
3022 {
3023     SetLastExceptionMsg();
3024     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3025     if (!pDoc)
3026     {
3027         SetLastExceptionMsg("Document doesn't support tiled rendering");
3028         return nullptr;
3029     }
3030     OUString aComments = pDoc->getPostIts();
3031     return strdup(aComments.toUtf8().getStr());
3032 }
3033 
3034 /// Returns the JSON representation of the positions of all the comments in the document
getPostItsPos(LibreOfficeKitDocument * pThis)3035 static char* getPostItsPos(LibreOfficeKitDocument* pThis)
3036 {
3037     SetLastExceptionMsg();
3038     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3039     if (!pDoc)
3040     {
3041         SetLastExceptionMsg("Document doesn't support tiled rendering");
3042         return nullptr;
3043     }
3044     OUString aComments = pDoc->getPostItsPos();
3045     return strdup(aComments.toUtf8().getStr());
3046 }
3047 
getRulerState(LibreOfficeKitDocument * pThis)3048 static char* getRulerState(LibreOfficeKitDocument* pThis)
3049 {
3050     SetLastExceptionMsg();
3051     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3052     if (!pDoc)
3053     {
3054         SetLastExceptionMsg("Document doesn't support tiled rendering");
3055         return nullptr;
3056     }
3057     OUString state = pDoc->getRulerState();
3058     return strdup(state.toUtf8().getStr());
3059 }
3060 
doc_postKeyEvent(LibreOfficeKitDocument * pThis,int nType,int nCharCode,int nKeyCode)3061 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, int nType, int nCharCode, int nKeyCode)
3062 {
3063     comphelper::ProfileZone aZone("doc_postKeyEvent");
3064 
3065     SolarMutexGuard aGuard;
3066     SetLastExceptionMsg();
3067 
3068     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3069     if (!pDoc)
3070     {
3071         SetLastExceptionMsg("Document doesn't support tiled rendering");
3072         return;
3073     }
3074 
3075     pDoc->postKeyEvent(nType, nCharCode, nKeyCode);
3076 }
3077 
doc_postWindowExtTextInputEvent(LibreOfficeKitDocument * pThis,unsigned nWindowId,int nType,const char * pText)3078 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis, unsigned nWindowId, int nType, const char* pText)
3079 {
3080     comphelper::ProfileZone aZone("doc_postWindowExtTextInputEvent");
3081 
3082     SolarMutexGuard aGuard;
3083     VclPtr<vcl::Window> pWindow;
3084     if (nWindowId == 0)
3085     {
3086         ITiledRenderable* pDoc = getTiledRenderable(pThis);
3087         if (!pDoc)
3088         {
3089             SetLastExceptionMsg("Document doesn't support tiled rendering");
3090             return;
3091         }
3092         pWindow = pDoc->getDocWindow();
3093     }
3094     else
3095     {
3096         pWindow = vcl::Window::FindLOKWindow(nWindowId);
3097     }
3098 
3099     if (!pWindow)
3100     {
3101         SetLastExceptionMsg("No window found for window id: " + OUString::number(nWindowId));
3102         return;
3103     }
3104 
3105     SfxLokHelper::postExtTextEventAsync(pWindow, nType, OUString::fromUtf8(OString(pText, strlen(pText))));
3106 }
3107 
doc_removeTextContext(LibreOfficeKitDocument * pThis,unsigned nLOKWindowId,int nCharBefore,int nCharAfter)3108 static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, int nCharBefore, int nCharAfter)
3109 {
3110     SolarMutexGuard aGuard;
3111     VclPtr<vcl::Window> pWindow;
3112     if (nLOKWindowId == 0)
3113     {
3114         ITiledRenderable* pDoc = getTiledRenderable(pThis);
3115         if (!pDoc)
3116         {
3117             gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3118             return;
3119         }
3120         pWindow = pDoc->getDocWindow();
3121     }
3122     else
3123     {
3124         pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3125     }
3126 
3127     if (!pWindow)
3128     {
3129         gImpl->maLastExceptionMsg = "No window found for window id: " + OUString::number(nLOKWindowId);
3130         return;
3131     }
3132 
3133     // Annoyingly - backspace and delete are handled in the apps via an accelerator
3134     // which are PostMessage'd by SfxViewShell::ExecKey_Impl so to stay in the same
3135     // order we do this synchronously here, unless we're in a dialog.
3136     if (nCharBefore > 0)
3137     {
3138         // backspace
3139         if (nLOKWindowId == 0)
3140         {
3141             KeyEvent aEvt(8, 1283);
3142             for (int i = 0; i < nCharBefore; ++i)
3143                 pWindow->KeyInput(aEvt);
3144         }
3145         else
3146             SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, 1283, nCharBefore - 1);
3147     }
3148 
3149     if (nCharAfter > 0)
3150     {
3151         // delete (forward)
3152         if (nLOKWindowId == 0)
3153         {
3154             KeyEvent aEvt(46, 1286);
3155             for (int i = 0; i < nCharAfter; ++i)
3156                 pWindow->KeyInput(aEvt);
3157         }
3158         else
3159             SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, 1286, nCharAfter - 1);
3160     }
3161 }
3162 
doc_postWindowKeyEvent(LibreOfficeKitDocument *,unsigned nLOKWindowId,int nType,int nCharCode,int nKeyCode)3163 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nCharCode, int nKeyCode)
3164 {
3165     comphelper::ProfileZone aZone("doc_postWindowKeyEvent");
3166 
3167     SolarMutexGuard aGuard;
3168     SetLastExceptionMsg();
3169 
3170     VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3171     if (!pWindow)
3172     {
3173         SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3174         return;
3175     }
3176 
3177     KeyEvent aEvent(nCharCode, nKeyCode, 0);
3178 
3179     switch (nType)
3180     {
3181         case LOK_KEYEVENT_KEYINPUT:
3182             Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
3183             break;
3184         case LOK_KEYEVENT_KEYUP:
3185             Application::PostKeyEvent(VclEventId::WindowKeyUp, pWindow, &aEvent);
3186             break;
3187         default:
3188             assert(false);
3189             break;
3190     }
3191 }
3192 
doc_renderShapeSelection(LibreOfficeKitDocument * pThis,char ** pOutput)3193 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput)
3194 {
3195     comphelper::ProfileZone aZone("doc_renderShapeSelection");
3196 
3197     SolarMutexGuard aGuard;
3198     SetLastExceptionMsg();
3199 
3200     LokChartHelper aChartHelper(SfxViewShell::Current());
3201 
3202     if (aChartHelper.GetWindow())
3203         return 0;
3204 
3205     try
3206     {
3207         LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3208 
3209         uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
3210 
3211         SvMemoryStream aOutStream;
3212         uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aOutStream);
3213 
3214         utl::MediaDescriptor aMediaDescriptor;
3215         switch (doc_getDocumentType(pThis))
3216         {
3217             case LOK_DOCTYPE_PRESENTATION:
3218                 aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
3219                 break;
3220             case LOK_DOCTYPE_TEXT:
3221                 aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export");
3222                 break;
3223             case LOK_DOCTYPE_SPREADSHEET:
3224                 aMediaDescriptor["FilterName"] <<= OUString("calc_svg_Export");
3225                 break;
3226             default:
3227                 SAL_WARN("lok", "Failed to render shape selection: Document type is not supported");
3228         }
3229         aMediaDescriptor["SelectionOnly"] <<= true;
3230         aMediaDescriptor["OutputStream"] <<= xOut;
3231 
3232         xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
3233 
3234         if (pOutput)
3235         {
3236             const size_t nOutputSize = aOutStream.GetEndOfData();
3237             *pOutput = static_cast<char*>(malloc(nOutputSize));
3238             if (*pOutput)
3239             {
3240                 std::memcpy(*pOutput, aOutStream.GetData(), nOutputSize);
3241                 return nOutputSize;
3242             }
3243         }
3244     }
3245     catch (const uno::Exception& exception)
3246     {
3247         css::uno::Any exAny( cppu::getCaughtException() );
3248         SetLastExceptionMsg(exception.Message);
3249         SAL_WARN("lok", "Failed to render shape selection: " << exceptionToString(exAny));
3250     }
3251 
3252     return 0;
3253 }
3254 
3255 /** Class to react on finishing of a dispatched command.
3256 
3257     This will call a LOK_COMMAND_FINISHED callback when postUnoCommand was
3258     called with the parameter requesting the notification.
3259 
3260     @see LibreOfficeKitCallbackType::LOK_CALLBACK_UNO_COMMAND_RESULT.
3261 */
3262 class DispatchResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
3263 {
3264     OString maCommand;                 ///< Command for which this is the result.
3265     std::shared_ptr<CallbackFlushHandler> mpCallback; ///< Callback to call.
3266 
3267 public:
DispatchResultListener(const char * pCommand,std::shared_ptr<CallbackFlushHandler> const & pCallback)3268     DispatchResultListener(const char* pCommand, std::shared_ptr<CallbackFlushHandler> const & pCallback)
3269         : maCommand(pCommand)
3270         , mpCallback(pCallback)
3271     {
3272         assert(mpCallback);
3273     }
3274 
dispatchFinished(const css::frame::DispatchResultEvent & rEvent)3275     virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
3276     {
3277         boost::property_tree::ptree aTree;
3278         aTree.put("commandName", maCommand.getStr());
3279 
3280         if (rEvent.State != frame::DispatchResultState::DONTKNOW)
3281         {
3282             bool bSuccess = (rEvent.State == frame::DispatchResultState::SUCCESS);
3283             aTree.put("success", bSuccess);
3284         }
3285 
3286         aTree.add_child("result", unoAnyToPropertyTree(rEvent.Result));
3287 
3288         std::stringstream aStream;
3289         boost::property_tree::write_json(aStream, aTree);
3290         OString aPayload = aStream.str().c_str();
3291         mpCallback->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
3292     }
3293 
disposing(const css::lang::EventObject &)3294     virtual void SAL_CALL disposing(const css::lang::EventObject&) override {}
3295 };
3296 
doc_sendDialogEvent(LibreOfficeKitDocument *,unsigned nWindowId,const char * pArguments)3297 static void doc_sendDialogEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nWindowId, const char* pArguments)
3298 {
3299     SolarMutexGuard aGuard;
3300 
3301     StringMap aMap(jsonToStringMap(pArguments));
3302     VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nWindowId);
3303     if (!pWindow)
3304     {
3305         SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3306         return;
3307     }
3308     else if (aMap.find("id") != aMap.end())
3309     {
3310         const OUString sClickAction("CLICK");
3311         const OUString sSelectAction("SELECT");
3312         const OUString sClearAction("CLEAR");
3313         const OUString sTypeAction("TYPE");
3314         const OUString sUpAction("UP");
3315         const OUString sDownAction("DOWN");
3316 
3317         try
3318         {
3319             WindowUIObject aUIObject(pWindow);
3320             std::unique_ptr<UIObject> pUIWindow(aUIObject.get_child(aMap["id"]));
3321             if (pUIWindow) {
3322                 bool bIsClickAction = false;
3323 
3324                 if (aMap.find("cmd") != aMap.end()) {
3325                     if (aMap["cmd"] == "selected")
3326                     {
3327                         aMap["POS"] = aMap["data"];
3328                         aMap["TEXT"] = aMap["data"];
3329 
3330                         pUIWindow->execute(sSelectAction, aMap);
3331                     }
3332                     else if (aMap["cmd"] == "plus")
3333                     {
3334                         pUIWindow->execute(sUpAction, aMap);
3335                     }
3336                     else if (aMap["cmd"] == "minus")
3337                     {
3338                         pUIWindow->execute(sDownAction, aMap);
3339                     }
3340                     else if (aMap["cmd"] == "set")
3341                     {
3342                         aMap["TEXT"] = aMap["data"];
3343 
3344                         pUIWindow->execute(sClearAction, aMap);
3345                         pUIWindow->execute(sTypeAction, aMap);
3346                     }
3347                     else
3348                         bIsClickAction = true;
3349                 }
3350                 else
3351                     bIsClickAction = true;
3352 
3353                 if (bIsClickAction)
3354                     pUIWindow->execute(sClickAction, aMap);
3355             }
3356         } catch(...) {}
3357 
3358         // force resend
3359         pWindow->GetParent()->Hide();
3360         pWindow->GetParent()->Show();
3361     }
3362 }
3363 
doc_postUnoCommand(LibreOfficeKitDocument * pThis,const char * pCommand,const char * pArguments,bool bNotifyWhenFinished)3364 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished)
3365 {
3366     comphelper::ProfileZone aZone("doc_postUnoCommand");
3367 
3368     SolarMutexGuard aGuard;
3369     SetLastExceptionMsg();
3370 
3371     SfxObjectShell* pDocSh = SfxObjectShell::Current();
3372     OUString aCommand(pCommand, strlen(pCommand), RTL_TEXTENCODING_UTF8);
3373     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3374 
3375     std::vector<beans::PropertyValue> aPropertyValuesVector(jsonToPropertyValuesVector(pArguments));
3376 
3377     if (!vcl::lok::isUnipoll())
3378     {
3379         beans::PropertyValue aSynchronMode;
3380         aSynchronMode.Name = "SynchronMode";
3381         aSynchronMode.Value <<= false;
3382         aPropertyValuesVector.push_back(aSynchronMode);
3383     }
3384 
3385     int nView = SfxLokHelper::getView();
3386     if (nView < 0)
3387         return;
3388 
3389     // Set/unset mobile view for LOK
3390     if (gImpl && aCommand == ".uno:LOKSetMobile")
3391     {
3392         comphelper::LibreOfficeKit::setMobile(nView);
3393         return;
3394     }
3395     else if (gImpl && aCommand == ".uno:LOKUnSetMobile")
3396     {
3397         comphelper::LibreOfficeKit::setMobile(nView, false);
3398         return;
3399     }
3400 
3401     // handle potential interaction
3402     if (gImpl && aCommand == ".uno:Save")
3403     {
3404         rtl::Reference<LOKInteractionHandler> const pInteraction(
3405             new LOKInteractionHandler("save", gImpl, pDocument));
3406         uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
3407 
3408         beans::PropertyValue aValue;
3409         aValue.Name = "InteractionHandler";
3410         aValue.Value <<= xInteraction;
3411         aPropertyValuesVector.push_back(aValue);
3412 
3413         bool bDontSaveIfUnmodified = false;
3414         aPropertyValuesVector.erase(std::remove_if(aPropertyValuesVector.begin(),
3415                                                    aPropertyValuesVector.end(),
3416                                                    [&bDontSaveIfUnmodified](const beans::PropertyValue& aItem){
3417                                                        if (aItem.Name == "DontSaveIfUnmodified")
3418                                                        {
3419                                                            bDontSaveIfUnmodified = aItem.Value.get<bool>();
3420                                                            return true;
3421                                                        }
3422                                                        return false;
3423                                                    }), aPropertyValuesVector.end());
3424 
3425         // skip saving and tell the result via UNO_COMMAND_RESULT
3426         if (bDontSaveIfUnmodified && !pDocSh->IsModified())
3427         {
3428             boost::property_tree::ptree aTree;
3429             aTree.put("commandName", pCommand);
3430             aTree.put("success", false);
3431 
3432             // Add the reason for not saving
3433             const uno::Any aResultValue = uno::makeAny(OUString("unmodified"));
3434             aTree.add_child("result", unoAnyToPropertyTree(aResultValue));
3435 
3436             std::stringstream aStream;
3437             boost::property_tree::write_json(aStream, aTree);
3438             OString aPayload = aStream.str().c_str();
3439             pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
3440             return;
3441         }
3442     }
3443     else if (gImpl && aCommand == ".uno:TransformDialog")
3444     {
3445         bool bNeedConversion = false;
3446         SfxViewShell* pViewShell = SfxViewShell::Current();
3447         LokChartHelper aChartHelper(pViewShell);
3448 
3449         if (aChartHelper.GetWindow() )
3450         {
3451             bNeedConversion = true;
3452         }
3453         else if (const SdrView* pView = pViewShell->GetDrawView())
3454         {
3455             if (OutputDevice* pOutputDevice = pView->GetFirstOutputDevice())
3456             {
3457                 bNeedConversion = (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM);
3458             }
3459         }
3460 
3461         if (bNeedConversion)
3462         {
3463             sal_Int32 value;
3464             for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
3465             {
3466                 if (rPropValue.Name == "TransformPosX"
3467                         || rPropValue.Name == "TransformPosY"
3468                         || rPropValue.Name == "TransformWidth"
3469                         || rPropValue.Name == "TransformHeight"
3470                         || rPropValue.Name == "TransformRotationX"
3471                         || rPropValue.Name == "TransformRotationY")
3472                 {
3473                     rPropValue.Value >>= value;
3474                     value = OutputDevice::LogicToLogic(value, MapUnit::MapTwip, MapUnit::Map100thMM);
3475                     rPropValue.Value <<= value;
3476                 }
3477             }
3478         }
3479 
3480         if (aChartHelper.GetWindow())
3481         {
3482             if (aPropertyValuesVector[0].Name != "Action")
3483             {
3484                 tools::Rectangle aChartBB = aChartHelper.GetChartBoundingBox();
3485                 int nLeft = OutputDevice::LogicToLogic(aChartBB.Left(), MapUnit::MapTwip, MapUnit::Map100thMM);
3486                 int nTop = OutputDevice::LogicToLogic(aChartBB.Top(), MapUnit::MapTwip, MapUnit::Map100thMM);
3487 
3488                 sal_Int32 value;
3489                 for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
3490                 {
3491                     if (rPropValue.Name == "TransformPosX" || rPropValue.Name == "TransformRotationX")
3492                     {
3493                         rPropValue.Value >>= value;
3494                         rPropValue.Value <<= value - nLeft;
3495                     }
3496                     else if (rPropValue.Name == "TransformPosY" || rPropValue.Name == "TransformRotationY")
3497                     {
3498                         rPropValue.Value >>= value;
3499                         rPropValue.Value <<= value - nTop;
3500                     }
3501                 }
3502             }
3503             util::URL aCommandURL;
3504             aCommandURL.Path = "LOKTransform";
3505             css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
3506             aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
3507             return;
3508         }
3509     }
3510 
3511     bool bResult = false;
3512     if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView))
3513     {
3514         bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector),
3515                 new DispatchResultListener(pCommand, pDocument->mpCallbackFlushHandlers[nView]));
3516     }
3517     else
3518         bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
3519 
3520     if (!bResult)
3521     {
3522         SetLastExceptionMsg("Failed to dispatch the .uno: command");
3523     }
3524 }
3525 
doc_postMouseEvent(LibreOfficeKitDocument * pThis,int nType,int nX,int nY,int nCount,int nButtons,int nModifier)3526 static void doc_postMouseEvent(LibreOfficeKitDocument* pThis, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
3527 {
3528     comphelper::ProfileZone aZone("doc_postMouseEvent");
3529 
3530     SolarMutexGuard aGuard;
3531     SetLastExceptionMsg();
3532 
3533     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3534     if (!pDoc)
3535     {
3536         SetLastExceptionMsg("Document doesn't support tiled rendering");
3537         return;
3538     }
3539 
3540     pDoc->postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier);
3541 }
3542 
doc_postWindowMouseEvent(LibreOfficeKitDocument *,unsigned nLOKWindowId,int nType,int nX,int nY,int nCount,int nButtons,int nModifier)3543 static void doc_postWindowMouseEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
3544 {
3545     comphelper::ProfileZone aZone("doc_postWindowMouseEvent");
3546 
3547     SolarMutexGuard aGuard;
3548     SetLastExceptionMsg();
3549 
3550     VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3551     if (!pWindow)
3552     {
3553         SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3554         return;
3555     }
3556 
3557     const Point aPos(nX, nY);
3558     MouseEvent aEvent(aPos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
3559 
3560     if (Dialog* pDialog = dynamic_cast<Dialog*>(pWindow.get()))
3561     {
3562         pDialog->EnableInput();
3563     }
3564 
3565     switch (nType)
3566     {
3567         case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
3568             Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aEvent);
3569             break;
3570         case LOK_MOUSEEVENT_MOUSEBUTTONUP:
3571             Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aEvent);
3572             break;
3573         case LOK_MOUSEEVENT_MOUSEMOVE:
3574             Application::PostMouseEvent(VclEventId::WindowMouseMove, pWindow, &aEvent);
3575             break;
3576         default:
3577             assert(false);
3578             break;
3579     }
3580 }
3581 
doc_postWindowGestureEvent(LibreOfficeKitDocument *,unsigned nLOKWindowId,const char * pType,int nX,int nY,int nOffset)3582 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, const char* pType, int nX, int nY, int nOffset)
3583 {
3584     comphelper::ProfileZone aZone("doc_postWindowGestureEvent");
3585 
3586     SolarMutexGuard aGuard;
3587     SetLastExceptionMsg();
3588 
3589     VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3590     if (!pWindow)
3591     {
3592         SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3593         return;
3594     }
3595 
3596     OString aType(pType);
3597     GestureEventType eEventType = GestureEventType::PanningUpdate;
3598 
3599     if (aType == "panBegin")
3600         eEventType = GestureEventType::PanningBegin;
3601     else if (aType == "panEnd")
3602         eEventType = GestureEventType::PanningEnd;
3603 
3604     GestureEvent aEvent {
3605         sal_Int32(nX),
3606         sal_Int32(nY),
3607         eEventType,
3608         sal_Int32(nOffset),
3609         PanningOrientation::Vertical,
3610     };
3611 
3612     if (Dialog* pDialog = dynamic_cast<Dialog*>(pWindow.get()))
3613     {
3614         pDialog->EnableInput();
3615     }
3616 
3617     Application::PostGestureEvent(VclEventId::WindowGestureEvent, pWindow, &aEvent);
3618 }
3619 
doc_setTextSelection(LibreOfficeKitDocument * pThis,int nType,int nX,int nY)3620 static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
3621 {
3622     comphelper::ProfileZone aZone("doc_setTextSelection");
3623 
3624     SolarMutexGuard aGuard;
3625     SetLastExceptionMsg();
3626 
3627     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3628     if (!pDoc)
3629     {
3630         SetLastExceptionMsg("Document doesn't support tiled rendering");
3631         return;
3632     }
3633 
3634     pDoc->setTextSelection(nType, nX, nY);
3635 }
3636 
3637 static bool getFromTransferrable(
3638     const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
3639     const OString &aInMimeType, OString &aRet);
3640 
encodeImageAsHTML(const css::uno::Reference<css::datatransfer::XTransferable> & xTransferable,const OString & aMimeType,OString & aRet)3641 static bool encodeImageAsHTML(
3642     const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
3643     const OString &aMimeType, OString &aRet)
3644 {
3645     if (!getFromTransferrable(xTransferable, aMimeType, aRet))
3646         return false;
3647 
3648     // Encode in base64.
3649     auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),
3650                                    aRet.getLength());
3651     OUStringBuffer aBase64Data;
3652     comphelper::Base64::encode(aBase64Data, aSeq);
3653 
3654     // Embed in HTML.
3655     aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
3656         "<html><head>"
3657         "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
3658         "name=\"generator\" content=\""
3659         + getGenerator().toUtf8()
3660         + "\"/>"
3661         "</head><body><img src=\"data:" + aMimeType + ";base64,"
3662         + aBase64Data.makeStringAndClear().toUtf8() + "\"/></body></html>";
3663 
3664     return true;
3665 }
3666 
encodeTextAsHTML(const css::uno::Reference<css::datatransfer::XTransferable> & xTransferable,const OString & aMimeType,OString & aRet)3667 static bool encodeTextAsHTML(
3668     const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
3669     const OString &aMimeType, OString &aRet)
3670 {
3671     if (!getFromTransferrable(xTransferable, aMimeType, aRet))
3672         return false;
3673 
3674     // Embed in HTML - FIXME: needs some escaping.
3675     aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
3676         "<html><head>"
3677         "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
3678         "name=\"generator\" content=\""
3679         + getGenerator().toUtf8()
3680         + "\"/></head><body><pre>" + aRet + "</pre></body></html>";
3681 
3682     return true;
3683 }
3684 
getFromTransferrable(const css::uno::Reference<css::datatransfer::XTransferable> & xTransferable,const OString & aInMimeType,OString & aRet)3685 static bool getFromTransferrable(
3686     const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
3687     const OString &aInMimeType, OString &aRet)
3688 {
3689     OString aMimeType(aInMimeType);
3690 
3691     // Take care of UTF-8 text here.
3692     bool bConvert = false;
3693     sal_Int32 nIndex = 0;
3694     if (aMimeType.getToken(0, ';', nIndex) == "text/plain")
3695     {
3696         if (aMimeType.getToken(0, ';', nIndex) == "charset=utf-8")
3697         {
3698             aMimeType = "text/plain;charset=utf-16";
3699             bConvert = true;
3700         }
3701     }
3702 
3703     datatransfer::DataFlavor aFlavor;
3704     aFlavor.MimeType = OUString::fromUtf8(aMimeType.getStr());
3705     if (aMimeType == "text/plain;charset=utf-16")
3706         aFlavor.DataType = cppu::UnoType<OUString>::get();
3707     else
3708         aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();
3709 
3710     if (!xTransferable->isDataFlavorSupported(aFlavor))
3711     {
3712         // Try harder for HTML it is our copy/paste meta-file format
3713         if (aInMimeType == "text/html")
3714         {
3715             // Desperate measures - convert text to HTML instead.
3716             if (encodeTextAsHTML(xTransferable, "text/plain;charset=utf-8", aRet))
3717                 return true;
3718             // If html is not supported, might be a graphic-selection,
3719             if (encodeImageAsHTML(xTransferable, "image/png", aRet))
3720                 return true;
3721         }
3722 
3723         SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported");
3724         return false;
3725     }
3726 
3727     uno::Any aAny;
3728     try
3729     {
3730         aAny = xTransferable->getTransferData(aFlavor);
3731     }
3732     catch (const css::datatransfer::UnsupportedFlavorException& e)
3733     {
3734         SetLastExceptionMsg("Unsupported flavor " + aFlavor.MimeType + " exception " + e.Message);
3735         return false;
3736     }
3737     catch (const css::uno::Exception& e)
3738     {
3739         SetLastExceptionMsg("Exception getting " + aFlavor.MimeType + " exception " + e.Message);
3740         return false;
3741     }
3742 
3743     if (aFlavor.DataType == cppu::UnoType<OUString>::get())
3744     {
3745         OUString aString;
3746         aAny >>= aString;
3747         if (bConvert)
3748             aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
3749         else
3750             aRet = OString(reinterpret_cast<const sal_Char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
3751     }
3752     else
3753     {
3754         uno::Sequence<sal_Int8> aSequence;
3755         aAny >>= aSequence;
3756         aRet = OString(reinterpret_cast<sal_Char*>(aSequence.getArray()), aSequence.getLength());
3757     }
3758 
3759     return true;
3760 }
3761 
doc_getTextSelection(LibreOfficeKitDocument * pThis,const char * pMimeType,char ** pUsedMimeType)3762 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType)
3763 {
3764     comphelper::ProfileZone aZone("doc_getTextSelection");
3765 
3766     SolarMutexGuard aGuard;
3767     SetLastExceptionMsg();
3768 
3769     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3770     if (!pDoc)
3771     {
3772         SetLastExceptionMsg("Document doesn't support tiled rendering");
3773         return nullptr;
3774     }
3775 
3776     css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection();
3777     if (!xTransferable)
3778     {
3779         SetLastExceptionMsg("No selection available");
3780         return nullptr;
3781     }
3782 
3783     const char *pType = pMimeType;
3784     if (!pType || pType[0] == '\0')
3785         pType = "text/plain;charset=utf-8";
3786 
3787     OString aRet;
3788     bool bSuccess = getFromTransferrable(xTransferable, OString(pType), aRet);
3789     if (!bSuccess)
3790         return nullptr;
3791 
3792     if (pUsedMimeType) // legacy
3793     {
3794         if (pMimeType)
3795             *pUsedMimeType = strdup(pMimeType);
3796         else
3797             *pUsedMimeType = nullptr;
3798     }
3799 
3800     return convertOString(aRet);
3801 }
3802 
doc_getSelectionType(LibreOfficeKitDocument * pThis)3803 static int doc_getSelectionType(LibreOfficeKitDocument* pThis)
3804 {
3805     comphelper::ProfileZone aZone("doc_getSelectionType");
3806 
3807     SolarMutexGuard aGuard;
3808     SetLastExceptionMsg();
3809 
3810     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3811     if (!pDoc)
3812     {
3813         SetLastExceptionMsg("Document doesn't support tiled rendering");
3814         return LOK_SELTYPE_NONE;
3815     }
3816 
3817     css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(pDoc->getSelection(), css::uno::UNO_QUERY);
3818     if (!xTransferable)
3819     {
3820         SetLastExceptionMsg("No selection available");
3821         return LOK_SELTYPE_NONE;
3822     }
3823 
3824     if (xTransferable->isComplex())
3825         return LOK_SELTYPE_COMPLEX;
3826 
3827     OString aRet;
3828     bool bSuccess = getFromTransferrable(xTransferable, "text/plain;charset=utf-8", aRet);
3829     if (!bSuccess)
3830         return LOK_SELTYPE_NONE;
3831 
3832     if (aRet.getLength() > 10000)
3833         return LOK_SELTYPE_COMPLEX;
3834 
3835     return aRet.getLength() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE;
3836 }
3837 
doc_getClipboard(LibreOfficeKitDocument * pThis,const char ** pMimeTypes,size_t * pOutCount,char *** pOutMimeTypes,size_t ** pOutSizes,char *** pOutStreams)3838 static int doc_getClipboard(LibreOfficeKitDocument* pThis,
3839                             const char **pMimeTypes,
3840                             size_t      *pOutCount,
3841                             char      ***pOutMimeTypes,
3842                             size_t     **pOutSizes,
3843                             char      ***pOutStreams)
3844 {
3845     comphelper::ProfileZone aZone("doc_getClipboard");
3846 
3847     SolarMutexGuard aGuard;
3848     SetLastExceptionMsg();
3849 
3850     assert (pOutCount);
3851     assert (pOutMimeTypes);
3852     assert (pOutSizes);
3853     assert (pOutStreams);
3854 
3855     *pOutCount = 0;
3856     *pOutMimeTypes = nullptr;
3857     *pOutSizes = nullptr;
3858     *pOutStreams = nullptr;
3859 
3860     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3861     if (!pDoc)
3862     {
3863         SetLastExceptionMsg("Document doesn't support tiled rendering");
3864         return 0;
3865     }
3866 
3867     rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView());
3868 
3869     css::uno::Reference<css::datatransfer::XTransferable> xTransferable = xClip->getContents();
3870     SAL_INFO("lok", "Got from clip: " << xClip.get() << " transferrable: " << xTransferable);
3871     if (!xTransferable)
3872     {
3873         SetLastExceptionMsg("No clipboard content available");
3874         return 0;
3875     }
3876 
3877     std::vector<OString> aMimeTypes;
3878     if (!pMimeTypes) // everything
3879     {
3880         const uno::Sequence< css::datatransfer::DataFlavor > flavors = xTransferable->getTransferDataFlavors();
3881         if (!flavors.getLength())
3882         {
3883             SetLastExceptionMsg("Flavourless selection");
3884             return 0;
3885         }
3886         for (const auto &it : flavors)
3887             aMimeTypes.push_back(OUStringToOString(it.MimeType, RTL_TEXTENCODING_UTF8));
3888     }
3889     else
3890     {
3891         for (size_t i = 0; pMimeTypes[i]; ++i)
3892             aMimeTypes.push_back(OString(pMimeTypes[i]));
3893     }
3894 
3895     *pOutCount = aMimeTypes.size();
3896     *pOutSizes = static_cast<size_t *>(malloc(*pOutCount * sizeof(size_t)));
3897     *pOutMimeTypes = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
3898     *pOutStreams = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
3899     for (size_t i = 0; i < aMimeTypes.size(); ++i)
3900     {
3901         if (aMimeTypes[i] == "text/plain;charset=utf-16")
3902             (*pOutMimeTypes)[i] = strdup("text/plain;charset=utf-8");
3903         else
3904             (*pOutMimeTypes)[i] = strdup(aMimeTypes[i].getStr());
3905 
3906         OString aRet;
3907         bool bSuccess = getFromTransferrable(xTransferable, (*pOutMimeTypes)[i], aRet);
3908         if (!bSuccess || aRet.getLength() < 1)
3909         {
3910             (*pOutSizes)[i] = 0;
3911             (*pOutStreams)[i] = nullptr;
3912         }
3913         else
3914         {
3915             (*pOutSizes)[i] = aRet.getLength();
3916             (*pOutStreams)[i] = convertOString(aRet);
3917         }
3918     }
3919 
3920     return 1;
3921 }
3922 
doc_setClipboard(LibreOfficeKitDocument * pThis,const size_t nInCount,const char ** pInMimeTypes,const size_t * pInSizes,const char ** pInStreams)3923 static int doc_setClipboard(LibreOfficeKitDocument* pThis,
3924                             const size_t   nInCount,
3925                             const char   **pInMimeTypes,
3926                             const size_t  *pInSizes,
3927                             const char   **pInStreams)
3928 {
3929 #ifdef IOS
3930     (void) pThis;
3931     (void) nInCount;
3932     (void) pInMimeTypes;
3933     (void) pInSizes;
3934     (void) pInStreams;
3935 #else
3936     comphelper::ProfileZone aZone("doc_setClipboard");
3937 
3938     SolarMutexGuard aGuard;
3939     SetLastExceptionMsg();
3940 
3941     ITiledRenderable* pDoc = getTiledRenderable(pThis);
3942     if (!pDoc)
3943     {
3944         SetLastExceptionMsg("Document doesn't support tiled rendering");
3945         return false;
3946     }
3947 
3948     uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(nInCount, pInMimeTypes, pInSizes, pInStreams));
3949 
3950     auto xClip = forceSetClipboardForCurrentView(pThis);
3951     xClip->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
3952 
3953     SAL_INFO("lok", "Set clip: " << xClip.get() << " to: " << xTransferable);
3954 
3955     if (!pDoc->isMimeTypeSupported())
3956     {
3957         SetLastExceptionMsg("Document doesn't support this mime type");
3958         return false;
3959     }
3960 #endif
3961     return true;
3962 }
3963 
doc_paste(LibreOfficeKitDocument * pThis,const char * pMimeType,const char * pData,size_t nSize)3964 static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, size_t nSize)
3965 {
3966     comphelper::ProfileZone aZone("doc_paste");
3967 
3968     SolarMutexGuard aGuard;
3969 
3970     const char *pInMimeTypes[1];
3971     const char *pInStreams[1];
3972     size_t pInSizes[1];
3973     pInMimeTypes[0] = pMimeType;
3974     pInSizes[0] = nSize;
3975     pInStreams[0] = pData;
3976 
3977     if (!doc_setClipboard(pThis, 1, pInMimeTypes, pInSizes, pInStreams))
3978         return false;
3979 
3980     uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
3981     {
3982         {"AnchorType", uno::makeAny(static_cast<sal_uInt16>(text::TextContentAnchorType_AS_CHARACTER))},
3983         {"IgnoreComments", uno::makeAny(true)},
3984     }));
3985     if (!comphelper::dispatchCommand(".uno:Paste", aPropertyValues))
3986     {
3987         SetLastExceptionMsg("Failed to dispatch the .uno: command");
3988         return false;
3989     }
3990 
3991     return true;
3992 }
3993 
doc_setGraphicSelection(LibreOfficeKitDocument * pThis,int nType,int nX,int nY)3994 static void doc_setGraphicSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
3995 {
3996     comphelper::ProfileZone aZone("doc_setGraphicSelection");
3997 
3998     SolarMutexGuard aGuard;
3999     SetLastExceptionMsg();
4000 
4001     ITiledRenderable* pDoc = getTiledRenderable(pThis);
4002     if (!pDoc)
4003     {
4004         SetLastExceptionMsg("Document doesn't support tiled rendering");
4005         return;
4006     }
4007 
4008     pDoc->setGraphicSelection(nType, nX, nY);
4009 }
4010 
doc_resetSelection(LibreOfficeKitDocument * pThis)4011 static void doc_resetSelection(LibreOfficeKitDocument* pThis)
4012 {
4013     comphelper::ProfileZone aZone("doc_resetSelection");
4014 
4015     SolarMutexGuard aGuard;
4016     SetLastExceptionMsg();
4017 
4018     ITiledRenderable* pDoc = getTiledRenderable(pThis);
4019     if (!pDoc)
4020     {
4021         SetLastExceptionMsg("Document doesn't support tiled rendering");
4022         return;
4023     }
4024 
4025     pDoc->resetSelection();
4026 }
4027 
getLanguages(const char * pCommand)4028 static char* getLanguages(const char* pCommand)
4029 {
4030     css::uno::Sequence< css::lang::Locale > aLocales;
4031 
4032     if (xContext.is())
4033     {
4034         css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
4035         if (xLangSrv.is())
4036         {
4037             css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
4038             if (xSpell.is())
4039                 aLocales = xSpell->getLocales();
4040         }
4041     }
4042 
4043     boost::property_tree::ptree aTree;
4044     aTree.put("commandName", pCommand);
4045     boost::property_tree::ptree aValues;
4046     boost::property_tree::ptree aChild;
4047     OUString sLanguage;
4048     for ( sal_Int32 itLocale = 0; itLocale < aLocales.getLength(); itLocale++ )
4049     {
4050         const LanguageTag aLanguageTag( aLocales[itLocale]);
4051         sLanguage = SvtLanguageTable::GetLanguageString(aLanguageTag.getLanguageType());
4052         if (sLanguage.startsWith("{") && sLanguage.endsWith("}"))
4053             continue;
4054 
4055         sLanguage += ";" + aLanguageTag.getBcp47(false);
4056         aChild.put("", sLanguage.toUtf8());
4057         aValues.push_back(std::make_pair("", aChild));
4058     }
4059     aTree.add_child("commandValues", aValues);
4060     std::stringstream aStream;
4061     boost::property_tree::write_json(aStream, aTree);
4062     char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4063     assert(pJson); // Don't handle OOM conditions
4064     strcpy(pJson, aStream.str().c_str());
4065     pJson[aStream.str().size()] = '\0';
4066     return pJson;
4067 }
4068 
getFonts(const char * pCommand)4069 static char* getFonts (const char* pCommand)
4070 {
4071     SfxObjectShell* pDocSh = SfxObjectShell::Current();
4072     const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
4073         pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
4074     const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
4075 
4076     boost::property_tree::ptree aTree;
4077     aTree.put("commandName", pCommand);
4078     boost::property_tree::ptree aValues;
4079     if ( pList )
4080     {
4081         sal_uInt16 nFontCount = pList->GetFontNameCount();
4082         for (sal_uInt16 i = 0; i < nFontCount; ++i)
4083         {
4084             boost::property_tree::ptree aChildren;
4085             const FontMetric& rFontMetric = pList->GetFontName(i);
4086             const sal_IntPtr* pAry = pList->GetSizeAry(rFontMetric);
4087             sal_uInt16 nSizeCount = 0;
4088             while (pAry[nSizeCount])
4089             {
4090                 boost::property_tree::ptree aChild;
4091                 aChild.put("", static_cast<float>(pAry[nSizeCount]) / 10);
4092                 aChildren.push_back(std::make_pair("", aChild));
4093                 nSizeCount++;
4094             }
4095             aValues.add_child(rFontMetric.GetFamilyName().toUtf8().getStr(), aChildren);
4096         }
4097     }
4098     aTree.add_child("commandValues", aValues);
4099     std::stringstream aStream;
4100     boost::property_tree::write_json(aStream, aTree);
4101     char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4102     assert(pJson); // Don't handle OOM conditions
4103     strcpy(pJson, aStream.str().c_str());
4104     pJson[aStream.str().size()] = '\0';
4105     return pJson;
4106 }
4107 
getFontSubset(const OString & aFontName)4108 static char* getFontSubset (const OString& aFontName)
4109 {
4110     OUString aFoundFont(::rtl::Uri::decode(OStringToOUString(aFontName, RTL_TEXTENCODING_UTF8), rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8));
4111     SfxObjectShell* pDocSh = SfxObjectShell::Current();
4112     const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
4113         pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
4114     const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
4115 
4116     boost::property_tree::ptree aTree;
4117     aTree.put("commandName", ".uno:FontSubset");
4118     boost::property_tree::ptree aValues;
4119 
4120     if ( pList && !aFoundFont.isEmpty() )
4121     {
4122         sal_uInt16 nFontCount = pList->GetFontNameCount();
4123         sal_uInt16 nItFont = 0;
4124         for (; nItFont < nFontCount; ++nItFont)
4125         {
4126             if (aFoundFont == pList->GetFontName(nItFont).GetFamilyName())
4127             {
4128                 break;
4129             }
4130         }
4131 
4132         if ( nItFont < nFontCount )
4133         {
4134             FontCharMapRef xFontCharMap (new FontCharMap());
4135             auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
4136             const vcl::Font& aFont(pList->GetFontName(nItFont));
4137 
4138             aDevice->SetFont(aFont);
4139             aDevice->GetFontCharMap(xFontCharMap);
4140             SubsetMap aSubMap(xFontCharMap);
4141 
4142             for (auto const& subset : aSubMap.GetSubsetMap())
4143             {
4144                 boost::property_tree::ptree aChild;
4145                 aChild.put("", static_cast<int>(ublock_getCode(subset.GetRangeMin())));
4146                 aValues.push_back(std::make_pair("", aChild));
4147             }
4148         }
4149     }
4150 
4151     aTree.add_child("commandValues", aValues);
4152     std::stringstream aStream;
4153     boost::property_tree::write_json(aStream, aTree);
4154     char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4155     assert(pJson); // Don't handle OOM conditions
4156     strcpy(pJson, aStream.str().c_str());
4157     pJson[aStream.str().size()] = '\0';
4158     return pJson;
4159 }
4160 
getStyles(LibreOfficeKitDocument * pThis,const char * pCommand)4161 static char* getStyles(LibreOfficeKitDocument* pThis, const char* pCommand)
4162 {
4163     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4164 
4165     boost::property_tree::ptree aTree;
4166     aTree.put("commandName", pCommand);
4167     uno::Reference<css::style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
4168     uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
4169     uno::Sequence<OUString> aStyleFamilies = xStyleFamilies->getElementNames();
4170 
4171     static const std::vector<OUString> aWriterStyles =
4172     {
4173         "Text body",
4174         "Quotations",
4175         "Title",
4176         "Subtitle",
4177         "Heading 1",
4178         "Heading 2",
4179         "Heading 3"
4180     };
4181 
4182     // We need to keep a list of the default style names
4183     // in order to filter these out later when processing
4184     // the full list of styles.
4185     std::set<OUString> aDefaultStyleNames;
4186 
4187     boost::property_tree::ptree aValues;
4188     for (sal_Int32 nStyleFam = 0; nStyleFam < aStyleFamilies.getLength(); ++nStyleFam)
4189     {
4190         boost::property_tree::ptree aChildren;
4191         OUString sStyleFam = aStyleFamilies[nStyleFam];
4192         uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(sStyleFam), uno::UNO_QUERY);
4193 
4194         // Writer provides a huge number of styles, we have a list of 7 "default" styles which
4195         // should be shown in the normal dropdown, which we should add to the start of the list
4196         // to simplify their selection.
4197         if (sStyleFam == "ParagraphStyles"
4198             && doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT)
4199         {
4200             for (const OUString& rStyle: aWriterStyles)
4201             {
4202                 aDefaultStyleNames.insert( rStyle );
4203 
4204                 boost::property_tree::ptree aChild;
4205                 aChild.put("", rStyle.toUtf8());
4206                 aChildren.push_back(std::make_pair("", aChild));
4207             }
4208         }
4209 
4210         const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
4211         for (const OUString& rStyle: aStyles )
4212         {
4213             // Filter out the default styles - they are already at the top
4214             // of the list
4215             if (aDefaultStyleNames.find(rStyle) == aDefaultStyleNames.end() ||
4216                 (sStyleFam != "ParagraphStyles" || doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) )
4217             {
4218                 boost::property_tree::ptree aChild;
4219                 aChild.put("", rStyle.toUtf8());
4220                 aChildren.push_back(std::make_pair("", aChild));
4221             }
4222         }
4223         aValues.add_child(sStyleFam.toUtf8().getStr(), aChildren);
4224     }
4225 
4226     // Header & Footer Styles
4227     {
4228         OUString sName;
4229         boost::property_tree::ptree aChild;
4230         boost::property_tree::ptree aChildren;
4231         const OUString sPageStyles("PageStyles");
4232         uno::Reference<beans::XPropertySet> xProperty;
4233         uno::Reference<container::XNameContainer> xContainer;
4234 
4235         if (xStyleFamilies->hasByName(sPageStyles) && (xStyleFamilies->getByName(sPageStyles) >>= xContainer))
4236         {
4237             uno::Sequence<OUString> aSeqNames = xContainer->getElementNames();
4238             for (sal_Int32 itName = 0; itName < aSeqNames.getLength(); itName++)
4239             {
4240                 bool bIsPhysical;
4241                 sName = aSeqNames[itName];
4242                 xProperty.set(xContainer->getByName(sName), uno::UNO_QUERY);
4243                 if (xProperty.is() && (xProperty->getPropertyValue("IsPhysical") >>= bIsPhysical) && bIsPhysical)
4244                 {
4245                     xProperty->getPropertyValue("DisplayName") >>= sName;
4246                     aChild.put("", sName.toUtf8());
4247                     aChildren.push_back(std::make_pair("", aChild));
4248                 }
4249             }
4250             aValues.add_child("HeaderFooter", aChildren);
4251         }
4252     }
4253 
4254     {
4255         boost::property_tree::ptree aCommandList;
4256 
4257         {
4258             boost::property_tree::ptree aChild;
4259 
4260             OUString sClearFormat = SvxResId(RID_SVXSTR_CLEARFORM);
4261 
4262             boost::property_tree::ptree aName;
4263             aName.put("", sClearFormat.toUtf8());
4264             aChild.push_back(std::make_pair("text", aName));
4265 
4266             boost::property_tree::ptree aCommand;
4267             aCommand.put("", ".uno:ResetAttributes");
4268             aChild.push_back(std::make_pair("id", aCommand));
4269 
4270             aCommandList.push_back(std::make_pair("", aChild));
4271         }
4272 
4273         aValues.add_child("Commands", aCommandList);
4274     }
4275 
4276     aTree.add_child("commandValues", aValues);
4277     std::stringstream aStream;
4278     boost::property_tree::write_json(aStream, aTree);
4279     char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4280     assert(pJson); // Don't handle OOM conditions
4281     strcpy(pJson, aStream.str().c_str());
4282     pJson[aStream.str().size()] = '\0';
4283     return pJson;
4284 }
4285 
4286 enum class UndoOrRedo
4287 {
4288     UNDO,
4289     REDO
4290 };
4291 
4292 /// Returns the JSON representation of either an undo or a redo stack.
getUndoOrRedo(LibreOfficeKitDocument * pThis,UndoOrRedo eCommand)4293 static char* getUndoOrRedo(LibreOfficeKitDocument* pThis, UndoOrRedo eCommand)
4294 {
4295     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4296 
4297     auto pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
4298     if (!pBaseModel)
4299         return nullptr;
4300 
4301     SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
4302     if (!pObjectShell)
4303         return nullptr;
4304 
4305     SfxUndoManager* pUndoManager = pObjectShell->GetUndoManager();
4306     if (!pUndoManager)
4307         return nullptr;
4308 
4309     OUString aString;
4310     if (eCommand == UndoOrRedo::UNDO)
4311         aString = pUndoManager->GetUndoActionsInfo();
4312     else
4313         aString = pUndoManager->GetRedoActionsInfo();
4314     char* pJson = strdup(aString.toUtf8().getStr());
4315     return pJson;
4316 }
4317 
4318 /// Returns the JSON representation of the redline stack.
getTrackedChanges(LibreOfficeKitDocument * pThis)4319 static char* getTrackedChanges(LibreOfficeKitDocument* pThis)
4320 {
4321     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4322 
4323     uno::Reference<document::XRedlinesSupplier> xRedlinesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
4324     std::stringstream aStream;
4325     // We want positions of the track changes also which is not possible from
4326     // UNO. Enable positioning information for text documents only for now, so
4327     // construct the tracked changes JSON from inside the sw/, not here using UNO
4328     if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT && xRedlinesSupplier.is())
4329     {
4330         uno::Reference<container::XEnumeration> xRedlines = xRedlinesSupplier->getRedlines()->createEnumeration();
4331         boost::property_tree::ptree aRedlines;
4332         for (size_t nIndex = 0; xRedlines->hasMoreElements(); ++nIndex)
4333         {
4334             uno::Reference<beans::XPropertySet> xRedline(xRedlines->nextElement(), uno::UNO_QUERY);
4335             boost::property_tree::ptree aRedline;
4336             aRedline.put("index", nIndex);
4337 
4338             OUString sAuthor;
4339             xRedline->getPropertyValue("RedlineAuthor") >>= sAuthor;
4340             aRedline.put("author", sAuthor.toUtf8().getStr());
4341 
4342             OUString sType;
4343             xRedline->getPropertyValue("RedlineType") >>= sType;
4344             aRedline.put("type", sType.toUtf8().getStr());
4345 
4346             OUString sComment;
4347             xRedline->getPropertyValue("RedlineComment") >>= sComment;
4348             aRedline.put("comment", sComment.toUtf8().getStr());
4349 
4350             OUString sDescription;
4351             xRedline->getPropertyValue("RedlineDescription") >>= sDescription;
4352             aRedline.put("description", sDescription.toUtf8().getStr());
4353 
4354             util::DateTime aDateTime;
4355             xRedline->getPropertyValue("RedlineDateTime") >>= aDateTime;
4356             OUString sDateTime = utl::toISO8601(aDateTime);
4357             aRedline.put("dateTime", sDateTime.toUtf8().getStr());
4358 
4359             aRedlines.push_back(std::make_pair("", aRedline));
4360         }
4361 
4362         boost::property_tree::ptree aTree;
4363         aTree.add_child("redlines", aRedlines);
4364         boost::property_tree::write_json(aStream, aTree);
4365     }
4366     else
4367     {
4368         ITiledRenderable* pDoc = getTiledRenderable(pThis);
4369         if (!pDoc)
4370         {
4371             SetLastExceptionMsg("Document doesn't support tiled rendering");
4372             return nullptr;
4373         }
4374         OUString aTrackedChanges = pDoc->getTrackedChanges();
4375         aStream << aTrackedChanges.toUtf8();
4376     }
4377 
4378     char* pJson = strdup(aStream.str().c_str());
4379     return pJson;
4380 }
4381 
4382 
4383 /// Returns the JSON representation of the redline author table.
getTrackedChangeAuthors(LibreOfficeKitDocument * pThis)4384 static char* getTrackedChangeAuthors(LibreOfficeKitDocument* pThis)
4385 {
4386     ITiledRenderable* pDoc = getTiledRenderable(pThis);
4387     if (!pDoc)
4388     {
4389         SetLastExceptionMsg("Document doesn't support tiled rendering");
4390         return nullptr;
4391     }
4392     OUString aAuthors = pDoc->getTrackedChangeAuthors();
4393     return strdup(aAuthors.toUtf8().getStr());
4394 }
4395 
doc_getCommandValues(LibreOfficeKitDocument * pThis,const char * pCommand)4396 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand)
4397 {
4398     comphelper::ProfileZone aZone("doc_getCommandValues");
4399 
4400     SolarMutexGuard aGuard;
4401     SetLastExceptionMsg();
4402 
4403     OString aCommand(pCommand);
4404     static const OString aViewRowColumnHeaders(".uno:ViewRowColumnHeaders");
4405     static const OString aCellCursor(".uno:CellCursor");
4406     static const OString aFontSubset(".uno:FontSubset&name=");
4407 
4408     if (!strcmp(pCommand, ".uno:LanguageStatus"))
4409     {
4410         return getLanguages(pCommand);
4411     }
4412     else if (!strcmp(pCommand, ".uno:CharFontName"))
4413     {
4414         return getFonts(pCommand);
4415     }
4416     else if (!strcmp(pCommand, ".uno:StyleApply"))
4417     {
4418         return getStyles(pThis, pCommand);
4419     }
4420     else if (aCommand == ".uno:Undo")
4421     {
4422         return getUndoOrRedo(pThis, UndoOrRedo::UNDO);
4423     }
4424     else if (aCommand == ".uno:Redo")
4425     {
4426         return getUndoOrRedo(pThis, UndoOrRedo::REDO);
4427     }
4428     else if (aCommand == ".uno:AcceptTrackedChanges")
4429     {
4430         return getTrackedChanges(pThis);
4431     }
4432     else if (aCommand == ".uno:TrackedChangeAuthors")
4433     {
4434         return getTrackedChangeAuthors(pThis);
4435     }
4436     else if (aCommand == ".uno:ViewAnnotations")
4437     {
4438         return getPostIts(pThis);
4439     }
4440     else if (aCommand == ".uno:ViewAnnotationsPosition")
4441     {
4442         return getPostItsPos(pThis);
4443     }
4444     else if (aCommand == ".uno:RulerState")
4445     {
4446         return getRulerState(pThis);
4447     }
4448     else if (aCommand.startsWith(aViewRowColumnHeaders))
4449     {
4450         ITiledRenderable* pDoc = getTiledRenderable(pThis);
4451         if (!pDoc)
4452         {
4453             SetLastExceptionMsg("Document doesn't support tiled rendering");
4454             return nullptr;
4455         }
4456 
4457         tools::Rectangle aRectangle;
4458         if (aCommand.getLength() > aViewRowColumnHeaders.getLength())
4459         {
4460             // Command has parameters.
4461             int nX = 0;
4462             int nY = 0;
4463             int nWidth = 0;
4464             int nHeight = 0;
4465             OString aArguments = aCommand.copy(aViewRowColumnHeaders.getLength() + 1);
4466             sal_Int32 nParamIndex = 0;
4467             do
4468             {
4469                 OString aParamToken = aArguments.getToken(0, '&', nParamIndex);
4470                 sal_Int32 nIndex = 0;
4471                 OString aKey;
4472                 OString aValue;
4473                 do
4474                 {
4475                     OString aToken = aParamToken.getToken(0, '=', nIndex);
4476                     if (!aKey.getLength())
4477                         aKey = aToken;
4478                     else
4479                         aValue = aToken;
4480                 }
4481                 while (nIndex >= 0);
4482                 if (aKey == "x")
4483                     nX = aValue.toInt32();
4484                 else if (aKey == "y")
4485                     nY = aValue.toInt32();
4486                 else if (aKey == "width")
4487                     nWidth = aValue.toInt32();
4488                 else if (aKey == "height")
4489                     nHeight = aValue.toInt32();
4490             }
4491             while (nParamIndex >= 0);
4492 
4493             aRectangle = tools::Rectangle(nX, nY, nX + nWidth, nY + nHeight);
4494         }
4495 
4496         OUString aHeaders = pDoc->getRowColumnHeaders(aRectangle);
4497         if (aHeaders.isEmpty())
4498             return nullptr;
4499         else
4500             return convertOUString(aHeaders);
4501     }
4502     else if (aCommand.startsWith(aCellCursor))
4503     {
4504         ITiledRenderable* pDoc = getTiledRenderable(pThis);
4505         if (!pDoc)
4506         {
4507             SetLastExceptionMsg("Document doesn't support tiled rendering");
4508             return nullptr;
4509         }
4510 
4511         // Command has parameters.
4512         int nOutputWidth = 0;
4513         int nOutputHeight = 0;
4514         long nTileWidth = 0;
4515         long nTileHeight = 0;
4516         if (aCommand.getLength() > aCellCursor.getLength())
4517         {
4518             OString aArguments = aCommand.copy(aCellCursor.getLength() + 1);
4519             sal_Int32 nParamIndex = 0;
4520             do
4521             {
4522                 OString aParamToken = aArguments.getToken(0, '&', nParamIndex);
4523                 sal_Int32 nIndex = 0;
4524                 OString aKey;
4525                 OString aValue;
4526                 do
4527                 {
4528                     OString aToken = aParamToken.getToken(0, '=', nIndex);
4529                     if (!aKey.getLength())
4530                         aKey = aToken;
4531                     else
4532                         aValue = aToken;
4533                 }
4534                 while (nIndex >= 0);
4535                 if (aKey == "outputWidth")
4536                     nOutputWidth = aValue.toInt32();
4537                 else if (aKey == "outputHeight")
4538                     nOutputHeight = aValue.toInt32();
4539                 else if (aKey == "tileWidth")
4540                     nTileWidth = aValue.toInt64();
4541                 else if (aKey == "tileHeight")
4542                     nTileHeight = aValue.toInt64();
4543             }
4544             while (nParamIndex >= 0);
4545         }
4546 
4547         return convertOString(pDoc->getCellCursor(nOutputWidth, nOutputHeight, nTileWidth, nTileHeight));
4548     }
4549     else if (aCommand.startsWith(aFontSubset))
4550     {
4551         return getFontSubset(OString(pCommand + aFontSubset.getLength()));
4552     }
4553     else
4554     {
4555         SetLastExceptionMsg("Unknown command, no values returned");
4556         return nullptr;
4557     }
4558 }
4559 
doc_setClientZoom(LibreOfficeKitDocument * pThis,int nTilePixelWidth,int nTilePixelHeight,int nTileTwipWidth,int nTileTwipHeight)4560 static void doc_setClientZoom(LibreOfficeKitDocument* pThis, int nTilePixelWidth, int nTilePixelHeight,
4561         int nTileTwipWidth, int nTileTwipHeight)
4562 {
4563     comphelper::ProfileZone aZone("doc_setClientZoom");
4564 
4565     SolarMutexGuard aGuard;
4566     SetLastExceptionMsg();
4567 
4568     ITiledRenderable* pDoc = getTiledRenderable(pThis);
4569     if (!pDoc)
4570     {
4571         SetLastExceptionMsg("Document doesn't support tiled rendering");
4572         return;
4573     }
4574 
4575     pDoc->setClientZoom(nTilePixelWidth, nTilePixelHeight, nTileTwipWidth, nTileTwipHeight);
4576 }
4577 
doc_setClientVisibleArea(LibreOfficeKitDocument * pThis,int nX,int nY,int nWidth,int nHeight)4578 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight)
4579 {
4580     comphelper::ProfileZone aZone("doc_setClientVisibleArea");
4581 
4582     SolarMutexGuard aGuard;
4583     SetLastExceptionMsg();
4584 
4585     ITiledRenderable* pDoc = getTiledRenderable(pThis);
4586     if (!pDoc)
4587     {
4588         SetLastExceptionMsg("Document doesn't support tiled rendering");
4589         return;
4590     }
4591 
4592     tools::Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
4593     pDoc->setClientVisibleArea(aRectangle);
4594 }
4595 
doc_setOutlineState(LibreOfficeKitDocument * pThis,bool bColumn,int nLevel,int nIndex,bool bHidden)4596 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden)
4597 {
4598     comphelper::ProfileZone aZone("doc_setOutlineState");
4599 
4600     SolarMutexGuard aGuard;
4601     SetLastExceptionMsg();
4602 
4603     ITiledRenderable* pDoc = getTiledRenderable(pThis);
4604     if (!pDoc)
4605     {
4606         SetLastExceptionMsg("Document doesn't support tiled rendering");
4607         return;
4608     }
4609 
4610     pDoc->setOutlineState(bColumn, nLevel, nIndex, bHidden);
4611 }
4612 
doc_createViewWithOptions(LibreOfficeKitDocument * pThis,const char * pOptions)4613 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis,
4614                                      const char* pOptions)
4615 {
4616     comphelper::ProfileZone aZone("doc_createView");
4617 
4618     SolarMutexGuard aGuard;
4619     SetLastExceptionMsg();
4620 
4621     OUString aOptions = getUString(pOptions);
4622     const OUString aLanguage = extractParameter(aOptions, "Language");
4623 
4624     if (!aLanguage.isEmpty())
4625     {
4626         // Set the LOK language tag, used for dialog tunneling.
4627         comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage));
4628     }
4629 
4630     int nId = SfxLokHelper::createView();
4631 
4632 #ifdef IOS
4633     (void) pThis;
4634 #else
4635     forceSetClipboardForCurrentView(pThis);
4636 #endif
4637 
4638     return nId;
4639 }
4640 
doc_createView(LibreOfficeKitDocument * pThis)4641 static int doc_createView(LibreOfficeKitDocument* pThis)
4642 {
4643     return doc_createViewWithOptions(pThis, nullptr); // No options.
4644 }
4645 
doc_destroyView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument *,int nId)4646 static void doc_destroyView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId)
4647 {
4648     comphelper::ProfileZone aZone("doc_destroyView");
4649 
4650     SolarMutexGuard aGuard;
4651     SetLastExceptionMsg();
4652 
4653     LOKClipboardFactory::releaseClipboardForView(nId);
4654 
4655     SfxLokHelper::destroyView(nId);
4656 }
4657 
doc_setView(LibreOfficeKitDocument * pThis,int nId)4658 static void doc_setView(LibreOfficeKitDocument* pThis, int nId)
4659 {
4660     comphelper::ProfileZone aZone("doc_setView");
4661 
4662     SolarMutexGuard aGuard;
4663     SetLastExceptionMsg();
4664 
4665     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4666     const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nId);
4667     if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4668         handlerIt->second->disableCallbacks();
4669 
4670     try
4671     {
4672         SfxLokHelper::setView(nId);
4673     }
4674     catch (const std::exception&)
4675     {
4676     }
4677 
4678     if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4679         handlerIt->second->enableCallbacks();
4680 }
4681 
doc_getView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument *)4682 static int doc_getView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
4683 {
4684     comphelper::ProfileZone aZone("doc_getView");
4685 
4686     SolarMutexGuard aGuard;
4687     SetLastExceptionMsg();
4688 
4689     return SfxLokHelper::getView();
4690 }
4691 
doc_getViewsCount(SAL_UNUSED_PARAMETER LibreOfficeKitDocument *)4692 static int doc_getViewsCount(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
4693 {
4694     comphelper::ProfileZone aZone("doc_getViewsCount");
4695 
4696     SolarMutexGuard aGuard;
4697     SetLastExceptionMsg();
4698 
4699     return SfxLokHelper::getViewsCount();
4700 }
4701 
doc_getViewIds(SAL_UNUSED_PARAMETER LibreOfficeKitDocument *,int * pArray,size_t nSize)4702 static bool doc_getViewIds(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int* pArray, size_t nSize)
4703 {
4704     comphelper::ProfileZone aZone("doc_getViewsIds");
4705 
4706     SolarMutexGuard aGuard;
4707     SetLastExceptionMsg();
4708 
4709     return SfxLokHelper::getViewIds(pArray, nSize);
4710 }
4711 
doc_setViewLanguage(SAL_UNUSED_PARAMETER LibreOfficeKitDocument *,int nId,const char * language)4712 static void doc_setViewLanguage(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId, const char* language)
4713 {
4714     comphelper::ProfileZone aZone("doc_setViewLanguage");
4715 
4716     SolarMutexGuard aGuard;
4717     SetLastExceptionMsg();
4718 
4719     SfxLokHelper::setViewLanguage(nId, OStringToOUString(language, RTL_TEXTENCODING_UTF8));
4720 }
4721 
4722 
4723 
doc_renderFont(LibreOfficeKitDocument * pThis,const char * pFontName,const char * pChar,int * pFontWidth,int * pFontHeight)4724 unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
4725                               const char* pFontName,
4726                               const char* pChar,
4727                               int* pFontWidth,
4728                               int* pFontHeight)
4729 {
4730     return doc_renderFontOrientation(pThis, pFontName, pChar, pFontWidth, pFontHeight, 0);
4731 }
4732 
doc_renderFontOrientation(SAL_UNUSED_PARAMETER LibreOfficeKitDocument *,const char * pFontName,const char * pChar,int * pFontWidth,int * pFontHeight,int pOrientation)4733 unsigned char* doc_renderFontOrientation(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/,
4734                               const char* pFontName,
4735                               const char* pChar,
4736                               int* pFontWidth,
4737                               int* pFontHeight,
4738                               int pOrientation)
4739 {
4740     comphelper::ProfileZone aZone("doc_renderFont");
4741 
4742     SolarMutexGuard aGuard;
4743     SetLastExceptionMsg();
4744 
4745     OString aSearchedFontName(pFontName);
4746     OUString aText(OStringToOUString(pChar, RTL_TEXTENCODING_UTF8));
4747     SfxObjectShell* pDocSh = SfxObjectShell::Current();
4748     const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
4749         pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
4750     const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
4751 
4752     const int nDefaultFontSize = 25;
4753 
4754     if ( pList )
4755     {
4756         sal_uInt16 nFontCount = pList->GetFontNameCount();
4757         for (sal_uInt16 i = 0; i < nFontCount; ++i)
4758         {
4759             const FontMetric& rFontMetric = pList->GetFontName(i);
4760             const OUString& aFontName = rFontMetric.GetFamilyName();
4761             if (aSearchedFontName != aFontName.toUtf8())
4762                 continue;
4763 
4764             if (aText.isEmpty())
4765                 aText = rFontMetric.GetFamilyName();
4766 
4767             auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
4768             ::tools::Rectangle aRect;
4769             vcl::Font aFont(rFontMetric);
4770             aFont.SetFontSize(Size(0, nDefaultFontSize));
4771             aFont.SetOrientation(pOrientation);
4772             aDevice->SetFont(aFont);
4773             aDevice->GetTextBoundRect(aRect, aText);
4774             if (aRect.IsEmpty())
4775                 break;
4776 
4777             int nFontWidth = aRect.BottomRight().X() + 1;
4778             int nFontHeight = aRect.BottomRight().Y() + 1;
4779 
4780             if (!(nFontWidth > 0 && nFontHeight > 0))
4781                 break;
4782 
4783             if (*pFontWidth > 0 && *pFontHeight > 0)
4784             {
4785                 double fScaleX = *pFontWidth / static_cast<double>(nFontWidth) / 1.5;
4786                 double fScaleY = *pFontHeight / static_cast<double>(nFontHeight) / 1.5;
4787 
4788                 double fScale = std::min(fScaleX, fScaleY);
4789 
4790                 if (fScale >= 1.0)
4791                 {
4792                     int nFontSize = fScale * nDefaultFontSize;
4793                     aFont.SetFontSize(Size(0, nFontSize));
4794                     aDevice->SetFont(aFont);
4795                 }
4796 
4797                 aRect = tools::Rectangle(0, 0, *pFontWidth, *pFontHeight);
4798 
4799                 nFontWidth = *pFontWidth;
4800                 nFontHeight = *pFontHeight;
4801 
4802             }
4803 
4804             unsigned char* pBuffer = static_cast<unsigned char*>(malloc(4 * nFontWidth * nFontHeight));
4805             if (!pBuffer)
4806                 break;
4807 
4808             memset(pBuffer, 0, nFontWidth * nFontHeight * 4);
4809             aDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
4810             aDevice->SetOutputSizePixelScaleOffsetAndBuffer(
4811                         Size(nFontWidth, nFontHeight), Fraction(1.0), Point(),
4812                         pBuffer);
4813 
4814             if (*pFontWidth > 0 && *pFontHeight > 0)
4815             {
4816                 DrawTextFlags const nStyle =
4817                         DrawTextFlags::Center
4818                         | DrawTextFlags::VCenter
4819                         | DrawTextFlags::Bottom
4820                         | DrawTextFlags::MultiLine
4821                         | DrawTextFlags::WordBreak;// | DrawTextFlags::WordBreakHyphenation ;
4822 
4823                 aDevice->DrawText(aRect, aText, nStyle);
4824             }
4825             else
4826             {
4827                 *pFontWidth = nFontWidth;
4828                 *pFontHeight = nFontHeight;
4829 
4830                 aDevice->DrawText(Point(0,0), aText);
4831             }
4832 
4833 
4834             return pBuffer;
4835         }
4836     }
4837     return nullptr;
4838 }
4839 
4840 
doc_paintWindow(LibreOfficeKitDocument * pThis,unsigned nLOKWindowId,unsigned char * pBuffer,const int nX,const int nY,const int nWidth,const int nHeight)4841 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
4842                             unsigned char* pBuffer,
4843                             const int nX, const int nY,
4844                             const int nWidth, const int nHeight)
4845 {
4846     doc_paintWindowDPI(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, 1.0);
4847 }
4848 
doc_paintWindowDPI(LibreOfficeKitDocument * pThis,unsigned nLOKWindowId,unsigned char * pBuffer,const int nX,const int nY,const int nWidth,const int nHeight,const double fDPIScale)4849 static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
4850                                unsigned char* pBuffer,
4851                                const int nX, const int nY,
4852                                const int nWidth, const int nHeight,
4853                                const double fDPIScale)
4854 {
4855     doc_paintWindowForView(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, fDPIScale, -1);
4856 }
4857 
doc_paintWindowForView(LibreOfficeKitDocument * pThis,unsigned nLOKWindowId,unsigned char * pBuffer,const int nX,const int nY,const int nWidth,const int nHeight,const double fDPIScale,int viewId)4858 static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
4859                                    unsigned char* pBuffer, const int nX, const int nY,
4860                                    const int nWidth, const int nHeight,
4861                                    const double fDPIScale, int viewId)
4862 {
4863     comphelper::ProfileZone aZone("doc_paintWindowDPI");
4864 
4865     SolarMutexGuard aGuard;
4866     SetLastExceptionMsg();
4867 
4868     VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4869     if (!pWindow)
4870     {
4871         SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
4872         return;
4873     }
4874 
4875     // Used to avoid work in setView if set.
4876     comphelper::LibreOfficeKit::setDialogPainting(true);
4877 
4878     if (viewId >= 0)
4879         doc_setView(pThis, viewId);
4880 
4881     // Setup cairo (or CoreGraphics, in the iOS case) to draw with the changed DPI scale (and return
4882     // back to 1.0 when the painting finishes)
4883     comphelper::ScopeGuard dpiScaleGuard([]() { comphelper::LibreOfficeKit::setDPIScale(1.0); });
4884     comphelper::LibreOfficeKit::setDPIScale(fDPIScale);
4885 
4886 #if defined(IOS)
4887 
4888     CGContextRef cgc = CGBitmapContextCreate(pBuffer, nWidth, nHeight, 8, nWidth*4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little);
4889 
4890     CGContextTranslateCTM(cgc, 0, nHeight);
4891     CGContextScaleCTM(cgc, fDPIScale, -fDPIScale);
4892 
4893     SystemGraphicsData aData;
4894     aData.rCGContext = cgc;
4895 
4896     ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::DEFAULT);
4897     pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
4898 
4899     pDevice->SetOutputSizePixel(Size(nWidth, nHeight));
4900 
4901     MapMode aMapMode(pDevice->GetMapMode());
4902     aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale)));
4903     pDevice->SetMapMode(aMapMode);
4904 
4905     pWindow->PaintToDevice(pDevice.get(), Point(0, 0), Size());
4906 
4907     CGContextRelease(cgc);
4908 
4909 #else
4910 
4911     ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::DEFAULT);
4912     pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
4913 
4914     pDevice->SetOutputSizePixelScaleOffsetAndBuffer(Size(nWidth, nHeight), Fraction(1.0), Point(), pBuffer);
4915 
4916     MapMode aMapMode(pDevice->GetMapMode());
4917     aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale)));
4918     pDevice->SetMapMode(aMapMode);
4919 
4920     pWindow->PaintToDevice(pDevice.get(), Point(0, 0), Size());
4921 #endif
4922 
4923     comphelper::LibreOfficeKit::setDialogPainting(false);
4924 }
4925 
doc_postWindow(LibreOfficeKitDocument *,unsigned nLOKWindowId,int nAction,const char * pData)4926 static void doc_postWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nAction, const char* pData)
4927 {
4928     comphelper::ProfileZone aZone("doc_postWindow");
4929 
4930     SolarMutexGuard aGuard;
4931     SetLastExceptionMsg();
4932 
4933     VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4934     if (!pWindow)
4935     {
4936         SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
4937         return;
4938     }
4939 
4940     if (nAction == LOK_WINDOW_CLOSE)
4941     {
4942         if (Dialog* pDialog = dynamic_cast<Dialog*>(pWindow.get()))
4943             pDialog->Close();
4944         else if (FloatingWindow* pFloatWin = dynamic_cast<FloatingWindow*>(pWindow.get()))
4945             pFloatWin->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll);
4946     }
4947     else if (nAction == LOK_WINDOW_PASTE)
4948     {
4949         OUString aMimeType;
4950         css::uno::Sequence<sal_Int8> aData;
4951         std::vector<beans::PropertyValue> aArgs(jsonToPropertyValuesVector(pData));
4952         {
4953             aArgs.size() == 2 &&
4954             aArgs[0].Name == "MimeType" && (aArgs[0].Value >>= aMimeType) &&
4955             aArgs[1].Name == "Data" && (aArgs[1].Value >>= aData);
4956         }
4957 
4958         if (!aMimeType.isEmpty() && aData.hasElements())
4959         {
4960             uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(aMimeType, aData));
4961             uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(new LOKClipboard);
4962             xClipboard->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
4963             pWindow->SetClipboard(xClipboard);
4964 
4965             KeyEvent aEvent(0, KEY_PASTE, 0);
4966             Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
4967         }
4968         else
4969             SetLastExceptionMsg("Window command 'paste': wrong parameters.");
4970     }
4971 }
4972 
4973 // CERTIFICATE AND DOCUMENT SIGNING
doc_insertCertificate(LibreOfficeKitDocument * pThis,const unsigned char * pCertificateBinary,const int nCertificateBinarySize,const unsigned char * pPrivateKeyBinary,const int nPrivateKeySize)4974 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
4975                                   const unsigned char* pCertificateBinary, const int nCertificateBinarySize,
4976                                   const unsigned char* pPrivateKeyBinary, const int nPrivateKeySize)
4977 {
4978     comphelper::ProfileZone aZone("doc_insertCertificate");
4979 
4980     if (!xContext.is())
4981         return false;
4982 
4983     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4984 
4985     if (!pDocument->mxComponent.is())
4986         return false;
4987 
4988     SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
4989     if (!pBaseModel)
4990         return false;
4991 
4992     SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
4993 
4994     if (!pObjectShell)
4995         return false;
4996 
4997     uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
4998     uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
4999     if (!xSecurityContext.is())
5000         return false;
5001 
5002     uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
5003     uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
5004 
5005     if (!xCertificateCreator.is())
5006         return false;
5007 
5008     uno::Sequence<sal_Int8> aCertificateSequence;
5009 
5010     std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
5011     std::string aCertificateBase64String = extractCertificate(aCertificateString);
5012     if (!aCertificateBase64String.empty())
5013     {
5014         OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
5015         comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
5016     }
5017     else
5018     {
5019         aCertificateSequence.realloc(nCertificateBinarySize);
5020         std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
5021     }
5022 
5023     uno::Sequence<sal_Int8> aPrivateKeySequence;
5024     std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeySize);
5025     std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
5026     if (!aPrivateKeyBase64String.empty())
5027     {
5028         OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String.c_str());
5029         comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
5030     }
5031     else
5032     {
5033         aPrivateKeySequence.realloc(nPrivateKeySize);
5034         std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeySize, aPrivateKeySequence.begin());
5035     }
5036 
5037     uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
5038 
5039     if (!xCertificate.is())
5040         return false;
5041 
5042     SolarMutexGuard aGuard;
5043 
5044     return pObjectShell->SignDocumentContentUsingCertificate(xCertificate);
5045 }
5046 
doc_addCertificate(LibreOfficeKitDocument * pThis,const unsigned char * pCertificateBinary,const int nCertificateBinarySize)5047 static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
5048                                   const unsigned char* pCertificateBinary, const int nCertificateBinarySize)
5049 {
5050     comphelper::ProfileZone aZone("doc_addCertificate");
5051 
5052     if (!xContext.is())
5053         return false;
5054 
5055     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5056 
5057     if (!pDocument->mxComponent.is())
5058         return false;
5059 
5060     SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
5061     if (!pBaseModel)
5062         return false;
5063 
5064     SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
5065 
5066     if (!pObjectShell)
5067         return false;
5068 
5069     uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
5070     uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
5071     if (!xSecurityContext.is())
5072         return false;
5073 
5074     uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
5075     uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
5076 
5077     if (!xCertificateCreator.is())
5078         return false;
5079 
5080     uno::Sequence<sal_Int8> aCertificateSequence;
5081 
5082     std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
5083     std::string aCertificateBase64String = extractCertificate(aCertificateString);
5084     if (!aCertificateBase64String.empty())
5085     {
5086         OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
5087         comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
5088     }
5089     else
5090     {
5091         aCertificateSequence.realloc(nCertificateBinarySize);
5092         std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
5093     }
5094 
5095     uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->addDERCertificateToTheDatabase(aCertificateSequence, "TCu,Cu,Tu");
5096 
5097     if (!xCertificate.is())
5098         return false;
5099 
5100     SAL_INFO("lok", "Certificate Added = IssuerName: " << xCertificate->getIssuerName() << " SubjectName: " << xCertificate->getSubjectName());
5101 
5102     return true;
5103 }
5104 
doc_getSignatureState(LibreOfficeKitDocument * pThis)5105 static int doc_getSignatureState(LibreOfficeKitDocument* pThis)
5106 {
5107     comphelper::ProfileZone aZone("doc_getSignatureState");
5108 
5109     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5110 
5111     if (!pDocument->mxComponent.is())
5112         return int(SignatureState::UNKNOWN);
5113 
5114     SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
5115     if (!pBaseModel)
5116         return int(SignatureState::UNKNOWN);
5117 
5118     SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
5119     if (!pObjectShell)
5120         return int(SignatureState::UNKNOWN);
5121 
5122     SolarMutexGuard aGuard;
5123 
5124     pObjectShell->RecheckSignature(false);
5125 
5126     return int(pObjectShell->GetDocumentSignatureState());
5127 }
5128 
doc_resizeWindow(LibreOfficeKitDocument *,unsigned nLOKWindowId,const int nWidth,const int nHeight)5129 static void doc_resizeWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId,
5130                              const int nWidth, const int nHeight)
5131 {
5132     SolarMutexGuard aGuard;
5133     if (gImpl)
5134         gImpl->maLastExceptionMsg.clear();
5135 
5136     VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5137     if (!pWindow)
5138     {
5139         gImpl->maLastExceptionMsg = "Document doesn't support dialog resizing, or window not found.";
5140         return;
5141     }
5142 
5143     pWindow->SetSizePixel(Size(nWidth, nHeight));
5144 }
5145 
lo_getError(LibreOfficeKit * pThis)5146 static char* lo_getError (LibreOfficeKit *pThis)
5147 {
5148     comphelper::ProfileZone aZone("lo_getError");
5149 
5150     SolarMutexGuard aGuard;
5151 
5152     LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
5153     return convertOUString(pLib->maLastExceptionMsg);
5154 }
5155 
lo_freeError(char * pFree)5156 static void lo_freeError(char* pFree)
5157 {
5158     free(pFree);
5159 }
5160 
lo_getFilterTypes(LibreOfficeKit * pThis)5161 static char* lo_getFilterTypes(LibreOfficeKit* pThis)
5162 {
5163     SolarMutexGuard aGuard;
5164     SetLastExceptionMsg();
5165 
5166     LibLibreOffice_Impl* pImpl = static_cast<LibLibreOffice_Impl*>(pThis);
5167 
5168     if (!xSFactory.is())
5169         xSFactory = comphelper::getProcessServiceFactory();
5170 
5171     if (!xSFactory.is())
5172     {
5173         pImpl->maLastExceptionMsg = "Service factory is not available";
5174         return nullptr;
5175     }
5176 
5177     uno::Reference<container::XNameAccess> xTypeDetection(xSFactory->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY);
5178     const uno::Sequence<OUString> aTypes = xTypeDetection->getElementNames();
5179     boost::property_tree::ptree aTree;
5180     for (const OUString& rType : aTypes)
5181     {
5182         uno::Sequence<beans::PropertyValue> aValues;
5183         if (xTypeDetection->getByName(rType) >>= aValues)
5184         {
5185             auto it = std::find_if(aValues.begin(), aValues.end(), [](const beans::PropertyValue& rValue) { return rValue.Name == "MediaType"; });
5186             OUString aValue;
5187             if (it != aValues.end() && (it->Value >>= aValue) && !aValue.isEmpty())
5188             {
5189                 boost::property_tree::ptree aChild;
5190                 aChild.put("MediaType", aValue.toUtf8());
5191                 aTree.add_child(rType.toUtf8().getStr(), aChild);
5192             }
5193         }
5194     }
5195     std::stringstream aStream;
5196     boost::property_tree::write_json(aStream, aTree);
5197     return strdup(aStream.str().c_str());
5198 }
5199 
lo_setOptionalFeatures(LibreOfficeKit * pThis,unsigned long long const features)5200 static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long const features)
5201 {
5202     comphelper::ProfileZone aZone("lo_setOptionalFeatures");
5203 
5204     SolarMutexGuard aGuard;
5205     SetLastExceptionMsg();
5206 
5207     LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis);
5208     pLib->mOptionalFeatures = features;
5209     if (features & LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK)
5210         comphelper::LibreOfficeKit::setPartInInvalidation(true);
5211     if (features & LOK_FEATURE_NO_TILED_ANNOTATIONS)
5212         comphelper::LibreOfficeKit::setTiledAnnotations(false);
5213     if (features & LOK_FEATURE_RANGE_HEADERS)
5214         comphelper::LibreOfficeKit::setRangeHeaders(true);
5215     if (features & LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK)
5216         comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
5217 }
5218 
lo_setDocumentPassword(LibreOfficeKit * pThis,const char * pURL,const char * pPassword)5219 static void lo_setDocumentPassword(LibreOfficeKit* pThis,
5220         const char* pURL, const char* pPassword)
5221 {
5222     comphelper::ProfileZone aZone("lo_setDocumentPassword");
5223 
5224     SolarMutexGuard aGuard;
5225     SetLastExceptionMsg();
5226 
5227     assert(pThis);
5228     assert(pURL);
5229     LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis);
5230     assert(pLib->mInteractionMap.find(OString(pURL)) != pLib->mInteractionMap.end());
5231     pLib->mInteractionMap.find(OString(pURL))->second->SetPassword(pPassword);
5232 }
5233 
lo_getVersionInfo(SAL_UNUSED_PARAMETER LibreOfficeKit *)5234 static char* lo_getVersionInfo(SAL_UNUSED_PARAMETER LibreOfficeKit* /*pThis*/)
5235 {
5236     SetLastExceptionMsg();
5237     const OUString sVersionStrTemplate(
5238         "{ "
5239         "\"ProductName\": \"%PRODUCTNAME\", "
5240         "\"ProductVersion\": \"%PRODUCTVERSION\", "
5241         "\"ProductExtension\": \"%PRODUCTEXTENSION\", "
5242         "\"BuildId\": \"%BUILDID\" "
5243         "}"
5244     );
5245     return convertOUString(ReplaceStringHookProc(sVersionStrTemplate));
5246 }
5247 
force_c_locale()5248 static void force_c_locale()
5249 {
5250     // force locale (and resource files loaded) to en-US
5251     OUString aLangISO("en-US");
5252     SvtSysLocaleOptions aLocalOptions;
5253     aLocalOptions.SetLocaleConfigString(aLangISO);
5254     aLocalOptions.SetUILocaleConfigString(aLangISO);
5255 }
5256 
aBasicErrorFunc(const OUString & rError,const OUString & rAction)5257 static void aBasicErrorFunc(const OUString& rError, const OUString& rAction)
5258 {
5259     OString aBuffer = "Unexpected dialog: " +
5260         OUStringToOString(rAction, RTL_TEXTENCODING_ASCII_US) +
5261         " Error: " +
5262         OUStringToOString(rError, RTL_TEXTENCODING_ASCII_US);
5263 
5264     fprintf(stderr, "Unexpected basic error dialog '%s'\n", aBuffer.getStr());
5265 }
5266 
initialize_uno(const OUString & aAppProgramURL)5267 static bool initialize_uno(const OUString& aAppProgramURL)
5268 {
5269 #ifdef IOS
5270     // For iOS we already hardcode the inifile as "rc" in the .app directory.
5271     rtl::Bootstrap::setIniFilename(aAppProgramURL + "/" SAL_CONFIGFILE("fundamental"));
5272     xContext = cppu::defaultBootstrap_InitialComponentContext(aAppProgramURL + "/rc");
5273 #elif defined MACOSX
5274     rtl::Bootstrap::setIniFilename(aAppProgramURL + "/../Resources/" SAL_CONFIGFILE("soffice"));
5275     xContext = cppu::defaultBootstrap_InitialComponentContext();
5276 #else
5277     rtl::Bootstrap::setIniFilename(aAppProgramURL + "/" SAL_CONFIGFILE("soffice"));
5278     xContext = cppu::defaultBootstrap_InitialComponentContext();
5279 #endif
5280 
5281     if (!xContext.is())
5282     {
5283         SetLastExceptionMsg("XComponentContext could not be created");
5284         SAL_INFO("lok", "XComponentContext could not be created");
5285         return false;
5286     }
5287 
5288     xFactory = xContext->getServiceManager();
5289     if (!xFactory.is())
5290     {
5291         SetLastExceptionMsg("XMultiComponentFactory could not be created");
5292         SAL_INFO("lok", "XMultiComponentFactory could not be created");
5293         return false;
5294     }
5295 
5296     xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
5297     comphelper::setProcessServiceFactory(xSFactory);
5298 
5299     SAL_INFO("lok", "Uno initialized  - " <<  xContext.is());
5300 
5301     // set UserInstallation to user profile dir in test/user-template
5302 //    rtl::Bootstrap aDefaultVars;
5303 //    aDefaultVars.set(OUString("UserInstallation"), aAppProgramURL + "../registry" );
5304     // configmgr setup ?
5305 
5306     return true;
5307 }
5308 
5309 // pre-unipoll version.
lo_startmain(void *)5310 static void lo_startmain(void*)
5311 {
5312     osl_setThreadName("lo_startmain");
5313 
5314     if (comphelper::SolarMutex::get())
5315         Application::GetSolarMutex().tryToAcquire();
5316 
5317     Application::UpdateMainThread();
5318 
5319     soffice_main();
5320 
5321     Application::ReleaseSolarMutex();
5322 }
5323 
5324 // unipoll version.
lo_runLoop(LibreOfficeKit *,LibreOfficeKitPollCallback pPollCallback,LibreOfficeKitWakeCallback pWakeCallback,void * pData)5325 static void lo_runLoop(LibreOfficeKit* /*pThis*/,
5326                        LibreOfficeKitPollCallback pPollCallback,
5327                        LibreOfficeKitWakeCallback pWakeCallback,
5328                        void* pData)
5329 {
5330 #if defined(IOS) || defined(ANDROID)
5331     Application::GetSolarMutex().acquire();
5332 #endif
5333 
5334     {
5335         SolarMutexGuard aGuard;
5336 
5337         vcl::lok::registerPollCallbacks(pPollCallback, pWakeCallback, pData);
5338         Application::UpdateMainThread();
5339         soffice_main();
5340     }
5341 #if defined(IOS) || defined(ANDROID)
5342     vcl::lok::unregisterPollCallbacks();
5343     Application::ReleaseSolarMutex();
5344 #endif
5345 }
5346 
5347 static bool bInitialized = false;
5348 
lo_status_indicator_callback(void * data,comphelper::LibreOfficeKit::statusIndicatorCallbackType type,int percent)5349 static void lo_status_indicator_callback(void *data, comphelper::LibreOfficeKit::statusIndicatorCallbackType type, int percent)
5350 {
5351     LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(data);
5352 
5353     if (!pLib->mpCallback)
5354         return;
5355 
5356     switch (type)
5357     {
5358     case comphelper::LibreOfficeKit::statusIndicatorCallbackType::Start:
5359         pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_START, nullptr, pLib->mpCallbackData);
5360         break;
5361     case comphelper::LibreOfficeKit::statusIndicatorCallbackType::SetValue:
5362         pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE,
5363             OUString(OUString::number(percent)).toUtf8().getStr(), pLib->mpCallbackData);
5364         break;
5365     case comphelper::LibreOfficeKit::statusIndicatorCallbackType::Finish:
5366         pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_FINISH, nullptr, pLib->mpCallbackData);
5367         break;
5368     }
5369 }
5370 
5371 /// Used only by LibreOfficeKit when used by Online to pre-initialize
preloadData()5372 static void preloadData()
5373 {
5374     comphelper::ProfileZone aZone("preload data");
5375 
5376     // Create user profile in the temp directory for loading the dictionaries
5377     OUString sUserPath;
5378     rtl::Bootstrap::get("UserInstallation", sUserPath);
5379     utl::TempFile aTempDir(nullptr, true);
5380     aTempDir.EnableKillingFile();
5381     rtl::Bootstrap::set("UserInstallation", aTempDir.GetURL());
5382 
5383     // Register the bundled extensions
5384     desktop::Desktop::SynchronizeExtensionRepositories(true);
5385     bool bAbort = desktop::Desktop::CheckExtensionDependencies();
5386     if(bAbort)
5387         std::cerr << "CheckExtensionDependencies failed" << std::endl;
5388 
5389     // preload all available dictionaries
5390     css::uno::Reference<css::linguistic2::XLinguServiceManager> xLngSvcMgr =
5391         css::linguistic2::LinguServiceManager::create(comphelper::getProcessComponentContext());
5392     css::uno::Reference<linguistic2::XSpellChecker> xSpellChecker(xLngSvcMgr->getSpellChecker());
5393 
5394     std::cerr << "Preloading dictionaries: ";
5395     css::uno::Reference<linguistic2::XSupportedLocales> xSpellLocales(xSpellChecker, css::uno::UNO_QUERY_THROW);
5396     uno::Sequence< css::lang::Locale > aLocales = xSpellLocales->getLocales();
5397     for (auto &it : aLocales)
5398     {
5399         std::cerr << it.Language << "_" << it.Country << " ";
5400         css::beans::PropertyValues aNone;
5401         xSpellChecker->isValid("forcefed", it, aNone);
5402     }
5403     std::cerr << "\n";
5404 
5405     // preload all available thesauri
5406     css::uno::Reference<linguistic2::XThesaurus> xThesaurus(xLngSvcMgr->getThesaurus());
5407     css::uno::Reference<linguistic2::XSupportedLocales> xThesLocales(xSpellChecker, css::uno::UNO_QUERY_THROW);
5408     aLocales = xThesLocales->getLocales();
5409     std::cerr << "Preloading thesauri: ";
5410     for (auto &it : aLocales)
5411     {
5412         std::cerr << it.Language << "_" << it.Country << " ";
5413         css::beans::PropertyValues aNone;
5414         xThesaurus->queryMeanings("forcefed", it, aNone);
5415     }
5416     std::cerr << "\n";
5417 
5418     css::uno::Reference< css::ui::XAcceleratorConfiguration > xGlobalCfg = css::ui::GlobalAcceleratorConfiguration::create(
5419         comphelper::getProcessComponentContext());
5420     xGlobalCfg->getAllKeyEvents();
5421 
5422     std::cerr << "Preload icons\n";
5423     ImageTree &images = ImageTree::get();
5424     images.getImageUrl("forcefed.png", "style", "FO_oo");
5425 
5426     std::cerr << "Preload languages\n";
5427 
5428     // force load language singleton
5429     SvtLanguageTable::HasLanguageType(LANGUAGE_SYSTEM);
5430     (void)LanguageTag::isValidBcp47("foo", nullptr);
5431 
5432     std::cerr << "Preload fonts\n";
5433 
5434     // Initialize fonts.
5435     css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
5436     if (xLangSrv.is())
5437     {
5438         css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
5439         if (xSpell.is())
5440             aLocales = xSpell->getLocales();
5441     }
5442 
5443     for (const auto& aLocale : std::as_const(aLocales))
5444     {
5445         //TODO: Add more types and cache more aggressively. For now this initializes the fontcache.
5446         using namespace ::com::sun::star::i18n::ScriptType;
5447         LanguageType nLang;
5448         nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), LATIN);
5449         OutputDevice::GetDefaultFont(DefaultFontType::LATIN_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
5450         nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), ASIAN);
5451         OutputDevice::GetDefaultFont(DefaultFontType::CJK_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
5452         nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), COMPLEX);
5453         OutputDevice::GetDefaultFont(DefaultFontType::CTL_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
5454     }
5455 
5456     // Set user profile's path back to the original one
5457     rtl::Bootstrap::set("UserInstallation", sUserPath);
5458 }
5459 
5460 class ProfileZoneDumper : public AutoTimer
5461 {
5462     static const int dumpTimeoutMS = 5000;
5463 public:
ProfileZoneDumper()5464     ProfileZoneDumper() : AutoTimer( "zone dumper" )
5465     {
5466         SetTimeout(dumpTimeoutMS);
5467         Start();
5468     }
Invoke()5469     virtual void Invoke() override
5470     {
5471         const css::uno::Sequence<OUString> aEvents =
5472             comphelper::ProfileRecording::getRecordingAndClear();
5473         OStringBuffer aOutput;
5474         for (const auto &s : aEvents)
5475         {
5476             aOutput.append(OUStringToOString(s, RTL_TEXTENCODING_UTF8));
5477             aOutput.append("\n");
5478         }
5479         OString aChunk = aOutput.makeStringAndClear();
5480         if (gImpl && gImpl->mpCallback)
5481             gImpl->mpCallback(LOK_CALLBACK_PROFILE_FRAME, aChunk.getStr(), gImpl->mpCallbackData);
5482     }
5483 };
5484 
lo_initialize(LibreOfficeKit * pThis,const char * pAppPath,const char * pUserProfileUrl)5485 static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const char* pUserProfileUrl)
5486 {
5487     enum {
5488         PRE_INIT,     // setup shared data in master process
5489         SECOND_INIT,  // complete init. after fork
5490         FULL_INIT     // do a standard complete init.
5491     } eStage;
5492 
5493     // Did we do a pre-initialize
5494     static bool bPreInited = false;
5495     static bool bUnipoll = false;
5496     static bool bProfileZones = false;
5497 
5498     { // cf. string lifetime for preinit
5499         std::vector<OUString> aOpts;
5500 
5501         // ':' delimited options - avoiding ABI change for new parameters
5502         const char *pOptions = getenv("SAL_LOK_OPTIONS");
5503         if (pOptions)
5504             aOpts = comphelper::string::split(OUString(pOptions, strlen(pOptions), RTL_TEXTENCODING_UTF8), ':');
5505         for (const auto &it : aOpts)
5506         {
5507             if (it == "unipoll")
5508                 bUnipoll = true;
5509             else if (it == "profile_events")
5510                 bProfileZones = true;
5511             else if (it == "sc_no_grid_bg")
5512                 comphelper::LibreOfficeKit::setCompatFlag(
5513                     comphelper::LibreOfficeKit::Compat::scNoGridBackground);
5514         }
5515     }
5516 
5517     // What stage are we at ?
5518     if (pThis == nullptr)
5519         eStage = PRE_INIT;
5520     else if (bPreInited)
5521         eStage = SECOND_INIT;
5522     else
5523         eStage = FULL_INIT;
5524 
5525     LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
5526 
5527     if (bInitialized)
5528         return 1;
5529 
5530     // Turn profile zones on early
5531     if (bProfileZones && eStage == SECOND_INIT)
5532     {
5533         comphelper::ProfileRecording::startRecording(true);
5534         new ProfileZoneDumper();
5535     }
5536 
5537     comphelper::ProfileZone aZone("lok-init");
5538 
5539     if (eStage == PRE_INIT)
5540         rtl_alloc_preInit(true);
5541     else if (eStage == SECOND_INIT)
5542         rtl_alloc_preInit(false);
5543 
5544     if (eStage != SECOND_INIT)
5545         comphelper::LibreOfficeKit::setActive();
5546 
5547     if (eStage != PRE_INIT)
5548         comphelper::LibreOfficeKit::setStatusIndicatorCallback(lo_status_indicator_callback, pLib);
5549 
5550     if (pUserProfileUrl && eStage != PRE_INIT)
5551     {
5552         OUString url(
5553             pUserProfileUrl, strlen(pUserProfileUrl), RTL_TEXTENCODING_UTF8);
5554         OUString path;
5555         if (url.startsWithIgnoreAsciiCase("vnd.sun.star.pathname:", &path))
5556         {
5557             OUString url2;
5558             osl::FileBase::RC e = osl::FileBase::getFileURLFromSystemPath(
5559                 path, url2);
5560             if (e == osl::FileBase::E_None)
5561                 url = url2;
5562             else
5563                 SAL_WARN("lok", "resolving <" << url << "> failed with " << +e);
5564         }
5565         rtl::Bootstrap::set("UserInstallation", url);
5566         if (eStage == SECOND_INIT)
5567             utl::Bootstrap::reloadData();
5568     }
5569 
5570     OUString aAppPath;
5571     if (pAppPath)
5572     {
5573         aAppPath = OUString(pAppPath, strlen(pAppPath), RTL_TEXTENCODING_UTF8);
5574     }
5575     else
5576     {
5577 #ifdef ANDROID
5578         aAppPath = OUString::fromUtf8(lo_get_app_data_dir()) + "/program";
5579 #else
5580         // Fun conversion dance back and forth between URLs and system paths...
5581         OUString aAppURL;
5582         ::osl::Module::getUrlFromAddress( reinterpret_cast< oslGenericFunction >(lo_initialize),
5583                                           aAppURL);
5584         osl::FileBase::getSystemPathFromFileURL( aAppURL, aAppPath );
5585 #endif
5586 
5587 #ifdef IOS
5588         // The above gives something like
5589         // "/private/var/containers/Bundle/Application/953AA851-CC15-4C60-A2CB-C2C6F24E6F71/Foo.app/Foo",
5590         // and we want to drop the final component (the binary name).
5591         sal_Int32 lastSlash = aAppPath.lastIndexOf('/');
5592         assert(lastSlash > 0);
5593         aAppPath = aAppPath.copy(0, lastSlash);
5594 #endif
5595     }
5596 
5597     OUString aAppURL;
5598     if (osl::FileBase::getFileURLFromSystemPath(aAppPath, aAppURL) != osl::FileBase::E_None)
5599         return 0;
5600 
5601 #ifdef IOS
5602     // A LibreOffice-using iOS app should have the ICU data file in the app bundle. Initialize ICU
5603     // to use that.
5604     NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
5605 
5606     int fd = open([[bundlePath stringByAppendingPathComponent:@"ICU.dat"] UTF8String], O_RDONLY);
5607     if (fd == -1)
5608         NSLog(@"Could not open ICU data file %s", [[bundlePath stringByAppendingPathComponent:@"ICU.dat"] UTF8String]);
5609     else
5610     {
5611         struct stat st;
5612         if (fstat(fd, &st) == -1)
5613             NSLog(@"fstat on ICU data file failed: %s", strerror(errno));
5614         else
5615         {
5616             void *icudata = mmap(0, (size_t) st.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
5617             if (icudata == MAP_FAILED)
5618                 NSLog(@"mmap failed: %s", strerror(errno));
5619             else
5620             {
5621                 UErrorCode icuStatus = U_ZERO_ERROR;
5622                 udata_setCommonData(icudata, &icuStatus);
5623                 if (U_FAILURE(icuStatus))
5624                     NSLog(@"udata_setCommonData failed");
5625                 else
5626                 {
5627                     // Quick test that ICU works...
5628                     UConverter *cnv = ucnv_open("iso-8859-3", &icuStatus);
5629                     if (U_SUCCESS(icuStatus))
5630                         ucnv_close(cnv);
5631                     else
5632                         NSLog(@"ucnv_open() failed: %s", u_errorName(icuStatus));
5633                 }
5634             }
5635         }
5636         close(fd);
5637     }
5638 #endif
5639 
5640     try
5641     {
5642         if (eStage != SECOND_INIT)
5643         {
5644             SAL_INFO("lok", "Attempting to initialize UNO");
5645 
5646             if (!initialize_uno(aAppURL))
5647                 return false;
5648 
5649             // Force headless -- this is only for bitmap rendering.
5650             rtl::Bootstrap::set("SAL_USE_VCLPLUGIN", "svp");
5651 
5652             // We specifically need to make sure we have the "headless"
5653             // command arg set (various code specifically checks via
5654             // CommandLineArgs):
5655             desktop::Desktop::GetCommandLineArgs().setHeadless();
5656 
5657 #ifdef IOS
5658             if (InitVCL() && [NSThread isMainThread])
5659             {
5660                 static bool bFirstTime = true;
5661                 if (bFirstTime)
5662                 {
5663                     Application::GetSolarMutex().release();
5664                     bFirstTime = false;
5665                 }
5666             }
5667             SfxApplication::GetOrCreate();
5668 #endif
5669 
5670             if (eStage == PRE_INIT)
5671             {
5672                 {
5673                     comphelper::ProfileZone aInit("Init vcl");
5674                     std::cerr << "Init vcl\n";
5675                     InitVCL();
5676                 }
5677 
5678                 // pre-load all graphic libraries.
5679                 GraphicFilter::GetGraphicFilter().preload();
5680 
5681                 // pre-load all component libraries.
5682                 if (!xContext.is())
5683                     throw css::uno::DeploymentException("preInit: XComponentContext is not created");
5684 
5685                 css::uno::Reference< css::uno::XInterface > xService;
5686                 xContext->getValueByName("/singletons/com.sun.star.lang.theServiceManager") >>= xService;
5687                 if (!xService.is())
5688                     throw css::uno::DeploymentException("preInit: XMultiComponentFactory is not created");
5689 
5690                 css::uno::Reference<css::lang::XInitialization> aService(
5691                     xService, css::uno::UNO_QUERY_THROW);
5692 
5693                 // pre-requisites:
5694                 // In order to load implementations and invoke
5695                 // component factory it is required:
5696                 // 1) defaultBootstrap_InitialComponentContext()
5697                 // 2) comphelper::setProcessServiceFactory(xSFactory);
5698                 // 3) InitVCL()
5699                 {
5700                     comphelper::ProfileZone aInit("preload");
5701                     aService->initialize({css::uno::makeAny<OUString>("preload")});
5702                 }
5703                 { // Force load some modules
5704                     comphelper::ProfileZone aInit("preload modules");
5705                     VclBuilder::preload();
5706                     VclAbstractDialogFactory::Create();
5707                 }
5708 
5709                 preloadData();
5710 
5711                 // Release Solar Mutex, lo_startmain thread should acquire it.
5712                 Application::ReleaseSolarMutex();
5713             }
5714 
5715             force_c_locale();
5716         }
5717 
5718         if (eStage != PRE_INIT)
5719         {
5720             SAL_INFO("lok", "Re-initialize temp paths");
5721             SvtPathOptions aOptions;
5722             OUString aNewTemp;
5723             osl::FileBase::getTempDirURL(aNewTemp);
5724             aOptions.SetTempPath(aNewTemp);
5725             desktop::Desktop::CreateTemporaryDirectory();
5726 
5727             // The RequestHandler is specifically set to be ready when all the other
5728             // init in Desktop::Main (run from soffice_main) is done. We can enable
5729             // the RequestHandler here (without starting any IPC thread;
5730             // shortcutting the invocation in Desktop::Main that would start the IPC
5731             // thread), and can then use it to wait until we're definitely ready to
5732             // continue.
5733 
5734             SAL_INFO("lok", "Enabling RequestHandler");
5735             RequestHandler::Enable(false);
5736             SAL_INFO("lok", "Starting soffice_main");
5737             RequestHandler::SetReady(false);
5738             if (!bUnipoll)
5739             {
5740                 // Start the main thread only in non-unipoll mode (i.e. multithreaded).
5741                 pLib->maThread = osl_createThread(lo_startmain, nullptr);
5742                 SAL_INFO("lok", "Waiting for RequestHandler");
5743                 RequestHandler::WaitForReady();
5744                 SAL_INFO("lok", "RequestHandler ready -- continuing");
5745             }
5746             else
5747                 InitVCL();
5748         }
5749 
5750         if (eStage != SECOND_INIT)
5751             ErrorRegistry::RegisterDisplay(aBasicErrorFunc);
5752 
5753         SAL_INFO("lok", "LOK Initialized");
5754         if (eStage == PRE_INIT)
5755             bPreInited = true;
5756         else
5757             bInitialized = true;
5758     }
5759     catch (css::uno::Exception& exception)
5760     {
5761         fprintf(stderr, "Bootstrapping exception '%s'\n",
5762                  OUStringToOString(exception.Message, RTL_TEXTENCODING_UTF8).getStr());
5763     }
5764 
5765     if (eStage == PRE_INIT)
5766     {
5767         comphelper::ThreadPool::getSharedOptimalPool().shutdown();
5768     }
5769 
5770 // Turn off quick editing on IOS and ANDROID
5771 #if defined IOS || defined ANDROID
5772     if (officecfg::Office::Impress::Misc::TextObject::QuickEditing::get())
5773     {
5774         std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
5775         officecfg::Office::Impress::Misc::TextObject::QuickEditing::set(false, batch);
5776         batch->commit();
5777     }
5778 #endif
5779 
5780     return bInitialized;
5781 }
5782 
5783 SAL_JNI_EXPORT
libreofficekit_hook_2(const char * install_path,const char * user_profile_url)5784 LibreOfficeKit *libreofficekit_hook_2(const char* install_path, const char* user_profile_url)
5785 {
5786     if (!gImpl)
5787     {
5788         SAL_INFO("lok", "Create libreoffice object");
5789 
5790         gImpl = new LibLibreOffice_Impl();
5791         if (!lo_initialize(gImpl, install_path, user_profile_url))
5792         {
5793             lo_destroy(gImpl);
5794         }
5795     }
5796     return static_cast<LibreOfficeKit*>(gImpl);
5797 }
5798 
5799 SAL_JNI_EXPORT
libreofficekit_hook(const char * install_path)5800 LibreOfficeKit *libreofficekit_hook(const char* install_path)
5801 {
5802     return libreofficekit_hook_2(install_path, nullptr);
5803 }
5804 
5805 SAL_JNI_EXPORT
lok_preinit(const char * install_path,const char * user_profile_url)5806 int lok_preinit(const char* install_path, const char* user_profile_url)
5807 {
5808     return lo_initialize(nullptr, install_path, user_profile_url);
5809 }
5810 
lo_destroy(LibreOfficeKit * pThis)5811 static void lo_destroy(LibreOfficeKit* pThis)
5812 {
5813     SolarMutexClearableGuard aGuard;
5814 
5815     LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
5816     gImpl = nullptr;
5817 
5818     SAL_INFO("lok", "LO Destroy");
5819 
5820     comphelper::LibreOfficeKit::setStatusIndicatorCallback(nullptr, nullptr);
5821     uno::Reference <frame::XDesktop2> xDesktop = frame::Desktop::create ( ::comphelper::getProcessComponentContext() );
5822     // FIXME: the terminate() call here is a no-op because it detects
5823     // that LibreOfficeKit::isActive() and then returns early!
5824     bool bSuccess = xDesktop.is() && xDesktop->terminate();
5825 
5826     if (!bSuccess)
5827     {
5828         bSuccess = GetpApp() && GetpApp()->QueryExit();
5829     }
5830 
5831     if (!bSuccess)
5832     {
5833         Application::Quit();
5834     }
5835 
5836     aGuard.clear();
5837 
5838     osl_joinWithThread(pLib->maThread);
5839     osl_destroyThread(pLib->maThread);
5840 
5841     delete pLib;
5842     bInitialized = false;
5843     SAL_INFO("lok", "LO Destroy Done");
5844 }
5845 
5846 #ifdef IOS
5847 
5848 // Used by the unmaintained LibreOfficeLight app. Once that has been retired, get rid of this, too.
5849 
5850 __attribute__((visibility("default")))
temporaryHackToInvokeCallbackHandlers(LibreOfficeKitDocument * pThis)5851 void temporaryHackToInvokeCallbackHandlers(LibreOfficeKitDocument* pThis)
5852 {
5853     SolarMutexGuard aGuard;
5854     LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5855 
5856     int nOrigViewId = doc_getView(pThis);
5857 
5858     if (nOrigViewId >= 0 && pDocument->mpCallbackFlushHandlers[nOrigViewId])
5859     {
5860         pDocument->mpCallbackFlushHandlers[nOrigViewId]->Invoke();
5861     }
5862 }
5863 
5864 #endif
5865 
5866 } // extern "C"
5867 
5868 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
5869