comphelper/source/misc/lok.cxx               |   24 ++
 desktop/qa/desktop_lib/test_desktop_lib.cxx  |    3 
 desktop/source/lib/init.cxx                  |   11 
 include/LibreOfficeKit/LibreOfficeKit.h      |    4 
 include/LibreOfficeKit/LibreOfficeKit.hxx    |    8 
 include/LibreOfficeKit/LibreOfficeKitTypes.h |    6 
 include/comphelper/lok.hxx                   |    4 
 include/tools/UnixWrappers.h                 |  310 +++++++++++++++++++++++++++
 sfx2/source/doc/guisaveas.cxx                |   21 -
 9 files changed, 378 insertions(+), 13 deletions(-)

New commits:
commit 6112dac3ab1d60097255e227f475aaff3d49d2a0
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Dec 9 14:17:42 2025 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Dec 11 15:49:35 2025 +0100

    cool#13770 lok: add a file save dialog callback
    
    Commit f4b9d6f024b27c4d8ddaabfe0eaa0b404137e3cf (Add horrible hack to
    ask the filename when exporting PDF in CODA-W, 2025-12-02) added a
    callback from core to the LOK client to trigger a file picker during PDF
    export.
    
    The problem is that in its current form, this callback is
    Windows-specific so other platforms (Linux, macOS) can't provide their
    implementation in a straightforward way.
    
    Fix the problem by reworkig this, so it works similar to the anyInput
    callback, which is cross-platform already.
    
    The behavior for the case when the new callback is not registered is
    meant to be unchanged.
    
    Change-Id: I27934774889fc827772b2665799eca358dd62e55
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195381
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins

diff --git a/comphelper/source/misc/lok.cxx b/comphelper/source/misc/lok.cxx
index c8330f992c3a..2ddf430c521c 100644
--- a/comphelper/source/misc/lok.cxx
+++ b/comphelper/source/misc/lok.cxx
@@ -14,6 +14,9 @@
 #include <osl/process.h>
 #include <i18nlangtag/languagetag.hxx>
 #include <sal/log.hxx>
+#ifdef _WIN32
+#include <tools/UnixWrappers.h>
+#endif
 
 #include <iostream>
 
@@ -51,6 +54,8 @@ static std::function<bool(void*, int)> g_pAnyInputCallback;
 static void* g_pAnyInputCallbackData;
 static std::function<int()> g_pMostUrgentPriorityGetter;
 
+static std::function<void(const char*, char*, size_t)> 
g_pFileSaveDialogCallback;
+
 static std::function<void(int)> g_pViewSetter;
 static std::function<int()> g_pViewGetter;
 
@@ -367,6 +372,25 @@ bool anyInput()
     return bRet;
 }
 
+void setFileSaveDialogCallback(const std::function<void(const char*, char*, 
size_t)>& pFileSaveDialogCallback)
+{
+    g_pFileSaveDialogCallback = pFileSaveDialogCallback;
+}
+
+bool fileSaveDialog(const OUString& rSuggested, OUString& rResult)
+{
+    if (!g_pFileSaveDialogCallback)
+    {
+        return false;
+    }
+
+    OString aSuggested = rSuggested.toUtf8();
+    char aResult[PATH_MAX];
+    g_pFileSaveDialogCallback(aSuggested.getStr(), aResult, PATH_MAX);
+    rResult = OUString::fromUtf8(aResult);
+    return true;
+}
+
 void setViewSetter(const std::function<void(int)>& pViewSetter)
 {
     g_pViewSetter = pViewSetter;
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx 
b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index d1cd353b712a..dc52bcc39edd 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -4239,10 +4239,11 @@ void DesktopLOKTest::testABI()
     CPPUNIT_ASSERT_EQUAL(classOffset(23), offsetof(struct 
_LibreOfficeKitClass, extractDocumentStructureRequest));
     CPPUNIT_ASSERT_EQUAL(classOffset(24), offsetof(struct 
_LibreOfficeKitClass, registerAnyInputCallback));
     CPPUNIT_ASSERT_EQUAL(classOffset(25), offsetof(struct 
_LibreOfficeKitClass, getDocsCount));
+    CPPUNIT_ASSERT_EQUAL(classOffset(26), offsetof(struct 
_LibreOfficeKitClass, registerFileSaveDialogCallback));
 
     // When extending LibreOfficeKit with a new function pointer,  add new 
assert for the offsetof the
     // new function pointer and bump this assert for the size of the class.
-    CPPUNIT_ASSERT_EQUAL(classOffset(26), sizeof(struct _LibreOfficeKitClass));
+    CPPUNIT_ASSERT_EQUAL(classOffset(27), sizeof(struct _LibreOfficeKitClass));
 
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct 
_LibreOfficeKitDocumentClass, destroy));
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct 
_LibreOfficeKitDocumentClass, saveAs));
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index ca25346f9b2a..24624b26ac9d 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -2742,6 +2742,9 @@ static void lo_registerAnyInputCallback(LibreOfficeKit* 
pThis,
                        LibreOfficeKitAnyInputCallback pAnyInputCallback,
                        void* pData);
 
+static void lo_registerFileSaveDialogCallback(LibreOfficeKit* pThis,
+                       LibreOfficeKitFileSaveDialogCallback 
pFileSaveDialogCallback);
+
 static void lo_sendDialogEvent(LibreOfficeKit* pThis,
                                unsigned long long int nLOKWindowId,
                                const char* pArguments);
@@ -2791,6 +2794,7 @@ LibLibreOffice_Impl::LibLibreOffice_Impl()
         m_pOfficeClass->setForkedChild = lo_setForkedChild;
         m_pOfficeClass->extractDocumentStructureRequest = 
lo_extractDocumentStructureRequest;
         m_pOfficeClass->registerAnyInputCallback = lo_registerAnyInputCallback;
+        m_pOfficeClass->registerFileSaveDialogCallback = 
lo_registerFileSaveDialogCallback;
         m_pOfficeClass->getDocsCount = lo_getDocsCount;
 
         gOfficeClass = m_pOfficeClass;
@@ -7932,6 +7936,13 @@ static void lo_registerAnyInputCallback(LibreOfficeKit* 
/*pThis*/,
     });
 }
 
+static void lo_registerFileSaveDialogCallback(LibreOfficeKit* /*pThis*/,
+                       LibreOfficeKitFileSaveDialogCallback 
pFileSaveDialogCallback)
+{
+    SolarMutexGuard aGuard;
+    
comphelper::LibreOfficeKit::setFileSaveDialogCallback(pFileSaveDialogCallback);
+}
+
 static int lo_getDocsCount(LibreOfficeKit* /*pThis*/)
 {
     SolarMutexGuard aGuard;
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h 
b/include/LibreOfficeKit/LibreOfficeKit.h
index 7860adf3c66b..e49cdf174892 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -165,6 +165,10 @@ struct _LibreOfficeKitClass
 
     /// @see lok::Office::getDocsCount().
     int (*getDocsCount) (LibreOfficeKit* pThis);
+
+    /// @see lok::Office::registerFileSaveDialogCallback()
+    void (*registerFileSaveDialogCallback)(LibreOfficeKit* pThis,
+            LibreOfficeKitFileSaveDialogCallback pCallback);
 };
 
 #define LIBREOFFICEKIT_DOCUMENT_HAS(pDoc,member) 
LIBREOFFICEKIT_HAS_MEMBER(LibreOfficeKitDocumentClass,member,(pDoc)->pClass->nSize)
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx 
b/include/LibreOfficeKit/LibreOfficeKit.hxx
index b6cdd929cb95..fae2b0de7c84 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -1316,6 +1316,14 @@ public:
     {
         return mpThis->pClass->getDocsCount(mpThis);
     }
+
+    /**
+     * Registers a callback that can display an interactive file save dialog.
+     */
+    void registerFileSaveDialogCallback(LibreOfficeKitFileSaveDialogCallback 
pCallback)
+    {
+        return mpThis->pClass->registerFileSaveDialogCallback(mpThis, 
pCallback);
+    }
 };
 
 /// Factory method to create a lok::Office instance.
