OpenVPN
msiex.c
Go to the documentation of this file.
1/*
2 * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
3 * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
4 *
5 * Copyright (C) 2018-2024 Simon Rozman <simon@rozman.si>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include "msiex.h"
26#include "../tapctl/error.h"
27
28#include <windows.h>
29#include <malloc.h>
30#include <memory.h>
31#include <msiquery.h>
32#ifdef _MSC_VER
33#pragma comment(lib, "msi.lib")
34#endif
35
36
37UINT
39 _In_ MSIHANDLE hInstall,
40 _In_z_ LPCTSTR szName,
41 _Out_ LPTSTR *pszValue)
42{
43 if (pszValue == NULL)
44 {
45 return ERROR_BAD_ARGUMENTS;
46 }
47
48 /* Try with stack buffer first. */
49 TCHAR szBufStack[128];
50 DWORD dwLength = _countof(szBufStack);
51 UINT uiResult = MsiGetProperty(hInstall, szName, szBufStack, &dwLength);
52 if (uiResult == ERROR_SUCCESS)
53 {
54 /* Copy from stack. */
55 *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
56 if (*pszValue == NULL)
57 {
58 msg(M_FATAL, "%s: malloc(%u) failed", dwLength * sizeof(TCHAR));
59 return ERROR_OUTOFMEMORY;
60 }
61
62 memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
63 return ERROR_SUCCESS;
64 }
65 else if (uiResult == ERROR_MORE_DATA)
66 {
67 /* Allocate on heap and retry. */
68 LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
69 if (szBufHeap == NULL)
70 {
71 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
72 return ERROR_OUTOFMEMORY;
73 }
74
75 uiResult = MsiGetProperty(hInstall, szName, szBufHeap, &dwLength);
76 if (uiResult == ERROR_SUCCESS)
77 {
78 *pszValue = szBufHeap;
79 }
80 else
81 {
82 free(szBufHeap);
83 }
84 return uiResult;
85 }
86 else
87 {
88 SetLastError(uiResult); /* MSDN does not mention MsiGetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
89 msg(M_NONFATAL | M_ERRNO, "%s: MsiGetProperty failed", __FUNCTION__);
90 return uiResult;
91 }
92}
93
94
95UINT
97 _In_ MSIHANDLE hRecord,
98 _In_ unsigned int iField,
99 _Out_ LPTSTR *pszValue)
100{
101 if (pszValue == NULL)
102 {
103 return ERROR_BAD_ARGUMENTS;
104 }
105
106 /* Try with stack buffer first. */
107 TCHAR szBufStack[128];
108 DWORD dwLength = _countof(szBufStack);
109 UINT uiResult = MsiRecordGetString(hRecord, iField, szBufStack, &dwLength);
110 if (uiResult == ERROR_SUCCESS)
111 {
112 /* Copy from stack. */
113 *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
114 if (*pszValue == NULL)
115 {
116 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
117 return ERROR_OUTOFMEMORY;
118 }
119
120 memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
121 return ERROR_SUCCESS;
122 }
123 else if (uiResult == ERROR_MORE_DATA)
124 {
125 /* Allocate on heap and retry. */
126 LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
127 if (szBufHeap == NULL)
128 {
129 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
130 return ERROR_OUTOFMEMORY;
131 }
132
133 uiResult = MsiRecordGetString(hRecord, iField, szBufHeap, &dwLength);
134 if (uiResult == ERROR_SUCCESS)
135 {
136 *pszValue = szBufHeap;
137 }
138 else
139 {
140 free(szBufHeap);
141 }
142 return uiResult;
143 }
144 else
145 {
146 SetLastError(uiResult); /* MSDN does not mention MsiRecordGetString() to set GetLastError(). But we do have an error code. Set last error manually. */
147 msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordGetString failed", __FUNCTION__);
148 return uiResult;
149 }
150}
151
152
153UINT
155 _In_ MSIHANDLE hInstall,
156 _In_ MSIHANDLE hRecord,
157 _Out_ LPTSTR *pszValue)
158{
159 if (pszValue == NULL)
160 {
161 return ERROR_BAD_ARGUMENTS;
162 }
163
164 /* Try with stack buffer first. */
165 TCHAR szBufStack[128];
166 DWORD dwLength = _countof(szBufStack);
167 UINT uiResult = MsiFormatRecord(hInstall, hRecord, szBufStack, &dwLength);
168 if (uiResult == ERROR_SUCCESS)
169 {
170 /* Copy from stack. */
171 *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
172 if (*pszValue == NULL)
173 {
174 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
175 return ERROR_OUTOFMEMORY;
176 }
177
178 memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
179 return ERROR_SUCCESS;
180 }
181 else if (uiResult == ERROR_MORE_DATA)
182 {
183 /* Allocate on heap and retry. */
184 LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
185 if (szBufHeap == NULL)
186 {
187 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
188 return ERROR_OUTOFMEMORY;
189 }
190
191 uiResult = MsiFormatRecord(hInstall, hRecord, szBufHeap, &dwLength);
192 if (uiResult == ERROR_SUCCESS)
193 {
194 *pszValue = szBufHeap;
195 }
196 else
197 {
198 free(szBufHeap);
199 }
200 return uiResult;
201 }
202 else
203 {
204 SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */
205 msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
206 return uiResult;
207 }
208}
209
210
211UINT
213 _In_ MSIHANDLE hInstall,
214 _In_ MSIHANDLE hRecord,
215 _In_ unsigned int iField,
216 _Out_ LPTSTR *pszValue)
217{
218 if (pszValue == NULL)
219 {
220 return ERROR_BAD_ARGUMENTS;
221 }
222
223 /* Read string to format. */
224 LPTSTR szValue = NULL;
225 UINT uiResult = msi_get_record_string(hRecord, iField, &szValue);
226 if (uiResult != ERROR_SUCCESS)
227 {
228 return uiResult;
229 }
230 if (szValue[0] == 0)
231 {
232 /* The string is empty. There's nothing left to do. */
233 *pszValue = szValue;
234 return ERROR_SUCCESS;
235 }
236
237 /* Create a temporary record. */
238 MSIHANDLE hRecordEx = MsiCreateRecord(1);
239 if (!hRecordEx)
240 {
241 uiResult = ERROR_INVALID_HANDLE;
242 msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
243 goto cleanup_szValue;
244 }
245
246 /* Populate the record with data. */
247 uiResult = MsiRecordSetString(hRecordEx, 0, szValue);
248 if (uiResult != ERROR_SUCCESS)
249 {
250 SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */
251 msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
252 goto cleanup_hRecordEx;
253 }
254
255 /* Do the formatting. */
256 uiResult = msi_format_record(hInstall, hRecordEx, pszValue);
257
258cleanup_hRecordEx:
259 MsiCloseHandle(hRecordEx);
260cleanup_szValue:
261 free(szValue);
262 return uiResult;
263}
UINT msi_get_string(_In_ MSIHANDLE hInstall, _In_z_ LPCTSTR szName, _Out_ LPTSTR *pszValue)
Gets MSI property value.
Definition msiex.c:38
UINT msi_format_field(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPTSTR *pszValue)
Formats MSI record field.
Definition msiex.c:212
UINT msi_get_record_string(_In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPTSTR *pszValue)
Gets MSI record string value.
Definition msiex.c:96
UINT msi_format_record(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _Out_ LPTSTR *pszValue)
Formats MSI record.
Definition msiex.c:154
#define M_FATAL
Definition error.h:89
#define M_NONFATAL
Definition error.h:90
#define msg(flags,...)
Definition error.h:144
#define M_ERRNO
Definition error.h:94
#define _In_z_
Definition basic.h:48
#define _Out_
Definition basic.h:57
#define _In_
Definition basic.h:42