Simplify BSTR RAII
[LibreOffice.git] / extensions / source / config / WinUserInfo / WinUserInfoBe.cxx
bloba412769ee639440a80d48e6c39329711bcb5bd14
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "WinUserInfoBe.hxx"
12 #include <com/sun/star/beans/Optional.hpp>
13 #include <comphelper/base64.hxx>
14 #include <comphelper/configurationhelper.hxx>
15 #include <com/sun/star/container/XNameAccess.hpp>
16 #include <com/sun/star/container/XNameReplace.hpp>
17 #include <com/sun/star/util/XChangesBatch.hpp>
18 #include <cppuhelper/supportsservice.hxx>
19 #include <map>
20 #include <o3tl/char16_t2wchar_t.hxx>
21 #include <tools/diagnose_ex.h>
23 #include <Iads.h>
24 #include <Adshlp.h>
25 #include <Lmcons.h>
26 #define SECURITY_WIN32
27 #include <Security.h>
29 #include <systools/win32/comtools.hxx>
31 namespace extensions
33 namespace config
35 namespace WinUserInfo
37 class WinUserInfoBe_Impl
39 public:
40 virtual ~WinUserInfoBe_Impl(){};
41 virtual OUString GetGivenName() = 0;
42 virtual OUString GetSn() { return ""; }
43 virtual OUString GetFathersname() { return ""; }
44 virtual OUString GetInitials() { return ""; }
45 virtual OUString GetStreet() { return ""; }
46 virtual OUString GetCity() { return ""; }
47 virtual OUString GetState() { return ""; }
48 virtual OUString GetApartment() { return ""; }
49 virtual OUString GetPostalCode() { return ""; }
50 virtual OUString GetCountry() { return ""; }
51 virtual OUString GetOrganization() { return ""; }
52 virtual OUString GetPosition() { return ""; }
53 virtual OUString GetTitle() { return ""; }
54 virtual OUString GetHomePhone() { return ""; }
55 virtual OUString GetTelephoneNumber() { return ""; }
56 virtual OUString GetFaxNumber() { return ""; }
57 virtual OUString GetMail() { return ""; }
63 namespace
65 constexpr OUStringLiteral givenname(u"givenname");
66 constexpr OUStringLiteral sn(u"sn");
67 constexpr char fathersname[]("fathersname");
68 constexpr OUStringLiteral initials(u"initials");
69 constexpr OUStringLiteral street(u"street");
70 constexpr OUStringLiteral l(u"l");
71 constexpr OUStringLiteral st(u"st");
72 constexpr char apartment[]("apartment");
73 constexpr OUStringLiteral postalcode(u"postalcode");
74 constexpr OUStringLiteral c(u"c");
75 constexpr OUStringLiteral o(u"o");
76 constexpr char position[]("position");
77 constexpr OUStringLiteral title(u"title");
78 constexpr OUStringLiteral homephone(u"homephone");
79 constexpr OUStringLiteral telephonenumber(u"telephonenumber");
80 constexpr OUStringLiteral facsimiletelephonenumber(u"facsimiletelephonenumber");
81 constexpr OUStringLiteral mail(u"mail");
83 // Backend class implementing access to Active Directory user data. It caches its encoded data
84 // in a configuration entry, to allow reusing it when user later doesn't have access to AD DC
85 // (otherwise the user would get different data when connected vs not connected).
86 class ADsUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl
88 public:
89 ADsUserAccess(const css::uno::Reference<css::uno::XComponentContext>& xContext)
91 try
93 struct CoInitializeGuard
95 CoInitializeGuard()
97 if (HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); FAILED(hr))
98 throw sal::systools::ComError("CoInitializeEx failed", hr);
100 ~CoInitializeGuard() { CoUninitialize(); }
101 } aCoInitializeGuard;
103 auto pADsys = sal::systools::COMReference<IADsADSystemInfo>().CoCreateInstance(
104 CLSID_ADSystemInfo, nullptr, CLSCTX_INPROC_SERVER);
106 smartBSTR sUserDN;
107 HRESULT hr = pADsys->get_UserName(&sUserDN.ptr);
108 if (FAILED(hr))
109 throw sal::systools::ComError("get_UserName failed", hr);
110 // If this user is an AD user, then without an active connection to the domain, all the
111 // above will succeed, and m_sUserDN will be correctly initialized, but the following
112 // call to ADsGetObject will fail, and we will attempt reading cached values.
113 m_sUserDN = o3tl::toU(sUserDN.ptr);
114 OUString sLdapUserDN = "LDAP://" + m_sUserDN;
115 sal::systools::COMReference<IADsUser> pUser;
116 hr = ADsGetObject(o3tl::toW(sLdapUserDN.getStr()), IID_IADsUser,
117 reinterpret_cast<void**>(&pUser));
118 if (FAILED(hr))
119 throw sal::systools::ComError("ADsGetObject failed", hr);
120 // Fetch all the required information right now, when we know to have access to AD
121 // (later the connection may already be lost)
122 m_aMap[givenname] = Str(pUser, &IADsUser::get_FirstName);
123 m_aMap[sn] = Str(pUser, &IADsUser::get_LastName);
124 m_aMap[initials] = Str(pUser, L"initials");
125 m_aMap[street] = Str(pUser, L"streetAddress");
126 m_aMap[l] = Str(pUser, L"l");
127 m_aMap[st] = Str(pUser, L"st");
128 m_aMap[postalcode] = Str(pUser, L"postalCode");
129 m_aMap[c] = Str(pUser, L"co");
130 m_aMap[o] = Str(pUser, L"company");
131 m_aMap[title] = Str(pUser, &IADsUser::get_Title);
132 m_aMap[homephone] = Str(pUser, L"homePhone");
133 m_aMap[telephonenumber] = Str(pUser, L"TelephoneNumber");
134 m_aMap[facsimiletelephonenumber] = Str(pUser, L"facsimileTelephoneNumber");
135 m_aMap[mail] = Str(pUser, &IADsUser::get_EmailAddress);
137 CacheData(xContext);
139 catch (sal::systools::ComError&)
141 // Maybe we temporarily lost connection to AD; try to get cached data
142 GetCachedData(xContext);
146 virtual OUString GetGivenName() override { return m_aMap[givenname]; }
147 virtual OUString GetSn() override { return m_aMap[sn]; }
148 virtual OUString GetInitials() override { return m_aMap[initials]; }
149 virtual OUString GetStreet() override { return m_aMap[street]; }
150 virtual OUString GetCity() override { return m_aMap[l]; }
151 virtual OUString GetState() override { return m_aMap[st]; }
152 virtual OUString GetPostalCode() override { return m_aMap[postalcode]; }
153 virtual OUString GetCountry() override { return m_aMap[c]; }
154 virtual OUString GetOrganization() override { return m_aMap[o]; }
155 virtual OUString GetTitle() override { return m_aMap[title]; }
156 virtual OUString GetHomePhone() override { return m_aMap[homephone]; }
157 virtual OUString GetTelephoneNumber() override { return m_aMap[telephonenumber]; }
158 virtual OUString GetFaxNumber() override { return m_aMap[facsimiletelephonenumber]; }
159 virtual OUString GetMail() override { return m_aMap[mail]; }
161 private:
162 struct smartBSTR
164 BSTR ptr = nullptr;
165 ~smartBSTR() { SysFreeString(ptr); }
168 typedef HRESULT (__stdcall IADsUser::*getstrfunc)(BSTR*);
169 static OUString Str(IADsUser* pUser, getstrfunc func)
171 smartBSTR sBstr;
172 if (FAILED((pUser->*func)(&sBstr.ptr)))
173 return "";
174 return OUString(o3tl::toU(sBstr.ptr));
176 static OUString Str(IADsUser* pUser, const wchar_t* property)
178 smartBSTR sBstrProp{ SysAllocString(property) };
179 struct AutoVariant : public VARIANT
181 AutoVariant() { VariantInit(this); }
182 ~AutoVariant() { VariantClear(this); }
183 } varArr;
184 if (FAILED(pUser->GetEx(sBstrProp.ptr, &varArr)))
185 return "";
186 SAFEARRAY* sa = V_ARRAY(&varArr);
187 LONG nStart, nEnd;
188 if (FAILED(SafeArrayGetLBound(sa, 1, &nStart)) || FAILED(SafeArrayGetUBound(sa, 1, &nEnd)))
189 return "";
190 AutoVariant varItem;
191 for (LONG i = nStart; i <= nEnd; i++)
193 if (FAILED(SafeArrayGetElement(sa, &i, &varItem)))
194 continue;
195 if (varItem.vt == VT_BSTR)
196 return OUString(o3tl::toU(V_BSTR(&varItem)));
197 VariantClear(&varItem);
199 return "";
202 void CacheData(const css::uno::Reference<css::uno::XComponentContext>& xContext)
206 OUString sCachedData = "user=" + m_sUserDN // user DN
207 + "\0" + givenname + "=" + GetGivenName() // 1st name
208 + "\0" + sn + "=" + GetSn() // sn
209 + "\0" + initials + "=" + GetInitials() // initials
210 + "\0" + street + "=" + GetStreet() // street
211 + "\0" + l + "=" + GetCity() // l
212 + "\0" + st + "=" + GetState() // st
213 + "\0" + postalcode + "=" + GetPostalCode() // p.code
214 + "\0" + c + "=" + GetCountry() // c
215 + "\0" + o + "=" + GetOrganization() // o
216 + "\0" + title + "=" + GetTitle() // title
217 + "\0" + homephone + "=" + GetHomePhone() // h.phone
218 + "\0" + telephonenumber + "=" + GetTelephoneNumber() // tel
219 + "\0" + facsimiletelephonenumber + "=" + GetFaxNumber() // fax
220 + "\0" + mail + "=" + GetMail(); // mail
221 const css::uno::Sequence<sal_Int8> seqCachedData(
222 reinterpret_cast<const sal_Int8*>(sCachedData.getStr()),
223 sCachedData.getLength() * sizeof(sal_Unicode));
224 OUStringBuffer sOutBuf;
225 comphelper::Base64::encode(sOutBuf, seqCachedData);
227 auto xIface = comphelper::ConfigurationHelper::openConfig(
228 xContext, "org.openoffice.UserProfile/WinUserInfo",
229 comphelper::EConfigurationModes::Standard);
230 css::uno::Reference<css::container::XNameReplace> xNameReplace(
231 xIface, css::uno::UNO_QUERY_THROW);
232 xNameReplace->replaceByName("Cache", css::uno::makeAny(sOutBuf.makeStringAndClear()));
234 css::uno::Reference<css::util::XChangesBatch> xChangesBatch(xIface,
235 css::uno::UNO_QUERY_THROW);
236 xChangesBatch->commitChanges();
238 catch (const css::uno::Exception&)
240 TOOLS_WARN_EXCEPTION("extensions.config",
241 "ADsUserAccess: access to configuration data failed:");
245 void GetCachedData(const css::uno::Reference<css::uno::XComponentContext>& xContext)
247 if (m_sUserDN.isEmpty())
248 throw css::uno::RuntimeException();
250 auto xIface = comphelper::ConfigurationHelper::openConfig(
251 xContext, "org.openoffice.UserProfile/WinUserInfo",
252 comphelper::EConfigurationModes::ReadOnly);
253 css::uno::Reference<css::container::XNameAccess> xNameAccess(xIface,
254 css::uno::UNO_QUERY_THROW);
255 OUString sCache;
256 xNameAccess->getByName("Cache") >>= sCache;
257 if (sCache.isEmpty())
258 throw css::uno::RuntimeException();
261 css::uno::Sequence<sal_Int8> seqCachedData;
262 comphelper::Base64::decode(seqCachedData, sCache);
263 sCache = OUString(reinterpret_cast<const sal_Unicode*>(seqCachedData.getConstArray()),
264 seqCachedData.getLength() / sizeof(sal_Unicode));
267 OUString sUserDN;
268 std::map<const OUString, OUString> aMap;
269 sal_Int32 nIndex = 0;
272 const OUString sEntry = sCache.getToken(0, '\0', nIndex);
273 sal_Int32 nEqIndex = 0;
274 const OUString sEntryName = sEntry.getToken(0, '=', nEqIndex);
275 OUString sEntryVal;
276 if (nEqIndex >= 0)
277 sEntryVal = sEntry.copy(nEqIndex);
278 if (sEntryName == "user")
279 sUserDN = sEntryVal;
280 else
281 aMap[sEntryName] = sEntryVal;
282 } while (nIndex >= 0);
284 if (sUserDN != m_sUserDN)
285 throw css::uno::RuntimeException();
286 m_aMap = std::move(aMap);
289 OUString m_sUserDN; // used to check if the cached data is for current user
290 std::map<const OUString, OUString> m_aMap;
293 class SysInfoUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl
295 public:
296 SysInfoUserAccess()
300 ULONG nSize = 0;
301 GetUserNameExW(NameDisplay, nullptr, &nSize);
302 if (GetLastError() != ERROR_MORE_DATA)
303 throw css::uno::RuntimeException();
304 auto pNameBuf(std::make_unique<wchar_t[]>(nSize));
305 if (!GetUserNameExW(NameDisplay, pNameBuf.get(), &nSize))
306 throw css::uno::RuntimeException();
307 m_sName = o3tl::toU(pNameBuf.get());
309 catch (css::uno::RuntimeException&)
311 // GetUserNameEx may fail in some cases (e.g., for built-in AD domain
312 // administrator account on non-DC systems), where GetUserName will
313 // still give a name.
314 DWORD nSize = UNLEN + 1;
315 auto pNameBuf(std::make_unique<wchar_t[]>(nSize));
316 if (!GetUserNameW(pNameBuf.get(), &nSize))
317 throw css::uno::RuntimeException();
318 m_sName = o3tl::toU(pNameBuf.get());
322 virtual OUString GetGivenName() override { return m_sName; }
324 private:
325 OUString m_sName;
329 namespace extensions
331 namespace config
333 namespace WinUserInfo
335 WinUserInfoBe::WinUserInfoBe(const css::uno::Reference<css::uno::XComponentContext>& xContext)
336 : WinUserInfoMutexHolder()
337 , BackendBase(mMutex)
341 m_pImpl.reset(new ADsUserAccess(xContext));
343 catch (css::uno::RuntimeException&)
345 m_pImpl.reset(new SysInfoUserAccess);
349 WinUserInfoBe::~WinUserInfoBe() {}
351 void WinUserInfoBe::setPropertyValue(OUString const&, css::uno::Any const&)
353 throw css::lang::IllegalArgumentException("setPropertyValue not supported",
354 static_cast<cppu::OWeakObject*>(this), -1);
357 css::uno::Any WinUserInfoBe::getPropertyValue(OUString const& PropertyName)
359 OUString sValue;
360 // Only process the first argument of possibly multiple space- or comma-separated arguments
361 OUString sToken = PropertyName.getToken(0, ' ').getToken(0, ',');
362 if (sToken == givenname)
364 sValue = m_pImpl->GetGivenName();
366 else if (sToken == sn)
368 sValue = m_pImpl->GetSn();
370 else if (sToken == fathersname)
372 sValue = m_pImpl->GetFathersname();
374 else if (sToken == initials)
376 sValue = m_pImpl->GetInitials();
378 else if (sToken == street)
380 sValue = m_pImpl->GetStreet();
382 else if (sToken == l)
384 sValue = m_pImpl->GetCity();
386 else if (sToken == st)
388 sValue = m_pImpl->GetState();
390 else if (sToken == apartment)
392 sValue = m_pImpl->GetApartment();
394 else if (sToken == postalcode)
396 sValue = m_pImpl->GetPostalCode();
398 else if (sToken == c)
400 sValue = m_pImpl->GetCountry();
402 else if (sToken == o)
404 sValue = m_pImpl->GetOrganization();
406 else if (sToken == position)
408 sValue = m_pImpl->GetPosition();
410 else if (sToken == title)
412 sValue = m_pImpl->GetTitle();
414 else if (sToken == homephone)
416 sValue = m_pImpl->GetHomePhone();
418 else if (sToken == telephonenumber)
420 sValue = m_pImpl->GetTelephoneNumber();
422 else if (sToken == facsimiletelephonenumber)
424 sValue = m_pImpl->GetFaxNumber();
426 else if (sToken == mail)
428 sValue = m_pImpl->GetMail();
430 else
431 throw css::beans::UnknownPropertyException(sToken, static_cast<cppu::OWeakObject*>(this));
433 return css::uno::makeAny(css::beans::Optional<css::uno::Any>(
434 !sValue.isEmpty(), sValue.isEmpty() ? css::uno::Any() : css::uno::makeAny(sValue)));
437 OUString SAL_CALL WinUserInfoBe::getImplementationName()
439 return "com.sun.star.comp.configuration.backend.WinUserInfoBe";
442 sal_Bool SAL_CALL WinUserInfoBe::supportsService(const OUString& aServiceName)
444 return cppu::supportsService(this, aServiceName);
447 css::uno::Sequence<OUString> SAL_CALL WinUserInfoBe::getSupportedServiceNames()
449 return { "com.sun.star.configuration.backend.WinUserInfoBe" };
455 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
456 extensions_WinUserInfoBe_get_implementation(css::uno::XComponentContext* context,
457 css::uno::Sequence<css::uno::Any> const&)
459 return cppu::acquire(new extensions::config::WinUserInfo::WinUserInfoBe(context));
462 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */