summaryrefslogtreecommitdiff
path: root/src/openvpnmsica/msiex.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpnmsica/msiex.c')
-rw-r--r--src/openvpnmsica/msiex.c265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/openvpnmsica/msiex.c b/src/openvpnmsica/msiex.c
new file mode 100644
index 0000000..00265d0
--- /dev/null
+++ b/src/openvpnmsica/msiex.c
@@ -0,0 +1,265 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
+ *
+ * Copyright (C) 2018 Simon Rozman <simon@rozman.si>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#elif defined(_MSC_VER)
+#include <config-msvc.h>
+#endif
+
+#include "msiex.h"
+#include "../tapctl/error.h"
+
+#include <windows.h>
+#include <malloc.h>
+#include <memory.h>
+#include <msiquery.h>
+#ifdef _MSC_VER
+#pragma comment(lib, "msi.lib")
+#endif
+
+
+UINT
+msi_get_string(
+ _In_ MSIHANDLE hInstall,
+ _In_z_ LPCTSTR szName,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Try with stack buffer first. */
+ TCHAR szBufStack[128];
+ DWORD dwLength = _countof(szBufStack);
+ UINT uiResult = MsiGetProperty(hInstall, szName, szBufStack, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ /* Copy from stack. */
+ *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (*pszValue == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
+ return ERROR_SUCCESS;
+ }
+ else if (uiResult == ERROR_MORE_DATA)
+ {
+ /* Allocate on heap and retry. */
+ LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (szBufHeap == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ uiResult = MsiGetProperty(hInstall, szName, szBufHeap, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ *pszValue = szBufHeap;
+ }
+ else
+ {
+ free(szBufHeap);
+ }
+ return uiResult;
+ }
+ else
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiGetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiGetProperty failed", __FUNCTION__);
+ return uiResult;
+ }
+}
+
+
+UINT
+msi_get_record_string(
+ _In_ MSIHANDLE hRecord,
+ _In_ unsigned int iField,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Try with stack buffer first. */
+ TCHAR szBufStack[128];
+ DWORD dwLength = _countof(szBufStack);
+ UINT uiResult = MsiRecordGetString(hRecord, iField, szBufStack, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ /* Copy from stack. */
+ *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (*pszValue == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
+ return ERROR_SUCCESS;
+ }
+ else if (uiResult == ERROR_MORE_DATA)
+ {
+ /* Allocate on heap and retry. */
+ LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (szBufHeap == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ uiResult = MsiRecordGetString(hRecord, iField, szBufHeap, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ *pszValue = szBufHeap;
+ }
+ else
+ {
+ free(szBufHeap);
+ }
+ return uiResult;
+ }
+ else
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiRecordGetString() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordGetString failed", __FUNCTION__);
+ return uiResult;
+ }
+}
+
+
+UINT
+msi_format_record(
+ _In_ MSIHANDLE hInstall,
+ _In_ MSIHANDLE hRecord,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Try with stack buffer first. */
+ TCHAR szBufStack[128];
+ DWORD dwLength = _countof(szBufStack);
+ UINT uiResult = MsiFormatRecord(hInstall, hRecord, szBufStack, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ /* Copy from stack. */
+ *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (*pszValue == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
+ return ERROR_SUCCESS;
+ }
+ else if (uiResult == ERROR_MORE_DATA)
+ {
+ /* Allocate on heap and retry. */
+ LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (szBufHeap == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ uiResult = MsiFormatRecord(hInstall, hRecord, szBufHeap, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ *pszValue = szBufHeap;
+ }
+ else
+ {
+ free(szBufHeap);
+ }
+ return uiResult;
+ }
+ else
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
+ return uiResult;
+ }
+}
+
+
+UINT
+msi_format_field(
+ _In_ MSIHANDLE hInstall,
+ _In_ MSIHANDLE hRecord,
+ _In_ unsigned int iField,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Read string to format. */
+ LPTSTR szValue = NULL;
+ UINT uiResult = msi_get_record_string(hRecord, iField, &szValue);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ return uiResult;
+ }
+ if (szValue[0] == 0)
+ {
+ /* The string is empty. There's nothing left to do. */
+ *pszValue = szValue;
+ return ERROR_SUCCESS;
+ }
+
+ /* Create a temporary record. */
+ MSIHANDLE hRecordEx = MsiCreateRecord(1);
+ if (!hRecordEx)
+ {
+ uiResult = ERROR_INVALID_HANDLE;
+ msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
+ goto cleanup_szValue;
+ }
+
+ /* Populate the record with data. */
+ uiResult = MsiRecordSetString(hRecordEx, 0, szValue);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
+ goto cleanup_hRecordEx;
+ }
+
+ /* Do the formatting. */
+ uiResult = msi_format_record(hInstall, hRecordEx, pszValue);
+
+cleanup_hRecordEx:
+ MsiCloseHandle(hRecordEx);
+cleanup_szValue:
+ free(szValue);
+ return uiResult;
+}