[Spice-commits] 4 commits - Makefile.am test-shell vdagent/file_xfer.cpp vdagent/shell.cpp vdagent/shell.h vdagent/test-shell.cpp vdagent/vdagent.cpp

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Mon Nov 18 21:52:15 UTC 2019


 Makefile.am            |   12 ++++
 test-shell             |    3 +
 vdagent/file_xfer.cpp  |   46 +++++++++++++++++-
 vdagent/shell.cpp      |  123 +++++++++++++++++++++++++++++++++++++++++++++++++
 vdagent/shell.h        |   22 ++++++++
 vdagent/test-shell.cpp |   48 +++++++++++++++++++
 vdagent/vdagent.cpp    |   12 ++++
 7 files changed, 262 insertions(+), 4 deletions(-)

New commits:
commit 1dd3f3586a4f3dfb35d2ec1ddbec0c25c4126c86
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Fri Nov 8 11:22:28 2019 +0000

    Add a test for filter_url function
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/Makefile.am b/Makefile.am
index 98856d3..77c86b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -103,6 +103,14 @@ test_log_win_SOURCES =			\
 	common/test-log.cpp		\
 	$(NULL)
 
+check_PROGRAMS += test-shell-win
+TESTS += test-shell
+test_shell_win_LDFLAGS = $(AM_LDFLAGS) -Wl,--subsystem,console
+test_shell_win_LDADD = -luuid -lole32 -loleaut32
+test_shell_win_SOURCES = \
+	vdagent/test-shell.cpp \
+	$(NULL)
+
 MANUFACTURER = The Spice Project
 PRODUCT = Spice agent
 
diff --git a/test-shell b/test-shell
new file mode 100755
index 0000000..3f286f1
--- /dev/null
+++ b/test-shell
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exec wine test-shell-win.exe
diff --git a/vdagent/test-shell.cpp b/vdagent/test-shell.cpp
new file mode 100644
index 0000000..d77a73c
--- /dev/null
+++ b/vdagent/test-shell.cpp
@@ -0,0 +1,48 @@
+/*
+   Copyright (C) 2019 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#undef NDEBUG
+#include "shell.cpp"
+
+#include <assert.h>
+
+static void check_filter(LPCWSTR in, LPCWSTR out)
+{
+    WCHAR buf[1000];
+    assert(in != NULL);
+    assert(wcslen(in) < 1000);
+    wcscpy(buf, in);
+    if (filter_url(buf)) {
+        assert(out != NULL);
+        assert(wcscmp(buf, out) == 0);
+    } else {
+        assert(out == NULL);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    check_filter(L"c:\\", NULL);
+    check_filter(L"file:///c:/", L"c:\\");
+    check_filter(L"file://test%20", L"test ");
+    check_filter(L"file://test%2", NULL);
+    check_filter(L"file://a%g0", NULL);
+    check_filter(L"file://a%E0", L"a\xe0");
+    check_filter(L"file:///b+c", L"b+c");
+    check_filter(L"file://a\u4141", L"a\u4141");
+
+    return 0;
+}
commit 9207d821d2a6a14e974b9d422cbb8503eb954e33
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Thu Nov 7 14:56:38 2019 +0000

    file_xfer: Open Download directory after a file transfer is finished
    
    Have the same behaviour of Linux machines.
    The directory is not opened again and again, only is not already
    opened.
    This is done checking the directory opened in explorer.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/Makefile.am b/Makefile.am
index 8c527b1..98856d3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,7 +23,7 @@ LIBS = -lversion
 
 bin_PROGRAMS = vdagent vdservice
 
-vdagent_LDADD = $(LIBPNG_LIBS) $(ZLIB_LIBS) -lwtsapi32 -lgdi32 -luuid -lole32 vdagent_rc.$(OBJEXT)
+vdagent_LDADD = $(LIBPNG_LIBS) $(ZLIB_LIBS) -lwtsapi32 -lgdi32 -luuid -lole32 -loleaut32 vdagent_rc.$(OBJEXT)
 vdagent_CXXFLAGS = $(AM_CXXFLAGS) $(LIBPNG_CFLAGS)
 vdagent_LDFLAGS = $(AM_LDFLAGS) -Wl,--subsystem,windows
 vdagent_SOURCES =			\
@@ -46,6 +46,8 @@ vdagent_SOURCES =			\
 	vdagent/image.h			\
 	vdagent/imagepng.cpp		\
 	vdagent/imagepng.h		\
+	vdagent/shell.cpp		\
+	vdagent/shell.h			\
 	$(NULL)
 
 vdagent_rc.$(OBJEXT): vdagent/vdagent.rc
diff --git a/vdagent/file_xfer.cpp b/vdagent/file_xfer.cpp
index 9046c0c..d158006 100644
--- a/vdagent/file_xfer.cpp
+++ b/vdagent/file_xfer.cpp
@@ -32,6 +32,7 @@
 
 #include "file_xfer.h"
 #include "as_user.h"
+#include "shell.h"
 
 #define FILENAME_RESERVED_CHAR_LIST \
     ":" /* streams and devices */ \
