toolkit/xre/nsWindowsRestart.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:b4cd4b4d8d18
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 // This file is not build directly. Instead, it is included in multiple
6 // shared objects.
7
8 #ifdef nsWindowsRestart_cpp
9 #error "nsWindowsRestart.cpp is not a header file, and must only be included once."
10 #else
11 #define nsWindowsRestart_cpp
12 #endif
13
14 #include "nsUTF8Utils.h"
15
16 #include <shellapi.h>
17
18 // Needed for CreateEnvironmentBlock
19 #include <userenv.h>
20 #pragma comment(lib, "userenv.lib")
21
22 /**
23 * Get the length that the string will take and takes into account the
24 * additional length if the string needs to be quoted and if characters need to
25 * be escaped.
26 */
27 static int ArgStrLen(const wchar_t *s)
28 {
29 int backslashes = 0;
30 int i = wcslen(s);
31 BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr;
32 // Only add doublequotes if the string contains a space or a tab
33 BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
34
35 if (addDoubleQuotes) {
36 i += 2; // initial and final duoblequote
37 }
38
39 if (hasDoubleQuote) {
40 while (*s) {
41 if (*s == '\\') {
42 ++backslashes;
43 } else {
44 if (*s == '"') {
45 // Escape the doublequote and all backslashes preceding the doublequote
46 i += backslashes + 1;
47 }
48
49 backslashes = 0;
50 }
51
52 ++s;
53 }
54 }
55
56 return i;
57 }
58
59 /**
60 * Copy string "s" to string "d", quoting the argument as appropriate and
61 * escaping doublequotes along with any backslashes that immediately precede
62 * doublequotes.
63 * The CRT parses this to retrieve the original argc/argv that we meant,
64 * see STDARGV.C in the MSVC CRT sources.
65 *
66 * @return the end of the string
67 */
68 static wchar_t* ArgToString(wchar_t *d, const wchar_t *s)
69 {
70 int backslashes = 0;
71 BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr;
72 // Only add doublequotes if the string contains a space or a tab
73 BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
74
75 if (addDoubleQuotes) {
76 *d = '"'; // initial doublequote
77 ++d;
78 }
79
80 if (hasDoubleQuote) {
81 int i;
82 while (*s) {
83 if (*s == '\\') {
84 ++backslashes;
85 } else {
86 if (*s == '"') {
87 // Escape the doublequote and all backslashes preceding the doublequote
88 for (i = 0; i <= backslashes; ++i) {
89 *d = '\\';
90 ++d;
91 }
92 }
93
94 backslashes = 0;
95 }
96
97 *d = *s;
98 ++d; ++s;
99 }
100 } else {
101 wcscpy(d, s);
102 d += wcslen(s);
103 }
104
105 if (addDoubleQuotes) {
106 *d = '"'; // final doublequote
107 ++d;
108 }
109
110 return d;
111 }
112
113 /**
114 * Creates a command line from a list of arguments. The returned
115 * string is allocated with "malloc" and should be "free"d.
116 *
117 * argv is UTF8
118 */
119 wchar_t*
120 MakeCommandLine(int argc, wchar_t **argv)
121 {
122 int i;
123 int len = 0;
124
125 // The + 1 of the last argument handles the allocation for null termination
126 for (i = 0; i < argc; ++i)
127 len += ArgStrLen(argv[i]) + 1;
128
129 // Protect against callers that pass 0 arguments
130 if (len == 0)
131 len = 1;
132
133 wchar_t *s = (wchar_t*) malloc(len * sizeof(wchar_t));
134 if (!s)
135 return nullptr;
136
137 wchar_t *c = s;
138 for (i = 0; i < argc; ++i) {
139 c = ArgToString(c, argv[i]);
140 if (i + 1 != argc) {
141 *c = ' ';
142 ++c;
143 }
144 }
145
146 *c = '\0';
147
148 return s;
149 }
150
151 /**
152 * Convert UTF8 to UTF16 without using the normal XPCOM goop, which we
153 * can't link to updater.exe.
154 */
155 static char16_t*
156 AllocConvertUTF8toUTF16(const char *arg)
157 {
158 // UTF16 can't be longer in units than UTF8
159 int len = strlen(arg);
160 char16_t *s = new char16_t[(len + 1) * sizeof(char16_t)];
161 if (!s)
162 return nullptr;
163
164 ConvertUTF8toUTF16 convert(s);
165 convert.write(arg, len);
166 convert.write_terminator();
167 return s;
168 }
169
170 static void
171 FreeAllocStrings(int argc, wchar_t **argv)
172 {
173 while (argc) {
174 --argc;
175 delete [] argv[argc];
176 }
177
178 delete [] argv;
179 }
180
181
182
183 /**
184 * Launch a child process with the specified arguments.
185 * @note argv[0] is ignored
186 * @note The form of this function that takes char **argv expects UTF-8
187 */
188
189 BOOL
190 WinLaunchChild(const wchar_t *exePath,
191 int argc, wchar_t **argv,
192 HANDLE userToken = nullptr,
193 HANDLE *hProcess = nullptr);
194
195 BOOL
196 WinLaunchChild(const wchar_t *exePath,
197 int argc, char **argv,
198 HANDLE userToken,
199 HANDLE *hProcess)
200 {
201 wchar_t** argvConverted = new wchar_t*[argc];
202 if (!argvConverted)
203 return FALSE;
204
205 for (int i = 0; i < argc; ++i) {
206 argvConverted[i] = reinterpret_cast<wchar_t*>(AllocConvertUTF8toUTF16(argv[i]));
207 if (!argvConverted[i]) {
208 FreeAllocStrings(i, argvConverted);
209 return FALSE;
210 }
211 }
212
213 BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken, hProcess);
214 FreeAllocStrings(argc, argvConverted);
215 return ok;
216 }
217
218 BOOL
219 WinLaunchChild(const wchar_t *exePath,
220 int argc,
221 wchar_t **argv,
222 HANDLE userToken,
223 HANDLE *hProcess)
224 {
225 wchar_t *cl;
226 BOOL ok;
227
228 cl = MakeCommandLine(argc, argv);
229 if (!cl) {
230 return FALSE;
231 }
232
233 STARTUPINFOW si = {0};
234 si.cb = sizeof(STARTUPINFOW);
235 si.lpDesktop = L"winsta0\\Default";
236 PROCESS_INFORMATION pi = {0};
237
238 if (userToken == nullptr) {
239 ok = CreateProcessW(exePath,
240 cl,
241 nullptr, // no special security attributes
242 nullptr, // no special thread attributes
243 FALSE, // don't inherit filehandles
244 0, // creation flags
245 nullptr, // inherit my environment
246 nullptr, // use my current directory
247 &si,
248 &pi);
249 } else {
250 // Create an environment block for the process we're about to start using
251 // the user's token.
252 LPVOID environmentBlock = nullptr;
253 if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) {
254 environmentBlock = nullptr;
255 }
256
257 ok = CreateProcessAsUserW(userToken,
258 exePath,
259 cl,
260 nullptr, // no special security attributes
261 nullptr, // no special thread attributes
262 FALSE, // don't inherit filehandles
263 0, // creation flags
264 environmentBlock,
265 nullptr, // use my current directory
266 &si,
267 &pi);
268
269 if (environmentBlock) {
270 DestroyEnvironmentBlock(environmentBlock);
271 }
272 }
273
274 if (ok) {
275 if (hProcess) {
276 *hProcess = pi.hProcess; // the caller now owns the HANDLE
277 } else {
278 CloseHandle(pi.hProcess);
279 }
280 CloseHandle(pi.hThread);
281 } else {
282 LPVOID lpMsgBuf = nullptr;
283 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
284 FORMAT_MESSAGE_FROM_SYSTEM |
285 FORMAT_MESSAGE_IGNORE_INSERTS,
286 nullptr,
287 GetLastError(),
288 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
289 (LPTSTR) &lpMsgBuf,
290 0,
291 nullptr);
292 wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)");
293 if (lpMsgBuf)
294 LocalFree(lpMsgBuf);
295 }
296
297 free(cl);
298
299 return ok;
300 }

mercurial