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 struct AutoVariant
: public VARIANT
162 AutoVariant() { VariantInit(this); }
163 ~AutoVariant() { VariantClear(this); }
165 if (FAILED(pUser
->GetEx(sal::systools::BStr(o3tl::toU(property
)), &varArr
)))
167 SAFEARRAY
* sa
= V_ARRAY(&varArr
);
169 if (FAILED(SafeArrayGetLBound(sa
, 1, &nStart
)) || FAILED(SafeArrayGetUBound(sa
, 1, &nEnd
)))
172 for (LONG i
= nStart
; i
<= nEnd
; i
++)
174 if (FAILED(SafeArrayGetElement(sa
, &i
, &varItem
)))
176 if (varItem
.vt
== VT_BSTR
)
177 return OUString(o3tl::toU(V_BSTR(&varItem
)));
178 VariantClear(&varItem
);
187 OUString sCachedData
= "user=" + m_sUserDN
// user DN
188 + "\0" + _givenname
+ "=" + GetGivenName() // 1st name
189 + "\0" + _sn
+ "=" + GetSn() // sn
190 + "\0" + _initials
+ "=" + GetInitials() // initials
191 + "\0" + _street
+ "=" + GetStreet() // street
192 + "\0" + _l
+ "=" + GetCity() // l
193 + "\0" + _st
+ "=" + GetState() // st
194 + "\0" + _postalcode
+ "=" + GetPostalCode() // p.code
195 + "\0" + _c
+ "=" + GetCountry() // c
196 + "\0" + _o
+ "=" + GetOrganization() // o
197 + "\0" + _title
+ "=" + GetTitle() // title
198 + "\0" + _homephone
+ "=" + GetHomePhone() // h.phone
199 + "\0" + _telephonenumber
+ "=" + GetTelephoneNumber() // tel
200 + "\0" + _facsimiletelephonenumber
+ "=" + GetFaxNumber() // fax
201 + "\0" + _mail
+ "=" + GetMail(); // mail
202 const css::uno::Sequence
<sal_Int8
> seqCachedData(
203 reinterpret_cast<const sal_Int8
*>(sCachedData
.getStr()),
204 sCachedData
.getLength() * sizeof(sal_Unicode
));
205 OUStringBuffer sOutBuf
;
206 comphelper::Base64::encode(sOutBuf
, seqCachedData
);
208 std::shared_ptr
<comphelper::ConfigurationChanges
> batch(
209 comphelper::ConfigurationChanges::create());
210 officecfg::UserProfile::WinUserInfo::Cache::set(sOutBuf
.makeStringAndClear(), batch
);
213 catch (const css::uno::Exception
&)
215 TOOLS_WARN_EXCEPTION("extensions.config",
216 "ADsUserAccess: access to configuration data failed:");
222 if (m_sUserDN
.isEmpty())
223 throw css::uno::RuntimeException();
225 OUString sCache
= officecfg::UserProfile::WinUserInfo::Cache::get();
227 if (sCache
.isEmpty())
228 throw css::uno::RuntimeException();
231 css::uno::Sequence
<sal_Int8
> seqCachedData
;
232 comphelper::Base64::decode(seqCachedData
, sCache
);
233 sCache
= OUString(reinterpret_cast<const sal_Unicode
*>(seqCachedData
.getConstArray()),
234 seqCachedData
.getLength() / sizeof(sal_Unicode
));
238 std::map
<const OUString
, OUString
> aMap
;
239 sal_Int32 nIndex
= 0;
242 const OUString sEntry
= sCache
.getToken(0, '\0', nIndex
);
243 sal_Int32 nEqIndex
= 0;
244 const OUString sEntryName
= sEntry
.getToken(0, '=', nEqIndex
);
247 sEntryVal
= sEntry
.copy(nEqIndex
);
248 if (sEntryName
== "user")
251 aMap
[sEntryName
] = sEntryVal
;
252 } while (nIndex
>= 0);
254 if (sUserDN
!= m_sUserDN
)
255 throw css::uno::RuntimeException();
256 m_aMap
= std::move(aMap
);
259 OUString m_sUserDN
; // used to check if the cached data is for current user
260 std::map
<const OUString
, OUString
> m_aMap
;
263 class SysInfoUserAccess
: public extensions::config::WinUserInfo::WinUserInfoBe_Impl
271 GetUserNameExW(NameDisplay
, nullptr, &nSize
);
272 if (GetLastError() != ERROR_MORE_DATA
)
273 throw css::uno::RuntimeException();
274 auto pNameBuf(std::make_unique
<wchar_t[]>(nSize
));
275 if (!GetUserNameExW(NameDisplay
, pNameBuf
.get(), &nSize
))
276 throw css::uno::RuntimeException();
277 m_sName
= o3tl::toU(pNameBuf
.get());
279 catch (css::uno::RuntimeException
&)
281 // GetUserNameEx may fail in some cases (e.g., for built-in AD domain
282 // administrator account on non-DC systems), where GetUserName will
283 // still give a name.
284 DWORD nSize
= UNLEN
+ 1;
285 auto pNameBuf(std::make_unique
<wchar_t[]>(nSize
));
286 if (!GetUserNameW(pNameBuf
.get(), &nSize
))
287 throw css::uno::RuntimeException();
288 m_sName
= o3tl::toU(pNameBuf
.get());
292 virtual OUString
GetGivenName() override
{ return m_sName
; }
303 namespace WinUserInfo
305 WinUserInfoBe::WinUserInfoBe()
306 : WinUserInfoMutexHolder()
307 , BackendBase(mMutex
)
311 m_pImpl
.reset(new ADsUserAccess());
313 catch (css::uno::RuntimeException
&)
315 m_pImpl
.reset(new SysInfoUserAccess
);
319 WinUserInfoBe::~WinUserInfoBe() {}
321 void WinUserInfoBe::setPropertyValue(OUString
const&, css::uno::Any
const&)
323 throw css::lang::IllegalArgumentException("setPropertyValue not supported",
324 static_cast<cppu::OWeakObject
*>(this), -1);
327 css::uno::Any
WinUserInfoBe::getPropertyValue(OUString
const& PropertyName
)
330 // Only process the first argument of possibly multiple space- or comma-separated arguments
331 OUString sToken
= PropertyName
.getToken(0, ' ').getToken(0, ',');
332 if (sToken
== _givenname
)
334 sValue
= m_pImpl
->GetGivenName();
336 else if (sToken
== _sn
)
338 sValue
= m_pImpl
->GetSn();
340 else if (sToken
== _fathersname
)
342 sValue
= m_pImpl
->GetFathersname();
344 else if (sToken
== _initials
)
346 sValue
= m_pImpl
->GetInitials();
348 else if (sToken
== _street
)
350 sValue
= m_pImpl
->GetStreet();
352 else if (sToken
== _l
)
354 sValue
= m_pImpl
->GetCity();
356 else if (sToken
== _st
)
358 sValue
= m_pImpl
->GetState();
360 else if (sToken
== _apartment
)
362 sValue
= m_pImpl
->GetApartment();
364 else if (sToken
== _postalcode
)
366 sValue
= m_pImpl
->GetPostalCode();
368 else if (sToken
== _c
)
370 sValue
= m_pImpl
->GetCountry();
372 else if (sToken
== _o
)
374 sValue
= m_pImpl
->GetOrganization();
376 else if (sToken
== _position
)
378 sValue
= m_pImpl
->GetPosition();
380 else if (sToken
== _title
)
382 sValue
= m_pImpl
->GetTitle();
384 else if (sToken
== _homephone
)
386 sValue
= m_pImpl
->GetHomePhone();
388 else if (sToken
== _telephonenumber
)
390 sValue
= m_pImpl
->GetTelephoneNumber();
392 else if (sToken
== _facsimiletelephonenumber
)
394 sValue
= m_pImpl
->GetFaxNumber();
396 else if (sToken
== _mail
)
398 sValue
= m_pImpl
->GetMail();
401 throw css::beans::UnknownPropertyException(sToken
, static_cast<cppu::OWeakObject
*>(this));
403 return css::uno::Any(css::beans::Optional
<css::uno::Any
>(
404 !sValue
.isEmpty(), sValue
.isEmpty() ? css::uno::Any() : css::uno::Any(sValue
)));
407 OUString SAL_CALL
WinUserInfoBe::getImplementationName()
409 return "com.sun.star.comp.configuration.backend.WinUserInfoBe";
412 sal_Bool SAL_CALL
WinUserInfoBe::supportsService(const OUString
& aServiceName
)
414 return cppu::supportsService(this, aServiceName
);
417 css::uno::Sequence
<OUString
> SAL_CALL
WinUserInfoBe::getSupportedServiceNames()
419 return { "com.sun.star.configuration.backend.WinUserInfoBe" };
425 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
426 extensions_WinUserInfoBe_get_implementation(css::uno::XComponentContext
*,
427 css::uno::Sequence
<css::uno::Any
> const&)
429 return cppu::acquire(new extensions::config::WinUserInfo::WinUserInfoBe());
432 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */