1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #ifdef _MSC_VER
25 #pragma warning(push, 1) /* disable warnings within system headers */
26 #endif
27 #define WIN32_LEAN_AND_MEAN
28 #include <windows.h>
29 #include <msiquery.h>
30 #ifdef _MSC_VER
31 #pragma warning(pop)
32 #endif
33 
34 #include <malloc.h>
35 #include <tchar.h>
36 #include <string>
37 #include <stdexcept>
38 #include <vector>
39 
40 class RegistryKeyGuard
41 {
42 public:
43     RegistryKeyGuard(HKEY hkey = 0) :
44         hkey_(hkey)
45     {
46     }
47 
48     ~RegistryKeyGuard()
49     {
50         if (hkey_)
51             RegCloseKey(hkey_);
52     }
53 private:
54     HKEY hkey_;
55 
56 private:
57     RegistryKeyGuard(const RegistryKeyGuard&);
58     RegistryKeyGuard& operator=(const RegistryKeyGuard&);
59 };
60 
61 typedef std::vector<TCHAR> CharacterBuffer_t;
62 
63 /* throws std::runtime_error when the value "Path" could
64    not be found or contains an empty string or is not of
65    type REG_SZ. All such conditions are invalid for a
66    properly installed product. */
67 std::string FindProductInstallationPath(HKEY hkey)
68 {
69     DWORD nSubKeys;
70     DWORD lLongestSubKey;
71 
72     if (RegQueryInfoKey(hkey, NULL, NULL, NULL, &nSubKeys, &lLongestSubKey, NULL, NULL, NULL, NULL, NULL, NULL) !=
73         ERROR_SUCCESS)
74         throw std::runtime_error("Cannot query info for registery key");
75 
76     CharacterBuffer_t buff(lLongestSubKey + 1);
77 
78     for (DWORD i = 0; i < nSubKeys; i++)
79     {
80         buff[0] = 0;
81         LONG ret = RegEnumKey(hkey, i, &buff[0], buff.size());
82 
83         if ((ret != ERROR_SUCCESS) && (ret != ERROR_MORE_DATA))
84             throw std::runtime_error("Error enumerating registry key");
85 
86         HKEY hSubKey;
87         if (RegOpenKey(hkey, &buff[0], &hSubKey) != ERROR_SUCCESS)
88             continue;
89 
90         RegistryKeyGuard guard(hSubKey);
91 
92         DWORD type;
93         TCHAR pbuff[MAX_PATH];
94         DWORD size = sizeof(pbuff);
95         if ((RegQueryValueEx(
96                 hSubKey, TEXT("Path"), NULL, &type, reinterpret_cast<LPBYTE>(pbuff), &size) != ERROR_SUCCESS) ||
97                 (type != REG_SZ))
98             continue;
99 
100         std::string path(pbuff);
101         std::string::size_type idx = path.rfind("program\\soffice.exe");
102         if (idx != std::string::npos)
103             return path.substr(0, idx);
104     } // for
105 
106     throw std::runtime_error("No valid product path found");
107 }
108 
109 UINT GetInstallProperty(MSIHANDLE handle, LPCTSTR name, CharacterBuffer_t* buffer)
110 {
111     DWORD size = buffer->size();
112     UINT ret = MsiGetProperty(handle, name, &(*buffer)[0], &size);
113 
114     if (ret == ERROR_MORE_DATA)
115     {
116         buffer->resize(size + 1);
117         size = buffer->size();
118         ret = MsiGetProperty(handle, name, &(*buffer)[0], &size);
119     }
120     return ret;
121 }
122 
123 /*
124     Try to find the installation path to an already installed product.
125     The installation path will be written in the Windows registry
126     during the installation. There may exist different products in
127     parallel e.g. StarOffice, StarSuite, OpenOffice.org. It will be
128     searched in this order for an installed product. If a product
129     will be found the path to the product will be set in the property
130     "INSTALLLOCATION" else nothing will be done.
131 */
132 extern "C" UINT __stdcall SetProductInstallationPath(MSIHANDLE handle)
133 {
134     //MessageBox(NULL, TEXT("SetProductInstallationPath"), TEXT("Language Pack Installation Helper"), MB_OK | MB_ICONINFORMATION);
135 
136     try
137     {
138         CharacterBuffer_t regKeyProdPath(MAX_PATH);
139 
140         GetInstallProperty(handle, TEXT("REGKEYPRODPATH"), &regKeyProdPath);
141 
142         HKEY hKey;
143         if ((RegOpenKey(HKEY_CURRENT_USER, &regKeyProdPath[0], &hKey) == ERROR_SUCCESS) ||
144             (RegOpenKey(HKEY_LOCAL_MACHINE, &regKeyProdPath[0], &hKey) == ERROR_SUCCESS))
145         {
146             RegistryKeyGuard guard(hKey);
147             std::string path = FindProductInstallationPath(hKey);
148             MsiSetProperty(handle, TEXT("INSTALLLOCATION"), path.c_str());
149         }
150     }
151     catch(std::runtime_error& ex)
152     {
153         ex = ex; // no warnings
154     }
155     return ERROR_SUCCESS;
156 }
157 
158 void MakeCfgimportCommandLine(CharacterBuffer_t* productPath)
159 {
160     char* p = &(*productPath)[0] + lstrlen(&(*productPath)[0]) - 1;
161 
162     if (*p != '\\')
163         lstrcat(&(*productPath)[0], "\\program\\configimport.exe --spool");
164     else
165         lstrcat(&(*productPath)[0], "program\\configimport.exe --spool");
166 }
167 
168 /*
169     Calls configimport.exe --spool
170 */
171 extern "C" UINT __stdcall RegisterLanguagePack(MSIHANDLE handle)
172 {
173     //MessageBox(NULL, TEXT("RegisterLanguagePack"), TEXT("Language Pack Installation Helper"), MB_OK | MB_ICONINFORMATION);
174 
175     CharacterBuffer_t productPath(MAX_PATH);
176     GetInstallProperty(handle, TEXT("INSTALLLOCATION"), &productPath);
177     MakeCfgimportCommandLine(&productPath);
178 
179     STARTUPINFO si;
180     ZeroMemory(&si, sizeof(si));
181     si.cb = sizeof(si);
182 
183     PROCESS_INFORMATION pi;
184     ZeroMemory(&pi, sizeof(pi));
185 
186     if (CreateProcess(
187             NULL, &productPath[0], NULL, NULL,
188             FALSE, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS, NULL,
189             NULL, &si, &pi))
190     {
191         // Wait until child process exits.
192         WaitForSingleObject(pi.hProcess, INFINITE);
193 
194         // Close process and thread handles.
195         CloseHandle(pi.hProcess);
196         CloseHandle(pi.hThread);
197     }
198     return ERROR_SUCCESS;
199 }
200 
201