⛏️ index : haiku.git

/*
 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Ingo Weinhold <ingo_weinhold@gmx.de>
 */


#include <package/CommitTransactionResult.h>

#include <Message.h>

//#include <package/DaemonDefs.h>


namespace BPackageKit {


// #pragma mark - BTransactionIssue


BTransactionIssue::BTransactionIssue()
	:
	fType(B_WRITABLE_FILE_TYPE_MISMATCH),
	fPackageName(),
	fPath1(),
	fPath2(),
 	fSystemError(B_OK),
 	fExitCode(0)
{
}


BTransactionIssue::BTransactionIssue(BType type, const BString& packageName,
	const BString& path1, const BString& path2, status_t systemError,
	int exitCode)
	:
	fType(type),
	fPackageName(packageName),
	fPath1(path1),
	fPath2(path2),
 	fSystemError(systemError),
 	fExitCode(exitCode)
{
}


BTransactionIssue::BTransactionIssue(const BTransactionIssue& other)
{
	*this = other;
}


BTransactionIssue::~BTransactionIssue()
{
}


BTransactionIssue::BType
BTransactionIssue::Type() const
{
	return fType;
}


const BString&
BTransactionIssue::PackageName() const
{
	return fPackageName;
}


const BString&
BTransactionIssue::Path1() const
{
	return fPath1;
}


const BString&
BTransactionIssue::Path2() const
{
	return fPath2;
}


status_t
BTransactionIssue::SystemError() const
{
	return fSystemError;
}


int
BTransactionIssue::ExitCode() const
{
	return fExitCode;
}


BString
BTransactionIssue::ToString() const
{
	const char* messageTemplate = "";
	switch (fType) {
		case B_WRITABLE_FILE_TYPE_MISMATCH:
			messageTemplate = "\"%path1%\" cannot be updated automatically,"
				" since its type doesn't match the type of \"%path2%\" which it"
				" is supposed to be updated with."
				" Please perform the update manually if needed.";
			break;
		case B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE:
			messageTemplate = "\"%path1%\" cannot be updated automatically,"
				" since it doesn't have a SYS:PACKAGE attribute."
				" Please perform the update manually if needed.";
			break;
		case B_WRITABLE_FILE_OLD_ORIGINAL_FILE_MISSING:
			messageTemplate = "\"%path1%\" cannot be updated automatically,"
				" since \"%path2%\" which we need to compare it with is"
				" missing."
				" Please perform the update manually if needed.";
			break;
		case B_WRITABLE_FILE_OLD_ORIGINAL_FILE_TYPE_MISMATCH:
			messageTemplate = "\"%path1%\" cannot be updated automatically,"
				" since its type doesn't match the type of \"%path2%\" which we"
				" need to compare it with."
				" Please perform the update manually if needed.";
			break;
		case B_WRITABLE_FILE_COMPARISON_FAILED:
			messageTemplate = "\"%path1%\" cannot be updated automatically,"
				" since the comparison with \"%path2%\" failed: %error%."
				" Please perform the update manually if needed.";
			break;
		case B_WRITABLE_FILE_NOT_EQUAL:			// !keep old
			messageTemplate = "\"%path1%\" cannot be updated automatically,"
				" since it was changed manually from previous version"
				" \"%path2%\"."
				" Please perform the update manually if needed.";
			break;
		case B_WRITABLE_SYMLINK_COMPARISON_FAILED:	// !keep old
			messageTemplate = "Symbolic link \"%path1%\" cannot be updated"
				" automatically, since the comparison with \"%path2%\" failed:"
				" %error%."
				" Please perform the update manually if needed.";
			break;
		case B_WRITABLE_SYMLINK_NOT_EQUAL:			// !keep old
			messageTemplate = "Symbolic link \"%path1%\" cannot be updated"
				" automatically, since it was changed manually from previous"
				" version \"%path2%\"."
				" Please perform the update manually if needed.";
			break;
		case B_POST_INSTALL_SCRIPT_NOT_FOUND:
			messageTemplate = "Failed to find post-installation script "
				" \"%path1%\": %error%.";
			break;
		case B_STARTING_POST_INSTALL_SCRIPT_FAILED:
			messageTemplate = "Failed to run post-installation script "
				" \"%path1%\": %error%.";
			break;
		case B_POST_INSTALL_SCRIPT_FAILED:
			messageTemplate = "The post-installation script "
				" \"%path1%\" failed with exit code %exitCode%.";
			break;
		case B_PRE_UNINSTALL_SCRIPT_NOT_FOUND:
			messageTemplate = "Failed to find pre-uninstall script "
				" \"%path1%\": %error%.";
			break;
		case B_STARTING_PRE_UNINSTALL_SCRIPT_FAILED:
			messageTemplate = "Failed to run pre-uninstall script "
				" \"%path1%\": %error%.";
			break;
		case B_PRE_UNINSTALL_SCRIPT_FAILED:
			messageTemplate = "The pre-uninstall script "
				" \"%path1%\" failed with exit code %exitCode%.";
			break;
	}

	BString message(messageTemplate);
	message.ReplaceAll("%path1%", fPath1)
		.ReplaceAll("%path2%", fPath2)
		.ReplaceAll("%error%", strerror(fSystemError))
		.ReplaceAll("%exitCode%", BString() << fExitCode);
	return message;
}


status_t
BTransactionIssue::AddToMessage(BMessage& message) const
{
	status_t error;
	if ((error = message.AddInt32("type", (int32)fType)) != B_OK
		|| (error = message.AddString("package", fPackageName)) != B_OK
		|| (error = message.AddString("path1", fPath1)) != B_OK
		|| (error = message.AddString("path2", fPath2)) != B_OK
		|| (error = message.AddInt32("system error", (int32)fSystemError))
				!= B_OK
		|| (error = message.AddInt32("exit code", (int32)fExitCode)) != B_OK) {
			return error;
	}

	return B_OK;
}


status_t
BTransactionIssue::ExtractFromMessage(const BMessage& message)
{
	status_t error;
	int32 type;
	int32 systemError;
	int32 exitCode;
	if ((error = message.FindInt32("type", &type)) != B_OK
		|| (error = message.FindString("package", &fPackageName)) != B_OK
		|| (error = message.FindString("path1", &fPath1)) != B_OK
		|| (error = message.FindString("path2", &fPath2)) != B_OK
		|| (error = message.FindInt32("system error", &systemError)) != B_OK
		|| (error = message.FindInt32("exit code", &exitCode)) != B_OK) {
			return error;
	}

	fType = (BType)type;
	fSystemError = (status_t)systemError;
	fExitCode = (int)exitCode;

	return B_OK;
}


BTransactionIssue&
BTransactionIssue::operator=(const BTransactionIssue& other)
{
	fType = other.fType;
	fPackageName = other.fPackageName;
	fPath1 = other.fPath1;
	fPath2 = other.fPath2;
 	fSystemError = other.fSystemError;
	fExitCode = other.fExitCode;

	return *this;
}


// #pragma mark - BCommitTransactionResult


BCommitTransactionResult::BCommitTransactionResult()
	:
	fError(B_TRANSACTION_INTERNAL_ERROR),
	fSystemError(B_ERROR),
	fErrorPackage(),
	fPath1(),
	fPath2(),
	fString1(),
	fString2(),
	fOldStateDirectory(),
	fIssues(10)
{
}


BCommitTransactionResult::BCommitTransactionResult(BTransactionError error)
	:
	fError(error),
	fSystemError(B_ERROR),
	fErrorPackage(),
	fPath1(),
	fPath2(),
	fString1(),
	fString2(),
	fOldStateDirectory(),
	fIssues(10)
{
}


BCommitTransactionResult::BCommitTransactionResult(
	const BCommitTransactionResult& other)
	:
	fError(B_TRANSACTION_INTERNAL_ERROR),
	fSystemError(B_ERROR),
	fErrorPackage(),
	fPath1(),
	fPath2(),
	fString1(),
	fString2(),
	fOldStateDirectory(),
	fIssues(10)
{
	*this = other;
}


BCommitTransactionResult::~BCommitTransactionResult()
{
}


void
BCommitTransactionResult::Unset()
{
	fError = B_TRANSACTION_INTERNAL_ERROR;
	fSystemError = B_ERROR;
	fErrorPackage.Truncate(0);
	fPath1.Truncate(0);
	fPath2.Truncate(0);
	fString1.Truncate(0);
	fString2.Truncate(0);
	fOldStateDirectory.Truncate(0);
	fIssues.MakeEmpty();
}


int32
BCommitTransactionResult::CountIssues() const
{
	return fIssues.CountItems();
}


const BTransactionIssue*
BCommitTransactionResult::IssueAt(int32 index) const
{
	if (index < 0 || index >= CountIssues())
		return NULL;
	return fIssues.ItemAt(index);
}


bool
BCommitTransactionResult::AddIssue(const BTransactionIssue& issue)
{
	BTransactionIssue* newIssue = new(std::nothrow) BTransactionIssue(issue);
	if (newIssue == NULL || !fIssues.AddItem(newIssue)) {
		delete newIssue;
		return false;
	}
	return true;
}


BTransactionError
BCommitTransactionResult::Error() const
{
	return fError > 0 ? (BTransactionError)fError : B_TRANSACTION_OK;
}


void
BCommitTransactionResult::SetError(BTransactionError error)
{
	fError = error;
}


status_t
BCommitTransactionResult::SystemError() const
{
	return fSystemError;
}


void
BCommitTransactionResult::SetSystemError(status_t error)
{
	fSystemError = error;
}


const BString&
BCommitTransactionResult::ErrorPackage() const
{
	return fErrorPackage;
}


void
BCommitTransactionResult::SetErrorPackage(const BString& packageName)
{
	fErrorPackage = packageName;
}


BString
BCommitTransactionResult::FullErrorMessage() const
{
	if (fError == 0)
		return "no error";

	const char* messageTemplate = "";
	switch ((BTransactionError)fError) {
		case B_TRANSACTION_OK:
			messageTemplate = "Everything went fine.";
			break;
		case B_TRANSACTION_NO_MEMORY:
			messageTemplate = "Out of memory.";
			break;
		case B_TRANSACTION_INTERNAL_ERROR:
			messageTemplate = "An internal error occurred. Specifics can be"
				" found in the syslog.";
			break;
		case B_TRANSACTION_INSTALLATION_LOCATION_BUSY:
			messageTemplate = "Another package operation is already in"
				" progress.";
			break;
		case B_TRANSACTION_CHANGE_COUNT_MISMATCH:
			messageTemplate = "The transaction is out of date.";
			break;
		case B_TRANSACTION_BAD_REQUEST:
			messageTemplate = "The requested transaction is invalid.";
			break;
		case B_TRANSACTION_NO_SUCH_PACKAGE:
			messageTemplate = "No such package \"%package%\".";
			break;
		case B_TRANSACTION_PACKAGE_ALREADY_EXISTS:
			messageTemplate = "The to be activated package \"%package%\" does"
				" already exist.";
			break;
		case B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH:
			if (fPath1.IsEmpty()) {
				if (fErrorPackage.IsEmpty()) {
					messageTemplate = "A file path could not be determined:"
						"%error%";
				} else {
					messageTemplate = "While processing package \"%package%\""
						" a file path could not be determined: %error%";
				}
			} else {
				if (fErrorPackage.IsEmpty()) {
					messageTemplate = "The file path for \"%path1%\" could not"
						" be determined: %error%";
				} else {
					messageTemplate = "While processing package \"%package%\""
						" the file path for \"%path1%\" could not be"
						" determined: %error%";
				}
			}
			break;
		case B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY:
			messageTemplate = "Failed to open directory \"%path1%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY:
			messageTemplate = "Failed to create directory \"%path1%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_REMOVE_DIRECTORY:
			messageTemplate = "Failed to remove directory \"%path1%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_MOVE_DIRECTORY:
			messageTemplate = "Failed to move directory \"%path1%\" to"
				" \"%path2%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_WRITE_ACTIVATION_FILE:
			messageTemplate = "Failed to write new package activation file"
				" \"%path1%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_READ_PACKAGE_FILE:
			messageTemplate = "Failed to read package file \"%path1%\":"
				" %error%";
			break;
		case B_TRANSACTION_FAILED_TO_EXTRACT_PACKAGE_FILE:
			messageTemplate = "Failed to extract \"%path1%\" from package"
				" \"%package%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_OPEN_FILE:
			messageTemplate = "Failed to open file \"%path1%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_MOVE_FILE:
			messageTemplate = "Failed to move file \"%path1%\" to \"%path2%\":"
				" %error%";
			break;
		case B_TRANSACTION_FAILED_TO_COPY_FILE:
			messageTemplate = "Failed to copy file \"%path1%\" to \"%path2%\":"
				" %error%";
			break;
		case B_TRANSACTION_FAILED_TO_WRITE_FILE_ATTRIBUTE:
			messageTemplate = "Failed to write attribute \"%string1%\" of file"
				" \"%path1%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_ACCESS_ENTRY:
			messageTemplate = "Failed to access entry \"%path1%\": %error%";
			break;
		case B_TRANSACTION_FAILED_TO_ADD_GROUP:
			messageTemplate = "Failed to add user group \"%string1%\" required"
				" by package \"%package%\".";
			break;
		case B_TRANSACTION_FAILED_TO_ADD_USER:
			messageTemplate = "Failed to add user \"%string1%\" required"
				" by package \"%package%\".";
			break;
		case B_TRANSACTION_FAILED_TO_ADD_USER_TO_GROUP:
			messageTemplate = "Failed to add user \"%string1%\" to group"
				" \"%string2%\" as required by package \"%package%\".";
			break;
		case B_TRANSACTION_FAILED_TO_CHANGE_PACKAGE_ACTIVATION:
			messageTemplate = "Failed to change the package activation in"
				" packagefs: %error%";
			break;
	}

	BString message(messageTemplate);
	message.ReplaceAll("%package%", fErrorPackage)
		.ReplaceAll("%path1%", fPath1)
		.ReplaceAll("%path2%", fPath2)
		.ReplaceAll("%string1%", fString1)
		.ReplaceAll("%string2%", fString2)
		.ReplaceAll("%error%", strerror(fSystemError));
	return message;
}


const BString&
BCommitTransactionResult::Path1() const
{
	return fPath1;
}


void
BCommitTransactionResult::SetPath1(const BString& path)
{
	fPath1 = path;
}


const BString&
BCommitTransactionResult::Path2() const
{
	return fPath2;
}


void
BCommitTransactionResult::SetPath2(const BString& path)
{
	fPath2 = path;
}


const BString&
BCommitTransactionResult::String1() const
{
	return fString1;
}


void
BCommitTransactionResult::SetString1(const BString& string)
{
	fString1 = string;
}


const BString&
BCommitTransactionResult::String2() const
{
	return fString2;
}


void
BCommitTransactionResult::SetString2(const BString& string)
{
	fString2 = string;
}


const BString&
BCommitTransactionResult::OldStateDirectory() const
{
	return fOldStateDirectory;
}


void
BCommitTransactionResult::SetOldStateDirectory(const BString& directory)
{
	fOldStateDirectory = directory;
}


status_t
BCommitTransactionResult::AddToMessage(BMessage& message) const
{
	status_t error;
	if ((error = message.AddInt32("error", (int32)fError)) != B_OK
		|| (error = message.AddInt32("system error", (int32)fSystemError))
			!= B_OK
		|| (error = message.AddString("error package", fErrorPackage)) != B_OK
		|| (error = message.AddString("path1", fPath1)) != B_OK
		|| (error = message.AddString("path2", fPath2)) != B_OK
		|| (error = message.AddString("string1", fString1)) != B_OK
		|| (error = message.AddString("string2", fString2)) != B_OK
		|| (error = message.AddString("old state", fOldStateDirectory))
				!= B_OK) {
		return error;
	}

	int32 count = fIssues.CountItems();
	for (int32 i = 0; i < count; i++) {
		const BTransactionIssue* issue = fIssues.ItemAt(i);
		BMessage issueMessage;
		if ((error = issue->AddToMessage(issueMessage)) != B_OK
			|| (error = message.AddMessage("issues", &issueMessage)) != B_OK) {
			return error;
		}
	}

	return B_OK;
}


status_t
BCommitTransactionResult::ExtractFromMessage(const BMessage& message)
{
	Unset();

	int32 resultError;
	int32 systemError;
	status_t error;
	if ((error = message.FindInt32("error", &resultError)) != B_OK
		|| (error = message.FindInt32("system error", &systemError)) != B_OK
		|| (error = message.FindString("error package", &fErrorPackage)) != B_OK
		|| (error = message.FindString("path1", &fPath1)) != B_OK
		|| (error = message.FindString("path2", &fPath2)) != B_OK
		|| (error = message.FindString("string1", &fString1)) != B_OK
		|| (error = message.FindString("string2", &fString2)) != B_OK
		|| (error = message.FindString("old state", &fOldStateDirectory))
				!= B_OK) {
		return error;
	}

	fError = (BTransactionError)resultError;
	fSystemError = (status_t)systemError;

	BMessage issueMessage;
	for (int32 i = 0; message.FindMessage("issues", i, &issueMessage) == B_OK;
			i++) {
		BTransactionIssue issue;
		error = issue.ExtractFromMessage(issueMessage);
		if (error != B_OK)
			return error;

		if (!AddIssue(issue))
			return B_NO_MEMORY;
	}

	return B_OK;
}


BCommitTransactionResult&
BCommitTransactionResult::operator=(const BCommitTransactionResult& other)
{
	Unset();

	fError = other.fError;
	fSystemError = other.fSystemError;
	fErrorPackage = other.fErrorPackage;
	fPath1 = other.fPath1;
	fPath2 = other.fPath2;
	fString1 = other.fString1;
	fString2 = other.fString2;
	fOldStateDirectory = other.fOldStateDirectory;

	for (int32 i = 0; const BTransactionIssue* issue = other.fIssues.ItemAt(i);
			i++) {
		AddIssue(*issue);
	}

	return *this;
}


} // namespace BPackageKit