summaryrefslogtreecommitdiff
path: root/shell/source/win32/spsupp/COMOpenDocuments.cxx
blob: 87a61691d4769885216d93ff85e2df53f13546ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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/.
*/

#include <sal/config.h>

#include <cstring>
#include <string>
#include <vector>

#include <COMOpenDocuments.hpp>
#include <spsuppServ.hpp>
#include <stdio.h>

namespace
{
template<class... Args>
HRESULT LOStart(Args... args)
{
    auto quote = [](const std::wstring& s) { return L"\"" + s + L"\""; };
    std::wstring sCmdLine((quote(GetHelperExe()) + ... + (L" " + quote(args))));
    LPWSTR pCmdLine = const_cast<LPWSTR>(sCmdLine.c_str());

    STARTUPINFOW si = {};
    si.cb = sizeof si;
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;
    PROCESS_INFORMATION pi = {};
    if (!CreateProcessW(nullptr, pCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi))
        return HRESULT_FROM_WIN32(GetLastError());

    WaitForSingleObject(pi.hProcess, INFINITE);
    DWORD nExitCode;
    const bool bGotExitCode = GetExitCodeProcess(pi.hProcess, &nExitCode);
    const DWORD nGetExitCodeError = GetLastError();

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    if (!bGotExitCode)
        return HRESULT_FROM_WIN32(nGetExitCodeError);
    if (nExitCode == 0)
        return S_OK;
    if (nExitCode == 1)
        return S_FALSE;
    return E_FAIL;
}

VARIANT_BOOL toVBool(bool b) { return b ? VARIANT_TRUE : VARIANT_FALSE; }

HRESULT ImplCreateNewDocument(IDispatch* /*pdisp*/, BSTR bstrTemplateLocation,
                              BSTR bstrDefaultSaveLocation, VARIANT_BOOL* pbResult)
{
    HRESULT hr = LOStart(L"CreateNewDocument", bstrTemplateLocation, bstrDefaultSaveLocation);
    *pbResult = toVBool(hr == S_OK);
    return hr;
}

HRESULT ImplEditDocument(IDispatch* /*pdisp*/, BSTR bstrDocumentLocation,
                         VARIANT_BOOL fUseLocalCopy, const VARIANT& varProgID,
                         VARIANT_BOOL* pbResult)
{
    const wchar_t* sUseLocalCopy = (fUseLocalCopy == VARIANT_FALSE) ? L"0" : L"1";
    const wchar_t* sProgId = (varProgID.vt == VT_BSTR) ? varProgID.bstrVal : L"";
    HRESULT hr = LOStart(L"EditDocument", bstrDocumentLocation, sUseLocalCopy, sProgId);
    *pbResult = toVBool(hr == S_OK);
    return hr;
}

HRESULT ImplViewDocument(IDispatch* /*pdisp*/, BSTR bstrDocumentLocation, int OpenType,
                         const VARIANT& varProgID, VARIANT_BOOL* pbResult)
{
    wchar_t sOpenType[16]{};
    swprintf(sOpenType, L"%d", OpenType);
    const wchar_t* sProgId = (varProgID.vt == VT_BSTR) ? varProgID.bstrVal : L"";
    HRESULT hr = LOStart(L"ViewDocument", bstrDocumentLocation, sOpenType, sProgId);
    *pbResult = toVBool(hr == S_OK);
    return hr;
}
} // namespace

LONG COMOpenDocuments::m_nObjCount = 0;
ITypeInfo* COMOpenDocuments::m_pTypeInfo = nullptr;

COMOpenDocuments::COMOpenDocuments()
{
    ::InterlockedIncrement(&m_nObjCount);
    if (m_pTypeInfo == nullptr)
    {
        ITypeLib* pITypeLib = GetTypeLib();
        HRESULT hr = pITypeLib->GetTypeInfoOfGuid(__uuidof(IOWSNewDocument3), &m_pTypeInfo);
        if (FAILED(hr))
            throw Error(hr);
    }
}

COMOpenDocuments::~COMOpenDocuments()
{
    if (::InterlockedDecrement(&m_nObjCount) == 0 && m_pTypeInfo)
    {
        m_pTypeInfo->Release();
        m_pTypeInfo = nullptr;
    }
}

// IUnknown methods

