summaryrefslogtreecommitdiff
path: root/src/openvpnmsica/dllmain.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpnmsica/dllmain.c')
-rw-r--r--src/openvpnmsica/dllmain.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/openvpnmsica/dllmain.c b/src/openvpnmsica/dllmain.c
new file mode 100644
index 0000000..201fd9a
--- /dev/null
+++ b/src/openvpnmsica/dllmain.c
@@ -0,0 +1,198 @@
+/*
+ * 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 "openvpnmsica.h"
+#include "../tapctl/error.h"
+
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+#ifdef _MSC_VER
+#pragma comment(lib, "msi.lib")
+#endif
+#include <stdio.h>
+#include <tchar.h>
+
+
+DWORD openvpnmsica_thread_data_idx = TLS_OUT_OF_INDEXES;
+
+
+/**
+ * DLL entry point
+ */
+BOOL WINAPI
+DllMain(
+ _In_ HINSTANCE hinstDLL,
+ _In_ DWORD dwReason,
+ _In_ LPVOID lpReserved)
+{
+ UNREFERENCED_PARAMETER(hinstDLL);
+ UNREFERENCED_PARAMETER(lpReserved);
+
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Allocate thread local storage index. */
+ openvpnmsica_thread_data_idx = TlsAlloc();
+ if (openvpnmsica_thread_data_idx == TLS_OUT_OF_INDEXES)
+ {
+ return FALSE;
+ }
+ /* Fall through. */
+
+ case DLL_THREAD_ATTACH:
+ {
+ /* Create thread local storage data. */
+ struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)calloc(1, sizeof(struct openvpnmsica_thread_data));
+ if (s == NULL)
+ {
+ return FALSE;
+ }
+
+ TlsSetValue(openvpnmsica_thread_data_idx, s);
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ if (openvpnmsica_thread_data_idx != TLS_OUT_OF_INDEXES)
+ {
+ /* Free thread local storage data and index. */
+ free(TlsGetValue(openvpnmsica_thread_data_idx));
+ TlsFree(openvpnmsica_thread_data_idx);
+ }
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Free thread local storage data. */
+ free(TlsGetValue(openvpnmsica_thread_data_idx));
+ break;
+ }
+
+ return TRUE;
+}
+
+
+bool
+dont_mute(unsigned int flags)
+{
+ UNREFERENCED_PARAMETER(flags);
+
+ return true;
+}
+
+
+void
+x_msg_va(const unsigned int flags, const char *format, va_list arglist)
+{
+ /* Secure last error before it is overridden. */
+ DWORD dwResult = (flags & M_ERRNO) != 0 ? GetLastError() : ERROR_SUCCESS;
+
+ struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)TlsGetValue(openvpnmsica_thread_data_idx);
+ if (s->hInstall == 0)
+ {
+ /* No MSI session, no fun. */
+ return;
+ }
+
+ /* Prepare the message record. The record will contain up to four fields. */
+ MSIHANDLE hRecordProg = MsiCreateRecord(4);
+
+ {
+ /* Field 2: The message string. */
+ char szBufStack[128];
+ int iResultLen = vsnprintf(szBufStack, _countof(szBufStack), format, arglist);
+ if (iResultLen < _countof(szBufStack))
+ {
+ /* Use from stack. */
+ MsiRecordSetStringA(hRecordProg, 2, szBufStack);
+ }
+ else
+ {
+ /* Allocate on heap and retry. */
+ char *szMessage = (char *)malloc(++iResultLen * sizeof(char));
+ if (szMessage != NULL)
+ {
+ vsnprintf(szMessage, iResultLen, format, arglist);
+ MsiRecordSetStringA(hRecordProg, 2, szMessage);
+ free(szMessage);
+ }
+ else
+ {
+ /* Use stack variant anyway, but make sure it's zero-terminated. */
+ szBufStack[_countof(szBufStack) - 1] = 0;
+ MsiRecordSetStringA(hRecordProg, 2, szBufStack);
+ }
+ }
+ }
+
+ if ((flags & M_ERRNO) == 0)
+ {
+ /* Field 1: MSI Error Code */
+ MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA);
+ }
+ else
+ {
+ /* Field 1: MSI Error Code */
+ MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA_ERRNO);
+
+ /* Field 3: The Windows error number. */
+ MsiRecordSetInteger(hRecordProg, 3, dwResult);
+
+ /* Field 4: The Windows error description. */
+ LPTSTR szErrMessage = NULL;
+ if (FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0,
+ dwResult,
+ 0,
+ (LPTSTR)&szErrMessage,
+ 0,
+ NULL) && szErrMessage)
+ {
+ /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
+ for (size_t i = 0, i_last = 0;; i++)
+ {
+ if (szErrMessage[i])
+ {
+ if (!_istspace(szErrMessage[i]))
+ {
+ i_last = i + 1;
+ }
+ }
+ else
+ {
+ szErrMessage[i_last] = 0;
+ break;
+ }
+ }
+ MsiRecordSetString(hRecordProg, 4, szErrMessage);
+ LocalFree(szErrMessage);
+ }
+ }
+
+ MsiProcessMessage(s->hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
+ MsiCloseHandle(hRecordProg);
+}