ACC SHELL

Path : /usr/share/YaST2/include/mail/
File Upload :
Current File : //usr/share/YaST2/include/mail/ui.ycp

/**
 * File:
 *   include/mail/ui.ycp
 *
 * Package:
 *   Configuration of mail
 *
 * Summary:
 *   User interface functions.
 *
 * Authors:
 *   Martin Vidner <mvidner@suse.cz>
 *
 * $Id: ui.ycp 61601 2010-04-07 12:52:02Z varkoly $
 *
 * All user interface functions.
 *
 */

{

textdomain "mail";

import "Wizard";
import "Progress";
import "Mode";
import "Mail";
import "MailAliases";
import "Hostname";
import "CWMFirewallInterfaces";

import "Popup";
import "Label";
import "Sequencer";
import "Package";

include "mail/helps.ycp";
include "mail/wj.ycp";

/**
 * Read settings dialog
 * @return `abort or `next
 */
define symbol ReadDialog () ``{
    Wizard::SetScreenShotName ("mail-0-read");
    // Set help text
    Wizard::RestoreHelp (ReadDialogHelp ());

    // A callback function for abort
    block<boolean> callback = ``{
	return UI::PollInput () == `abort;
    };

    // Read the configuration
    boolean was_ok = true;
    if (Mode::screen_shot())
    {
	Mail::Fake ();
	// make it possible to snap this dialog
	sleep (3000);
	UI::PollInput ();
    }
    else
    {
	was_ok = Mail::Read ( callback );
    }

    // TODO FIXME possibly handle the abort
    if (was_ok)
    {
	if (! Mail::CreateConfig ())
	{
	    string setting = "MAIL_CREATE_CONFIG";
	    // Translators: continue/cancel dialog
	    // %1 is a sysconfig variable name
	    was_ok = Popup::ContinueCancel (sformat (_("The setting %1 is turned off. You have
probably modified the configuration files directly.
If you continue, it will be turned on and
SuSEconfig will overwrite manual changes.
"), setting));
	}
    }
    else
    {
	if (Mail::mta == `other)
	{
	    // After text freeze, but
	    // a) either something is very broken -> user must know
	    // b) user installed a different MTA -> knowledgeable enough to
	    // ba) never see this message anyway
	    // bb) read English
	    // TODO: look at exim and mention it in the popup
	    // Translators: error popup
	    Popup::Error (_("YaST can only configure Postfix and Sendmail,
but neither of them is installed."));
	}
    }
    Wizard::RestoreScreenShotName ();
    return ( was_ok? `next : `abort );
}

/**
 * Confirmation dialog before saving and installing needed packages
 * @return `back or `next
 */
define symbol ConfirmDialog () ``{
    // not to be displayed, #37554.
    // but ProbePackages still has to be called.

    // continue-cancel popup
    string message1 = _("The configuration will be written now.\n");
    string message2 = Mail::ProbePackages ();
//    return Popup::ContinueCancel (message1 + message2) ? `next : `back;
    return `next;
}

/**
 * Write settings dialog
 * @return `abort or `next
 */
define symbol WriteDialog () ``{
    if (Mode::screen_shot())
    {
	y2milestone ("Screenshot mode - skipping Write");
	return `next;
    }

    // Install packages if needed.
    // Cannot do it in Write, autoinstall does it differently.
    if (size (Mail::install_packages) > 0 || size (Mail::remove_packages) > 0)
    {
	Package::DoInstallAndRemove (Mail::install_packages, Mail::remove_packages);
    }

    // Set help text
    Wizard::RestoreHelp (WriteDialogHelp ());

    // A callback function for abort
    block<boolean> callback = ``{
	return UI::PollInput () == `abort;
    };

    // Read the configuration
    boolean was_ok = Mail::Write ( callback);

    // TODO FIXME possibly handle the abort

    return ( was_ok? `next : `abort );
}

/**
 * MTA selection dialog
 * (only for autoinstallation, otherwise probed in Mail::Read)
 * @return `abort or `next
 */
define symbol MtaSelectionDialog () ``{
    Wizard::SetScreenShotName ("mail-0-mta");

    symbol mta = Mail::mta;
    // for now. TODO: disable Next if none selected
    if (mta != `sendmail && mta != `postfix)
    {
	mta = `postfix;
    }

    // Translators: dialog caption
    // Mailer: Sendmail or Postfix
    string caption = _("Mail transfer agent");
    term contents =
	`Frame (
	    // Translators: frame label
	    // Mailer: Sendmail or Postfix
	    _("Mail transfer agent"),
	    `RadioButtonGroup (
		`id (`mtag),
		`HSquash (
		    `VBox (
			`HSpacing (23), // qt bug workaround, #23979
			`VSpacing (0.2),
			// MTA name does not need translation
			`Left (`RadioButton (`id (`sendmail), `opt(`autoShortcut), "Sendmail", mta == `sendmail)),
			// MTA name does not need translation
			`Left (`RadioButton (`id (`postfix), `opt(`autoShortcut), "Postfix", mta == `postfix)),
			`VSpacing (0.2)
			)
		    )
		)
	    );

    Wizard::SetContentsButtons (caption, contents, MtaSelectionDialogHelp (), Label::BackButton (), Label::NextButton ());

    any ret = nil;
    while (true)
    {
	ret = UI::UserInput ();
	if (ret == `cancel)
	{
	    ret = `abort;
	}

	if (ret == `back || ret == `next ||
	    (ret == `abort && Popup::ReallyAbort (Mail::touched)))
	{
	    break;
	}
    }

    if (ret == `next)
    {
	mta = (symbol) UI::QueryWidget (`id (`mtag), `CurrentButton);
	Mail::Touch (Mail::mta != mta);
	Mail::mta = mta;
    }
    Wizard::RestoreScreenShotName ();
    return (symbol) ret;
}

/**
 * A command line argument can override what is read from SCR.
 * Used when starting mail from lan/modem.
 */
symbol preselect_connection_type = nil;

/**
 * D1
 * @return `back, `abort, `next or `none
 */
define symbol ConnectionTypeDialog () ``{
    Wizard::SetScreenShotName ("mail-1-conntype");

    list widgets = [];

    symbol ct = Mail::connection_type;
    if (preselect_connection_type != nil)
    {
	ct = preselect_connection_type;
    }

    // Translators: dialog caption
    string caption = _("General Settings");
    term contents =
	`Frame (
	    // Translators: frame label
	    _("Connection type"),
	    RadioButtonVBox (
		`ctg,
		[
		    // Translators: radio button label
		    `RadioButton (`id (`permanent), `opt (`notify), _("&Permanent"), ct == `permanent),
		    // Translators: radio button label
		    `RadioButton (`id (`dialup), `opt (`notify), _("&Dial-up"), ct == `dialup),
		    // Translators: radio button label
		    `RadioButton (`id (`none), `opt (`notify), _("No &connection"), ct == `none),
		    // Translators: radio button label
		    `RadioButton (`id (`nodaemon), `opt (`notify), _("Do not start Postfix as Daemon"), ct == `nodaemon),
		    ]
		)
	    );

    boolean amavis_allowed = Mail::amavis_allowed;

    term amavis_t = nil;
    if (amavis_allowed)
    {
	amavis_t = `Left (WJ_MakeWidget (`use_amavis));
	widgets = add (widgets, `use_amavis);
    }
    else
    {
	amavis_t = `Empty (`id (`use_amavis));
    }

    contents = `HSquash (
	`VBox (
	    contents,
	    `VSpacing (1),
	    amavis_t
	    )
	);

    Wizard::SetContentsButtons (caption, contents,
				WJ_MakeHelp (
				    prepend (widgets,
					     ConnectionTypeDialogHelp ())),
				Label::BackButton (), Label::NextButton ());
    any ret = nil;
    while (true)
    {
	ct = (symbol) UI::QueryWidget (`id (`ctg), `CurrentButton);
	if (ct == `permanent || ct == `dialup)
	{
	    UI::ChangeWidget (`id (`use_amavis), `Enabled, true);
	    Wizard::RestoreNextButton();
	    //argh, slow
	    //Wizard::RestoreNextButton ();
	}
	else if (ct == `nodaemon)
	{
	    UI::ChangeWidget (`id (`use_amavis), `Value, false);
	    UI::ChangeWidget (`id (`use_amavis), `Enabled, false);
	}
	else if (ct == `none)
	{
	    UI::ChangeWidget (`id (`use_amavis), `Value, false);
	    UI::ChangeWidget (`id (`use_amavis), `Enabled, false);
	    Wizard::SetNextButton(`next, Label::FinishButton() );
	}

	ret = UI::UserInput ();
	if (ret == `cancel)
	{
	    ret = `abort;
	}

	if (ret == `back ||
	    (ret == `abort && Popup::ReallyAbort (Mail::touched)))
	{
	    break;
	}
	else if (ret == `next)
	{
	    if (WJ_Validate (widgets))
	    {
		break;
	    }
	}
    }

    if (ret == `next)
    {
	WJ_Set (widgets);

	ct = (symbol) UI::QueryWidget (`id (`ctg), `CurrentButton);
	Mail::Touch (Mail::connection_type != ct);
	Mail::connection_type = ct;

	ret = (ct == `none)? `none: ret;
    }
    // avoid overriding the choice the user has made
    preselect_connection_type = nil;
    Wizard::RestoreScreenShotName ();
    return (symbol) ret;
}

/**
 * D2
 * @return `back, `abort, `next or `outgoing_details
 */
define symbol OutgoingDialog () ``{
    Wizard::SetScreenShotName ("mail-21-outgoing");

    boolean TLSnone = ( Mail::smtp_use_TLS == "no" );
    boolean TLSuse  = ( Mail::smtp_use_TLS == "yes" );
    boolean TLSmust = ( Mail::smtp_use_TLS == "must" );

    // what buttons can be used to leave this dialog
    //(except the std wizarding ones)
    list<symbol> buttons = [];
    list<symbol> widgets = [
	`outgoing_mail_server,
	];

    // Translators: dialog caption
    string caption = _("Outgoing Mail");

    term o_contents =
	`VSquash (
	    `VBox (
		WJ_MakeWidget (`outgoing_mail_server),
		// TLS
		`Label (_("The server use &TLS")),
                `RadioButtonGroup(`id(`TLS),
                  `HBox(
                    `Left(`RadioButton(`id("no"),   _("No"),     TLSnone)),
                    `Left(`RadioButton(`id("yes"),  _("Use"),    TLSuse)),
                    `Left(`RadioButton(`id("must"), _("Enforce"),TLSmust))
                  )
		),
		// Translators: button
		// TODO ...
		`PushButton (`id(`outgoing_details), _("&Masquerading")),
		// Translators: button
		// TODO ...
		`PushButton (`id(`outgoing_auth_opts), _("&Authentication"))
		)
	    );
    buttons = add (buttons, `outgoing_details);
    buttons = add (buttons, `outgoing_auth_opts);

    // frame label
    term o_frame = `Frame (_("Outgoing Mail"), o_contents);
    term contents = `HSquash ( `VBox (
	`VStretch (),
	o_frame,
	`VStretch ()
	));

    string help = ""; //TODO
    Wizard::SetContentsButtons (
	caption, contents, WJ_MakeHelp (widgets),
	Label::BackButton (),
	Label::NextButton ());

    symbol ret = nil;
    while (true)
    {
	ret = (symbol) UI::UserInput ();
	if (ret == `cancel)
	{
	    ret = `abort;
	}

	if (ret == `back ||
	    (ret == `abort && Popup::ReallyAbort (Mail::touched)))
	{
	    break;
	}
	else if (ret == `next || contains (buttons, ret))
	{
	    // input validation
	    // For consistency, all querywidgets are done here

	    if (WJ_Validate (widgets))
	    {
		// all checks OK, break the input loop
		break;
	    }
	}
    }

    if (ret == `next || contains (buttons, ret))
    {
        Mail::smtp_use_TLS = (string)UI::QueryWidget (`id (`TLS), `Value);
	WJ_Set (widgets);
    }
    Wizard::RestoreScreenShotName ();
    return (symbol) ret;
}

/**
 * D2
 * @return `back, `abort, `next or `outgoing_details
 */
define symbol IncomingDialog () ``{
    Wizard::SetScreenShotName ("mail-22-incoming");

    list<any> buttons = [];
    // watch out, fm_widgets are not part of widgets
    // because of special validation requirements
    list<symbol> widgets = [
	`listen_remote,
	];
    // firewall widget using CWM
    map<string, any> fw_settings = $[
	"services": [ "smtp" ],
	"display_details": true,
	];
    map<string,any> fw_cwm_widget = CWMFirewallInterfaces::CreateOpenFirewallWidget (fw_settings);    

    // Translators: dialog caption
    string caption = _("Incoming Mail");

    term i_contents = `VBox ();

    i_contents = add (i_contents, `Left (WJ_MakeWidget (`listen_remote)));
    i_contents = add (i_contents, fw_cwm_widget["custom_widget"]:`Empty ());

    list<symbol> fm_widgets = [];
    if (true) // always embed the first account
    {
	// Edit the first fetchmail item here.
	// Copy it into the edit buffer.
	fetchmail_item = Mail::fetchmail[0]:$[];

	fm_widgets = [
	    `fm_server,
	    `fm_protocol,
	    `fm_remote_user,
	    `fm_password,
	    `fm_local_user,
	    ];

	term fm_contents = `VBox (
	    `HSpacing (40), // prevent fm_password being squashed in curses
	    `HBox (
		WJ_MakeWidget (`fm_server),
		`HSpacing (1),
		WJ_MakeWidget (`fm_protocol)
		),
	    `HBox (
		WJ_MakeWidget (`fm_remote_user),
		`HSpacing (1),
		WJ_MakeWidget (`fm_password)
		),
	    `HBox (
		`Bottom (WJ_MakeWidget (`fm_local_user)),
		`HSpacing (1),
		// pushbutton
		`Bottom (`PushButton (`id (`downloading), `opt (`key_F7), _("&Details...")))
		),
	    `HBox (
		`Left(`ComboBox (`id (`fm_start),_("Start &fetchmail"),["manual","daemon"]))
		)
	    );
	// frame label: mail downloading (fetchmail)
	term fm_frame = `Frame (_("&Downloading"), fm_contents);
	i_contents = add (i_contents,
			  `HBox (
			      `HSpacing (1),
			      fm_frame,
			      `HSpacing (1)
			      )
	    );
    }

    i_contents = add (i_contents, WJ_MakeWidget (`root_alias));
    widgets = add (widgets, `root_alias);

    if (Mail::mta == `postfix)
    {
	i_contents = add (i_contents, WJ_MakeWidget (`delivery_mode));
	widgets = add (widgets, `delivery_mode);
    }

    i_contents = add (
	i_contents,
	// menu button: details of incoming mail
	`HBox (
	    // pushbutton
	    `PushButton (`id (`aliases), _("&Aliases...")),
	    // pushbutton
	    `PushButton (`id (`virtual), _("&Virtual domains..."))
	    )
	);
    buttons = flatten ([buttons, [`downloading, `aliases, `virtual]]);

    // frame label
//    term i_frame = `Frame (_("Incoming Mail"), i_contents);
    term contents = `HSquash ( `VBox (
	`VStretch (),
	i_contents,
	`VStretch ()
	));

    string help = ""; //TODO
    Wizard::SetContentsButtons (
	caption, contents, WJ_MakeHelp (widgets) + fw_cwm_widget["help"]:"",
	Label::BackButton (),
	Label::FinishButton ());

    // set combo boxes to proper values
    WJ_GetWidget (`fm_protocol);
    WJ_GetWidget (`fm_local_user);
    WJ_GetWidget (`delivery_mode);
    // initialize the widget (set the current value)
    CWMFirewallInterfaces::OpenFirewallInit (fw_cwm_widget, "");
    UI::ChangeWidget(`id(`fm_start),`Value, Mail::fetchmail_mode);

    // nothing entered in the dowloading items - don't save them
    boolean fm_empty = true;

    map event = nil;
    any ret = nil;
    while (true)
    {
	event = UI::WaitForEvent ();
	ret = event["ID"]:nil;
	if (ret == `cancel)
	{
	    ret = `abort;
	}
	// handle the events, enable/disable the button, show the popup if button clicked
	CWMFirewallInterfaces::OpenFirewallHandle (fw_cwm_widget, "", event);

	if (ret == `back ||
	    (ret == `abort && Popup::ReallyAbort (Mail::touched)))
	{
	    break;
	}
	else if (ret == `next || contains (buttons, ret))
	{
	    Mail::fetchmail_mode = (string) UI::QueryWidget(`id (`fm_start), `Value);
	    // input validation
	    // For consistency, all querywidgets are done here
	    if (WJ_Validate (widgets))
	    {
		fm_empty =
		    UI::QueryWidget(`id (`fm_server), `Value) == "" &&
		    UI::QueryWidget(`id (`fm_remote_user), `Value) == "" &&
		    UI::QueryWidget(`id (`fm_password), `Value) == "" &&
		    UI::QueryWidget(`id (`fm_local_user), `Value) == "";
		if (fm_empty || WJ_Validate (fm_widgets))
		{
		    // all checks OK, break the input loop
		    break;
		}
	    }
	}
    }

    if (ret == `next || contains (buttons, ret))
    {
	WJ_Set (widgets);
	// grab current settings, store them to SuSEFirewall::
	CWMFirewallInterfaces::OpenFirewallStore (fw_cwm_widget, "", event);

	if (!fm_empty)
	{
	    WJ_Set (fm_widgets);
	    // CHECK: aliasing is not harmful here
	    Mail::fetchmail[0] = fetchmail_item;

	    // -------------- fix of bug #29919:
	    // -------------- propose local domains when fetchmail is used:
	    if (Mail::fetchmail != [] && Mail::local_domains == [] &&
		Mail::mta == `postfix)
	    {
		list<string> ld = [ "\\$myhostname",
				    "localhost.\\$mydomain",
				    "\\$mydomain" ];
		// popup text
		// %1: variable name (eg. POSTFIX_LOCALDOMAINS)
		// %2: file name (eg. /etc/sysconfig/postfix)
		// %3: value (about 50 characters)
		if (Popup::YesNo (sformat (_("To be able to deliver mail to your local MTA,
the value of %1 in %2 will be set to
\"%3\"."), "POSTFIX_LOCALDOMAINS", "/etc/sysconfig/postfix", mergestring (ld, ", "))))
		{
		    Mail::local_domains = ld;
		}
	    }
	}
    }
    Wizard::RestoreScreenShotName ();
    return (symbol) ret;
}

/**
 * (taken from y2c_users ui.ycp)
 * @param username a string
 * @return Whether a string contains only valid user name characters
 */
define boolean check_username (string username) ``{
//DUH, auth_dialogs.ycp, users.ycp and Users.ycp have conflicing definitions!
    string valid_logname_chars  = "0123456789abcdefghijklmnopqrstuvwxyz-_";

    string firstchar = substring (username, 0, 1);
    return username != "" &&
	( (firstchar >= "a" && firstchar <= "z" ) || firstchar == "_"  ) &&
	findfirstnotof (username, valid_logname_chars) == nil;
}

/**
 * (taken from y2c_users ui.ycp)
 * @return Describe a valid username
 */
define string valid_username () ``{
    // There is a check whether the information from the UI is
    // correct and complete.  The login name may contain only
    // certain characters and must begin with a letter.
    // Already in Translation Memory
    return _("The user login may contain only
lower case letters, digits, \"-\" and \"_\"
and must begin with a letter or \"_\".
Please try again.
");
}

/**
 * See RFC 2822, 3.4
 * But for now, no-spaces@valid_domainname
 * @param address an address to check
 * @return valid?
 */
define boolean check_mail_address (string address) ``{
    list<string> parts = splitstring (address, "@");
    if (size (parts) != 2)
    {
	return false;
    }

    return check_mail_local_part (parts[0]:"")
	&& Hostname::CheckDomain (parts[1]:"");
}

/**
 * D2.1.1
 * Used for adding and editing a user masquerading entry.
 * @param defaultv	$["user": "address":] or just $[]
 * @param existing	current masqueading list
 * @return		$["comment": "", "user": "address":] or $[] on cancel
 */
define map MasqueradeUserPopup (map defaultv, list<map> existing) ``{
    string user = defaultv["user"]:"";
    string address = defaultv["address"]:"";
    list<string> forbidden = maplist (map e, existing, ``( e["user"]:"" ));

    term contents =
	`HBox(
	    `HSpacing(1),
	    `VBox(
		`VSpacing(0.2),
		// Translators: popup dialog heading
		`Heading (_("Sender address rewriting")),
		Mode::config () ?
		// Translators: text entry label
		`Left (`TextEntry (`id (`user), _("&Local user"), user)) :
		`Left (`ComboBox (`id (`user), `opt (`editable, `hstretch), _("&Local user"), GetLocalUsers ())),
		// Translators: text entry label
		`Left (`TextEntry (`id (`address), _("&Display as"), address)),
		`VSpacing(0.2),
		`HBox(`PushButton(`id(`ok), `opt(`default, `key_F10), Label::OKButton()),
		      `PushButton(`id(`cancel), `opt (`key_F9), Label::CancelButton())),
		`VSpacing(0.2)
		),
	    `HSpacing(1)
	);

    UI::OpenDialog (`opt(`decorated), contents);
    UI::ChangeWidget (`id (`user), `Value, user);
    UI::SetFocus (`id (`user));

    any ret = nil;
    while (true)
    {
	ret = UI::UserInput ();
	if (ret == `cancel)
	{
	    break;
	}
	else if (ret == `ok)
	{
	    // Input validation
	    user	= (string) UI::QueryWidget (`id (`user), `Value);
	    address	= (string) UI::QueryWidget (`id (`address), `Value);


	    if (!check_username (user))
	    {
		UI::SetFocus (`id (`user));
		Popup::Error (valid_username ());
	    }
	    else if (contains (forbidden, user))
	    {
		UI::SetFocus (`id (`user));
		// Translators: error message
		Popup::Error (_("The address for this user is already defined."));
	    }
	    else if (!check_mail_address (address))
	    {
		// string valid_mail_address
		// no-spaces@valid_domainname
		UI::SetFocus (`id (`address));
		// error popup
		Popup::Error (_("The mail address format is incorrect."));
	    }
	    else
	    {
		// all checks OK, break the input loop
		break;
	    }
	}
    }

    UI::CloseDialog ();

    return (ret == `ok)? $["comment": "", "user": user, "address": address] : $[];
}

/**
 * D2.1
 * @return `back, `abort or `next
 */
define symbol OutgoingDetailsDialog () ``{
    Wizard::SetScreenShotName ("mail-2o-outgoing-details");

    string mod = listToString (Mail::masquerade_other_domains);
    list<string> lmod = [];
    list<map> mu = Mail::masquerade_users;

    // Translators: dialog caption
    string caption = _("Masquerading");
    term contents =
	`VBox (
	    `VSpacing (0.2),
	    `RadioButtonGroup (
		`id (`mdg),
		`VBox (
		    `VSpacing (0.2),
		    WJ_MakeWidget (`from_header),
		    `VSpacing (0.2),
		    WJ_MakeWidget (`local_domains),
		    `VSpacing (0.2),
		    `Left (`RadioButton (
			       `id (`masqlocal),
			       // Translators: radio button label
			       _("Masquerade &local domains"),
			       mod == "")),
//		    `HBox (
//			`HSpacing (2),
//			`TextEntry (`id (`masqdomains), `opt (`disabled), _("That is"), ld)
////			`Left (`Label (`opt (`outputField, `hstretch), ld))
//			),
		    // Translators: radio button label
		    `Left (`RadioButton (`id (`masqothers), _("Ma&squerade other domains"), mod != "")),
		    `HBox (
			`HSpacing (2),
			// Translators: text entry label
			`TextEntry (`id (`masqdomains), `opt (`notify), _("Do&mains to masquerade"), mod)
			)
		    )
		),

	    `VSpacing (1),

	    `Table (
		`id (`tab),
		`opt (`notify, `immediate),
		// Translators: table column headings
		`header (_("Local user"),
		// Translators: table column headings
			 _("Display as")),
		makeItems (mu, ["user", "address"])
		),
/*
	    `HBox (
		`HWeight (1, `ComboBox (`id (`user), `opt (`editable), _("Local user"),
					["holly", "jane", "tarzan"])),
		`HWeight (2, `TextEntry (`id (`address), _("Display as"), "holly@red.dwarf"))
		),
*/
	    `HBox(
		// Translators: button
		`PushButton(`id(`add), `opt (`key_F3), _("A&dd")),
		// Translators: button
		`PushButton(`id(`edit), `opt (`key_F4), _("&Edit")),
		// Translators: button
		`PushButton(`id(`delete), `opt (`key_F5), _("Dele&te"))
		),
	    `VSpacing (1)
	    );

    string help = WJ_MakeHelp ( [`from_header, `local_domains ]) +
	MasqueradingDialogHelp ();

    Wizard::SetContentsButtons (caption, contents, help , Label::BackButton (), Label::OKButton ());

    symbol ret = nil;
    edit_touched = false;
    while (true)
    {
	boolean any_items = UI::QueryWidget (`id (`tab), `CurrentItem) != nil;
	UI::ChangeWidget (`id (`edit), `Enabled, any_items);
	UI::ChangeWidget (`id (`delete), `Enabled, any_items);

	// Kludge, because a `Table still does not have a shortcut.
	// watch out, a textentry sends UI too
	if (ret != `masqdomains)
	{
	    UI::SetFocus (`id (`tab));
	}

	ret = (symbol) UI::UserInput ();
	if (ret == `cancel)
	{
	    ret = `abort;
	}

	if (ret == `masqdomains)
	{
	    UI::ChangeWidget (`id (`mdg), `CurrentButton, `masqothers);
	}
	else if (contains ([`add, `edit, `delete], ret))
	{
	    mu = EditTable ((symbol)ret, mu, ["user", "address"],
		MasqueradeUserPopup, `tab);
	}
	else if ((ret == `abort
		  && Popup::ReallyAbort (Mail::touched || edit_touched))
		 || ret == `back)
	{
	    break;
	}
	else if (ret == `next)
	{
	    // Input validation
	    // For consistency, all querywidgets are done here.
	    // The table contents is maintained and validated
	    // at the EditTable call.

	    symbol rb = (symbol) UI::QueryWidget (`id (`mdg), `CurrentButton);
	    if (rb == `masqothers)
	    {
		mod = (string) UI::QueryWidget (`id (`masqdomains), `Value);
		lmod = stringToList (mod);
	    }
	    else
	    {
		lmod = [];
	    }

	    if (! Validate_from_header (`from_header))
	    {
		y2debug ("nothing");
	    }
	    else if (! Validate_local_domains (`local_domains))
	    {
		y2debug ("nothing, already done");
	    }
	    else if (find (string s, lmod, ``(! Hostname::CheckDomain (s))) != nil)
	    {
		UI::SetFocus (`id (`masqdomains));
		// Translators: error popup
		// Already in Translation Memory
		string msg = _("The domain name is incorrect");
		// TODO: describe a valid domain name
		Popup::Error (msg);
	    }
	    else
	    {
		// all checks OK, break the input loop
		break;
	    }
	}
    }

    if (ret == `next)
    {
	// all querywidgets are already done by the validation part
	Set_from_header (`from_header);
	Set_local_domains (`local_domains);

	Mail::Touch (Mail::masquerade_other_domains != lmod);
	Mail::masquerade_other_domains = lmod;

	Mail::Touch (Mail::masquerade_users != mu);
	Mail::masquerade_users = mu;
    }
    Wizard::RestoreScreenShotName ();
    return (symbol) ret;
}

/**
 * D2.2
 * Outgoing server authentification
 * @return `back, `abort or `next
 */
define symbol OutgoingAuthOptions() ``{
    Wizard::SetScreenShotName ("mail-2o-outgoing-authentication");

    //string mod = listToString (Mail::masquerade_other_domains);
    //list<string> lmod = [];
    //list<map> mu = Mail::masquerade_users;

    // Translators: dialog caption
    string caption = _("Outgoing Server Authentication");
    term contents =
	`VBox(
	    `HBox(
		`HWeight(1,
		    `Empty()
		),
		`HWeight(3,
		    `VBox(
			// text entry
			`TextEntry(`id(`server), _("Outgoing &Server")),
			// text entry
			`TextEntry(`id(`user),   _("&User name")),
			// password entry
			`Password( `id(`passw),  _("&Password"))
		    )
		),
		`HWeight(1,
		    `Empty()
		)
	    )
	);
    //);

    Wizard::SetContentsButtons (caption, contents, AuthenticationDialogHelp (), Label::BackButton (), Label::OKButton ());

    any ret = nil;
    map config = Mail::smtp_auth[0]:$[];

    if (config == $[]) {
	config = add(config, "server",   Mail::outgoing_mail_server);
	config = add(config, "user",     "");
	config = add(config, "password", "");
    }

    UI::ChangeWidget(`id(`server), `Value, config["server"]:"");
    UI::ChangeWidget(`id(`user),   `Value, config["user"]:"");
    UI::ChangeWidget(`id(`passw),  `Value, config["password"]:"");

    while (true)
    {
	ret = UI::UserInput ();
	if (ret == `cancel)
	{
	    ret = `abort;
	}

	if ((ret == `abort && Popup::ReallyAbort (Mail::touched))
	    || ret == `back)
	{
	    break;
	}
	else if (ret == `next)
	{
	    string server   = (string) UI::QueryWidget (`id (`server), `Value);
	    string user     = (string) UI::QueryWidget (`id (`user), `Value);
	    string password = (string) UI::QueryWidget (`id (`passw), `Value);

	    if (server == "" && user == "" && password == "")
	    {
		// wants to delete it, ok
		config = $[];
		break;
	    }
	    else
	    {
		config["server"]   = server;
		config["user"]     = user;
		config["password"] = password;
	    }

	    // validity checks: reuse fetchmail widgets
	    if (Validate_outgoing_mail_server (`server) &&
		Validate_fm_remote_user (`user))
	    {
		break;
	    }
	}
    }

    if (ret == `next)
    {
	// all querywidgets are already done by the validation part
	Mail::Touch (Mail::smtp_auth[0]:$[] != config);
	Mail::smtp_auth[0] = config;
	// removing? had to add it first so that there is something to remove.
	if (config == $[])
	{
	    Mail::smtp_auth = remove (Mail::smtp_auth, 0);
	}
	else
	{
	    // #158220
	    Mail::Touch (Mail::outgoing_mail_server != config["server"]:"");
	    Mail::outgoing_mail_server = config["server"]:"";
	}
    }
    Wizard::RestoreScreenShotName ();
    return (symbol)ret;
}

/**
 * D3.1
 * @param defaultv $[server:, protocol:, remote_user:, local_user:, password:, ...]
 * @param existing	unused
 * @return edited data (with no other fields) or $[] on cancel
 */
define map FetchmailPopup (map defaultv, list<map> existing) ``{
    fetchmail_item = defaultv;
    fetchmail_item_touched = false;

    list<symbol> widgets = [
	`fm_server,
	`fm_protocol,
	`fm_remote_user,
	`fm_password,
	`fm_local_user,
	];

    term contents =
	`HBox(
	    `HSpacing(1),
	    `VBox(
		`VSpacing(0.2),
		// Translators: popup dialog heading
		`Heading (_("Mail downloading")),
		`Left (WJ_MakeWidget (`fm_server)),
		`Left (WJ_MakeWidget (`fm_protocol)),
		`Left (WJ_MakeWidget (`fm_remote_user)),
		`Left (WJ_MakeWidget (`fm_password)),
		`Left (WJ_MakeWidget (`fm_local_user)),
		`VSpacing(0.2),
		`HBox(`PushButton(`id(`ok), `opt(`default, `key_F10), Label::OKButton()),
		      `PushButton(`id(`cancel), `opt (`key_F9), Label::CancelButton())),
		`VSpacing(0.2)
		),
	    `HSpacing(1)
	);

    UI::OpenDialog (`opt(`decorated), contents);
    // set combo boxes to proper values
    WJ_GetWidget (`fm_protocol);
    WJ_GetWidget (`fm_local_user);
    UI::SetFocus (`id (`fm_server));

    any ret = nil;
    while (true)
    {
	ret = UI::UserInput ();
	if (ret == `cancel)
	{
	    break;
	}
	else if (ret == `ok)
	{
	    // Input validation
	    if (WJ_Validate (widgets))
	    {
		// all checks OK, break the input loop
		break;
	    }
	}
    }

    WJ_Set (widgets); // TODO hope it is ok to mess it up
    UI::CloseDialog ();

    return (ret == `ok)? fetchmail_item : $[];
}


/**
 * D3
 * @return `back, `abort or `next
 */
define symbol DownloadingDialog () ``{
    Wizard::SetScreenShotName ("mail-2ia-download");
    //List of maps: $[server:, protocol:, remote_user:, local_user:, password:]
    list<map> fm = Mail::fetchmail;

    // Translators: dialog caption
    string caption = _("Mail downloading");
    term contents =
	`VBox (
	    `VSpacing (0.2),
	    /*
	     * no time to implement this :-(
	     * #22903
	    `Frame (
		// Translators: frame label
		// When should Fetchmail run
		// Fetchmail is a program name, do not translate
		_("Run Fetchmail"),
		RadioButtonVBox (
		    `run_fm_g,
		    [
			// Translators: radio button label
			`RadioButton (`id (`run_fm_man), _("&Manually"), true), // TODO fake
			// Translators: radio button label
			`RadioButton (`id (`run_fm_ppp), _("For &dial-up network connections"), false),
			// Translators: radio button label
			`RadioButton (`id (`run_fm_net), _("For &all network connections"), false),
			]
		    )
		),
	    `VSpacing (0.5),
	    */
	    `Table (
		`id (`tab),
		`opt (`notify, `immediate),
		// Translators: table column headings
		`header (_("Server"),
		// Translators: table column headings
			 _("Protocol"),
		// Translators: table column headings
			 _("User"),
		// Translators: table column headings
			 _("Local user")),
		makeItems (fm, ["server", "protocol", "remote_user", "local_user"])
		),
	    `HBox(
		// Translators: button
		`PushButton(`id(`add), `opt (`key_F3), _("A&dd")),
		// Translators: button
		`PushButton(`id(`edit), `opt (`key_F4), _("&Edit")),
		// Translators: button
		`PushButton(`id(`delete), `opt (`key_F5), _("De&lete"))
		),
	    `VSpacing (0.2)
	    );

    Wizard::SetContentsButtons (caption, contents, DownloadingDialogHelp (), Label::BackButton (), Label::OKButton ());

    UI::ChangeWidget (`id (`edit), `Enabled, false);
    UI::ChangeWidget (`id (`delete), `Enabled, false);

    symbol ret = nil;
    edit_touched = false;
    while (true)
    {
	boolean any_items = UI::QueryWidget (`id (`tab), `CurrentItem) != nil;
	UI::ChangeWidget (`id (`edit), `Enabled, any_items);
	UI::ChangeWidget (`id (`delete), `Enabled, any_items);

	// Kludge, because a `Table still does not have a shortcut.
	UI::SetFocus (`id (`tab));

	ret = (symbol) UI::UserInput ();
	if (ret == `cancel)
	{
	    ret = `abort;
	}


	if (contains ([`add, `edit, `delete], ret))
	{
	    fm = EditTable (ret, fm,
		["server", "protocol", "remote_user", "local_user"],
		FetchmailPopup, `tab);
	}
	else if ((ret == `abort
		  && Popup::ReallyAbort (Mail::touched || edit_touched))
		 || ret == `next || ret == `back)
	{
	    break;
	}
    }

    if (ret == `next)
    {
	Mail::Touch (Mail::fetchmail != fm);
	Mail::fetchmail = fm;
    }
    Wizard::RestoreScreenShotName ();
    return ret;
}

/**
 * D1.1.1, 1.2.1
 * Used for adding and editing an alias/virtual domain entry.
 * @param defaultv	$["alias": "destinations": ?comment] or just $[]
 * @param existing	current entry list
 * @return		$["comment": ""?, "alias": "destinations":] or $[] on cancel
 */
define map AliasPopup (map defaultv, list<map> existing) ``{
    string alias = defaultv["alias"]:"";
    string destinations = defaultv["destinations"]:"";
    list<string> forbidden = maplist (map e, existing, ``( e["alias"]:"" ));

    term contents =
	`HBox(
	    `HSpacing(1),
	    `VBox(
		`VSpacing(0.2),
		// Translators: popup dialog heading
		`Heading (_("Incoming mail redirection")),
		// Translators: text entry label
		`Left (`TextEntry (`id (`alias), _("&Alias"), alias)),
		// Translators: text entry label
		`Left (`TextEntry (`id (`destinations), _("&Destinations"), destinations)),
		`VSpacing(0.2),
		`HBox(`PushButton(`id(`ok), `opt(`default, `key_F10), Label::OKButton()),
		      `PushButton(`id(`cancel), `opt (`key_F9), Label::CancelButton())),
		`VSpacing(0.2)
		),
	    `HSpacing(1)
	);

    UI::OpenDialog (`opt(`decorated), contents);
    UI::SetFocus (`id (`alias));

    any ret = nil;
    while (true)
    {
	ret = UI::UserInput ();
	if (ret == `cancel)
	{
	    break;
	}
	else if (ret == `ok)
	{
	    // Input validation
	    alias = (string) UI::QueryWidget (`id (`alias), `Value);
	    destinations = (string)UI::QueryWidget (`id (`destinations),`Value);

	    // TODO: this only works because check_mail_local part is too
	    // permissive. Virtusertable aliases may contain @ so it is not
	    // a local part and the check will need to be improved.
	    // (But a postfix-style virtual domain will have an @-less entry.)
	    if (!check_mail_local_part (alias))
	    {
		UI::SetFocus (`id (`alias));
		// Translators: error message
		Popup::Message (_("The alias format is incorrect."));
	    }
	    else if (contains (forbidden, alias))
	    {
		UI::SetFocus (`id (`alias));
		// Translators: error message
		Popup::Message (_("The destinations for this alias are already defined."));
	    }
	    else
	    {
		// all checks OK, break the input loop
		break;
	    }
	}
    }

    UI::CloseDialog ();

    return (ret == `ok)? $["comment": "", "alias": alias, "destinations": destinations] : $[];
}


/**
 * D1.1
 * @return `back, `abort or `next
 */
define symbol AliasesDialog () ``{
    Wizard::SetScreenShotName ("mail-2ib-aliases");
    list<map> aliases = MailAliases::MergeRootAlias (MailAliases::aliases);

    // Translators: dialog caption
    string caption = _("Aliases");
    term contents =
	`VBox (
	    `VSpacing (0.2),
	    `Table (
		`id (`tab),
		`opt (`notify, `immediate),
		// Translators: table column headings
		`header (_("Alias"),
		// Translators: table column headings
			 _("Destinations")),
		makeItems (aliases, ["alias", "destinations"])
		),
	    `Left( `HBox(
		// Translators: button
		`PushButton(`id(`add), `opt (`key_F3), _("A&dd")),
		// Translators: button
		`PushButton(`id(`edit), `opt (`key_F4), _("&Edit")),
		// Translators: button
		`PushButton(`id(`delete), `opt (`key_F5), _("De&lete"))
		)),
	    `VSpacing (0.2)
	    );

    Wizard::SetContentsButtons (caption, contents, AliasesDialogHelp (), Label::BackButton (), Label::OKButton ());

    any ret = nil;
    edit_touched = false;
    while (true)
    {
	boolean any_items = UI::QueryWidget (`id (`tab), `CurrentItem) != nil;
	UI::ChangeWidget (`id (`edit), `Enabled, any_items);
	UI::ChangeWidget (`id (`delete), `Enabled, any_items);

	// Kludge, because a `Table still does not have a shortcut.
	UI::SetFocus (`id (`tab));

	ret = UI::UserInput ();

	symbol ret_sym = nil;
	if (is(ret,symbol))
	{
	    ret_sym = (symbol) ret;
	}

	if (ret == `cancel)
	{
	    ret = `abort;
	}

	if (contains ([`add, `edit, `delete], ret_sym))
	{
	    aliases = EditTable((symbol)ret, aliases, ["alias", "destinations"],
		AliasPopup, `tab);
	}
	else if (ret == "man_aliases")
	{
	    y2milestone ("TODO: man aliases");
	}
	else if ((ret == `abort
		  && Popup::ReallyAbort (Mail::touched || edit_touched))
		 || ret == `next || ret == `back)
	{
	    break;
	}
    }

    if (ret == `next)
    {
	Mail::Touch (edit_touched);
	MailAliases::aliases = aliases;
	MailAliases::FilterRootAlias ();
    }
    Wizard::RestoreScreenShotName ();
    return (symbol) ret;

}


/**
 * D1.2
 * @return `back, `abort or `next
 */
define symbol VirtualDialog () ``{
    Wizard::SetScreenShotName ("mail-2ic-virtdomains");
    list<map> vu = Mail::virtual_users;

    // Translators: dialog caption
    string caption = _("Virtual domains");
    term contents =
	`VBox (
	    `VSpacing (0.2),
	    `Table (
		`id (`tab),
		`opt (`notify, `immediate),
		// Translators: table column headings
		`header (_("Alias"),
		// Translators: table column headings
			 _("Destinations")),
		makeItems (vu, ["alias", "destinations"])
		),
	    `Left(`HBox(
		// Translators: button
		`PushButton(`id(`add), `opt (`key_F3), _("A&dd")),
		// Translators: button
		`PushButton(`id(`edit), `opt (`key_F4), _("&Edit")),
		// Translators: button
		`PushButton(`id(`delete), `opt (`key_F5), _("De&lete"))
		)),
	    `VSpacing (0.2)
	    );

    Wizard::SetContentsButtons (caption, contents, VirtualDialogHelp (), Label::BackButton (), Label::OKButton ());

    UI::ChangeWidget (`id (`edit), `Enabled, false);
    UI::ChangeWidget (`id (`delete), `Enabled, false);

    symbol ret = nil;
    edit_touched = false;
    while (true)
    {
	boolean any_items = UI::QueryWidget (`id (`tab), `CurrentItem) != nil;
	UI::ChangeWidget (`id (`edit), `Enabled, any_items);
	UI::ChangeWidget (`id (`delete), `Enabled, any_items);

	// Kludge, because a `Table still does not have a shortcut.
	UI::SetFocus (`id (`tab));

	ret = (symbol) UI::UserInput ();
	if (ret == `cancel)
	{
	    ret = `abort;
	}


	if (contains ([`add, `edit, `delete], ret))
	{
	    vu = EditTable((symbol)ret, vu, ["alias", "destinations"],
		AliasPopup, `tab);
	}
	else if ((ret == `abort
		  && Popup::ReallyAbort (Mail::touched || edit_touched))
		 || ret == `next || ret == `back)
	{
	    break;
	}
    }

    if (ret == `next)
    {
	Mail::Touch (Mail::virtual_users != vu);
	Mail::virtual_users = vu;;
    }
    Wizard::RestoreScreenShotName ();
    return (symbol) ret;

}

/**
 * A Wizard Sequencer helper
 * @return	`next
 */
define symbol JustNext () ``{
    return `next;
}

map dialogs = $[
    "read"	: [ ``( ReadDialog () ), true ],
    "confirm"	: [ ``( ConfirmDialog () ), true],
    "write"	: [ ``( WriteDialog () ), true ],

    "mta":		``( MtaSelectionDialog () ),

    "connection_type":	``( ConnectionTypeDialog () ),
    "outgoing":		``( OutgoingDialog () ),
    "incoming":		``( IncomingDialog () ),
    "outgoing-details":	``( OutgoingDetailsDialog () ),
    "outgoing-auth":	``( OutgoingAuthOptions () ),
    "downloading":	``( DownloadingDialog () ),
    "aliases":		``( AliasesDialog () ),
    "virtual":		``( VirtualDialog () ),
    "common-next":	[ ``( JustNext () ), true ],
    ];

map common_sequence = $[
    //"ws_start" : must be defined in an overriding sequence
    "connection_type" :
    $[
	`abort	: `abort,
	`next	: "outgoing",
	`none	: "common-next",
	],
    "outgoing" :
    $[
	`abort	: `abort,
	`outgoing_details	: "outgoing-details",
	`outgoing_auth_opts	: "outgoing-auth",
	`next			: "incoming",
	],
    "incoming" :
    $[
	`abort	: `abort,
	`downloading		: "downloading",
	`aliases		: "aliases",
	`virtual		: "virtual",
	`next			: "common-next",
	],
    "outgoing-details" :
    $[
	`abort	: `abort,
	`next	: "outgoing",
	],
    "outgoing-auth" :
    $[
	`abort : `abort,
	`next  : "outgoing",
	],
    "downloading" :
    $[
	`abort	: `abort,
	`next	: "incoming",
	],
    "aliases" :
    $[
	`abort	: `abort,
	`next	: "incoming",
	],
    "virtual" :
    $[
	`abort	: `abort,
	`next	: "incoming",
	],
    "common-next" :
    $[
	`next	: `next,
	],
    ];

/**
 * Whole configuration of mail
 * @return `back, `abort or `next
 */
define symbol MailSequence () ``{
    map sequence = $[
	"ws_start" : "read",
	"read" :
	$[
	    `abort	: `abort,
	    `next	: "connection_type",
	    ],

	// common_sequence here

	// override
	"common-next" :
	$[
	    `next	: "confirm",
	    ],
	"confirm" :
	$[
	    `next	: "write"
	    ],
	"write" : $[
	    `abort	: `abort,
	    `next	: `next
	    ]
	];

    Wizard::CreateDialog ();
    Wizard::SetDesktopIcon("mail");

    // the second map must override the first!
    sequence = union (common_sequence, sequence);
    any ret = Sequencer::Run (dialogs, sequence);

    UI::CloseDialog ();
    return (symbol) ret;
}

/**
 * Whole configuration of mail but without reading and writing.
 * MTA is selected first.
 * For use with autoinstallation.
 * @return `back, `abort or `next
 */
define symbol MailAutoSequence () ``{
    map sequence = $[
	"ws_start":	"mta",
	"mta":
	$[
	    `abort	: `abort,
	    `next	: "connection_type",
	    ],

	// common_sequence here
	];

    // Translators: dialog caption
    string caption = _("Mail configuration");
    // label
    term contents = `Label (_("Initializing..."));

    Wizard::CreateDialog ();
    Wizard::SetDesktopIcon("mail");
    Wizard::SetContentsButtons ( caption,
				contents,
				"",
				Label::BackButton (),
				Label::NextButton ());

    // the second map must override the first!
    sequence = union (common_sequence, sequence);
    any ret = Sequencer::Run (dialogs, sequence);

    UI::CloseDialog ();
    return (symbol) ret;
}



}

ACC SHELL 2018