STDMETHODIMP COMOpenDocuments::QueryInterface(REFIID riid, void **ppvObject)
{
    *ppvObject = nullptr;
    if (IsEqualIID(riid, __uuidof(IUnknown)) ||
        IsEqualIID(riid, __uuidof(IDispatch)) ||
        IsEqualIID(riid, __uuidof(IOWSNewDocument)) ||
        IsEqualIID(riid, __uuidof(IOWSNewDocument2)) ||
        IsEqualIID(riid, __uuidof(IOWSNewDocument3)))
    {
        *ppvObject = static_cast<IOWSNewDocument3*>(this);
    }
    else if (IsEqualIID(riid, __uuidof(IObjectSafety)))
    {
        *ppvObject = static_cast<IObjectSafety*>(this);
    }
    else
    {
        return E_NOINTERFACE;
    }

    static_cast<IUnknown*>(*ppvObject)->AddRef();
    return S_OK;
}

// IDispatch methods

STDMETHODIMP COMOpenDocuments::GetTypeInfoCount(UINT *pctinfo)
{
    if (pctinfo == nullptr)
        return E_INVALIDARG;

    *pctinfo = 1;
    return S_OK;
}

STDMETHODIMP COMOpenDocuments::GetTypeInfo(UINT iTInfo, LCID /*lcid*/, ITypeInfo **ppTInfo)
{
    if (ppTInfo == nullptr)
        return E_INVALIDARG;
    *ppTInfo = nullptr;

    if (iTInfo != 0)
        return DISP_E_BADINDEX;

    (*ppTInfo = m_pTypeInfo)->AddRef();
    return S_OK;
}

STDMETHODIMP COMOpenDocuments::GetIDsOfNames(
    REFIID /*riid*/,
    LPOLESTR *rgszNames,
    UINT cNames,
    LCID /*lcid*/,
    DISPID *rgDispId)
{
    return m_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
}

