12 #include "kcoreaddons_debug.h" 14 #include <QStandardPaths> 17 #include <type_traits> 19 #include <qt_windows.h> 27 struct netApiBufferDeleter {
28 void operator()(
void *buffer)
31 NetApiBufferFree(buffer);
37 class ScopedNetApiBuffer :
public std::unique_ptr<T, netApiBufferDeleter>
42 inline explicit ScopedNetApiBuffer(T *data)
43 :
std::unique_ptr<T, ::netApiBufferDeleter>(data, ::netApiBufferDeleter()) {}
46 const auto handleCloser = [](
HANDLE h)
48 if (h != INVALID_HANDLE_VALUE) {
52 typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(handleCloser)> ScopedHANDLE;
58 template<
typename T>
struct NetApiTypeInfo {};
59 #define NETAPI_TYPE_INFO(prefix, n) template<> struct NetApiTypeInfo<prefix##_##n> { enum { level = n }; }; 60 NETAPI_TYPE_INFO(GROUP_INFO, 0)
61 NETAPI_TYPE_INFO(GROUP_INFO, 3)
62 NETAPI_TYPE_INFO(USER_INFO, 0)
63 NETAPI_TYPE_INFO(USER_INFO, 4)
64 NETAPI_TYPE_INFO(USER_INFO, 11)
65 NETAPI_TYPE_INFO(GROUP_USERS_INFO, 0)
69 ScopedNetApiBuffer<T> getUserInfo(LPCWSTR server, const
QString &userName, NET_API_STATUS *errCode)
71 LPBYTE userInfoTmp =
nullptr;
73 NET_API_STATUS
status = NetUserGetInfo(server, (LPCWSTR)userName.utf16(), NetApiTypeInfo<T>::level, &userInfoTmp);
74 if (status != NERR_Success) {
75 userInfoTmp =
nullptr;
80 return ScopedNetApiBuffer<T>((T*)userInfoTmp);
91 template<
class T,
class Callback,
class EnumFunction>
92 static void netApiEnumerate(uint maxCount, Callback callback, EnumFunction enumFunc)
94 NET_API_STATUS nStatus = NERR_Success;
95 DWORD_PTR resumeHandle = 0;
97 int level = NetApiTypeInfo<T>::level;
99 LPBYTE buffer =
nullptr;
100 DWORD entriesRead = 0;
101 DWORD totalEntries = 0;
102 nStatus = enumFunc(level, &buffer, &entriesRead, &totalEntries, &resumeHandle);
107 ScopedNetApiBuffer<T> groupInfo((T*)buffer);
108 if (nStatus == NERR_Success || nStatus == ERROR_MORE_DATA) {
109 for (DWORD i = 0; total < maxCount && i < entriesRead; i++, total++) {
110 callback(groupInfo.get()[i]);
113 qCWarning(KCOREADDONS_DEBUG,
"NetApi enumerate function failed: status = %d", (
int)nStatus);
115 }
while (nStatus == ERROR_MORE_DATA);
118 template<
class T,
class Callback>
119 void enumerateAllUsers(uint maxCount, Callback callback)
121 netApiEnumerate<T>(maxCount, callback, [](
int level, LPBYTE *buffer, DWORD *count, DWORD *total, PDWORD_PTR resumeHandle) {
126 return NetUserEnum(
nullptr, level, 0, buffer, MAX_PREFERRED_LENGTH, count, total, (PDWORD)resumeHandle);
130 template<
typename T,
class Callback>
131 void enumerateAllGroups(uint maxCount, Callback callback)
133 netApiEnumerate<T>(maxCount, callback, [](
int level, LPBYTE *buffer, DWORD *count, DWORD *total, PDWORD_PTR resumeHandle) {
134 return NetGroupEnum(
nullptr, level, buffer, MAX_PREFERRED_LENGTH, count, total, resumeHandle);
138 template<
typename T,
class Callback>
139 void enumerateGroupsForUser(uint maxCount,
const QString &name, Callback callback)
141 LPCWSTR nameStr = (LPCWSTR)name.
utf16();
142 netApiEnumerate<T>(maxCount, callback, [&](
int level, LPBYTE *buffer, DWORD *count, DWORD *total, PDWORD_PTR resumeHandle) -> NET_API_STATUS {
143 Q_UNUSED(resumeHandle);
144 NET_API_STATUS ret = NetUserGetGroups(
nullptr, nameStr, level, buffer, MAX_PREFERRED_LENGTH, count, total);
146 if (ret == ERROR_MORE_DATA) {
147 qCWarning(KCOREADDONS_DEBUG) <<
"NetUserGetGroups for user" << name <<
"returned ERROR_MORE_DATA. This should not happen!";
154 template<
typename T,
class Callback>
155 void enumerateUsersForGroup(
const QString &name, uint maxCount, Callback callback)
157 LPCWSTR nameStr = (LPCWSTR)name.
utf16();
158 netApiEnumerate<T>(maxCount, callback, [nameStr](
int level, LPBYTE *buffer, DWORD *count, DWORD *total, PDWORD_PTR resumeHandle) {
159 return NetGroupGetUsers(
nullptr, nameStr, level, buffer, MAX_PREFERRED_LENGTH, count, total, resumeHandle);
166 Private() : isAdmin(false) {}
170 : uid(uid), gid(gid), loginName(loginName), fullName(fullName),
171 domain(domain), homeDir(homeDir), isAdmin(isAdmin)
183 WCHAR profileDirPath[MAX_PATH];
184 DWORD bufSize = MAX_PATH;
185 BOOL result = GetProfilesDirectoryW(profileDirPath, &bufSize);
195 static Ptr sharedNull;
211 DWORD nameBufferLen = UNLEN + 1;
212 WCHAR nameBuffer[UNLEN + 1];
213 DWORD domainBufferLen = UNLEN + 1;
214 WCHAR domainBuffer[UNLEN + 1];
216 if (!LookupAccountSidW(
nullptr, uid.
nativeId(), nameBuffer, &nameBufferLen, domainBuffer, &domainBufferLen, &use)) {
217 qCWarning(KCOREADDONS_DEBUG) <<
"Could not lookup user " << uid.
toString() <<
"error =" << GetLastError();
222 if (use != SidTypeUser && use != SidTypeDeletedAccount) {
223 qCWarning(KCOREADDONS_DEBUG).nospace()
224 <<
"SID for " << domainName <<
"\\" << loginName <<
" (" << uid.
toString()
225 <<
") is not of type user (" << SidTypeUser <<
" or " << SidTypeDeletedAccount
226 <<
"). Got type " << use <<
" instead.";
230 LPWSTR servernameTmp =
nullptr;
231 NET_API_STATUS status = NetGetAnyDCName(
nullptr, 0, (LPBYTE *)&servernameTmp);
232 if (status != NERR_Success) {
236 ScopedNetApiBuffer<WCHAR> servername(servernameTmp);
241 bool isAdmin =
false;
245 if (
auto userInfo4 = getUserInfo<USER_INFO_4>(servername.get(), loginName, &status)) {
246 Q_ASSERT(
KUserId(userInfo4->usri4_user_sid) == uid);
249 isAdmin = userInfo4->usri4_priv == USER_PRIV_ADMIN;
251 const DWORD primaryGroup = userInfo4->usri4_primary_group_id;
256 UCHAR numSubauthorities = *GetSidSubAuthorityCount(group.
nativeId());
257 PDWORD lastSubAutority = GetSidSubAuthority(group.
nativeId(), numSubauthorities - 1);
258 *lastSubAutority = primaryGroup;
259 }
else if (
auto userInfo11 = getUserInfo<USER_INFO_11>(servername.get(), loginName, &status)) {
262 isAdmin = userInfo11->usri11_priv == USER_PRIV_ADMIN;
264 qCWarning(KCOREADDONS_DEBUG).nospace() <<
"Could not get information for user " << domainName <<
"\\" << loginName
265 <<
": error code = " << status;
269 homeDir = guessHomeDir(loginName, uid);
273 enumerateGroupsForUser<GROUP_USERS_INFO_0>(1, loginName, [&](
const GROUP_USERS_INFO_0 &info) {
277 return Ptr(
new Private(uid, group, loginName, fullName, domainName, homeDir, isAdmin));
285 if (mode == UseEffectiveUID) {
287 }
else if (mode == UseRealUserID) {
290 d = Private::sharedNull;
327 return isValid() && d->uid == user.d->uid;
332 return d->uid.isValid();
355 COMInitializer() : result(CoInitialize(nullptr)) {}
358 if (SUCCEEDED(result)) {
367 W32Library(HMODULE h): h(h) {}
388 struct FaceIconPath_XP {
389 typedef HRESULT (WINAPI *funcptr_t)(LPCWSTR, DWORD, LPWSTR);
390 static const int ordinal = 233;
391 static HRESULT getPicturePath(funcptr_t SHGetUserPicturePathXP, LPCWSTR username, LPWSTR buf, UINT bufsize)
395 return SHGetUserPicturePathXP(username, 0, buf);
398 struct FaceIconPath_Vista {
399 typedef HRESULT (WINAPI *funcptr_t)(LPCWSTR, DWORD, LPWSTR, UINT);
400 static const int ordinal = 261;
401 static HRESULT getPicturePath(funcptr_t SHGetUserPicturePathV, LPCWSTR username, LPWSTR buf, UINT bufsize)
403 return SHGetUserPicturePathV(username, 0, buf, bufsize);
407 template <
typename Platform>
408 static QString faceIconPathImpl(LPCWSTR username)
410 static COMInitializer COMinit;
412 static W32Library shellMod = LoadLibraryA(
"shell32.dll");
416 static typename Platform::funcptr_t sgupp_ptr =
reinterpret_cast<typename Platform::funcptr_t
>(
417 GetProcAddress(shellMod, MAKEINTRESOURCEA(Platform::ordinal)));
422 WCHAR pathBuf[MAX_PATH];
424 HRESULT res = Platform::getPicturePath(sgupp_ptr, username, pathBuf, MAX_PATH);
437 LPCWSTR username =
reinterpret_cast<const WCHAR *
>(d->loginName.utf16());
438 return faceIconPathImpl<FaceIconPath_Vista>(username);
458 if (which == FullName) {
476 : name(name), gid(id)
479 PBYTE groupInfoTmp =
nullptr;
480 NET_API_STATUS status = NetGroupGetInfo(
nullptr, (LPCWSTR)name.
utf16(), 0, &groupInfoTmp);
482 ScopedNetApiBuffer<GROUP_INFO_0> groupInfo((GROUP_INFO_0 *)groupInfoTmp);
483 if (status != NERR_Success) {
484 qCWarning(KCOREADDONS_DEBUG) <<
"Failed to find group with name" << name <<
"error =" << status;
510 DWORD bufferLen = UNLEN + 1;
511 WCHAR buffer[UNLEN + 1];
512 DWORD domainBufferLen = UNLEN + 1;
513 WCHAR domainBuffer[UNLEN + 1];
516 if (LookupAccountSidW(NULL, gid.
nativeId(), buffer, &bufferLen, domainBuffer, &domainBufferLen, &eUse)) {
517 if (eUse == SidTypeGroup || eUse == SidTypeWellKnownGroup) {
527 : d(new Private(nameFromGroupId(gid), gid))
534 d =
new Private(nameFromGroupId(groupId), groupId);
545 d =
new Private(nameFromGroupId(gid), gid);
561 return isValid() && d->gid == group.d->gid && d->name == group.d->name;
566 return d->gid.isValid() && !d->name.isEmpty();
591 enumerateAllUsers<USER_INFO_0>(maxCount, [&result](
const USER_INFO_0 &info) {
600 enumerateAllUsers<USER_INFO_0>(maxCount, [&result](
const USER_INFO_0 &info) {
611 enumerateAllGroups<GROUP_INFO_0>(maxCount, [&result](
const GROUP_INFO_0 &groupInfo) {
620 enumerateAllGroups<GROUP_INFO_0>(maxCount, [&result](
const GROUP_INFO_0 &groupInfo) {
632 enumerateGroupsForUser<GROUP_USERS_INFO_0>(maxCount, d->loginName, [&result](
const GROUP_USERS_INFO_0 &info) {
644 enumerateGroupsForUser<GROUP_USERS_INFO_0>(maxCount, d->loginName, [&result](
const GROUP_USERS_INFO_0 &info) {
656 enumerateGroupsForUser<GROUP_USERS_INFO_0>(maxCount, d->name, [&result](
const GROUP_USERS_INFO_0 &info) {
668 enumerateGroupsForUser<GROUP_USERS_INFO_0>(maxCount, d->name, [&result](
const GROUP_USERS_INFO_0 &info) {
674 static const auto invalidSidString = QStringLiteral(
"<invalid SID>");
676 static QString sidToString(
void *sid)
678 if (!sid || !IsValidSid(sid)) {
679 return invalidSidString;
682 if (!ConvertSidToStringSidW(sid, &sidStr)) {
683 return invalidSidString;
691 char sidBuffer[SECURITY_MAX_SID_SIZE];
693 static WindowsSIDWrapper *copySid(PSID sid)
695 if (!sid || !IsValidSid(sid)) {
699 WindowsSIDWrapper *
copy =
new WindowsSIDWrapper();
700 bool success = CopySid(SECURITY_MAX_SID_SIZE, copy->sidBuffer, sid);
702 QString sidString = sidToString(sid);
703 qCWarning(KCOREADDONS_DEBUG,
"Failed to copy SID %s, error = %d", qPrintable(sidString), (
int)GetLastError());
736 : data(WindowsSIDWrapper::copySid(nativeId))
752 return data->sidBuffer;
762 return EqualSid(data->sidBuffer, other.data->sidBuffer);
770 return !(*
this == other);
776 return sidToString(data ? data->sidBuffer :
nullptr);
780 template<
class T,
class Callback>
781 static T sidFromName(
const QString &name, Callback callback)
788 char buffer[SECURITY_MAX_SID_SIZE];
789 DWORD sidLength = SECURITY_MAX_SID_SIZE;
792 WCHAR domainBuffer[1024];
793 DWORD domainBufferSize = 1024;
794 SID_NAME_USE sidType;
795 bool ok = LookupAccountNameW(
nullptr, (LPCWSTR)name.
utf16(), buffer, &sidLength, domainBuffer, &domainBufferSize, &sidType);
797 qCWarning(KCOREADDONS_DEBUG) <<
"Failed to lookup account" << name <<
"error code =" << GetLastError();
800 return callback(buffer, sidType);
805 return sidFromName<KUserId>(name, [&](PSID sid, SID_NAME_USE sidType) ->
KUserId {
806 if (sidType != SidTypeUser && sidType != SidTypeDeletedAccount)
808 qCWarning(KCOREADDONS_DEBUG).nospace() <<
"Failed to lookup user name " << name
809 <<
": resulting SID " << sidToString(sid) <<
" is not a user." 810 " Got SID type " << sidType <<
" instead.";
819 return sidFromName<KGroupId>(name, [&](PSID sid, SID_NAME_USE sidType) ->
KGroupId {
820 if (sidType != SidTypeGroup && sidType != SidTypeWellKnownGroup)
822 qCWarning(KCOREADDONS_DEBUG).nospace() <<
"Failed to lookup user name " << name
823 <<
": resulting SID " << sidToString(sid) <<
" is not a group." 824 " Got SID type " << sidType <<
" instead.";
831 static std::unique_ptr<char[]> queryProcessInformation(TOKEN_INFORMATION_CLASS type)
834 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &_token)) {
835 qCWarning(KCOREADDONS_DEBUG,
"Failed to get the token for the current process: %d", (
int)GetLastError());
838 ScopedHANDLE token(_token, handleCloser);
841 if (!GetTokenInformation(token.get(), type,
nullptr, 0, &requiredSize)) {
842 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
843 qCWarning(KCOREADDONS_DEBUG,
"Failed to get the required size for the token information %d: %d",
844 type, (
int)GetLastError());
848 std::unique_ptr<char[]> buffer(
new char[requiredSize]);
849 if (!GetTokenInformation(token.get(), type, buffer.get(), requiredSize, &requiredSize)) {
850 qCWarning(KCOREADDONS_DEBUG,
"Failed to get token information %d from current process: %d",
851 type, (
int)GetLastError());
859 std::unique_ptr<char[]> userTokenBuffer = queryProcessInformation(TokenUser);
860 TOKEN_USER *userToken = (TOKEN_USER *)userTokenBuffer.get();
861 return KUserId(userToken->User.Sid);
866 std::unique_ptr<char[]> primaryGroupBuffer = queryProcessInformation(TokenPrimaryGroup);
867 TOKEN_PRIMARY_GROUP *primaryGroup = (TOKEN_PRIMARY_GROUP *)primaryGroupBuffer.get();
868 return KGroupId(primaryGroup->PrimaryGroup);
873 return currentUserId();
881 KCOREADDONS_EXPORT uint
qHash(
const KUserId &
id, uint seed)
QString fromWCharArray(const wchar_t *string, int size)
KUserGroup(const QString &name)
Create an object from a group name.
static QList< KUser > allUsers(uint maxCount=KCOREADDONS_UINT_MAX)
bool isValid() const
Returns whether the group is valid.
static KGroupId currentGroupId()
QString name() const
The name of the group.
Use the effective user id.
static KGroupId currentEffectiveGroupId()
NativeType nativeId() const
QAction * create(StandardAction id, const QObject *recvr, Func slot, QObject *parent)
QVariant property(UserProperty which) const
Returns an extended property.
Represents a group on your system.
static KUserId currentUserId()
QByteArray fromRawData(const char *data, int size)
bool operator==(const KUserOrGroupId &other) const
static KGroupId fromName(const QString &name)
static QStringList allUserNames(uint maxCount=KCOREADDONS_UINT_MAX)
Represents a user on your system.
KUserOrGroupId()
Creates an invalid KUserOrGroupId.
bool operator==(const KUser &user) const
Two KUser objects are equal if the userId() are identical.
static KUserId fromName(const QString &name)
KIOCORE_EXPORT CopyJob * copy(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
void append(const T &value)
QList< KUser > users(uint maxCount=KCOREADDONS_UINT_MAX) const
QString loginName() const
The login name of the user.
bool isEmpty() const const
bool operator==(const KUserGroup &group) const
Two KUserGroup objects are equal if their gid()s are identical.
KUserGroup & operator=(const KUserGroup &group)
Copies a group.
QStringList userNames(uint maxCount=KCOREADDONS_UINT_MAX) const
static KUserId currentEffectiveUserId()
KCALENDARCORE_EXPORT uint qHash(const KCalendarCore::Period &key)
QString homeDir() const
The path to the user's home directory.
A platform independent group ID.
const ushort * utf16() const const
QString shell() const
The path to the user's login shell.
KGroupId()
Creates an invalid KGroupId.
bool isSuperUser() const
Checks whether the user is the super user (root).
QStringList groupNames(uint maxCount=KCOREADDONS_UINT_MAX) const
QList< KUserGroup > groups(uint maxCount=KCOREADDONS_UINT_MAX) const
static QList< KUserGroup > allGroups(uint maxCount=KCOREADDONS_UINT_MAX)
bool operator!=(const KUserOrGroupId &other) const
KUser & operator=(const KUser &user)
Copies a user.
bool isValid() const
Returns true if the user is valid.
static QStringList allGroupNames(uint maxCount=KCOREADDONS_UINT_MAX)
KUser(UIDMode mode=UseEffectiveUID)
Creates an object that contains information about the current user.
QString faceIconPath() const
The path to the user's face file.
A platform independent user ID.
A platform independent user or group ID.