@@ -218,6 +219,20 @@ void FileXferTask::success()
         CloseHandle(handle);
         handle = INVALID_HANDLE_VALUE;
     }
+
+    // open download directory
+    AsUser as_user;
+    if (!as_user.begin()) {
+        vd_printf("as_user failed");
+        return;
+    }
+
+    TCHAR file_path[MAX_PATH];
+    if (!get_download_directory(file_path)) {
+        return;
+    }
+
+    open_shell_directory(file_path);
 }
 
 void FileXfer::handle_status(VDAgentFileXferStatusMessage* status)
diff --git a/vdagent/shell.cpp b/vdagent/shell.cpp
new file mode 100644
index 0000000..747b66f
--- /dev/null
+++ b/vdagent/shell.cpp
@@ -0,0 +1,123 @@
+/*
+   Copyright (C) 2019 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "shell.h"
+
+#include <stdio.h>
+#include <exdisp.h>
+#include <comutil.h>
+
+static bool filter_url(LPWSTR str)
+{
+    // check and strip "file:///?"
+    WCHAR *src = str;
+    if (wcsncmp(str, L"file:///", 8) == 0) {
+        src += 8;
+    } else if (wcsncmp(str, L"file://", 7) == 0) {
+        src += 7;
+    } else {
+        return false;
+    }
+
+    WCHAR *dst = str;
+    while (*src) {
+        WCHAR c = *src++, save_char;
+        switch (c) {
+        // convert Unix to DOS path separators
+        case L'/':
+            c = L'\\';
+            break;
+        // hexadecimal encoding
+        case L'%':
+            if (!iswxdigit(src[0]) || !iswxdigit(src[1])) {
+                return false;
+            }
+            save_char = src[2];
+            src[2] = 0;
+            c = (WCHAR) wcstol(src, NULL, 16);
+            src[2] = save_char;
+            src += 2;
+            break;
+        // NOTE: do not convert + to space, paths like "C++"
+        // remain the same
+        default:
+            break;
+        }
+        *dst++ = c;
+    }
+    *dst = 0;
+
+    return true;
+}
+
+bool open_shell_directory(LPCWSTR directory)
+{
+    HRESULT result;
+
+    IShellWindows *shellWindows = nullptr;
+    result = CoCreateInstance(
+        CLSID_ShellWindows,
+        NULL,
+        CLSCTX_ALL,
+        IID_IShellWindows,
+        (void**)&shellWindows
+    );
+    if (FAILED(result)) {
+        return false;
+    }
+
+    long windowsCount = 0;
+    result = shellWindows->get_Count(&windowsCount);
+    if (FAILED(result)) {
+        shellWindows->Release();
+        return false;
+    }
+
+    bool found = false;
+    for (long i = 0; i < windowsCount; i++) {
+        IDispatch *disp = nullptr;
+        _variant_t va(i, VT_I4);
+
+        result = shellWindows->Item(va, &disp);
+        if (FAILED(result)) {
+            continue;
+        }
+
+        IWebBrowser2 *pBrowser2 = nullptr;
+        result = disp->QueryInterface(IID_IWebBrowserApp, (void**)&pBrowser2);
+
+        if (SUCCEEDED(result)) {
+            BSTR url;
+            pBrowser2->get_LocationURL(&url);
+
+            if (filter_url(url) && lstrcmpiW(url, directory) == 0) {
+                found = true;
+            }
+            SysFreeString(url);
+            pBrowser2->Release();
+        }
+
+        disp->Release();
+    }
+    shellWindows->Release();
+
+    if (!found) {
+        ShellExecuteW(NULL, L"open", directory, NULL, NULL, SW_SHOWNORMAL);
+    }
+
+    return true;
+}
diff --git a/vdagent/shell.h b/vdagent/shell.h
new file mode 100644
index 0000000..49cf653
--- /dev/null
+++ b/vdagent/shell.h
@@ -0,0 +1,22 @@
+/*
+   Copyright (C) 2019 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <windows.h>
+
+bool open_shell_directory(LPCWSTR directory);
diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp
index 177e663..9b5e312 100644
--- a/vdagent/vdagent.cpp
+++ b/vdagent/vdagent.cpp
@@ -423,6 +423,18 @@ void VDAgent::input_desktop_message_loop()
     }
     CloseDesktop(hdesk);
 
+    if (FAILED(CoInitialize(NULL))) {
+        vd_printf("CoInitialize failed");
+        _running = false;
+        return;
+    }
+    struct ComDeinit {
+        ~ComDeinit()
+        {
+            CoUninitialize();
+        }
+    } com_deinit;
+
     // loading the display settings for the current session's logged on user only
     // after 1) we receive logon event, and 2) the desktop switched from Winlogon
     if (_tcscmp(desktop_name, TEXT("Winlogon")) == 0) {
commit 8064ae397aa65a33a8b47fb77de77766b40e40b2
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Wed Feb 27 12:10:52 2019 +0000

    file_xfer: Save files in Downloads directory
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/Makefile.am b/Makefile.am
index 9b779a7..8c527b1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,7 +23,7 @@ LIBS = -lversion
 
 bin_PROGRAMS = vdagent vdservice
 
-vdagent_LDADD = $(LIBPNG_LIBS) $(ZLIB_LIBS) -lwtsapi32 -lgdi32 vdagent_rc.$(OBJEXT)
+vdagent_LDADD = $(LIBPNG_LIBS) $(ZLIB_LIBS) -lwtsapi32 -lgdi32 -luuid -lole32 vdagent_rc.$(OBJEXT)
 vdagent_CXXFLAGS = $(AM_CXXFLAGS) $(LIBPNG_CFLAGS)
 vdagent_LDFLAGS = $(AM_LDFLAGS) -Wl,--subsystem,windows
 vdagent_SOURCES =			\
diff --git a/vdagent/file_xfer.cpp b/vdagent/file_xfer.cpp
index 66c9359..9046c0c 100644
--- a/vdagent/file_xfer.cpp
+++ b/vdagent/file_xfer.cpp
@@ -48,10 +48,27 @@ FileXfer::~FileXfer()
 {
 }
 
+typedef HRESULT WINAPI
+SHGetKnownFolderPath_type(REFKNOWNFOLDERID rfid, DWORD dwFlags,
+                          HANDLE hToken, PWSTR *ppszPath);
+
 static bool get_download_directory(TCHAR file_path[MAX_PATH])
 {
-    if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL,
-            SHGFP_TYPE_CURRENT, file_path))) {
+    file_path[0] = 0;
+    PWSTR path;
+    HMODULE shell32 = GetModuleHandle(L"shell32.dll");
+    SHGetKnownFolderPath_type *SHGetKnownFolderPath_p =
+        (SHGetKnownFolderPath_type *) GetProcAddress(shell32, "SHGetKnownFolderPath");
+    if (SHGetKnownFolderPath_p &&
+        SUCCEEDED(SHGetKnownFolderPath_p(FOLDERID_Downloads, 0, NULL, &path))) {
+        if (_tcslen(path) < MAX_PATH) {
+            _tcscpy(file_path, path);
+        }
+        CoTaskMemFree(path);
+    }
+    if (file_path[0] == 0 &&
+        FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL,
+                               SHGFP_TYPE_CURRENT, file_path))) {
         vd_printf("failed getting desktop path");
         return false;
     }
commit 5fbeb1af4eacfe2dad73d42f06f3bc454a18df05
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Thu Nov 7 16:11:39 2019 +0000

    file_xfer: Separate code to get download directory location
    
    Allows to reuse code to get download directory.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/vdagent/file_xfer.cpp b/vdagent/file_xfer.cpp
index ada3b47..66c9359 100644
--- a/vdagent/file_xfer.cpp
+++ b/vdagent/file_xfer.cpp
@@ -48,6 +48,16 @@ FileXfer::~FileXfer()
 {
 }
 
+static bool get_download_directory(TCHAR file_path[MAX_PATH])
+{
+    if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL,
+            SHGFP_TYPE_CURRENT, file_path))) {
+        vd_printf("failed getting desktop path");
+        return false;
+    }
+    return true;
+}
+
 void FileXfer::handle_start(VDAgentFileXferStartMessage* start,
                             VDAgentFileXferStatusMessage* status)
 {
@@ -77,9 +87,7 @@ void FileXfer::handle_start(VDAgentFileXferStartMessage* start,
         return;
     }
 
-    if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY | CSIDL_FLAG_CREATE, NULL,
-            SHGFP_TYPE_CURRENT, file_path))) {
-        vd_printf("failed getting desktop path");
+    if (!get_download_directory(file_path)) {
         return;
     }
     if (!GetDiskFreeSpaceEx(file_path, &free_bytes, NULL, NULL)) {


More information about the Spice-commits mailing list