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