1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "precompiled_desktop.hxx"
29 #include "sal/config.h"
30 
31 #include <cstddef>
32 
33 #define WIN32_LEAN_AND_MEAN
34 #if defined _MSC_VER
35 #pragma warning(push, 1)
36 #endif
37 #include <windows.h>
38 #include <shlwapi.h>
39 #if defined _MSC_VER
40 #pragma warning(pop)
41 #endif
42 
43 #include "tools/pathutils.hxx"
44 
45 #include "extendloaderenvironment.hxx"
46 
47 namespace {
48 
49 void fail() {
50     LPWSTR buf = NULL;
51     FormatMessageW(
52         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
53         GetLastError(), 0, reinterpret_cast< LPWSTR >(&buf), 0, NULL);
54     MessageBoxW(NULL, buf, NULL, MB_OK | MB_ICONERROR);
55     LocalFree(buf);
56     TerminateProcess(GetCurrentProcess(), 255);
57 }
58 
59 bool contains(WCHAR const * paths, WCHAR const * path, WCHAR const * pathEnd) {
60     WCHAR const * q = path;
61     for (WCHAR const * p = paths;; ++p) {
62         WCHAR c = *p;
63         switch (c) {
64         case L'\0':
65             return q == pathEnd;
66         case L';':
67             if (q == pathEnd) {
68                 return true;
69             }
70             q = path;
71             break;
72         default:
73             if (q != NULL) {
74                 if (q != pathEnd && *q == c) {
75                     ++q;
76                 } else {
77                     q = NULL;
78                 }
79             }
80             break;
81         }
82     }
83 }
84 
85 }
86 
87 namespace desktop_win32 {
88 
89 void extendLoaderEnvironment(WCHAR * binPath, WCHAR * iniDirectory) {
90     if (!GetModuleFileNameW(NULL, iniDirectory, MAX_PATH)) {
91         fail();
92     }
93     WCHAR * iniDirEnd = tools::filename(iniDirectory);
94     WCHAR name[MAX_PATH + MY_LENGTH(L".bin")];
95         // hopefully std::size_t is large enough to not overflow
96     WCHAR * nameEnd = name;
97     for (WCHAR * p = iniDirEnd; *p != L'\0'; ++p) {
98         *nameEnd++ = *p;
99     }
100     if (!(nameEnd - name >= 4 && nameEnd[-4] == L'.' &&
101           (nameEnd[-3] == L'E' || nameEnd[-3] == L'e') &&
102           (nameEnd[-2] == L'X' || nameEnd[-2] == L'x') &&
103           (nameEnd[-1] == L'E' || nameEnd[-1] == L'e')))
104     {
105         *nameEnd = L'.';
106         nameEnd += 4;
107     }
108     nameEnd[-3] = 'b';
109     nameEnd[-2] = 'i';
110     nameEnd[-1] = 'n';
111     tools::buildPath(binPath, iniDirectory, iniDirEnd, name, nameEnd - name);
112     *iniDirEnd = L'\0';
113     WCHAR path[MAX_PATH];
114     WCHAR * pathEnd = tools::buildPath(
115         path, iniDirectory, iniDirEnd, MY_STRING(L"..\\basis-link"));
116     if (pathEnd == NULL) {
117         fail();
118     }
119     std::size_t const maxEnv = 32767;
120     WCHAR pad[2 * MAX_PATH + maxEnv];
121         // hopefully std::size_t is large enough to not overflow
122     WCHAR * padEnd = NULL;
123     WCHAR env[maxEnv];
124     DWORD n = GetEnvironmentVariableW(L"PATH", env, maxEnv);
125     if (n >= maxEnv || n == 0 && GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
126         fail();
127     }
128     env[n] = L'\0';
129     bool exclude1;
130     pathEnd = tools::resolveLink(path);
131     if (pathEnd == NULL) {
132         if (GetLastError() != ERROR_FILE_NOT_FOUND) {
133             fail();
134         }
135         // This path is only taken by testtool.exe in basis program directory;
136         // its PATH needs to include the brand program directory:
137         pathEnd = tools::buildPath(
138             path, iniDirectory, iniDirEnd, MY_STRING(L".."));
139         if (pathEnd == NULL) {
140             fail();
141         }
142         padEnd = tools::buildPath(
143             pad, path, pathEnd, MY_STRING(L"\\..\\program"));
144         if (padEnd == NULL) {
145             fail();
146         }
147         exclude1 = contains(env, pad, padEnd);
148     } else {
149         exclude1 = true;
150     }
151     WCHAR * pad2 = exclude1 ? pad : padEnd + 1;
152     pathEnd = tools::buildPath(path, path, pathEnd, MY_STRING(L"\\ure-link"));
153     if (pathEnd == NULL) {
154         fail();
155     }
156     pathEnd = tools::resolveLink(path);
157     if (pathEnd == NULL) {
158         fail();
159     }
160     padEnd = tools::buildPath(pad2, path, pathEnd, MY_STRING(L"\\bin"));
161     if (padEnd == NULL) {
162         fail();
163     }
164     bool exclude2 = contains(env, pad2, padEnd);
165     if (!(exclude1 && exclude2)) {
166         if (!(exclude1 || exclude2)) {
167             pad2[-1] = L';';
168         }
169         WCHAR * p = exclude2 ? pad2 - 1 : padEnd;
170         if (n != 0) {
171             *p++ = L';';
172         }
173         for (DWORD i = 0; i <= n; ++i) {
174             *p++ = env[i];
175         }
176         if (!SetEnvironmentVariableW(L"PATH", pad)) {
177             fail();
178         }
179     }
180 }
181 
182 }
183