1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
20 #include <o3tl/char16_t2wchar_t.hxx>
21 #include <tools/diagnose_ex.h>
26 #define SECURITY_WIN32
29 #include <systools/win32/comtools.hxx>
37 class WinUserInfoBe_Impl
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 ""; }
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
89 ADsUserAccess(const css::uno::Reference
<css::uno::XComponentContext
>& xContext
)
93 struct 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
);
107 HRESULT hr
= pADsys
->get_UserName(&sUserDN
.ptr
);
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
));
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
);
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
]; }
165 ~smartBSTR() { SysFreeString(ptr
); }
168 typedef HRESULT (__stdcall
IADsUser::*getstrfunc
)(BSTR
*);
169 static OUString
Str(IADsUser
* pUser
, getstrfunc func
)
172 if (FAILED((pUser
->*func
)(&sBstr
.ptr
)))
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); }
184 if (FAILED(pUser
->GetEx(sBstrProp
.ptr
, &varArr
)))
186 SAFEARRAY
* sa
= V_ARRAY(&varArr
);
188 if (FAILED(SafeArrayGetLBound(sa
, 1, &nStart
)) || FAILED(SafeArrayGetUBound(sa
, 1, &nEnd
)))
191 for (LONG i
= nStart
; i
<= nEnd
; i
++)
193 if (FAILED(SafeArrayGetElement(sa
, &i
, &varItem
)))
195 if (varItem
.vt
== VT_BSTR
)
196 return OUString(o3tl::toU(V_BSTR(&varItem
)));
197 VariantClear(&varItem
);
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
);
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
));
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
);
277 sEntryVal
= sEntry
.copy(nEqIndex
);
278 if (sEntryName
== "user")
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
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
; }
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
)
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();
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: */