1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include <windows.h>
6 #include "../../../../toolkit/mozapps/update/common/pathhash.h"
7 
8 #pragma comment(lib, "advapi32.lib")
9 
10 typedef struct _stack_t {
11   struct _stack_t *next;
12   TCHAR text[MAX_PATH];
13 } stack_t;
14 
15 int popstring(stack_t **stacktop, LPTSTR str, int len);
16 void pushstring(stack_t **stacktop, LPCTSTR str, int len);
17 
18 /**
19  * Determines if the specified service exists or not
20  *
21  * @param  serviceName The name of the service to check
22  * @param  exists      Whether or not the service exists
23  * @return TRUE if there were no errors
24  */
25 static BOOL
IsServiceInstalled(LPCWSTR serviceName,BOOL & exists)26 IsServiceInstalled(LPCWSTR serviceName, BOOL &exists)
27 {
28   exists = FALSE;
29 
30   // Get a handle to the local computer SCM database with full access rights.
31   SC_HANDLE serviceManager = OpenSCManager(NULL, NULL,
32                                            SC_MANAGER_ENUMERATE_SERVICE);
33   if (!serviceManager) {
34     return FALSE;
35   }
36 
37   SC_HANDLE serviceHandle = OpenServiceW(serviceManager,
38                                          serviceName,
39                                          SERVICE_QUERY_CONFIG);
40   if (!serviceHandle && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST) {
41     CloseServiceHandle(serviceManager);
42     return FALSE;
43   }
44 
45   if (serviceHandle) {
46     CloseServiceHandle(serviceHandle);
47     exists = TRUE;
48   }
49 
50   CloseServiceHandle(serviceManager);
51   return TRUE;
52 }
53 
54 /**
55  * Determines if the specified service is installed or not
56  *
57  * @param  stacktop  A pointer to the top of the stack
58  * @param  variables A pointer to the NSIS variables
59  * @return 0 if the service does not exist
60  *         1 if the service does exist
61  *         -1 if there was an error.
62  */
63 extern "C" void __declspec(dllexport)
IsInstalled(HWND hwndParent,int string_size,TCHAR * variables,stack_t ** stacktop,void * extra)64 IsInstalled(HWND hwndParent, int string_size,
65             TCHAR *variables, stack_t **stacktop, void *extra)
66 {
67   TCHAR tmp[MAX_PATH] = { L'\0' };
68   WCHAR serviceName[MAX_PATH] = { '\0' };
69   popstring(stacktop, tmp, MAX_PATH);
70 
71 #if !defined(UNICODE)
72     MultiByteToWideChar(CP_ACP, 0, tmp, -1, serviceName, MAX_PATH);
73 #else
74     wcscpy(serviceName, tmp);
75 #endif
76 
77   BOOL serviceInstalled;
78   if (!IsServiceInstalled(serviceName, serviceInstalled)) {
79     pushstring(stacktop, TEXT("-1"), 3);
80   } else {
81     pushstring(stacktop, serviceInstalled ? TEXT("1") : TEXT("0"), 2);
82   }
83 }
84 
85 /**
86  * Stops the specified service.
87  *
88  * @param  serviceName The name of the service to stop
89  * @return TRUE if the operation was successful
90  */
91 static BOOL
StopService(LPCWSTR serviceName)92 StopService(LPCWSTR serviceName)
93 {
94   // Get a handle to the local computer SCM database with full access rights.
95   SC_HANDLE serviceManager = OpenSCManager(NULL, NULL,
96                                            SC_MANAGER_ENUMERATE_SERVICE);
97   if (!serviceManager) {
98     return FALSE;
99   }
100 
101   SC_HANDLE serviceHandle = OpenServiceW(serviceManager,
102                                          serviceName,
103                                          SERVICE_STOP);
104   if (!serviceHandle) {
105     CloseServiceHandle(serviceManager);
106     return FALSE;
107   }
108 
109   //Stop the service so it deletes faster and so the uninstaller
110   // can actually delete its EXE.
111   DWORD totalWaitTime = 0;
112   SERVICE_STATUS status;
113   static const int maxWaitTime = 1000 * 60; // Never wait more than a minute
114   BOOL stopped = FALSE;
115   if (ControlService(serviceHandle, SERVICE_CONTROL_STOP, &status)) {
116     do {
117       Sleep(status.dwWaitHint);
118       // + 10 milliseconds to make sure we always approach maxWaitTime
119       totalWaitTime += (status.dwWaitHint + 10);
120       if (status.dwCurrentState == SERVICE_STOPPED) {
121         stopped = true;
122         break;
123       } else if (totalWaitTime > maxWaitTime) {
124         break;
125       }
126     } while (QueryServiceStatus(serviceHandle, &status));
127   }
128 
129   CloseServiceHandle(serviceHandle);
130   CloseServiceHandle(serviceManager);
131   return stopped;
132 }
133 
134 /**
135  * Stops the specified service
136  *
137  * @param  stacktop  A pointer to the top of the stack
138  * @param  variables A pointer to the NSIS variables
139  * @return 1 if the service was stopped, 0 on error
140  */
141 extern "C" void __declspec(dllexport)
Stop(HWND hwndParent,int string_size,TCHAR * variables,stack_t ** stacktop,void * extra)142 Stop(HWND hwndParent, int string_size,
143      TCHAR *variables, stack_t **stacktop, void *extra)
144 {
145   TCHAR tmp[MAX_PATH] = { L'\0' };
146   WCHAR serviceName[MAX_PATH] = { '\0' };
147 
148   popstring(stacktop, tmp, MAX_PATH);
149 
150 #if !defined(UNICODE)
151     MultiByteToWideChar(CP_ACP, 0, tmp, -1, serviceName, MAX_PATH);
152 #else
153     wcscpy(serviceName, tmp);
154 #endif
155 
156   if (StopService(serviceName)) {
157     pushstring(stacktop, TEXT("1"), 2);
158   } else {
159     pushstring(stacktop, TEXT("0"), 2);
160   }
161 }
162 
163 /**
164  * Determines a unique registry path from a file or directory path
165  *
166  * @param  stacktop  A pointer to the top of the stack
167  * @param  variables A pointer to the NSIS variables
168  * @return The unique registry path or an empty string on error
169  */
170 extern "C" void __declspec(dllexport)
PathToUniqueRegistryPath(HWND hwndParent,int string_size,TCHAR * variables,stack_t ** stacktop,void * extra)171 PathToUniqueRegistryPath(HWND hwndParent, int string_size,
172                          TCHAR *variables, stack_t **stacktop,
173                          void *extra)
174 {
175   TCHAR tmp[MAX_PATH] = { L'\0' };
176   WCHAR installBasePath[MAX_PATH] = { '\0' };
177   popstring(stacktop, tmp, MAX_PATH);
178 
179 #if !defined(UNICODE)
180     MultiByteToWideChar(CP_ACP, 0, tmp, -1, installBasePath, MAX_PATH);
181 #else
182     wcscpy(installBasePath, tmp);
183 #endif
184 
185   WCHAR registryPath[MAX_PATH + 1] = { '\0' };
186   if (CalculateRegistryPathFromFilePath(installBasePath, registryPath)) {
187     pushstring(stacktop, registryPath, wcslen(registryPath) + 1);
188   } else {
189     pushstring(stacktop, TEXT(""), 1);
190   }
191 }
192 
193 BOOL WINAPI
DllMain(HANDLE hInst,ULONG ul_reason_for_call,LPVOID lpReserved)194 DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
195 {
196   return TRUE;
197 }
198 
199 /**
200  * Removes an element from the top of the NSIS stack
201  *
202  * @param  stacktop A pointer to the top of the stack
203  * @param  str      The string to pop to
204  * @param  len      The max length
205  * @return 0 on success
206 */
popstring(stack_t ** stacktop,TCHAR * str,int len)207 int popstring(stack_t **stacktop, TCHAR *str, int len)
208 {
209   // Removes the element from the top of the stack and puts it in the buffer
210   stack_t *th;
211   if (!stacktop || !*stacktop) {
212     return 1;
213   }
214 
215   th = (*stacktop);
216   lstrcpyn(str,th->text, len);
217   *stacktop = th->next;
218   GlobalFree((HGLOBAL)th);
219   return 0;
220 }
221 
222 /**
223  * Adds an element to the top of the NSIS stack
224  *
225  * @param  stacktop A pointer to the top of the stack
226  * @param  str      The string to push on the stack
227  * @param  len      The length of the string to push on the stack
228  * @return 0 on success
229 */
pushstring(stack_t ** stacktop,const TCHAR * str,int len)230 void pushstring(stack_t **stacktop, const TCHAR *str, int len)
231 {
232   stack_t *th;
233   if (!stacktop) {
234     return;
235   }
236 
237   th = (stack_t*)GlobalAlloc(GPTR, sizeof(stack_t) + len);
238   lstrcpyn(th->text, str, len);
239   th->next = *stacktop;
240   *stacktop = th;
241 }
242