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 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 #elif defined(_MSC_VER)
24 #include <config-msvc.h>
25 #endif
26 
27 #include "msiex.h"
28 #include "../tapctl/error.h"
29 
30 #include <windows.h>
31 #include <malloc.h>
32 #include <memory.h>
33 #include <msiquery.h>
34 #ifdef _MSC_VER
35 #pragma comment(lib, "msi.lib")
36 #endif
37 
38 
39 UINT
41  _In_ MSIHANDLE hInstall,
42  _In_z_ LPCTSTR szName,
43  _Out_ LPTSTR *pszValue)
44 {
45  if (pszValue == NULL)
46  {
47  return ERROR_BAD_ARGUMENTS;
48  }
49 
50  /* Try with stack buffer first. */
51  TCHAR szBufStack[128];
52  DWORD dwLength = _countof(szBufStack);
53  UINT uiResult = MsiGetProperty(hInstall, szName, szBufStack, &dwLength);
54  if (uiResult == ERROR_SUCCESS)
55  {
56  /* Copy from stack. */
57  *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
58  if (*pszValue == NULL)
59  {
60  msg(M_FATAL, "%s: malloc(%u) failed", dwLength * sizeof(TCHAR));
61  return ERROR_OUTOFMEMORY;
62  }
63 
64  memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
65  return ERROR_SUCCESS;
66  }
67  else if (uiResult == ERROR_MORE_DATA)
68  {
69  /* Allocate on heap and retry. */
70  LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
71  if (szBufHeap == NULL)
72  {
73  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
74  return ERROR_OUTOFMEMORY;
75  }
76 
77  uiResult = MsiGetProperty(hInstall, szName, szBufHeap, &dwLength);
78  if (uiResult == ERROR_SUCCESS)
79  {
80  *pszValue = szBufHeap;
81  }
82  else
83  {
84  free(szBufHeap);
85  }
86  return uiResult;
87  }
88  else
89  {
90  SetLastError(uiResult); /* MSDN does not mention MsiGetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
91  msg(M_NONFATAL | M_ERRNO, "%s: MsiGetProperty failed", __FUNCTION__);
92  return uiResult;
93  }
94 }
95 
96 
97 UINT
99  _In_ MSIHANDLE hRecord,
100  _In_ unsigned int iField,
101  _Out_ LPTSTR *pszValue)
102 {
103  if (pszValue == NULL)
104  {
105  return ERROR_BAD_ARGUMENTS;
106  }
107 
108  /* Try with stack buffer first. */
109  TCHAR szBufStack[128];
110  DWORD dwLength = _countof(szBufStack);
111  UINT uiResult = MsiRecordGetString(hRecord, iField, szBufStack, &dwLength);
112  if (uiResult == ERROR_SUCCESS)
113  {
114  /* Copy from stack. */
115  *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
116  if (*pszValue == NULL)
117  {
118  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
119  return ERROR_OUTOFMEMORY;
120  }
121 
122  memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
123  return ERROR_SUCCESS;
124  }
125  else if (uiResult == ERROR_MORE_DATA)
126  {
127  /* Allocate on heap and retry. */
128  LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
129  if (szBufHeap == NULL)
130  {
131  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
132  return ERROR_OUTOFMEMORY;
133  }
134 
135  uiResult = MsiRecordGetString(hRecord, iField, szBufHeap, &dwLength);
136  if (uiResult == ERROR_SUCCESS)
137  {
138  *pszValue = szBufHeap;
139  }
140  else
141  {
142  free(szBufHeap);
143  }
144  return uiResult;
145  }
146  else
147  {
148  SetLastError(uiResult); /* MSDN does not mention MsiRecordGetString() to set GetLastError(). But we do have an error code. Set last error manually. */
149  msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordGetString failed", __FUNCTION__);
150  return uiResult;
151  }
152 }
153 
154 
155 UINT
157  _In_ MSIHANDLE hInstall,
158  _In_ MSIHANDLE hRecord,
159  _Out_ LPTSTR *pszValue)
160 {
161  if (pszValue == NULL)
162  {
163  return ERROR_BAD_ARGUMENTS;
164  }
165 
166  /* Try with stack buffer first. */
167  TCHAR szBufStack[128];
168  DWORD dwLength = _countof(szBufStack);
169  UINT uiResult = MsiFormatRecord(hInstall, hRecord, szBufStack, &dwLength);
170  if (uiResult == ERROR_SUCCESS)
171  {
172  /* Copy from stack. */
173  *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
174  if (*pszValue == NULL)
175  {
176  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
177  return ERROR_OUTOFMEMORY;
178  }
179 
180  memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
181  return ERROR_SUCCESS;
182  }
183  else if (uiResult == ERROR_MORE_DATA)
184  {
185  /* Allocate on heap and retry. */
186  LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
187  if (szBufHeap == NULL)
188  {
189  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
190  return ERROR_OUTOFMEMORY;
191  }
192 
193  uiResult = MsiFormatRecord(hInstall, hRecord, szBufHeap, &dwLength);
194  if (uiResult == ERROR_SUCCESS)
195  {
196  *pszValue = szBufHeap;
197  }
198  else
199  {
200  free(szBufHeap);
201  }
202  return uiResult;
203  }
204  else
205  {
206  SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */
207  msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
208  return uiResult;
209  }
210 }
211 
212 
213 UINT
215  _In_ MSIHANDLE hInstall,
216  _In_ MSIHANDLE hRecord,
217  _In_ unsigned int iField,
218  _Out_ LPTSTR *pszValue)
219 {
220  if (pszValue == NULL)
221  {
222  return ERROR_BAD_ARGUMENTS;
223  }
224 
225  /* Read string to format. */
226  LPTSTR szValue = NULL;
227  UINT uiResult = msi_get_record_string(hRecord, iField, &szValue);
228  if (uiResult != ERROR_SUCCESS)
229  {
230  return uiResult;
231  }
232  if (szValue[0] == 0)
233  {
234  /* The string is empty. There's nothing left to do. */
235  *pszValue = szValue;
236  return ERROR_SUCCESS;
237  }
238 
239  /* Create a temporary record. */
240  MSIHANDLE hRecordEx = MsiCreateRecord(1);
241  if (!hRecordEx)
242  {
243  uiResult = ERROR_INVALID_HANDLE;
244  msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
245  goto cleanup_szValue;
246  }
247 
248  /* Populate the record with data. */
249  uiResult = MsiRecordSetString(hRecordEx, 0, szValue);
250  if (uiResult != ERROR_SUCCESS)
251  {
252  SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */
253  msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
254  goto cleanup_hRecordEx;
255  }
256 
257  /* Do the formatting. */
258  uiResult = msi_format_record(hInstall, hRecordEx, pszValue);
259 
260 cleanup_hRecordEx:
261  MsiCloseHandle(hRecordEx);
262 cleanup_szValue:
263  free(szValue);
264  return uiResult;
265 }
#define M_NONFATAL
Definition: error.h:95
UINT msi_get_record_string(_In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPTSTR *pszValue)
Gets MSI record string value.
Definition: msiex.c:98
LPCTSTR szName
Definition: openvpnmsica.c:67
#define malloc
Definition: cmocka.c:1795
#define _Out_
Definition: basic.h:46
#define _In_
Definition: basic.h:34
#define M_ERRNO
Definition: error.h:99
#define msg
Definition: error.h:173
UINT msi_get_string(_In_ MSIHANDLE hInstall, _In_z_ LPCTSTR szName, _Out_ LPTSTR *pszValue)
Gets MSI property value.
Definition: msiex.c:40
#define _In_z_
Definition: basic.h:40
#define M_FATAL
Definition: error.h:94
#define free
Definition: cmocka.c:1850
UINT msi_format_field(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPTSTR *pszValue)
Formats MSI record field.
Definition: msiex.c:214
UINT msi_format_record(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _Out_ LPTSTR *pszValue)
Formats MSI record.
Definition: msiex.c:156