STDMETHODIMP COMOpenDocuments::Invoke(
    DISPID dispIdMember,
    REFIID /*riid*/, // IID_NULL (see https://msdn.microsoft.com/en-us/library/windows/desktop/ms221479)
    LCID /*lcid*/,
    WORD wFlags,
    DISPPARAMS *pDispParams,
    VARIANT *pVarResult,
    EXCEPINFO *pExcepInfo,
    UINT *puArgErr)
{
    return DispInvoke(this, m_pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}

// IOWSNewDocument methods

// Creates a document based on the specified document template
STDMETHODIMP COMOpenDocuments::CreateNewDocument(
    BSTR bstrTemplateLocation,    // A string that contains the URL of the document template from which the document is created, or the programmatic identifier (progID) of the application to invoke when creating the document
    BSTR bstrDefaultSaveLocation, // A string that contains the path that specifies a suggested default location for saving the new document
    VARIANT_BOOL *pbResult)       // true if the document creation succeeds; otherwise false
{
    return CreateNewDocument2(nullptr, bstrTemplateLocation, bstrDefaultSaveLocation, pbResult);
}

// Opens the specified document for editing with its associated application
// or with the specified editor
STDMETHODIMP COMOpenDocuments::EditDocument(
    BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for editing
    VARIANT varProgID,         // An optional string that contains the ProgID of the application with which to edit the document. If this argument is omitted, the default editor for the document is used
    VARIANT_BOOL *pbResult)    // true if the document was successfully opened; otherwise false
{
    return EditDocument3(nullptr, bstrDocumentLocation, FALSE, varProgID, pbResult);
}

// IOWSNewDocument2 methods

// Opens the document for reading instead of editing, so that the document is not locked on the server
//
// Use the ViewDocument method to open a document in its appropriate application,
// instead of inside of an application instance embedded within the browser
STDMETHODIMP COMOpenDocuments::ViewDocument(
    BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading
    VARIANT varProgID,         // An optional string that contains the ProgID of the application with which to open the document. If this argument is omitted, the default viewer for the document is used
    VARIANT_BOOL *pbResult)    // true if the document was successfully opened; otherwise false
{
    return ViewDocument3(nullptr, bstrDocumentLocation, 0, varProgID, pbResult);
}

// Opens the document for reading instead of editing, so that the document
// is not locked on the server and in a specified window
//
// Use the ViewDocument method to open a document in its appropriate application,
// instead of inside of an application instance embedded within the browser
STDMETHODIMP COMOpenDocuments::ViewDocument2(
    IDispatch *pdisp,          // An Object that represents the window from which the ViewDocument2 method is being activated
    BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading
    VARIANT varProgID,         // An optional string that contains the ProgID of the application with which to open the document. If this argument is omitted, the default viewer for the document is used
    VARIANT_BOOL *pbResult)    // true if the document was successfully opened; otherwise false
{
    return ViewDocument3(pdisp, bstrDocumentLocation, 0, varProgID, pbResult);
}

// Opens the specified document for editing with its associated application
// or with the specified editor based on the specified window object
STDMETHODIMP COMOpenDocuments::EditDocument2(
    IDispatch *pdisp,          // An Object that represents the window from which the EditDocument2 method is being activated
    BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for editing
    VARIANT varProgID,         // An optional string that contains the ProgID of the application with which to edit the document. If this argument is omitted, the default editor for the document is used
    VARIANT_BOOL *pbResult)    // true if the document was successfully opened; otherwise false
{
    return EditDocument3(pdisp, bstrDocumentLocation, FALSE, varProgID, pbResult);
}

// Creates a document based on the specified document template and window object
STDMETHODIMP COMOpenDocuments::CreateNewDocument2(
    IDispatch* pdisp,                 // An Object that represents the window from which the CreateNewDocument2 method is being activated
    BSTR bstrTemplateLocation,        // A string that contains the URL of the document template from which the document is created, or the programmatic identifier (progID) of the application to invoke when creating the document
    BSTR bstrDefaultSaveLocation,     // A string that contains the path that specifies a suggested default location for saving the new document
    VARIANT_BOOL* pbResult)           // true if the document creation succeeds; otherwise false
{
    if (!pbResult)
        return E_POINTER;
    // TODO: resolve the program from varProgID (nullptr -> default?)
    return ImplCreateNewDocument(pdisp, bstrTemplateLocation, bstrDefaultSaveLocation, pbResult);
}

// Used with the OpenDocuments.CreateNewDocument2 method to determine
// whether the security dialog box that appears when a document is opened has already appeared
//
// If the PromptedOnLastOpen method returns true, the window containing the document library view
// refreshes itself the next time it receives focus. One refresh can occur after the new document
// is saved to the server
STDMETHODIMP COMOpenDocuments::PromptedOnLastOpen(
    VARIANT_BOOL* pbResult) // true if the security dialog box that appears when a document is opened has already appeared; otherwise false
{
    // This method is used by SharePoint e.g. after calling ViewDocument3. Needs to be implemented,
    // otherwise IE would show download bar ("Do you want to open Foo.xls?"), as if opening with
    // LibreOffice failed, even if actually it succeeded.
    if (!pbResult)
        return E_POINTER;
    // Returning true makes SharePoint library to refresh only when focused next time; false makes
    // it refresh instantly. The JavaScript code involved is this:
    //			var fRefreshOnNextFocus=stsOpen.PromptedOnLastOpen();
    //			if (fRefreshOnNextFocus)
    //				window.onfocus=RefreshOnNextFocus;
    //			else
    //				SetWindowRefreshOnFocus();
    // It seems to be no reason to require immediate refresh, so just return true.
    *pbResult = VARIANT_TRUE;
    return S_OK;
}

// IOWSNewDocument3 methods

// Opens the document for reading instead of editing, so that the document
// is not locked on the server in a specified window, and with a specified type
//
// The following table shows possible values for OpenType
//
// 0 When checked out, or when the document library does not require check out, the user can read or edit the document
// 1 When another user has checked it out, the user can only read the document
// 2 When the current user has checked it out, the user can only edit the document
// 3 When the document is not checked out and the document library requires that documents be checked out to be edited, the user can only read the document, or check it out and edit it
// 4 When the current user has checked it out, the user can only edit the local copy of the document
STDMETHODIMP COMOpenDocuments::ViewDocument3(
    IDispatch* pdisp,          // An Object that represents the window from which the ViewDocument3 method is being activated
    BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading
    int OpenType,              // A Long integer that specifies the rights for opening the document
    VARIANT varProgID,         // An optional string that contains the ProgID of the application with which to open the document. If this argument is omitted, the default viewer for the document is used
    VARIANT_BOOL *pbResult)    // true if the document was successfully opened; otherwise false
{
    if (!pbResult)
        return E_POINTER;
    return ImplViewDocument(pdisp, bstrDocumentLocation, OpenType, varProgID, pbResult);
}

// Checks in the specified document to a library
STDMETHODIMP COMOpenDocuments::CheckinDocument(
    BSTR /*bstrDocumentLocation*/,  // A string that contains the URL of the document to check in
    int /*CheckinType*/,            // A Long that specifies the type of check-in, where 0 = minor check-in, 1 = major check-in, and 2 = overwrite check-in
    BSTR /*CheckinComment*/,        // A string that contains a comment for checking in the document
    VARIANT_BOOL /*bKeepCheckout*/, // Optional. true to check in changes that have been made to the document yet keep the document checked out; otherwise, false. The default value is false
    VARIANT_BOOL* /*pbResult*/)     // true if the document is successfully checked in; otherwise, false
{
    // TODO
    return E_NOTIMPL;
}

// Discards the check out of a document to the client computer and deletes the local draft
STDMETHODIMP COMOpenDocuments::DiscardLocalCheckout(
    BSTR /*bstrDocumentLocationRaw*/, // A string that contains the URL of the document
    VARIANT_BOOL* /*pbResult*/)       // true if the operation to discard the local checkout of the document is successful; otherwise, false
{
    // TODO
    return E_NOTIMPL;
}

// Deprecated. Returns E_NOTIMPL
STDMETHODIMP COMOpenDocuments::ViewInExcel(
    BSTR /*SiteUrl*/,
    BSTR /*FileName*/,
    BSTR /*SessionId*/,
    BSTR /*Cmd*/,
    BSTR /*Sheet*/,
    int /*Row*/,
    int /*Column*/,
    VARIANT /*varProgID*/)
{
    return E_NOTIMPL;
}

// Checks out a document from a library
STDMETHODIMP COMOpenDocuments::CheckoutDocumentPrompt(
    BSTR /*bstrDocumentLocationRaw*/,    // A string that contains the URL of the document to check out
    VARIANT_BOOL /*fEditAfterCheckout*/, // true to open the document in an editing application; otherwise, false
    VARIANT /*varProgID*/,               // An optional string that contains the ProgID of the application that is used to work with the document. If this argument is omitted, the default application for the document is used
    VARIANT_BOOL* /*pbResult*/)          // true if the document is successfully checked out; otherwise, false
{
    // TODO
    return E_NOTIMPL;
}

// Opens the specified document for editing with its associated application
// or with the specified editor based on the specified window object,
// and specifies whether to use a local copy
STDMETHODIMP COMOpenDocuments::EditDocument3(
    IDispatch* pdisp,               // An Object that represents the window from which the EditDocument3 method is being activated
    BSTR bstrDocumentLocation,      // A string that contains the URL of the document to open for editing
    VARIANT_BOOL fUseLocalCopy,     // true to use a local copy; otherwise false
    VARIANT varProgID,              // An optional string that contains the ProgID of the application with which to edit the document. If this argument is omitted, the default editor for the document is used
    VARIANT_BOOL *pbResult)         // true if the document was successfully opened; otherwise false
{
    if (!pbResult)
        return E_POINTER;
    // TODO: resolve the program from varProgID (nullptr -> default?)
    return ImplEditDocument(pdisp, bstrDocumentLocation, fUseLocalCopy, varProgID, pbResult);
}

// Creates a new blog post in the editing application
STDMETHODIMP COMOpenDocuments::NewBlogPost(
    BSTR /*bstrProviderId*/, // A string that contains the GUID of the blog provider
    BSTR /*bstrBlogUrl*/,    // A string that contains the absolute URL of the blog site
    BSTR /*bstrBlogName*/)   // A string that contains the GUID of the blog site and the GUID of the post list separated by the pound sign (#)
{
    return E_NOTIMPL;
}

// IObjectSafety methods

HRESULT STDMETHODCALLTYPE COMOpenDocuments::GetInterfaceSafetyOptions(
    REFIID riid,
    DWORD *pdwSupportedOptions,
    DWORD *pdwEnabledOptions)
{
    IUnknown* pUnk;
    HRESULT hr = QueryInterface(riid, reinterpret_cast<void**>(&pUnk));
    if (FAILED(hr))
    {
        return hr;
    }

    // We know about it; release reference and return required information
    pUnk->Release();
    *pdwSupportedOptions = iSupportedOptionsMask;
    *pdwEnabledOptions = m_iEnabledOptions;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE COMOpenDocuments::SetInterfaceSafetyOptions(
    REFIID riid,
    DWORD dwOptionSetMask,
    DWORD dwEnabledOptions)
{
    IUnknown* pUnk;
    HRESULT hr = QueryInterface(riid, reinterpret_cast<void**>(&pUnk));
    if (FAILED(hr))
    {
        return hr;
    }
    pUnk->Release();

    // Are there unsupported options in mask?
    if (dwOptionSetMask & ~iSupportedOptionsMask)
        return E_FAIL;

    m_iEnabledOptions = (m_iEnabledOptions & ~dwOptionSetMask) | (dwOptionSetMask & dwEnabledOptions);
    return S_OK;
}

// Non-COM methods

LONG COMOpenDocuments::GetObjectCount() { return m_nObjCount; }

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */