/*
 * Copyright 2012, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "KeyRequestWindow.h"
 
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <GridLayout.h>
#include <GridView.h>
#include <GroupLayout.h>
#include <GroupView.h>
#include <Key.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <NetworkDevice.h>
#include <PopUpMenu.h>
#include <SpaceLayoutItem.h>
#include <StringView.h>
#include <TextControl.h>
#include <TextView.h>
#include <View.h>
 
#include <new>
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "KeyRequestWindow"
 
 
static const uint32 kMessageCancel = 'btcl';
static const uint32 kMessageUnlock = 'btul';
 
 
class KeyRequestView : public BView {
public:
	KeyRequestView()
		:
		BView("KeyRequestView", B_WILL_DRAW),
		fPassword(NULL)
	{
		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
 
		BGroupLayout* rootLayout = new(std::nothrow) BGroupLayout(B_VERTICAL);
		if (rootLayout == NULL)
			return;
 
		SetLayout(rootLayout);
 
		BGridView* controls = new(std::nothrow) BGridView();
		if (controls == NULL)
			return;
 
		BGridLayout* layout = controls->GridLayout();
 
		float inset = ceilf(be_plain_font->Size() * 0.7);
		rootLayout->SetInsets(inset, inset, inset, inset);
		rootLayout->SetSpacing(inset);
		layout->SetSpacing(inset, inset);
 
		BStringView* label = new(std::nothrow) BStringView("keyringLabel",
			B_TRANSLATE("Keyring:"));
		if (label == NULL)
			return;
 
		int32 row = 0;
		layout->AddView(label, 0, row);
 
		fKeyringName = new(std::nothrow) BStringView("keyringName", "");
		if (fKeyringName == NULL)
			return;
 
		layout->AddView(fKeyringName, 1, row++);
 
		fPassword = new(std::nothrow) BTextControl(B_TRANSLATE("Password:"), "", NULL);
		if (fPassword == NULL)
			return;
 
		BLayoutItem* layoutItem = fPassword->CreateTextViewLayoutItem();
		layoutItem->SetExplicitMinSize(BSize(fPassword->StringWidth(
				"0123456789012345678901234567890123456789") + inset,
			B_SIZE_UNSET));
 
		layout->AddItem(fPassword->CreateLabelLayoutItem(), 0, row);
		layout->AddItem(layoutItem, 1, row++);
 
		BGroupView* buttons = new(std::nothrow) BGroupView(B_HORIZONTAL);
		if (buttons == NULL)
			return;
 
		fCancelButton = new(std::nothrow) BButton(B_TRANSLATE("Cancel"),
			new BMessage(kMessageCancel));
		buttons->GroupLayout()->AddView(fCancelButton);
 
		buttons->GroupLayout()->AddItem(BSpaceLayoutItem::CreateGlue());
 
		fUnlockButton = new(std::nothrow) BButton(B_TRANSLATE("Unlock"),
			new BMessage(kMessageUnlock));
		buttons->GroupLayout()->AddView(fUnlockButton);
 
		BTextView* message = new(std::nothrow) BTextView("message");
		message->SetText(B_TRANSLATE("An application wants to access the "
			"keyring below, but it is locked with a passphrase. Please enter "
			"the passphrase to unlock the keyring.\n"
			"If you unlock the keyring, it stays unlocked until the system is "
			"shut down or the keyring is manually locked again.\n"
			"If you cancel this dialog the keyring will remain locked."));
		message->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
		rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
		message->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
		message->MakeEditable(false);
		message->MakeSelectable(false);
		message->SetWordWrap(true);
 
		rootLayout->AddView(message);
		rootLayout->AddView(controls);
		rootLayout->AddView(buttons);
	}
 
	virtual void
	AttachedToWindow()
	{
		fCancelButton->SetTarget(Window());
		fUnlockButton->SetTarget(Window());
		fUnlockButton->MakeDefault(true);
		fPassword->MakeFocus();
	}
 
	void
	SetUp(const BString& keyringName)
	{
		fKeyringName->SetText(keyringName);
	}
 
	status_t
	Complete(BMessage& keyMessage)
	{
		BPasswordKey password(fPassword->Text(), B_KEY_PURPOSE_KEYRING, "");
		return password.Flatten(keyMessage);
	}
 
private:
	BStringView* fKeyringName;
	BTextControl* fPassword;
	BButton* fCancelButton;
	BButton* fUnlockButton;
};
 
 
KeyRequestWindow::KeyRequestWindow()
	:
	BWindow(BRect(50, 50, 100, 100), B_TRANSLATE("Unlock keyring"),
		B_TITLED_WINDOW, B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS
			| B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_AUTO_UPDATE_SIZE_LIMITS
			| B_CLOSE_ON_ESCAPE),
	fRequestView(NULL),
	fDoneSem(-1),
	fResult(B_ERROR)
{
	fDoneSem = create_sem(0, "keyring unlock dialog");
	if (fDoneSem < 0)
		return;
 
	BLayout* layout = new(std::nothrow) BGroupLayout(B_HORIZONTAL);
	if (layout == NULL)
		return;
 
	SetLayout(layout);
 
	fRequestView = new(std::nothrow) KeyRequestView();
	if (fRequestView == NULL)
		return;
 
	layout->AddView(fRequestView);
}
 
 
KeyRequestWindow::~KeyRequestWindow()
{
	if (fDoneSem >= 0)
		delete_sem(fDoneSem);
}
 
 
bool
KeyRequestWindow::QuitRequested()
{
	fResult = B_CANCELED;
	release_sem(fDoneSem);
	return false;
}
 
 
void
KeyRequestWindow::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case kMessageCancel:
		case kMessageUnlock:
			fResult = message->what == kMessageUnlock ? B_OK : B_CANCELED;
			release_sem(fDoneSem);
			return;
	}
 
	BWindow::MessageReceived(message);
}
 
 
status_t
KeyRequestWindow::RequestKey(const BString& keyringName, BMessage& keyMessage)
{
	fRequestView->SetUp(keyringName);
 
	ResizeToPreferred();
	CenterOnScreen();
	Show();
 
	while (acquire_sem(fDoneSem) == B_INTERRUPTED)
		;
 
	status_t result = fResult;
	if (result == B_OK)
		result = fRequestView->Complete(keyMessage);
 
	LockLooper();
	Quit();
	return result;
}

V773 The function was exited without releasing the 'controls' pointer. A memory leak is possible.