diff --git a/headers/build/os/package/User.h b/headers/build/os/package/User.h new file mode 100644 index 0000000000..537633d3fe --- /dev/null +++ b/headers/build/os/package/User.h @@ -0,0 +1 @@ +#include <../os/package/User.h> diff --git a/headers/os/package/PackageInfo.h b/headers/os/package/PackageInfo.h index ade89a03cc..3cda4350b8 100644 --- a/headers/os/package/PackageInfo.h +++ b/headers/os/package/PackageInfo.h @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -86,6 +87,9 @@ public: const BObjectList& UserSettingsFileInfos() const; + const BObjectList& Users() const; + const BStringList& Groups() const; + const BObjectList& ProvidesList() const; const BObjectList& RequiresList() const; @@ -135,6 +139,12 @@ public: status_t AddUserSettingsFileInfo( const BUserSettingsFileInfo& info); + void ClearUsers(); + status_t AddUser(const BUser& user); + + void ClearGroups(); + status_t AddGroup(const BString& group); + void ClearProvidesList(); status_t AddProvides(const BPackageResolvable& provides); @@ -196,6 +206,8 @@ private: typedef BObjectList UserSettingsFileInfoList; + typedef BObjectList UserList; + private: status_t _ReadFromPackageFile( const PackageFileLocation& fileLocation); @@ -218,6 +230,8 @@ private: const char* field, const UserSettingsFileInfoList& infos); + static status_t _AddUsers(BMessage* archive, const char* field, + const UserList& users); static status_t _ExtractVersion(BMessage* archive, const char* field, int32 index, @@ -236,6 +250,8 @@ private: static status_t _ExtractUserSettingsFileInfos( BMessage* archive, const char* field, UserSettingsFileInfoList& _infos); + static status_t _ExtractUsers(BMessage* archive, + const char* field, UserList& _users); private: BString fName; @@ -259,6 +275,9 @@ private: BObjectList fGlobalSettingsFileInfos; BObjectList fUserSettingsFileInfos; + UserList fUsers; + BStringList fGroups; + ResolvableList fProvidesList; ResolvableExpressionList fRequiresList; diff --git a/headers/os/package/PackageInfoAttributes.h b/headers/os/package/PackageInfoAttributes.h index 89b6814c58..b54c995a2d 100644 --- a/headers/os/package/PackageInfoAttributes.h +++ b/headers/os/package/PackageInfoAttributes.h @@ -45,6 +45,10 @@ enum BPackageInfoAttributeID { // list of global settings file infos B_PACKAGE_INFO_USER_SETTINGS_FILES, // list of user settings file infos + B_PACKAGE_INFO_USERS, + // list of (Unix) users defined/needed + B_PACKAGE_INFO_GROUPS, + // list of (Unix) groups defined/needed // B_PACKAGE_INFO_ENUM_COUNT, }; diff --git a/headers/os/package/User.h b/headers/os/package/User.h new file mode 100644 index 0000000000..2a263a45cc --- /dev/null +++ b/headers/os/package/User.h @@ -0,0 +1,52 @@ +/* + * Copyright 2013, Haiku, Inc. + * Distributed under the terms of the MIT License. + */ +#ifndef _PACKAGE__USER_H_ +#define _PACKAGE__USER_H_ + + +#include +#include + + +namespace BPackageKit { + + +class BUser { +public: + BUser(); + BUser(const BString& name, + const BString& realName, + const BString& home, const BString& shell, + const BStringList& groups); + ~BUser(); + + status_t InitCheck() const; + + const BString& Name() const; + const BString& RealName() const; + const BString& Home() const; + const BString& Shell() const; + const BStringList& Groups() const; + + status_t SetTo(const BString& name, + const BString& realName, + const BString& home, const BString& shell, + const BStringList& groups); + + static bool IsValidUserName(const char* name); + +private: + BString fName; + BString fRealName; + BString fHome; + BString fShell; + BStringList fGroups; +}; + + +} // namespace BPackageKit + + +#endif // _PACKAGE__USER_H_ diff --git a/headers/os/package/hpkg/PackageAttributes.h b/headers/os/package/hpkg/PackageAttributes.h index 90888eb5c1..ffa1dd4a51 100644 --- a/headers/os/package/hpkg/PackageAttributes.h +++ b/headers/os/package/hpkg/PackageAttributes.h @@ -70,3 +70,10 @@ B_DEFINE_HPKG_ATTRIBUTE(44, UINT, "package:settings-file-update-type", PACKAGE_SETTINGS_FILE_UPDATE_TYPE) B_DEFINE_HPKG_ATTRIBUTE(45, STRING, "package:settings-file-template", PACKAGE_SETTINGS_FILE_TEMPLATE) +B_DEFINE_HPKG_ATTRIBUTE(46, STRING, "package:user", PACKAGE_USER) +B_DEFINE_HPKG_ATTRIBUTE(47, STRING, "package:user.real-name", + PACKAGE_USER_REAL_NAME) +B_DEFINE_HPKG_ATTRIBUTE(48, STRING, "package:user.home", PACKAGE_USER_HOME) +B_DEFINE_HPKG_ATTRIBUTE(49, STRING, "package:user.shell", PACKAGE_USER_SHELL) +B_DEFINE_HPKG_ATTRIBUTE(50, STRING, "package:user.group", PACKAGE_USER_GROUP) +B_DEFINE_HPKG_ATTRIBUTE(51, STRING, "package:group", PACKAGE_GROUP) diff --git a/headers/os/package/hpkg/PackageInfoAttributeValue.h b/headers/os/package/hpkg/PackageInfoAttributeValue.h index 700f5d533c..e0b5db67b3 100644 --- a/headers/os/package/hpkg/PackageInfoAttributeValue.h +++ b/headers/os/package/hpkg/PackageInfoAttributeValue.h @@ -59,6 +59,16 @@ struct BUserSettingsFileInfoData { }; +struct BUserData { + const char* name; + const char* realName; + const char* home; + const char* shell; + const char* const* groups; + size_t groupCount; +}; + + struct BPackageInfoAttributeValue { union { uint64 unsignedInt; @@ -68,6 +78,7 @@ struct BPackageInfoAttributeValue { BPackageResolvableExpressionData resolvableExpression; BGlobalSettingsFileInfoData globalSettingsFileInfo; BUserSettingsFileInfoData userSettingsFileInfo; + BUserData user; }; BPackageInfoAttributeID attributeID; diff --git a/headers/private/package/hpkg/ReaderImplBase.h b/headers/private/package/hpkg/ReaderImplBase.h index 465631bfd5..c5c9694b5e 100644 --- a/headers/private/package/hpkg/ReaderImplBase.h +++ b/headers/private/package/hpkg/ReaderImplBase.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -103,6 +104,7 @@ protected: class PackageResolvableExpressionAttributeHandler; class GlobalSettingsFileInfoAttributeHandler; class UserSettingsFileInfoAttributeHandler; + class UserAttributeHandler; class PackageAttributeHandler; class LowLevelAttributeHandler; @@ -344,6 +346,25 @@ public: }; +class ReaderImplBase::UserAttributeHandler + : public PackageInfoAttributeHandlerBase { +public: + UserAttributeHandler( + BPackageInfoAttributeValue& + packageInfoValue); + + virtual status_t HandleAttribute( + AttributeHandlerContext* context, uint8 id, + const AttributeValue& value, + AttributeHandler** _handler); + + virtual status_t Delete(AttributeHandlerContext* context); + +private: + Array fGroups; +}; + + class ReaderImplBase::PackageAttributeHandler : public AttributeHandler { public: virtual status_t HandleAttribute( diff --git a/headers/private/package/hpkg/WriterImplBase.h b/headers/private/package/hpkg/WriterImplBase.h index c45d850330..58c43d40e7 100644 --- a/headers/private/package/hpkg/WriterImplBase.h +++ b/headers/private/package/hpkg/WriterImplBase.h @@ -163,6 +163,12 @@ private: = B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MAJOR; private: + PackageAttribute* _AddStringAttribute(BHPKGAttributeID id, + const BString& value, + DoublyLinkedList& list); + inline PackageAttribute* _AddStringAttributeIfNotEmpty( + BHPKGAttributeID id, const BString& value, + DoublyLinkedList& list); void _WritePackageAttributes( const PackageAttributeList& attributes); @@ -252,6 +258,16 @@ WriterImplBase::SetFinished(bool finished) } +inline WriterImplBase::PackageAttribute* +WriterImplBase::_AddStringAttributeIfNotEmpty(BHPKGAttributeID id, + const BString& value, DoublyLinkedList& list) +{ + if (value.IsEmpty()) + return NULL; + return _AddStringAttribute(id, value, list); +} + + } // namespace BPrivate } // namespace BHPKG diff --git a/src/bin/package/command_list.cpp b/src/bin/package/command_list.cpp index 219f03c3c3..882f4c9be4 100644 --- a/src/bin/package/command_list.cpp +++ b/src/bin/package/command_list.cpp @@ -310,6 +310,22 @@ struct PackageContentListHandler : VersionPolicy::PackageContentHandler { printf("\n"); break; + case B_PACKAGE_INFO_USERS: + printf("\tuser: %s\n", value.user.name); + if (value.user.realName != NULL) + printf("\t\treal name: %s\n", value.user.realName); + if (value.user.home != NULL) + printf("\t\thome: %s\n", value.user.home); + if (value.user.shell != NULL) + printf("\t\tshell: %s\n", value.user.shell); + for (size_t i = 0; i < value.user.groupCount; i++) + printf("\t\tgroup: %s\n", value.user.groups[i]); + break; + + case B_PACKAGE_INFO_GROUPS: + printf("\tgroup: %s\n", value.string); + break; + case B_PACKAGE_INFO_INSTALL_PATH: printf("\tinstall path: %s\n", value.string); break; diff --git a/src/build/libpackage/Jamfile b/src/build/libpackage/Jamfile index d0b83cb11b..e1b530e63b 100644 --- a/src/build/libpackage/Jamfile +++ b/src/build/libpackage/Jamfile @@ -105,6 +105,7 @@ BuildPlatformSharedLibrary libpackage_build.so RepositoryInfo.cpp Request.cpp TempfileManager.cpp + User.cpp ValidateChecksumJob.cpp $(HPKG_SOURCES) diff --git a/src/kits/package/Jamfile b/src/kits/package/Jamfile index 358b0f2607..0afbade17f 100644 --- a/src/kits/package/Jamfile +++ b/src/kits/package/Jamfile @@ -97,6 +97,7 @@ SharedLibrary libpackage.so RepositoryInfo.cpp Request.cpp TempfileManager.cpp + User.cpp ValidateChecksumJob.cpp $(HPKG_SOURCES) diff --git a/src/kits/package/PackageInfo.cpp b/src/kits/package/PackageInfo.cpp index 269ea5e44a..5297f5f681 100644 --- a/src/kits/package/PackageInfo.cpp +++ b/src/kits/package/PackageInfo.cpp @@ -1,5 +1,6 @@ /* * Copyright 2011, Oliver Tappe + * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -52,6 +53,8 @@ const char* const BPackageInfo::kElementNames[B_PACKAGE_INFO_ENUM_COUNT] = { "base-package", "global-settings-files", "user-settings-files", + "users", + "groups" }; @@ -160,18 +163,20 @@ BPackageInfo::BPackageInfo() BArchivable(), fFlags(0), fArchitecture(B_PACKAGE_ARCHITECTURE_ENUM_COUNT), - fCopyrightList(5), - fLicenseList(5), - fURLList(5), - fSourceURLList(5), - fGlobalSettingsFileInfos(5, true), - fUserSettingsFileInfos(5, true), + fCopyrightList(4), + fLicenseList(4), + fURLList(4), + fSourceURLList(4), + fGlobalSettingsFileInfos(4, true), + fUserSettingsFileInfos(4, true), + fUsers(4, true), + fGroups(4), fProvidesList(20, true), fRequiresList(20, true), fSupplementsList(20, true), - fConflictsList(5, true), - fFreshensList(5, true), - fReplacesList(5) + fConflictsList(4, true), + fFreshensList(4, true), + fReplacesList(4) { } @@ -181,18 +186,20 @@ BPackageInfo::BPackageInfo(BMessage* archive, status_t* _error) BArchivable(archive), fFlags(0), fArchitecture(B_PACKAGE_ARCHITECTURE_ENUM_COUNT), - fCopyrightList(5), - fLicenseList(5), - fURLList(5), - fSourceURLList(5), - fGlobalSettingsFileInfos(5, true), - fUserSettingsFileInfos(5, true), + fCopyrightList(4), + fLicenseList(4), + fURLList(4), + fSourceURLList(4), + fGlobalSettingsFileInfos(4, true), + fUserSettingsFileInfos(4, true), + fUsers(4, true), + fGroups(4), fProvidesList(20, true), fRequiresList(20, true), fSupplementsList(20, true), - fConflictsList(5, true), - fFreshensList(5, true), - fReplacesList(5) + fConflictsList(4, true), + fFreshensList(4, true), + fReplacesList(4) { status_t error; int32 architecture; @@ -216,6 +223,8 @@ BPackageInfo::BPackageInfo(BMessage* archive, status_t* _error) "global-settings-files", fGlobalSettingsFileInfos)) == B_OK && (error = _ExtractUserSettingsFileInfos(archive, "user-settings-files", fUserSettingsFileInfos)) == B_OK + && (error = _ExtractUsers(archive, "users", fUsers)) == B_OK + && (error = _ExtractStringList(archive, "groups", fGroups)) == B_OK && (error = _ExtractResolvables(archive, "provides", fProvidesList)) == B_OK && (error = _ExtractResolvableExpressions(archive, "requires", @@ -326,6 +335,50 @@ BPackageInfo::InitCheck() const || fProvidesList.IsEmpty()) return B_NO_INIT; + // check global settings files + int32 globalSettingsFileCount = fGlobalSettingsFileInfos.CountItems(); + for (int32 i = 0; i < globalSettingsFileCount; i++) { + const BGlobalSettingsFileInfo* info + = fGlobalSettingsFileInfos.ItemAt(i); + status_t error = info->InitCheck(); + if (error != B_OK) + return error; + } + + // check user settings files + int32 userSettingsFileCount = fUserSettingsFileInfos.CountItems(); + for (int32 i = 0; i < userSettingsFileCount; i++) { + const BUserSettingsFileInfo* info = fUserSettingsFileInfos.ItemAt(i); + status_t error = info->InitCheck(); + if (error != B_OK) + return error; + } + + // check users + int32 userCount = fUsers.CountItems(); + for (int32 i = 0; i < userCount; i++) { + const BUser* user = fUsers.ItemAt(i); + status_t error = user->InitCheck(); + if (error != B_OK) + return B_NO_INIT; + + // make sure the user's groups are specified as groups + const BStringList& userGroups = user->Groups(); + int32 groupCount = userGroups.CountStrings(); + for (int32 k = 0; k < groupCount; k++) { + const BString& group = userGroups.StringAt(k); + if (!fGroups.HasString(group)) + return B_BAD_VALUE; + } + } + + // check groups + int32 groupCount = fGroups.CountStrings(); + for (int32 i = 0; i< groupCount; i++) { + if (!BUser::IsValidUserName(fGroups.StringAt(i))) + return B_BAD_VALUE; + } + return B_OK; } @@ -449,6 +502,20 @@ BPackageInfo::UserSettingsFileInfos() const } +const BObjectList& +BPackageInfo::Users() const +{ + return fUsers; +} + + +const BStringList& +BPackageInfo::Groups() const +{ + return fGroups; +} + + const BObjectList& BPackageInfo::ProvidesList() const { @@ -629,6 +696,13 @@ BPackageInfo::ClearSourceURLList() } +status_t +BPackageInfo::AddSourceURL(const BString& url) +{ + return fSourceURLList.Add(url) ? B_OK : B_NO_MEMORY; +} + + void BPackageInfo::ClearGlobalSettingsFileInfos() { @@ -671,10 +745,37 @@ BPackageInfo::AddUserSettingsFileInfo(const BUserSettingsFileInfo& info) } -status_t -BPackageInfo::AddSourceURL(const BString& url) +void +BPackageInfo::ClearUsers() { - return fSourceURLList.Add(url) ? B_OK : B_NO_MEMORY; + fUsers.MakeEmpty(); +} + + +status_t +BPackageInfo::AddUser(const BUser& user) +{ + BUser* newUser = new (std::nothrow) BUser(user); + if (newUser == NULL || !fUsers.AddItem(newUser)) { + delete newUser; + return B_NO_MEMORY; + } + + return B_OK; +} + + +void +BPackageInfo::ClearGroups() +{ + fGroups.MakeEmpty(); +} + + +status_t +BPackageInfo::AddGroup(const BString& group) +{ + return fGroups.Add(group) ? B_OK : B_NO_MEMORY; } @@ -807,6 +908,8 @@ BPackageInfo::Clear() fSourceURLList.MakeEmpty(); fGlobalSettingsFileInfos.MakeEmpty(); fUserSettingsFileInfos.MakeEmpty(); + fUsers.MakeEmpty(); + fGroups.MakeEmpty(); fRequiresList.MakeEmpty(); fProvidesList.MakeEmpty(); fSupplementsList.MakeEmpty(); @@ -842,6 +945,8 @@ BPackageInfo::Archive(BMessage* archive, bool deep) const "global-settings-files", fGlobalSettingsFileInfos)) != B_OK || (error = _AddUserSettingsFileInfos(archive, "user-settings-files", fUserSettingsFileInfos)) != B_OK + || (error = _AddUsers(archive, "users", fUsers)) != B_OK + || (error = archive->AddStrings("groups", fGroups)) != B_OK || (error = _AddResolvables(archive, "provides", fProvidesList)) != B_OK || (error = _AddResolvableExpressions(archive, "requires", fRequiresList)) != B_OK @@ -887,6 +992,8 @@ BPackageInfo::GetConfigString(BString& _string) const .Write("source-urls", fSourceURLList) .Write("global-settings-files", fGlobalSettingsFileInfos) .Write("user-settings-files", fUserSettingsFileInfos) + .Write("users", fUsers) + .Write("groups", fGroups) .Write("provides", fProvidesList) .BeginRequires(fBasePackage) .Write("requires", fRequiresList) @@ -1135,6 +1242,44 @@ BPackageInfo::_AddUserSettingsFileInfos(BMessage* archive, const char* field, } +/*static*/ status_t +BPackageInfo::_AddUsers(BMessage* archive, const char* field, + const UserList& users) +{ + // construct the field names we need + FieldName nameField(field, ":name"); + FieldName realNameField(field, ":realName"); + FieldName homeField(field, ":home"); + FieldName shellField(field, ":shell"); + FieldName groupsField(field, ":groups"); + + if (!nameField.IsValid() || !realNameField.IsValid() || !homeField.IsValid() + || !shellField.IsValid() || !groupsField.IsValid()) + return B_BAD_VALUE; + + // add fields + int32 count = users.CountItems(); + for (int32 i = 0; i < count; i++) { + const BUser* user = users.ItemAt(i); + BString groups = user->Groups().Join(" "); + if (groups.IsEmpty() && !user->Groups().IsEmpty()) + return B_NO_MEMORY; + + status_t error; + if ((error = archive->AddString(nameField, user->Name())) != B_OK + || (error = archive->AddString(realNameField, user->RealName())) + != B_OK + || (error = archive->AddString(homeField, user->Home())) != B_OK + || (error = archive->AddString(shellField, user->Shell())) != B_OK + || (error = archive->AddString(groupsField, groups)) != B_OK) { + return error; + } + } + + return B_OK; +} + + /*static*/ status_t BPackageInfo::_ExtractVersion(BMessage* archive, const char* field, int32 index, BPackageVersion& _version) @@ -1401,4 +1546,70 @@ BPackageInfo::_ExtractUserSettingsFileInfos(BMessage* archive, } +/*static*/ status_t +BPackageInfo::_ExtractUsers(BMessage* archive, const char* field, + UserList& _users) +{ + // construct the field names we need + FieldName nameField(field, ":name"); + FieldName realNameField(field, ":realName"); + FieldName homeField(field, ":home"); + FieldName shellField(field, ":shell"); + FieldName groupsField(field, ":groups"); + + if (!nameField.IsValid() || !realNameField.IsValid() || !homeField.IsValid() + || !shellField.IsValid() || !groupsField.IsValid()) + return B_BAD_VALUE; + + // get the number of items + type_code type; + int32 count; + if (archive->GetInfo(nameField, &type, &count) != B_OK) { + // the field is missing + return B_OK; + } + + // extract fields + for (int32 i = 0; i < count; i++) { + BString name; + status_t error = archive->FindString(nameField, i, &name); + if (error != B_OK) + return error; + + BString realName; + error = archive->FindString(realNameField, i, &realName); + if (error != B_OK) + return error; + + BString home; + error = archive->FindString(homeField, i, &home); + if (error != B_OK) + return error; + + BString shell; + error = archive->FindString(shellField, i, &shell); + if (error != B_OK) + return error; + + BString groupsString; + error = archive->FindString(groupsField, i, &groupsString); + if (error != B_OK) + return error; + + BStringList groups; + if (!groupsString.Split(" ", false, groups)) + return B_NO_MEMORY; + + BUser* user = new(std::nothrow) BUser(name, realName, home, shell, + groups); + if (user == NULL || !_users.AddItem(user)) { + delete user; + return B_NO_MEMORY; + } + } + + return B_OK; +} + + } // namespace BPackageKit diff --git a/src/kits/package/PackageInfoParser.cpp b/src/kits/package/PackageInfoParser.cpp index b2fbd444f6..28219f52bf 100644 --- a/src/kits/package/PackageInfoParser.cpp +++ b/src/kits/package/PackageInfoParser.cpp @@ -703,6 +703,118 @@ BPackageInfo::Parser::_ParseUserSettingsFileInfos( } +void +BPackageInfo::Parser::_ParseUsers(UserList* users) +{ + struct UserParser : public ListElementParser { + Parser& parser; + UserList* users; + + UserParser(Parser& parser, UserList* users) + : + parser(parser), + users(users) + { + } + + virtual void operator()(const Token& token) + { + if (token.type != TOKEN_WORD) { + throw ParseError("expected a user name", + token.pos); + } + + BString realName; + BString home; + BString shell; + BStringList groups; + + for (;;) { + Token nextToken = parser._NextToken(); + if (nextToken.type != TOKEN_WORD) { + parser._RewindTo(nextToken); + break; + } + + if (nextToken.text == "real-name") { + nextToken = parser._NextToken(); + if (nextToken.type != TOKEN_WORD + && nextToken.type != TOKEN_QUOTED_STRING) { + throw ParseError("expected string (a user real name)", + nextToken.pos); + } + realName = nextToken.text; + } else if (nextToken.text == "home") { + nextToken = parser._NextToken(); + if (nextToken.type != TOKEN_WORD + && nextToken.type != TOKEN_QUOTED_STRING) { + throw ParseError("expected string (a home path)", + nextToken.pos); + } + home = nextToken.text; + } else if (nextToken.text == "shell") { + nextToken = parser._NextToken(); + if (nextToken.type != TOKEN_WORD + && nextToken.type != TOKEN_QUOTED_STRING) { + throw ParseError("expected string (a shell path)", + nextToken.pos); + } + shell = nextToken.text; + } else if (nextToken.text == "groups") { + for (;;) { + nextToken = parser._NextToken(); + if (nextToken.type == TOKEN_WORD) { + if (!groups.Add(nextToken.text)) + throw std::bad_alloc(); + } else if (nextToken.type == TOKEN_ITEM_SEPARATOR + || nextToken.type == TOKEN_CLOSE_BRACE) { + parser._RewindTo(nextToken); + break; + } else { + throw ParseError("expected a group name", + nextToken.pos); + } + } + break; + } else { + throw ParseError( + "expected 'real-name', 'home', 'shell', or 'groups'", + nextToken.pos); + } + } + + BString templatePath; + + Token nextToken = parser._NextToken(); + if (nextToken.type == TOKEN_WORD && nextToken.text == "template") { + nextToken = parser._NextToken(); + if (nextToken.type != TOKEN_WORD + && nextToken.type != TOKEN_QUOTED_STRING) { + throw ParseError( + "expected string (a settings template file path)", + nextToken.pos); + } + templatePath = nextToken.text; + } else if (nextToken.type == TOKEN_ITEM_SEPARATOR + || nextToken.type == TOKEN_CLOSE_BRACE) { + parser._RewindTo(nextToken); + } else { + throw ParseError( + "expected 'template', semicolon, new line or '}'", + nextToken.pos); + } + + if (!users->AddItem(new BUser(token.text, realName, home, shell, + groups))) { + throw std::bad_alloc(); + } + } + } resolvableExpressionParser(*this, users); + + _ParseList(resolvableExpressionParser, false); +} + + void BPackageInfo::Parser::_Parse(BPackageInfo* packageInfo) { @@ -814,6 +926,14 @@ BPackageInfo::Parser::_Parse(BPackageInfo* packageInfo) &packageInfo->fUserSettingsFileInfos); break; + case B_PACKAGE_INFO_USERS: + _ParseUsers(&packageInfo->fUsers); + break; + + case B_PACKAGE_INFO_GROUPS: + _ParseStringList(&packageInfo->fGroups); + break; + case B_PACKAGE_INFO_PROVIDES: _ParseResolvableList(&packageInfo->fProvidesList); break; diff --git a/src/kits/package/PackageInfoParser.h b/src/kits/package/PackageInfoParser.h index beb547c105..5bbbb24e90 100644 --- a/src/kits/package/PackageInfoParser.h +++ b/src/kits/package/PackageInfoParser.h @@ -79,6 +79,7 @@ private: GlobalSettingsFileInfoList* infos); void _ParseUserSettingsFileInfos( UserSettingsFileInfoList* infos); + void _ParseUsers(UserList* users); void _Parse(BPackageInfo* packageInfo); diff --git a/src/kits/package/PackageInfoStringBuilder.h b/src/kits/package/PackageInfoStringBuilder.h index 7bd29cb396..2d140a95e8 100644 --- a/src/kits/package/PackageInfoStringBuilder.h +++ b/src/kits/package/PackageInfoStringBuilder.h @@ -175,6 +175,37 @@ private: } } + void _WriteListElement(const BUser* value) + { + _WriteMaybeQuoted(value->Name()); + + if (!value->RealName().IsEmpty()) { + _Write(" real-name "); + _WriteMaybeQuoted(value->RealName()); + } + + if (!value->Home().IsEmpty()) { + _Write(" home "); + _WriteMaybeQuoted(value->Home()); + } + + if (!value->Shell().IsEmpty()) { + _Write(" shell "); + _WriteMaybeQuoted(value->Shell()); + } + + if (!value->Groups().IsEmpty()) { + _Write(" groups "); + BString groups = value->Groups().Join(" "); + if (groups.IsEmpty()) { + if (fError == B_OK) + fError = B_NO_MEMORY; + return; + } + _Write(groups); + } + } + static inline bool _IsValueEmpty(const char* value) { return value[0] == '\0'; diff --git a/src/kits/package/User.cpp b/src/kits/package/User.cpp new file mode 100644 index 0000000000..973b73dd33 --- /dev/null +++ b/src/kits/package/User.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + + +namespace BPackageKit { + + +BUser::BUser() + : + fName(), + fRealName(), + fHome(), + fShell(), + fGroups() +{ +} + + +BUser::BUser(const BString& name, const BString& realName, const BString& home, + const BString& shell, const BStringList& groups) + : + fName(name), + fRealName(realName), + fHome(home), + fShell(shell), + fGroups(groups) +{ +} + + +BUser::~BUser() +{ +} + + +status_t +BUser::InitCheck() const +{ + if (fName.IsEmpty()) + return B_NO_INIT; + if (!IsValidUserName(fName)) + return B_BAD_VALUE; + return B_OK; +} + + +const BString& +BUser::Name() const +{ + return fName; +} + + +const BString& +BUser::RealName() const +{ + return fRealName; +} + + +const BString& +BUser::Home() const +{ + return fHome; +} + + +const BString& +BUser::Shell() const +{ + return fShell; +} + + +const BStringList& +BUser::Groups() const +{ + return fGroups; +} + + +status_t +BUser::SetTo(const BString& name, const BString& realName, const BString& home, + const BString& shell, const BStringList& groups) +{ + fName = name; + fRealName = realName; + fHome = home; + fShell = shell; + fGroups = groups; + + return fGroups.CountStrings() == groups.CountStrings() ? B_OK : B_NO_MEMORY; +} + + +/*static*/ bool +BUser::IsValidUserName(const char* name) +{ + if (name[0] == '\0') + return false; + + for (; name[0] != '\0'; name++) { + if (!isalnum(name[0]) && name[0] != '_') + return false; + } + + return true; +} + + +} // namespace BPackageKit diff --git a/src/kits/package/hpkg/ReaderImplBase.cpp b/src/kits/package/hpkg/ReaderImplBase.cpp index d4041f12e7..b626335e48 100644 --- a/src/kits/package/hpkg/ReaderImplBase.cpp +++ b/src/kits/package/hpkg/ReaderImplBase.cpp @@ -402,6 +402,69 @@ ReaderImplBase::UserSettingsFileInfoAttributeHandler::HandleAttribute( return B_OK; } + +// #pragma mark - UserAttributeHandler + + +ReaderImplBase::UserAttributeHandler::UserAttributeHandler( + BPackageInfoAttributeValue& packageInfoValue) + : + PackageInfoAttributeHandlerBase(packageInfoValue), + fGroups() +{ +} + + +status_t +ReaderImplBase::UserAttributeHandler::HandleAttribute( + AttributeHandlerContext* context, uint8 id, const AttributeValue& value, + AttributeHandler** _handler) +{ + switch (id) { + case B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_REAL_NAME: + fPackageInfoValue.user.realName = value.string; + break; + + case B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_HOME: + fPackageInfoValue.user.home = value.string; + break; + + case B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_SHELL: + fPackageInfoValue.user.shell = value.string; + break; + + case B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_GROUP: + if (!fGroups.Add(value.string)) + return B_NO_MEMORY; + break; + + default: + if (context->ignoreUnknownAttributes) + break; + + context->errorOutput->PrintError("Error: Invalid package " + "attribute section: unexpected package attribute id %d " + "encountered when parsing user settings file info\n", + id); + return B_BAD_DATA; + } + + return B_OK; +} + + +status_t +ReaderImplBase::UserAttributeHandler::Delete(AttributeHandlerContext* context) +{ + if (!fGroups.IsEmpty()) { + fPackageInfoValue.user.groups = fGroups.Elements(); + fPackageInfoValue.user.groupCount = fGroups.Count(); + } + + return PackageInfoAttributeHandlerBase::Delete(context); +} + + // #pragma mark - PackageAttributeHandler @@ -569,6 +632,21 @@ ReaderImplBase::PackageAttributeHandler::HandleAttribute( } break; + case B_HPKG_ATTRIBUTE_ID_PACKAGE_USER: + fPackageInfoValue.user.name = value.string; + fPackageInfoValue.attributeID = B_PACKAGE_INFO_USERS; + if (_handler != NULL) { + *_handler = new(std::nothrow) UserAttributeHandler( + fPackageInfoValue); + if (*_handler == NULL) + return B_NO_MEMORY; + } + break; + + case B_HPKG_ATTRIBUTE_ID_PACKAGE_GROUP: + fPackageInfoValue.SetTo(B_PACKAGE_INFO_GROUPS, value.string); + break; + default: if (context->ignoreUnknownAttributes) break; diff --git a/src/kits/package/hpkg/WriterImplBase.cpp b/src/kits/package/hpkg/WriterImplBase.cpp index d204e7d17c..3a882439d5 100644 --- a/src/kits/package/hpkg/WriterImplBase.cpp +++ b/src/kits/package/hpkg/WriterImplBase.cpp @@ -480,6 +480,36 @@ WriterImplBase::RegisterPackageInfo(PackageAttributeList& attributeList, } } + // user list + const BObjectList& users = packageInfo.Users(); + for (int32 i = 0; i < users.CountItems(); ++i) { + const BUser* user = users.ItemAt(i); + PackageAttribute* attribute = _AddStringAttribute( + B_HPKG_ATTRIBUTE_ID_PACKAGE_USER, user->Name(), attributeList); + + _AddStringAttributeIfNotEmpty( + B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_REAL_NAME, user->RealName(), + attribute->children); + _AddStringAttributeIfNotEmpty( + B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_HOME, user->Home(), + attribute->children); + _AddStringAttributeIfNotEmpty( + B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_SHELL, user->Shell(), + attribute->children); + + for (int32 k = 0; k < user->Groups().CountStrings(); k++) { + _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_GROUP, + user->Groups().StringAt(k), attribute->children); + } + } + + // group list + const BStringList& groups = packageInfo.Groups(); + for (int32 i = 0; i < groups.CountStrings(); i++) { + _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_GROUP, + groups.StringAt(i), attributeList); + } + // checksum (optional, only exists in repositories) if (packageInfo.Checksum().Length() > 0) { PackageAttribute* checksum = new PackageAttribute( @@ -740,6 +770,18 @@ WriterImplBase::RawWriteBuffer(const void* buffer, size_t size, off_t offset) } +WriterImplBase::PackageAttribute* +WriterImplBase::_AddStringAttribute(BHPKGAttributeID id, const BString& value, + DoublyLinkedList& list) +{ + PackageAttribute* attribute = new PackageAttribute(id, + B_HPKG_ATTRIBUTE_TYPE_STRING, B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE); + attribute->string = fPackageStringCache.Get(value); + list.Add(attribute); + return attribute; +} + + void WriterImplBase::_WritePackageAttributes( const PackageAttributeList& packageAttributes)