diff --git a/include/LibreOfficeKit/LibreOfficeKitTypes.h 
b/include/LibreOfficeKit/LibreOfficeKitTypes.h
index 939766567607..167b4f65ca8d 100644
--- a/include/LibreOfficeKit/LibreOfficeKitTypes.h
+++ b/include/LibreOfficeKit/LibreOfficeKitTypes.h
@@ -10,6 +10,8 @@
 #ifndef INCLUDED_LIBREOFFICEKIT_LIBREOFFICEKIT_TYPES_H
 #define INCLUDED_LIBREOFFICEKIT_LIBREOFFICEKIT_TYPES_H
 
+#include <stddef.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -28,6 +30,10 @@ typedef void (*LibreOfficeKitWakeCallback)(void* pData);
 /// @see lok::Office::registerAnyInputCallback()
 typedef bool (*LibreOfficeKitAnyInputCallback)(void* pData, int 
nMostUrgentPriority);
 
+/// @see lok::Office::registerFileSaveDialogCallback()
+typedef void (*LibreOfficeKitFileSaveDialogCallback)(const char* 
pSuggestedUri, char* pResultUri,
+                                                     size_t nResultUri);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/comphelper/lok.hxx b/include/comphelper/lok.hxx
index 9d28f839c9c0..ebdb8439ba0e 100644
--- a/include/comphelper/lok.hxx
+++ b/include/comphelper/lok.hxx
@@ -150,6 +150,10 @@ setAnyInputCallback(const std::function<bool(void*, int)>& 
pAnyInputCallback, vo
                     const std::function<int()>& pMostUrgentPriorityGetter);
 COMPHELPER_DLLPUBLIC bool anyInput();
 
+COMPHELPER_DLLPUBLIC void setFileSaveDialogCallback(
+    const std::function<void(const char*, char*, size_t)>& 
pFileSaveDialogCallback);
+COMPHELPER_DLLPUBLIC bool fileSaveDialog(const OUString& rSuggested, OUString& 
rResult);
+
 // These allow setting callbacks, so that set/get of a LOK view is possible 
even in code that is
 // below sfx2.
 COMPHELPER_DLLPUBLIC void setViewSetter(const std::function<void(int)>& 
pViewSetter);
diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx
index a12ecb01acc7..4ef76c1b278e 100644
--- a/sfx2/source/doc/guisaveas.cxx
+++ b/sfx2/source/doc/guisaveas.cxx
@@ -1718,25 +1718,18 @@ bool 
SfxStoringHelper::FinishGUIStoreModel(::comphelper::SequenceAsHashMap::cons
     {
         OUString aFileName;
         aFileNameIter->second >>= aFileName;
-#ifdef _WIN32
         if (comphelper::LibreOfficeKit::isActive())
         {
-            // FIXME: Horrible hack. In CODA-W, we didn't actually
-            // display any dialog yet, so call into a function in
-            // CODA.cpp.
-            typedef void (*ofd_t)(const std::wstring& suggestedURI, 
std::string& result);
-            // Use mangled name to catch changes in parameters...
-            ofd_t ofd = (ofd_t)GetProcAddress(GetModuleHandle(NULL), 
"?output_file_dialog_from_core@@YAXAEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@AEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z");
-            if (ofd != NULL)
+            // In the LOK case, we didn't actually display any dialog yet, so 
invoke a callback if
+            // that's set.
+            OUString aNewURI;
+            if (comphelper::LibreOfficeKit::fileSaveDialog(aFileName, aNewURI))
             {
-                std::string newURI;
-                (*ofd)(std::wstring(o3tl::toW(aFileName)), newURI);
-                if (newURI == "")
+                if (aNewURI.isEmpty())
                     return false;
-                aFileName = OUString::fromUtf8(newURI.c_str());
+                aFileName = aNewURI;
             }
         }
-#endif
 
         aURL.SetURL( aFileName );
         DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "Illegal 
URL!" );
commit ddcfcd67afe12d080019654efc7ad8db938d2c9a
Author:     Tor Lillqvist <[email protected]>
AuthorDate: Thu Feb 27 23:47:55 2025 +0200
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Dec 11 15:49:28 2025 +0100

    Add wrappers to use Unix-style file APIs on Windows
    
    Of course, LibreOffice has tons of APIs from before whose whole point
    is to hide system dependencies behind a cross-platform API. All the
    "sal" and "osl" stuff, for starters. The wrappers in this file is not
    intended to replace those. The point with these wrappers is to quickly
    port code that was written for Unix *only* to Windows, with minimal
    changes to the source code. Basically you just need to prefix calls to
    functions like open() and stat() with wrap_.
    
    In Windows, "file descriptors" are a thing in the C library, not the
    operating system. But the C library does provide the same core set of
    functionality as Unix-like systems do. The functions use almost the
    same names, except that for some reason, to avoid compilation
    warnings, you need to call variants with names preceded by an
    underscore, like _close().
    
    Another Windows complication is that the file system is based on
    UTF-16 names. You can't just pass file names as eight-bit characters
    to _open() and expect it to work for all file names. Instead, you need
    to call a function with a "w" prefix, like _wopen(), that takes a wide
    character string (UTF-16).
    
    This header is in C, not C++, because we want it to be usable from
    some external libraries written in C that we compile as part of
    LibreOffice in some circumstances, particularly for Windows.
    
    All functions behave like the wrapped ones, set errno on errors.
    
    Change-Id: Ia093ccf976659ceabbef6e0071da73d4df0b63b7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195422
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/include/tools/UnixWrappers.h b/include/tools/UnixWrappers.h
new file mode 100644
index 000000000000..70d560ff0907
--- /dev/null
+++ b/include/tools/UnixWrappers.h
@@ -0,0 +1,310 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/* Wrappers to use Unix-style file APIs on Windows.
+ *
+ * Of course, LibreOffice has tons of APIs from before whose whole point is to 
hide system
+ * dependencies behind a cross-platform API. All the "sal" and "osl" stuff, 
for starters. The
+ * wrappers in this file is not intended to replace those. The point with 
these wrappers is to
+ * quickly port code that was written for Unix *only* to Windows, with minimal 
changes to the source
+ * code. Basically you just need to prefix calls to functions like open() and 
stat() with wrap_.
+ *
+ * In Windows, "file descriptors" are a thing in the C library, not the 
operating system. But the C
+ * library does provide the same core set of functionality as Unix-like 
systems do. The functions
+ * use almost the same names, except that for some reason, to avoid 
compilation warnings, you need
+ * to call variants with names preceded by an underscore, like _close().
+
+ * Another Windows complication is that the file system is based on UTF-16 
names. You can't just
+ * pass file names as eight-bit characters to _open() and expect it to work 
for all file
+ * names. Instead, you need to call a function with a "w" prefix, like 
_wopen(), that takes a wide
+ * character string (UTF-16).
+
+ * This header is in C, not C++, because we want it to be usable from some 
external libraries
+ * written in C that we compile as part of LibreOffice in some circumstances, 
particularly for
+ * Windows.
+ *
+ * All functions behave like the wrapped ones, set errno on errors.
+ */
+
+#include <wchar.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#include <io.h>
+#include <string.h>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+/* Undo the mapping in <Windows.h> of CreateFont => CreateFontW etc, as vcl 
also uses these
+ * identifiers.
+ */
+#undef CreateFont
+#undef GetGlyphOutline
+#else
+#include <unistd.h>
+#include <sys/mman.h>
+#endif
+
+#ifdef _WIN32
+
+#define MAP_FAILED ((void*)-1)
+
+#define S_ISDIR(m) (((m)&_S_IFMT) == _S_IFDIR)
+
+#define F_OK 00
+#define W_OK 02
+#define R_OK 04
+#define X_OK R_OK
+
+#define PATH_MAX MAX_PATH
+
+#endif
+
+#ifdef _WIN32
+[[maybe_unused]] static wchar_t* string_to_wide_string(const char* string)
+{
+    const int len = strlen(string);
+    if (len == 0)
+    {
+        return _wcsdup(L"");
+    }
+
+    const int wlen = MultiByteToWideChar(CP_UTF8, 0, string, len, NULL, 0);
+    if (wlen <= 0)
+    {
+        return NULL;
+    }
+
+    wchar_t* result = (wchar_t*)malloc(wlen * 2 + 2);
+    MultiByteToWideChar(CP_UTF8, 0, string, len, result, wlen);
+    result[wlen] = L'
+
+    return result;
+}
+
+#endif
+
+/* Pass in UTF-8 filename */
+[[maybe_unused]] static int wrap_open(const char* path, int flags, int mode)
+{
+#ifdef _WIN32
+    wchar_t* wpath = string_to_wide_string(path);
+    int result = _wopen(wpath, flags, mode);
+    free(wpath);
+    return result;
+#else
+    return open(path, flags, mode);
+#endif
+}
+
+#if 0
+
+/* Pass in UTF-16 filename */
+[[maybe_unused]] static int wrap_wopen(const wchar_t* path, int flags, int 
mode)
+{
+#ifdef _WIN32
+    return _wopen(path, flags, mode);
+#else
+    rtl_String* str;
+    int len = wcslen(path);
+    bool success = rtl_convertUStringToString(&str, path, len, 
RTL_TEXTENCODING_UTF8,
+                                              
RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+                                                  | 
RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR);
+    int result;
+    if (success)
+    {
+        char* spath = malloc(len + 1);
+        strcat(spath, str, len);
+        spath[len] = 0;
+        result = open(str->buffer, flags, path);
+        free(spath);
+    }
+    else
+    {
+        result = -1;
+        errno = EIO;
+    }
+    rtl_string_release(str);
+    return result;
+#endif
+}
+
+#endif
+
+[[maybe_unused]] static int wrap_read(int fd, void* buf, int nbytes)
+{
+#ifdef _WIN32
+    return _read(fd, buf, nbytes);
+#else
+    return read(fd, buf, nbytes);
+#endif
+}
+
+[[maybe_unused]] static int wrap_write(int fd, const void* buf, int nbytes)
+{
+#ifdef _WIN32
+    return _write(fd, buf, nbytes);
+#else
+    return write(fd, buf, nbytes);
+#endif
+}
+
+[[maybe_unused]] static int wrap_fstat(int fd, struct stat* st)
+{
+#ifdef _WIN32
+    /* Sadly just "struct stat" in the Microsoft C library means a legacy one 
with 32-bit size,
+     * 32-bit time one. But it is a *different* type than struct _stat32, even 
if identical.
+     */
+    struct _stat32 st32;
+    int result = _fstat32(fd, &st32);
+    if (result != -1)
+    {
+        st->st_dev = st32.st_dev;
+        st->st_ino = st32.st_ino;
+        st->st_mode = st32.st_mode;
+        st->st_nlink = st32.st_nlink;
+        st->st_uid = st32.st_uid;
+        st->st_gid = st32.st_gid;
+        st->st_rdev = st32.st_rdev;
+        st->st_size = st32.st_size;
+        st->st_atime = st32.st_atime;
+        st->st_mtime = st32.st_mtime;
+        st->st_ctime = st32.st_ctime;
+    }
+    return result;
+#else
+    return fstat(fd, st);
+#endif
+}
+
+[[maybe_unused]] static void* wrap_mmap(int64_t size, int fd, intptr_t* handle)
+{
+#ifdef _WIN32
+    *handle = (intptr_t)CreateFileMappingW((HANDLE)_get_osfhandle(fd), NULL,
+                                           SEC_COMMIT | PAGE_READONLY, 0, 0, 
NULL);
+    return MapViewOfFile((HANDLE)*handle, FILE_MAP_READ, 0, 0, size);
+#else
+    /* No handle needs to be stored */
+    (void)handle;
+    return mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+#endif
+}
+
+[[maybe_unused]] static int wrap_munmap(void* pointer, int64_t size, intptr_t 
handle)
+{
+#ifdef _WIN32
+    (void)size;
+    CloseHandle((HANDLE)handle);
+    if (!UnmapViewOfFile(pointer))
+    {
+        errno = EIO;
+        return -1;
+    }
+    return 0;
+#else
+    (void)handle;
+    return munmap(pointer, size);
+#endif
+}
+
+[[maybe_unused]] static int wrap_dup(int fd)
+{
+#ifdef _WIN32
+    return _dup(fd);
+#else
+    return dup(fd);
+#endif
+}
+
+[[maybe_unused]] static int wrap_close(int fd)
+{
+#ifdef _WIN32
+    return _close(fd);
+#else
+    return close(fd);
+#endif
+}
+
+/* This doesn't usea file descriptor but is related to the above functionality 
so keep here
+ * anyway.
+ */
+
+[[maybe_unused]] static FILE* wrap_fopen(const char* path, const char* mode)
+{
+#ifdef _WIN32
+    wchar_t* wpath = string_to_wide_string(path);
+    wchar_t* wmode = string_to_wide_string(mode);
+    FILE* result = _wfopen(wpath, wmode);
+    free(wpath);
+    free(wmode);
+    return result;
+#else
+    return fopen(path, mode);
+#endif
+}
+
+[[maybe_unused]] static int wrap_stat(const char* path, struct stat* st)
+{
+#ifdef _WIN32
+    struct _stat32 st32;
+    wchar_t* wpath = string_to_wide_string(path);
+    int result = _wstat32(wpath, &st32);
+    free(wpath);
+    if (result != -1)
+    {
+        st->st_dev = st32.st_dev;
+        st->st_ino = st32.st_ino;
+        st->st_mode = st32.st_mode;
+        st->st_nlink = st32.st_nlink;
+        st->st_uid = st32.st_uid;
+        st->st_gid = st32.st_gid;
+        st->st_rdev = st32.st_rdev;
+        st->st_size = st32.st_size;
+        st->st_atime = st32.st_atime;
+        st->st_mtime = st32.st_mtime;
+        st->st_ctime = st32.st_ctime;
+    }
+    return result;
+#else
+    return stat(path, st);
+#endif
+}
+
+[[maybe_unused]] static int wrap_access(const char* path, int mode)
+{
+#ifdef _WIN32
+    wchar_t* wpath = string_to_wide_string(path);
+    int result = _waccess(wpath, mode);
+    free(wpath);
+    return result;
+#else
+    return access(path, mode);
+#endif
+}
+
+[[maybe_unused]] static const char* wrap_realpath(const char* path, char* 
resolved_path)
+{
+#ifdef _WIN32
+    strcpy(resolved_path, path);
+    return resolved_path;
+#else
+    return realpath(path, resolved_path);
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
commit b0ecad5a3d83729aaf7c8a44983d20eedcbbdbff
Author:     Tor Lillqvist <[email protected]>
AuthorDate: Tue Nov 4 20:40:34 2025 +0200
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Dec 11 15:49:22 2025 +0100

    Make the CODA-W PDF export file save dialog hack a bit more secure
    
    Look for the mangled ++ name, to catch a potential mismatch in
    function signature. Also, check that the returned function pointer is
    non-null before calling it.
    
    Change-Id: Ib2e0d1cd4b3e26081800b736b74e5972d0819736
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195380
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx
index 1c9dd37343f7..a12ecb01acc7 100644
--- a/sfx2/source/doc/guisaveas.cxx
+++ b/sfx2/source/doc/guisaveas.cxx
@@ -1725,12 +1725,16 @@ bool 
SfxStoringHelper::FinishGUIStoreModel(::comphelper::SequenceAsHashMap::cons
             // display any dialog yet, so call into a function in
             // CODA.cpp.
             typedef void (*ofd_t)(const std::wstring& suggestedURI, 
std::string& result);
-            ofd_t ofd = (ofd_t)GetProcAddress(GetModuleHandle(NULL), 
"output_file_dialog_from_core");
-            std::string newURI;
-            (*ofd)(std::wstring(o3tl::toW(aFileName)), newURI);
-            if (newURI == "")
-                return false;
-            aFileName = OUString::fromUtf8(newURI.c_str());
+            // Use mangled name to catch changes in parameters...
+            ofd_t ofd = (ofd_t)GetProcAddress(GetModuleHandle(NULL), 
"?output_file_dialog_from_core@@YAXAEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@AEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z");
+            if (ofd != NULL)
+            {
+                std::string newURI;
+                (*ofd)(std::wstring(o3tl::toW(aFileName)), newURI);
+                if (newURI == "")
+                    return false;
+                aFileName = OUString::fromUtf8(newURI.c_str());
+            }
         }
 #endif
 

Reply via email to