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/configuration.hxx>
15 #include <cppuhelper/supportsservice.hxx>
17 #include <o3tl/char16_t2wchar_t.hxx>
18 #include <comphelper/diagnose_ex.hxx>
19 #include <officecfg/UserProfile.hxx>
24 #define SECURITY_WIN32
27 #include <systools/win32/comtools.hxx>
28 #include <systools/win32/oleauto.hxx>
36 class WinUserInfoBe_Impl
39 virtual ~WinUserInfoBe_Impl(){};
40 virtual OUString
GetGivenName() = 0;
41 virtual OUString
GetSn() { return ""; }
42 virtual OUString
GetFathersname() { return ""; }
43 virtual OUString
GetInitials() { return ""; }
44 virtual OUString
GetStreet() { return ""; }
45 virtual OUString
GetCity() { return ""; }
46 virtual OUString
GetState() { return ""; }
47 virtual OUString
GetApartment() { return ""; }
48 virtual OUString
GetPostalCode() { return ""; }
49 virtual OUString
GetCountry() { return ""; }
50 virtual OUString
GetOrganization() { return ""; }
51 virtual OUString
GetPosition() { return ""; }
52 virtual OUString
GetTitle() { return ""; }
53 virtual OUString
GetHomePhone() { return ""; }
54 virtual OUString
GetTelephoneNumber() { return ""; }
55 virtual OUString
GetFaxNumber() { return ""; }
56 virtual OUString
GetMail() { return ""; }
64 constexpr OUString
givenname(u
"givenname"_ustr
);
65 constexpr OUString
sn(u
"sn"_ustr
);
66 constexpr char fathersname
[]("fathersname");
67 constexpr OUString
initials(u
"initials"_ustr
);
68 constexpr OUString
street(u
"street"_ustr
);
69 constexpr OUString
l(u
"l"_ustr
);
70 constexpr OUString
st(u
"st"_ustr
);
71 constexpr char apartment
[]("apartment");
72 constexpr OUString
postalcode(u
"postalcode"_ustr
);
73 constexpr OUString
c(u
"c"_ustr
);
74 constexpr OUString
o(u
"o"_ustr
);
75 constexpr char position
[]("position");
76 constexpr OUString
title(u
"title"_ustr
);
77 constexpr OUString
homephone(u
"homephone"_ustr
);
78 constexpr OUString
telephonenumber(u
"telephonenumber"_ustr
);
79 constexpr OUString
facsimiletelephonenumber(u
"facsimiletelephonenumber"_ustr
);
80 constexpr OUString
mail(u
"mail"_ustr
);
82 // Backend class implementing access to Active Directory user data. It caches its encoded data
83 // in a configuration entry, to allow reusing it when user later doesn't have access to AD DC
84 // (otherwise the user would get different data when connected vs not connected).
85 class ADsUserAccess
: public extensions::config::WinUserInfo::WinUserInfoBe_Impl
92 sal::systools::CoInitializeGuard
aCoInitializeGuard(COINIT_APARTMENTTHREADED
);
94 sal::systools::COMReference
<IADsADSystemInfo
> pADsys(CLSID_ADSystemInfo
, nullptr,
95 CLSCTX_INPROC_SERVER
);
97 sal::systools::BStr sUserDN
;
98 sal::systools::ThrowIfFailed(pADsys
->get_UserName(&sUserDN
), "get_UserName failed");
99 // If this user is an AD user, then without an active connection to the domain, all the
100 // above will succeed, and m_sUserDN will be correctly initialized, but the following
101 // call to ADsGetObject will fail, and we will attempt reading cached values.
103 OUString sLdapUserDN
= "LDAP://" + m_sUserDN
;
104 sal::systools::COMReference
<IADsUser
> pUser
;
105 sal::systools::ThrowIfFailed(ADsGetObject(o3tl::toW(sLdapUserDN
.getStr()), IID_IADsUser
,
106 reinterpret_cast<void**>(&pUser
)),
107 "ADsGetObject failed");
108 // Fetch all the required information right now, when we know to have access to AD
109 // (later the connection may already be lost)
110 m_aMap
[givenname
] = Str(pUser
, &IADsUser::get_FirstName
);
111 m_aMap
[sn
] = Str(pUser
, &IADsUser::get_LastName
);
112 m_aMap
[initials
] = Str(pUser
, L
"initials");
113 m_aMap
[street
] = Str(pUser
, L
"streetAddress");
114 m_aMap
[l
] = Str(pUser
, L
"l");
115 m_aMap
[st
] = Str(pUser
, L
"st");
116 m_aMap
[postalcode
] = Str(pUser
, L
"postalCode");
117 m_aMap
[c
] = Str(pUser
, L
"co");
118 m_aMap
[o
] = Str(pUser
, L
"company");
119 m_aMap
[title
] = Str(pUser
, &IADsUser::get_Title
);
120 m_aMap
[homephone
] = Str(pUser
, L
"homePhone");
121 m_aMap
[telephonenumber
] = Str(pUser
, L
"TelephoneNumber");
122 m_aMap
[facsimiletelephonenumber
] = Str(pUser
, L
"facsimileTelephoneNumber");
123 m_aMap
[mail
] = Str(pUser
, &IADsUser::get_EmailAddress
);
127 catch (sal::systools::ComError
&)
129 // Maybe we temporarily lost connection to AD; try to get cached data
134 virtual OUString
GetGivenName() override
{ return m_aMap
[givenname
]; }
135 virtual OUString
GetSn() override
{ return m_aMap
[sn
]; }
136 virtual OUString
GetInitials() override
{ return m_aMap
[initials
]; }
137 virtual OUString
GetStreet() override
{ return m_aMap
[street
]; }
138 virtual OUString
GetCity() override
{ return m_aMap
[l
]; }
139 virtual OUString
GetState() override
{ return m_aMap
[st
]; }
140 virtual OUString
GetPostalCode() override
{ return m_aMap
[postalcode
]; }
141 virtual OUString
GetCountry() override
{ return m_aMap
[c
]; }
142 virtual OUString
GetOrganization() override
{ return m_aMap
[o
]; }
143 virtual OUString
GetTitle() override
{ return m_aMap
[title
]; }
144 virtual OUString
GetHomePhone() override
{ return m_aMap
[homephone
]; }
145 virtual OUString
GetTelephoneNumber() override
{ return m_aMap
[telephonenumber
]; }
146 virtual OUString
GetFaxNumber() override
{ return m_aMap
[facsimiletelephonenumber
]; }
147 virtual OUString
GetMail() override
{ return m_aMap
[mail
]; }
150 typedef HRESULT (__stdcall
IADsUser::*getstrfunc
)(BSTR
*);
151 static OUString
Str(IADsUser
* pUser
, getstrfunc func
)
153 sal::systools::BStr sBstr
;
154 if (FAILED((pUser
->*func
)(&sBstr
)))
156 return OUString(sBstr
);
158 static OUString
Str(IADsUser
* pUser
, const wchar_t* property
)
160 sal::systools::BStr sBstrProp
{ o3tl::toU(property
) };
161 struct AutoVariant
: public VARIANT
163 AutoVariant() { VariantInit(this); }
164 ~AutoVariant() { VariantClear(this); }
166 if (FAILED(pUser
->GetEx(sBstrProp
, &varArr
)))
168 SAFEARRAY
* sa
= V_ARRAY(&varArr
);
170 if (FAILED(SafeArrayGetLBound(sa
, 1, &nStart
)) || FAILED(SafeArrayGetUBound(sa
, 1, &nEnd
)))
173 for (LONG i
= nStart
; i
<= nEnd
; i
++)
175 if (FAILED(SafeArrayGetElement(sa
, &i
, &varItem
)))
177 if (varItem
.vt
== VT_BSTR
)
178 return OUString(o3tl::toU(V_BSTR(&varItem
)));
179 VariantClear(&varItem
);
188 OUString sCachedData
= "user=" + m_sUserDN
// user DN
189 + "\0" + givenname
+ "=" + GetGivenName() // 1st name
190 + "\0" + sn
+ "=" + GetSn() // sn
191 + "\0" + initials
+ "=" + GetInitials() // initials
192 + "\0" + street
+ "=" + GetStreet() // street
193 + "\0" + l
+ "=" + GetCity() // l
194 + "\0" + st
+ "=" + GetState() // st
195 + "\0" + postalcode
+ "=" + GetPostalCode() // p.code
196 + "\0" + c
+ "=" + GetCountry() // c
197 + "\0" + o
+ "=" + GetOrganization() // o
198 + "\0" + title
+ "=" + GetTitle() // title
199 + "\0" + homephone
+ "=" + GetHomePhone() // h.phone
200 + "\0" + telephonenumber
+ "=" + GetTelephoneNumber() // tel
201 + "\0" + facsimiletelephonenumber
+ "=" + GetFaxNumber() // fax
202 + "\0" + mail
+ "=" + GetMail(); // mail
203 const css::uno::Sequence
<sal_Int8
> seqCachedData(
204 reinterpret_cast<const sal_Int8
*>(sCachedData
.getStr()),
205 sCachedData
.getLength() * sizeof(sal_Unicode
));
206 OUStringBuffer sOutBuf
;
207 comphelper::Base64::encode(sOutBuf
, seqCachedData
);
209 std::shared_ptr
<comphelper::ConfigurationChanges
> batch(
210 comphelper::ConfigurationChanges::create());
211 officecfg::UserProfile::WinUserInfo::Cache::set(sOutBuf
.makeStringAndClear(), batch
);
214 catch (const css::uno::Exception
&)
216 TOOLS_WARN_EXCEPTION("extensions.config",
217 "ADsUserAccess: access to configuration data failed:");
223 if (m_sUserDN
.isEmpty())
224 throw css::uno::RuntimeException();
226 OUString sCache
= officecfg::UserProfile::WinUserInfo::Cache::get();
228 if (sCache
.isEmpty())
229 throw css::uno::RuntimeException();
232 css::uno::Sequence
<sal_Int8
> seqCachedData
;
233 comphelper::Base64::decode(seqCachedData
, sCache
);
234 sCache
= OUString(reinterpret_cast<const sal_Unicode
*>(seqCachedData
.getConstArray()),
235 seqCachedData
.getLength() / sizeof(sal_Unicode
));
239 std::map
<const OUString
, OUString
> aMap
;
240 sal_Int32 nIndex
= 0;
243 const OUString sEntry
= sCache
.getToken(0, '\0', nIndex
);
244 sal_Int32 nEqIndex
= 0;
245 const OUString sEntryName
= sEntry
.getToken(0, '=', nEqIndex
);
248 sEntryVal
= sEntry
.copy(nEqIndex
);
249 if (sEntryName
== "user")
252 aMap
[sEntryName
] = sEntryVal
;
253 } while (nIndex
>= 0);
255 if (sUserDN
!= m_sUserDN
)
256 throw css::uno::RuntimeException();
257 m_aMap
= std::move(aMap
);
260 OUString m_sUserDN
; // used to check if the cached data is for current user
261 std::map
<const OUString
, OUString
> m_aMap
;
264 class SysInfoUserAccess
: public extensions::config::WinUserInfo::WinUserInfoBe_Impl
272 GetUserNameExW(NameDisplay
, nullptr, &nSize
);
273 if (GetLastError() != ERROR_MORE_DATA
)
274 throw css::uno::RuntimeException();
275 auto pNameBuf(std::make_unique
<wchar_t[]>(nSize
));
276 if (!GetUserNameExW(NameDisplay
, pNameBuf
.get(), &nSize
))
277 throw css::uno::RuntimeException();
278 m_sName
= o3tl::toU(pNameBuf
.get());
280 catch (css::uno::RuntimeException
&)
282 // GetUserNameEx may fail in some cases (e.g., for built-in AD domain
283 // administrator account on non-DC systems), where GetUserName will
284 // still give a name.
285 DWORD nSize
= UNLEN
+ 1;
286 auto pNameBuf(std::make_unique
<wchar_t[]>(nSize
));
287 if (!GetUserNameW(pNameBuf
.get(), &nSize
))
288 throw css::uno::RuntimeException();
289 m_sName
= o3tl::toU(pNameBuf
.get());
293 virtual OUString
GetGivenName() override
{ return m_sName
; }
304 namespace WinUserInfo
306 WinUserInfoBe::WinUserInfoBe()
307 : WinUserInfoMutexHolder()
308 , BackendBase(mMutex
)
312 m_pImpl
.reset(new ADsUserAccess());
314 catch (css::uno::RuntimeException
&)
316 m_pImpl
.reset(new SysInfoUserAccess
);
320 WinUserInfoBe::~WinUserInfoBe() {}
322 void WinUserInfoBe::setPropertyValue(OUString
const&, css::uno::Any
const&)
324 throw css::lang::IllegalArgumentException("setPropertyValue not supported",
325 static_cast<cppu::OWeakObject
*>(this), -1);
328 css::uno::Any
WinUserInfoBe::getPropertyValue(OUString
const& PropertyName
)
331 // Only process the first argument of possibly multiple space- or comma-separated arguments
332 OUString sToken
= PropertyName
.getToken(0, ' ').getToken(0, ',');
333 if (sToken
== givenname
)
335 sValue
= m_pImpl
->GetGivenName();
337 else if (sToken
== sn
)
339 sValue
= m_pImpl
->GetSn();
341 else if (sToken
== fathersname
)
343 sValue
= m_pImpl
->GetFathersname();
345 else if (sToken
== initials
)
347 sValue
= m_pImpl
->GetInitials();
349 else if (sToken
== street
)
351 sValue
= m_pImpl
->GetStreet();
353 else if (sToken
== l
)
355 sValue
= m_pImpl
->GetCity();
357 else if (sToken
== st
)
359 sValue
= m_pImpl
->GetState();
361 else if (sToken
== apartment
)
363 sValue
= m_pImpl
->GetApartment();
365 else if (sToken
== postalcode
)
367 sValue
= m_pImpl
->GetPostalCode();
369 else if (sToken
== c
)
371 sValue
= m_pImpl
->GetCountry();
373 else if (sToken
== o
)
375 sValue
= m_pImpl
->GetOrganization();
377 else if (sToken
== position
)
379 sValue
= m_pImpl
->GetPosition();
381 else if (sToken
== title
)
383 sValue
= m_pImpl
->GetTitle();
385 else if (sToken
== homephone
)
387 sValue
= m_pImpl
->GetHomePhone();
389 else if (sToken
== telephonenumber
)
391 sValue
= m_pImpl
->GetTelephoneNumber();
393 else if (sToken
== facsimiletelephonenumber
)
395 sValue
= m_pImpl
->GetFaxNumber();
397 else if (sToken
== mail
)
399 sValue
= m_pImpl
->GetMail();
402 throw css::beans::UnknownPropertyException(sToken
, static_cast<cppu::OWeakObject
*>(this));
404 return css::uno::Any(css::beans::Optional
<css::uno::Any
>(
405 !sValue
.isEmpty(), sValue
.isEmpty() ? css::uno::Any() : css::uno::Any(sValue
)));
408 OUString SAL_CALL
WinUserInfoBe::getImplementationName()
410 return "com.sun.star.comp.configuration.backend.WinUserInfoBe";
413 sal_Bool SAL_CALL
WinUserInfoBe::supportsService(const OUString
& aServiceName
)
415 return cppu::supportsService(this, aServiceName
);
418 css::uno::Sequence
<OUString
> SAL_CALL
WinUserInfoBe::getSupportedServiceNames()
420 return { "com.sun.star.configuration.backend.WinUserInfoBe" };
426 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
427 extensions_WinUserInfoBe_get_implementation(css::uno::XComponentContext
*,
428 css::uno::Sequence
<css::uno::Any
> const&)
430 return cppu::acquire(new extensions::config::WinUserInfo::WinUserInfoBe());
433 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */