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-2023 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 
37 UINT
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 
95 UINT
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 
153 UINT
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 
211 UINT
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 
258 cleanup_hRecordEx:
259  MsiCloseHandle(hRecordEx);
260 cleanup_szValue:
261  free(szValue);
262  return uiResult;
263 }
M_ERRNO
#define M_ERRNO
Definition: error.h:100
M_FATAL
#define M_FATAL
Definition: error.h:95
M_NONFATAL
#define M_NONFATAL
Definition: error.h:96
msiex.h
msi_get_string
UINT msi_get_string(_In_ MSIHANDLE hInstall, _In_z_ LPCTSTR szName, _Out_ LPTSTR *pszValue)
Gets MSI property value.
Definition: msiex.c:38
msi_get_record_string
UINT msi_get_record_string(_In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPTSTR *pszValue)
Gets MSI record string value.
Definition: msiex.c:96
msi_format_field
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
msi_format_record
UINT msi_format_record(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _Out_ LPTSTR *pszValue)
Formats MSI record.
Definition: msiex.c:154
_In_
#define _In_
Definition: basic.h:42
_Out_
#define _Out_
Definition: basic.h:57
config.h
_In_z_
#define _In_z_
Definition: basic.h:48
msg
#define msg(flags,...)
Definition: error.h:150