/**
 * File:
 *   modules/Mail.ycp
 *
 * Package:
 *   Configuration of mail
 *
 * Summary:
 *   Data for configuration of mail, input and output functions.
 *
 * Authors:
 *   Martin Vidner <mvidner@suse.cz>
 *
 * $Id: Mail.ycp 20775 2005-01-17 11:33:40Z jsuchome $
 *
 * Representation of the configuration of mail.
 * Input and output routines.
 *
 */

{
    // Set the name of the module
    module "Mail";
    textdomain "mail";

    import "MailAliases";
    import "Mode";
    import "Report";
    import "Service";
    import "Summary";
    import "Progress";
    import "Package";
    import "PackageSystem";

    import "SuSEFirewall";

    /* ---------------------------------------------------------------- */


    /**
     * Required packages
     */
    global list required_packages = [];

    /**
     * `sendmail, `postfix or `other
     * Initialized by ReadMta
     */
    global symbol mta = nil;

    /**
     *	If true, don't run SuSEconfig or restart the services.
     *  Autoinstall uses this to do  all in one place.
     */
    global boolean write_only = false;

    boolean create_config = false;
    /**
     * If MAIL_CREATE_CONFIG is not yes, the user
     * does not want SuSEconfig to modify sendmail.cf/main.cf.
     * So we will warn him before setting it to yes.
     * @return	Is it yes?
     */
    global define boolean CreateConfig () ``{
	return create_config;
    }

    /**
     * `permanent, `dialup or `none
     */
    global symbol connection_type = `permanent;

    /**
     * If false, port 25 will listen only for localhost
     */
    global boolean listen_remote = false;

    /**
     * Use a virus scanner (AMaViS).
     * amavisd-new (mta-independent) must be installed.
     * It will be installed if amavis_allowed and it is not installed.
     */
    global boolean use_amavis = false;

    /**
     * Is amavis available on the installation media?
     */
    global boolean amavis_allowed = true;

    /**
     * Domains for locally delivered mail.
     * (ahost.acompany.com is a domain)
     */
    global list<string> local_domains = [];

    /**
     * A relay server for outgoing mail.
     * May be enclosed in [brackets] to prevent MX lookups.
     */
    global string outgoing_mail_server = "";

    /**
     * Mail will appear to come from this domain. Applies also for the
     * envelope. Does not apply for mail from root.
     */
    global string from_header = "";

    /**
     * If empty, from_header will be applied to mails coming from
     * local_domains, otherwise from these domains. (Remember: mail
     * domains)
     */
    global list<string> masquerade_other_domains = [];

    /**
     * User specific sender masquerading.
     * List of maps: $[comment:, user:, address:] (all are strings)
     */
    global list<map> masquerade_users = [];

    /**
     * sysconfig/postfix:POSTFIX_MDA
     * #26052
     */
    global symbol postfix_mda = `local;

    /**
     * When should fetchmail run:
     * <dl>
     * <dt> `manual  <dd>
     * <dt> `ppp     <dd> for a ppp conection, /etc/ppp/ip-{up,down}.d
     * <dt> `network <dd> for any network connection,
     *                    /etc/sysconfig/network/if-{up,down}.d
     * </dl>
     * #22903
     * Unused yet!
     * #37139, just enable rcfetchmail.
     */
    global symbol fetchmail_mode = `manual;

    /**
     *List of maps:
     * $[server:, protocol:, remote_user:, local_user:, password:,
     * enabled:(bool), other_(server|client)_options: ]
     */
    global list<map> fetchmail = [];

    /**
     * Domain-specific aliases.
     * List of maps: $[comment:, alias:, destinations:] (all are strings)
     */
    global list<map> virtual_users = [];

    /**
     * SMTP AUTH (#23000)
     * list of maps:
     * The ui only handles the first list item, the rest is for autoyast
     * $[server: string, user: string, password: string(plain text)]
     * There are other map keys that must be preserved on editing.
     */
    global list<map> smtp_auth = [];

    /**
     * Sysconfig setting that enables the feature.
     * For postfix, it is a simple yes/no which we set to (size(smtp_auth)>0)
     * For sendmail, it is a list of methods which we set to empty or all
     * but we don't touch it if it was something in between, marked as nil.
     * Must default to non-nil.
     */
    boolean enable_smtp_auth = false;

    /* ---------------------------------------------------------------- */
    // constants

    /**
     * The full set of authentication mechanisms for sendmail
     */
    string sendmail_all_mechanisms = "plain gssapi digest-md5 cram-md5";//const

    /**
     * Fetchmail protocols, as defined in rcfile_l.l
     * Probably not all of them are compatible with our simplified scheme
     * but it does not hurt to include them.
     * Must check for validity: the agent matches [[:alnum:]]+,
     * lowercase names are valid too.
     */
    global list<string> protocol_choices = [
	"AUTO",
	"POP2",
	"POP3",
	"IMAP",
	"APOP",
	"KPOP",
	"SDPS",
	"ETRN",
	"ODMR",
	];

    /* ---------------------------------------------------------------- */

    /**
     * Has the configuration been changed?
     * Can be used as an argument to Popup::ReallyAbort
     */
    global boolean touched = false;

    /**
     * A convenient shortcut for setting touched.
     * @param really	if true, set Mail::touched
     * @example Mail::Touch (Mail::var != ui_var);
     */
    global define void Touch (boolean really) ``{
	touched = touched || really;
    }

    /* ---------------------------------------------------------------- */

    /**
     * Read only, set by ProbePackages.
     * Use as an argument to Package::DoInstallAndRemove
     */
    global list<string> install_packages = [];
    /**
     * Read only, set by ProbePackages.
     * Use as an argument to Package::DoInstallAndRemove
     */
    global list<string> remove_packages = [];

    /**
     * Of the four available amavis packages, amavis-postfix does not need
     * a service running, others do.
     * Update: only one package, amavisd-new, but let's keep the variable,
     * just in case.
     * We query rpm in WriteGeneral (so that it works for autoinst too).
     * This is only used if use_amavis is on, of course.
     */
    boolean amavis_service = true;

    /**
     * Detect which packages have to be installed
     * and return a descriptive string for a plain text pop-up.
     * @return "" or "Foo will be installed.\nBar will be installed.\n"
     */
    global define string ProbePackages () ``{
	string message = "";
	install_packages = [];
	remove_packages = [];

	if (use_amavis)
	{
	    string pkg  = "amavisd-new";
	    if (! Package::Installed (pkg))
	    {
		install_packages = add (install_packages, pkg);
		// Translators: popup message part, ends with a newline
		message = message + _("AMaViS, a virus scanner, will be installed.\n");
	    }
	}

	if (size (fetchmail) > 0 &&
	    ! Package::Installed ("fetchmail"))
	{
	    install_packages = add (install_packages, "fetchmail");
	    // Translators: popup message part, ends with a newline
	    message = message + _("Fetchmail, a mail downloading utility, will be installed.\n");
	}

	return message;
    }

    /* ---------------------------------------------------------------- */

    /**
     * Detect the MTA installed
     */
    define void ReadMta () ``{
	// so that AY cloning works, #45071
	if (PackageSystem::Installed ("sendmail"))
	{
	    mta = `sendmail;
	}
	else if (PackageSystem::Installed ("postfix"))
	{
	    mta = `postfix;
	}
	else
	{
	    mta = `other;
	}
    }

    /**
     * Whether rcfetchmail should run
     */
    define boolean RunFetchmailGlobally () ``{
	return size (fetchmail) > 0;
    }

    /**
     * Read all mail settings from the SCR
     * @param abort A block that can be called by Read to find
     *	      out whether abort is requested. Returns true if abort
     *	      was pressed.
     * @return True on success
     */
    global define boolean Read (block<boolean> abort) ``{
	// Translators: dialog caption
	string caption = _("Initializing mail configuration");

	Progress::New (caption, " ", 0, [
			   // Translators: progress label
			   // do not translate MTA
			   _("Determining Mail Transport Agent (MTA)"),
			   // Translators: progress label
			   _("Reading general settings"),
			   // Translators: progress label
			   _("Reading masquerading settings"),
			   // Translators: progress label
			   _("Reading downloading settings"),
			   // Translators: progress label
			   _("Reading alias tables"),
			   // Translators: progress label
			   // smtp-auth
			   _("Reading authentication settings..."),
			   ],
		       [], "");

	// announce 1
	Progress::NextStage ();
	if (eval (abort))
	{
	    return false;
	}
	// read 1
	ReadMta ();
	if (mta == `other)
	{
	    return false;
	}

	// announce 2
	Progress::NextStage ();
	if (eval (abort))
	{
	    return false;
	}
	// read 2
	// create_config
	create_config = SCR::Read (.sysconfig.mail.MAIL_CREATE_CONFIG)== "yes";

	// open port
	listen_remote = SCR::Read (.sysconfig.mail.SMTPD_LISTEN_REMOTE)=="yes";
	SuSEFirewall::Read ();

	// connection_type:
	boolean nc = false;
	boolean ex = false;
	// the service must be always running
	//boolean service = false;
	if (mta == `sendmail)
	{
	    nc = SCR::Read (.sysconfig.sendmail.SENDMAIL_NOCANONIFY) == "yes";
	    ex = SCR::Read (.sysconfig.sendmail.SENDMAIL_EXPENSIVE) == "yes";
	}
	else if (mta == `postfix)
	{
	    nc = SCR::Read (.sysconfig.postfix.POSTFIX_NODNS) == "yes";
	    ex = SCR::Read (.sysconfig.postfix.POSTFIX_DIALUP) == "yes";
	}
	else
	{
	    return false;
	}
	if (nc)
	{
	    connection_type = (ex)? `dialup : `none;
	}
	else
	{
	    connection_type = `permanent;
	}

	// amavis
	string amavis_pkg = "amavisd-new";
	amavis_allowed = PackageSystem::Installed (amavis_pkg) ||
	    Mode::commandline () ||
	    PackageSystem::Available (amavis_pkg);
	use_amavis = amavis_allowed && (SCR::Read (.sysconfig.amavis.USE_AMAVIS) == "yes");

	// local_domains
	string ld_s = "";
	if (mta == `sendmail)
	{
	    ld_s = (string) SCR::Read (.sysconfig.sendmail.SENDMAIL_LOCALHOST);
	}
	else if (mta == `postfix)
	{
	    ld_s = (string) SCR::Read (.sysconfig.postfix.POSTFIX_LOCALDOMAINS);
	}
	else
	{
	    return false;
	}
	local_domains = filter (string s, splitstring(ld_s, " ,;"),``(s != ""));

	// outgoing_mail_server
	if (mta == `sendmail)
	{
	    outgoing_mail_server = (string)
		SCR::Read (.sysconfig.sendmail.SENDMAIL_SMARTHOST);
	}
	else if (mta == `postfix)
	{
	    outgoing_mail_server = (string)
		SCR::Read (.sysconfig.postfix.POSTFIX_RELAYHOST);
	}
	else
	{
	    return false;
	}

	// postfix_mda
	if (mta == `postfix)
	{
	    string postfix_mda_s = (string)
		SCR::Read (.sysconfig.postfix.POSTFIX_MDA);
	    if (postfix_mda_s == "local")
	    {
		postfix_mda = `local;
	    }
	    else if (postfix_mda_s == "procmail")
	    {
		postfix_mda = `procmail;
	    }
	    else if (postfix_mda_s == "cyrus")
	    {
		postfix_mda = `cyrus;
	    }
	    else
	    {
		postfix_mda = nil;
	    }
	}

	// announce 3
	Progress::NextStage ();
	if (eval (abort))
	{
	    return false;
	}
	// read 3
	// from_header
	from_header = (string) SCR::Read (.sysconfig.mail.FROM_HEADER);
	// handle nonexistent file
	if (from_header == nil)
	{
	    from_header = "";
	}

	// masquerade_other_domains
	string mod_s = "";
	if (mta == `sendmail)
	{
	    mod_s = (string) SCR::Read (.sysconfig.sendmail.MASQUERADE_DOMAINS);
	}
	else if (mta == `postfix)
	{
	    mod_s = (string) SCR::Read (.sysconfig.postfix.POSTFIX_MASQUERADE_DOMAIN);
	}
	else
	{
	    return false;
	}
	masquerade_other_domains = filter (string s, splitstring (mod_s, " ,;"),
	    ``(s != ""));

	// masquerade_users
	list<map> mu_raw = [];
	if (mta == `sendmail)
	{
	    mu_raw = (list<map>)SCR::Read (.mail.sendmail.generics.table);
	}
	else if (mta == `postfix)
	{
	    mu_raw = (list<map>)SCR::Read (.mail.postfix.sendercanonical.table);
	}
	else
	{
	    return false;
	}
	masquerade_users = maplist (map e, mu_raw,
				    ``($[
					   "comment"	: e["comment"]: "",
					   "user"	: e["key"]: "",
					   "address"	: e["value"]: "",
					   ]));

	// announce 4
	Progress::NextStage ();
	if (eval (abort))
	{
	    return false;
	}
	// read 4
	// TODO
	fetchmail_mode = `manual;

	// if we are testing as non-root, it will fail, that's OK
	map out = (map) SCR::Execute (.target.bash_output, "/usr/bin/id --user");
	boolean root = out["stdout"]:"" == "0\n";

	fetchmail = (list<map>) SCR::Read (.mail.fetchmail.accounts);
	if (fetchmail == nil && root)
	{
	    // Translators: error message,
	    // %1 is a file name,
	    // %2 is a long file name - leave it on a separate line
	    Report::Error (sformat(_("Error reading file %1. The file must have
a fixed format to be readable by YaST2.  For details, see
%2"),
			 "/etc/fetchmailrc",
			 "/usr/share/doc/packages/yast2-mail/fetchmailrc.txt"));
	    return false;
	}
	//TODO what to do with a difficult syntax etc?

	// announce 5
	Progress::NextStage ();
	if (eval (abort))
	{
	    return false;
	}
	// read 5
//	MailAliases::merge_aliases = false;

	// aliases
	if (!MailAliases::ReadAliases ())
	{
	    return false;
	}
	// virtual_users
	list<map> v_raw = [];
	if (mta == `sendmail)
	{
	    v_raw = (list<map>) SCR::Read (.mail.sendmail.virtuser.table);
	}
	else if (mta == `postfix)
	{
	    v_raw = (list<map>) SCR::Read (.mail.postfix.virtual.table);
	}
	else
	{
	    return false;
	}
	virtual_users =  maplist (map e, v_raw,
				  ``($[
					 "comment"	: e["comment"]: "",
					 "alias"	: e["key"]: "",
					 "destinations"	: e["value"]: "",
					 ]));

	// announce 6
	Progress::NextStage ();
	if (eval (abort))
	{
	    return false;
	}
	// read 6
	if (mta == `sendmail)
	{
	    smtp_auth = (list<map>) SCR::Read (.mail.sendmail.auth.accounts);
	    string mechanisms =
		(string) SCR::Read (.sysconfig.sendmail.SMTP_AUTH_MECHANISMS);
	    if (mechanisms != sendmail_all_mechanisms && mechanisms != "")
	    {
		enable_smtp_auth = nil;
	    }
	}
	else if (mta == `postfix)
	{
	    smtp_auth = (list<map>) SCR::Read (.mail.postfix.auth.accounts);
	}
	else
	{
	    return false;
	}


	// complete
	Progress::NextStage ();
	return true;
    }

    /**
     * Make up data for screnshots
     */
    global define void Fake () ``{
	mta = `postfix;
	create_config = true;
	listen_remote = true;
	connection_type = `dialup;
	amavis_allowed = true;
	use_amavis = true;
	// good example?
	local_domains = ["branch1.example.com", "branch2.example.com"];
	outgoing_mail_server = "mail.example.com";
	from_header = "example.com";
	masquerade_other_domains = [];
	masquerade_users = [
	    $["user": "hyde", "address": "DrJekyll@Example.com"],
	    ];
	fetchmail = [
	    $[
		"server": "pop3.example.net",
		"protocol": "POP3",
		"remote_user": "jekyll",
		"local_user": "hyde",
		"password": "stephenson",
		],
	    ];

	// just patch out root
	MailAliases::ReadAliases ();
	MailAliases::root_alias = "hyde";

	// TODO virtual
	enable_smtp_auth = true;
	smtp_auth = [
	    $[
		"server": "mail.example.com",
		"user": "jekyll",
		"password": "foo"
		],
	    ];
    }

    /**
     * Part of Write.
     * @return success
     */
    define boolean WriteGeneral () ``{
	// create_config
	// if the user wanted it false, we did not proceed
	SCR::Write (.sysconfig.mail.MAIL_CREATE_CONFIG, "yes");
	// listen_remote
	SCR::Write (.sysconfig.mail.SMTPD_LISTEN_REMOTE,
		    listen_remote? "yes":"no");
	SuSEFirewall::WriteOnly ();

	// connection_type
	// nocanonify/nodns
	// expensive/dialup
	path nc_nd = nil;
	path ex_di = nil;
	string service = nil;
	if (mta == `sendmail)
	{
	    nc_nd = .sysconfig.sendmail.SENDMAIL_NOCANONIFY;
	    ex_di = .sysconfig.sendmail.SENDMAIL_EXPENSIVE;
	    service = "sendmail";
	}
	else if (mta == `postfix)
	{
	    nc_nd = .sysconfig.postfix.POSTFIX_NODNS;
	    ex_di = .sysconfig.postfix.POSTFIX_DIALUP;
	    service = "postfix";
	}
	else
	{
	    return false;
	}

	if (connection_type == `permanent)
	{
	    SCR::Write (nc_nd, "no");
	    SCR::Write (ex_di, "no");
	}
	else if (connection_type == `dialup)
	{
	    SCR::Write (nc_nd, "yes");
	    SCR::Write (ex_di, "yes");
	}
	else if (connection_type == `none)
	{
	    SCR::Write (nc_nd, "yes");
	    SCR::Write (ex_di, "no");
	}
	else
	{
	    y2internal("Unrecognized connection_type: %1", connection_type);
	    return false;
	}
	Service::Enable (service);

	// amavis
	SCR::Write (.sysconfig.amavis.USE_AMAVIS, use_amavis? "yes":"no");
	// used also in WriteServices
	amavis_service = true;
	if (amavis_service)
	{
	    Service::Adjust ("amavis", use_amavis? "enable":"disable");
	}

	// SENDMAIL_ARGS
	// by default they contain -q30m, not good for dial-up
	// SENDMAIL_CLIENT_ARGS must contain -q... or it will not run!
	if (mta == `sendmail)
	{
	    string default_permanent = "-L sendmail -Am -bd -q30m -om";
	    string default_dialup = "-L sendmail -Am -bd -om";
	    string args = (string) SCR::Read (.sysconfig.sendmail.SENDMAIL_ARGS);

	    if (connection_type == `permanent && args == default_dialup)
	    {
		SCR::Write (.sysconfig.sendmail.SENDMAIL_ARGS, default_permanent);
	    }
	    else if (connection_type == `dialup &&
		     // if empty, sendmail init-script uses the default
		     (args == default_permanent || args == ""))
	    {
		SCR::Write (.sysconfig.sendmail.SENDMAIL_ARGS, default_dialup);
	    }
	}

	// local_domains
	if (mta == `sendmail)
	{
	    string ld_s = mergestring (local_domains, " ");
	    SCR::Write (.sysconfig.sendmail.SENDMAIL_LOCALHOST, ld_s);
	}
	else if (mta == `postfix)
	{
	    string ld_s = mergestring (local_domains, ","); // noted in #12672
	    SCR::Write (.sysconfig.postfix.POSTFIX_LOCALDOMAINS, ld_s);
	}
	else
	{
	    return false;
	}

	// outgoing_mail_server
	if (mta == `sendmail)
	{
	    SCR::Write (.sysconfig.sendmail.SENDMAIL_SMARTHOST, outgoing_mail_server);
	}
	else if (mta == `postfix)
	{
	    SCR::Write (.sysconfig.postfix.POSTFIX_RELAYHOST, outgoing_mail_server);
	}
	else
	{
	    return false;
	}

	// postfix_mda
	if (mta == `postfix)
	{
	    string s_mda = "local";		// default to local
	    if (postfix_mda == `procmail)
	    {
		s_mda = "procmail";
	    }
	    else if (postfix_mda == `cyrus)
	    {
		s_mda = "cyrus";
	    }
	    SCR::Write (.sysconfig.postfix.POSTFIX_MDA, s_mda);
	}
	return true;
    }

    /**
     * Part of Write.
     * @return success
     */
    define boolean WriteMasquerading () ``{
	// from_header
	SCR::Write (.sysconfig.mail.FROM_HEADER, from_header);
	// masquerade_other_domains
	if (mta == `sendmail)
	{
	    string mod = mergestring (masquerade_other_domains, " ");
	    SCR::Write (.sysconfig.sendmail.MASQUERADE_DOMAINS, mod);
	}
	else if (mta == `postfix)
	{
	    string mod = mergestring (masquerade_other_domains, ",");
	    SCR::Write (.sysconfig.postfix.POSTFIX_MASQUERADE_DOMAIN, mod);
	}
	else
	{
	    return false;
	}

	// masquerade_users
	list<map> mu_raw = maplist (map e, masquerade_users,
				    ``($[
					   "comment"	: e["comment"]:"",
					   // TODO check that nonempty
					   "key"	: e["user"]:"",
					   "value"	: e["address"]:"",
					   ]));
	if (mta == `sendmail)
	{
	    SCR::Write (.mail.sendmail.generics.table, mu_raw);
	}
	else if (mta == `postfix)
	{
	    SCR::Write (.mail.postfix.sendercanonical.table, mu_raw);
	}
	else
	{
	    return false;
	}
	return true;
    }

    /**
     * Part of Write.
     * @return success
     */
    define boolean WriteDownloading () ``{
	// fetchmail
	// TODO ?other settings: autofetch? at device up?
	SCR::Write (.mail.fetchmail.accounts, fetchmail);
	if (! SCR::Write (.mail.fetchmail, nil))
	{
	    // Translators: error message
	    Report::Error (_("Error writing the fetchmail configuration."));
	    return false;
	}

	if (RunFetchmailGlobally ())
	{
	    Service::Enable ("fetchmail");
	}
	else
	{
	    Service::Disable ("fetchmail");
	}
	return true;
    }

    /**
     * Part of Write.
     * @return success
     */
    define boolean WriteAliasesAndVirtual () ``{
	// aliases
	if (! MailAliases::WriteAliases ())
	{
	    return false;
	}

	// virtual_users
	list<map> v_raw = maplist (map e, virtual_users,
				   ``($[
					  "comment"	: e["comment"]:"",
					  "key"		: e["alias"]:"",
					  "value"	: e["destinations"]:"",
					  ]));
	if (mta == `sendmail)
	{
	    SCR::Write (.mail.sendmail.virtuser.table, v_raw);
	}
	else if (mta == `postfix)
	{
	    SCR::Write (.mail.postfix.virtual.table, v_raw);
	}
	else
	{
	    return false;
	}
	return true;
    }

    /**
     * Part of Write.
     * @return success
     */
    define boolean WriteSmtpAuth () ``{
	// TODO how to remove the only entry?
	// filter it out in the dialog?
	if (enable_smtp_auth != nil)
	{
	    enable_smtp_auth = size (smtp_auth) > 0;
	}

	if (mta == `sendmail)
	{
	    SCR::Write (.mail.sendmail.auth.accounts, smtp_auth);
	    if (enable_smtp_auth != nil)
	    {
		SCR::Write (.sysconfig.sendmail.SMTP_AUTH_MECHANISMS,
			    enable_smtp_auth? sendmail_all_mechanisms: "");
	    }
	}
	else if (mta == `postfix)
	{
	    SCR::Write (.mail.postfix.auth.accounts, smtp_auth);
	    SCR::Write (.sysconfig.postfix.POSTFIX_SMTP_AUTH,
			enable_smtp_auth? "yes": "no");
	}
	else
	{
	    return false;
	}
	return true;
    }

    /**
     * Part of Write.
     * @return success
     */
    define boolean WriteFlush () ``{
	//flush the agents
	if (!SCR::Write (.sysconfig.mail, nil))
	{
	    // Translators: error message
	    Report::Error (sformat (_("Error writing file %1"), "/etc/sysconfig/mail"));
	    return false;
	}
	if (!SCR::Write (.sysconfig.amavis, nil))
	{
	    // Translators: error message
	    Report::Error (sformat (_("Error writing file %1"), "/etc/sysconfig/amavis"));
	    return false;
	}
	list<path> tables = nil;
	if (mta == `sendmail)
	{
	    if (!SCR::Write (.sysconfig.sendmail, nil))
	    {
		// Translators: error message
		Report::Error (sformat (_("Error writing file %1"), "/etc/sysconfig/sendmail"));
		return false;
	    }
	    tables = [
		.mail.sendmail.generics,
		.mail.aliases,
		.mail.sendmail.virtuser,
		.mail.sendmail.auth,
		];
	}
	else if (mta == `postfix)
	{
	    if (!SCR::Write (.sysconfig.postfix, nil))
	    {
		// Translators: error message
		Report::Error (sformat (_("Error writing file %1"), "/etc/sysconfig/postfix"));
		return false;
	    }
	    tables = [
		.mail.postfix.sendercanonical,
		.mail.aliases,
		.mail.postfix.virtual,
		.mail.postfix.auth,
		];
	}
	else
	{
	    return false;
	}

	foreach (path p, tables, ``{
	    if (!SCR::Write (p, nil))
	    {
		string filename =
		    (string) SCR::Read (p + topath (".meta.filename"));
		// Translators: error message
		Report::Error (sformat (_("Error writing file %1"), filename));
		return false;
	    }
	});
	return true;
    }

    /**
     * Part of Write.
     * @return success
     */
    define boolean WriteSuSEconfig () ``{
	//run SuSEconfig with the necessary modules
	// TODO other modules using FROM_HEADER?
	integer ret = 0;
	if (mta == `sendmail)
	{
	    ret = (integer)
		SCR::Execute(.target.bash,"/sbin/SuSEconfig --module sendmail");
	}
	else if (mta == `postfix)
	{
	    ret = (integer)
		SCR::Execute(.target.bash, "/sbin/SuSEconfig --module postfix");
	}
	else
	{
	    return false;
	}

	if (ret != 0)
	{
	    // Translators: error message
	    Report::Error (_("Error running SuSEconfig."));
	    return false;
	}
	return true;
    }

    /**
     * Part of Write.
     * @return success
     */
    define boolean WriteServices () ``{
	if (amavis_service)
	{
	    Service::Stop ("amavis");
	    if (use_amavis)
	    {
		if (!Service::Start ("amavis"))
		{
		    // Translators: error message
		    Report::Error (sformat (_("Error starting service %1."), "amavis"));
		    return false;
		}
	    }
	}

	Service::Stop ("fetchmail");
	if (RunFetchmailGlobally ())
	{
	    if (!Service::Start ("fetchmail"))
	    {
		// Translators: error message
		Report::Error (sformat (_("Error starting service %1."), "fetchmail"));
		return false;

	    }
	}

	string service = "";
	if (mta == `sendmail)
	{
	    service = "sendmail";
	}
	else if (mta == `postfix)
	{
	    service = "postfix";
	}
	else
	{
	    return false;
	}

	if (!Service::Restart (service))
	{
	    // Translators: error message
	    Report::Error (sformat (_("Error starting service %1."), service));
	    return false;
	}

	// ServiceAdjust enable/disable is done in WriteGeneral

	return SuSEFirewall::ActivateConfiguration ();
    }

    /**
     * Update the SCR according to mail settings
     * @param abort A block that can be called by Write to find
     *	      out whether abort is requested. Returns true if abort
     *	      was pressed.
     * @return True on success
     */
    global define boolean Write (block<boolean> abort) ``{
	list<list> stages = [
	    // Translators: progress label
	    [ _("Writing general settings"), WriteGeneral ],
	    ];
	if (connection_type != `none)
	{
	    // Translators: progress label
	    stages = add (stages, [ _("Writing masquerading settings"), WriteMasquerading ]);
	    // Translators: progress label
	    stages = add (stages, [ _("Writing alias tables"), WriteAliasesAndVirtual ]);
	    // Write them unconditionally, because it is now possible to
	    // enter them also in the Permanent mode. Bug #17417.
	    // Translators: progress label
	    stages = add (stages, [ _("Writing downloading settings"), WriteDownloading ]);
	    // Translators: progress label
	    stages = add (stages, [ _("Writing authentication settings..."), WriteSmtpAuth ]);
	}
	// Translators: progress label
	stages = add (stages, [ _("Finishing writing configuration files"), WriteFlush ]);
	// autoinstallation does it all together later
	if (! write_only)
	{
	    // Translators: progress label
	    stages = add (stages, [ _("Running SuSEconfig"), WriteSuSEconfig ]);

	    // Translators: progress label
	    stages = add (stages, [ _("Restarting services"), WriteServices ]);
	}

	// Translators: dialog caption
	string caption = _("Saving mail configuration");
	// We do not set help text here, because it was set outside
	Progress::New (caption, " ", 0, maplist (list e, stages, ``(e[0]:"")), [], "");

	foreach (list e, stages, ``{
	    Progress::NextStage ();
	    if (eval (abort))
	    {
		// TODO: finishes only this iteration, not the function
		return false;
	    }

	    any af = e[1]:nil;
	    boolean() f = (boolean ()) af;
	    if (! f ())
	    {
		// TODO: finishes only this iteration, not the function
		return false;
	    }
	});

	// complete
	Progress::NextStage ();
	return true;
    }


    /**
     * Get all mail settings from the first parameter
     * (For use by autoinstallation.)
     * @param settings The YCP structure to be imported.
     * @return True on success
     */
    global define boolean Import (map Settings) ``{
	map<string,any> settings = (map<string,any>) Settings;

	y2debug ("before %1", settings); // may contain passwords
	settings = mapmap(string k, any v, settings,``{
	    if (k == "mta" && is(v,symbol)) {
		return($[k : v]);
	    } else if (k == "connection_type" && is(v,symbol)) {
		return($[k : v]);
	    } else if (k == "postfix_mda" && is(v,symbol)) {
		return($[k : v]);
	    }
	    if (k == "mta" && v == "sendmail") {
		return($["mta" : `sendmail]);
	    } else if (k == "mta" && v == "postfix") {
		return($["mta" : `postfix]);
	    } else if (k == "mta") {
		return($["mta" : `other]);
	    } else if (k == "connection_type" && v == "permanent") {
		return($["connection_type" : `permanent]);
	    } else if (k == "connection_type" && v == "dialup") {
		return($["connection_type" : `dialup]);
	    } else  if (k == "connection_type") {
		return($["connection_type" : `none]);
	    } else if (k == "postfix_mda" && v == "local") {
		return($["postfix_mda" : `local]);
	    } else if (k == "postfix_mda" && v == "procmail") {
		return($["postfix_mda" : `procmail]);
	    } else if (k == "postfix_mda") {
		return($["postfix_mda" : `cyrus]);
	    } else {
		return ($[k : v]);
	    }
	});

	mta = settings["mta"]: `other;
	connection_type = settings["connection_type"]: `none;
	listen_remote = settings["listen_remote"]: false;
	use_amavis = settings["use_amavis"]: false;
	local_domains = settings["local_domains"]: [];
	outgoing_mail_server = settings["outgoing_mail_server"]: "";
	postfix_mda = settings["postfix_mda"]: `local;
	from_header = settings["from_header"]: "";
	masquerade_other_domains = settings["masquerade_other_domains"]: [];
	masquerade_users = settings["masquerade_users"]: [];
	fetchmail = settings["fetchmail"]: [];
	MailAliases::aliases = settings["aliases"]: [];
	MailAliases::FilterRootAlias ();
//	MailAliases::merge_aliases = settings["merge_aliases"]: false;
	virtual_users = settings["virtual_users"]: [];
	smtp_auth = settings["smtp_auth"]: [];
	y2debug ("after %1", settings); // may contain passwords
	return true;
    }


    /**
     * Dump the mail settings to a single map
     * (For use by autoinstallation.)
     * @return Dumped settings (later acceptable by Import ())
     */
    global define map Export () ``{
	map<string,any> settings = $[
	    "mta": mta,
	    "connection_type": connection_type,
	    "listen_remote": listen_remote,
	    "use_amavis": use_amavis,
	    "local_domains": local_domains,
	    "outgoing_mail_server": outgoing_mail_server,
	    "from_header": from_header,
	    "masquerade_other_domains": masquerade_other_domains,
	    "masquerade_users": masquerade_users,
	    "fetchmail": fetchmail,
	    "aliases": MailAliases::MergeRootAlias (MailAliases::aliases),
//	    "merge_aliases": MailAliases::merge_aliases,
	    "virtual_users": virtual_users,
	    "smtp_auth": smtp_auth,
	    ];
	if (mta == `postfix)
	{
	    settings = add (settings, "postfix_mda", postfix_mda);
	}
	// Dont export empty fields
	foreach (string k, any v, settings, ``{
	    if (contains ([ "", nil, [], $[] ], v))
	    {
		settings = remove(settings, k);
	    }
	});
	return settings;
    }

    /**
     * Summarizes a list of data
     * @param title passed to Summary::AddHeader
     * @param value a list (of scalars, lists or maps)
     * @param index if the entries are not scalars,
     *   use this index to get a scalar
     * @return Summary-formatted description
     */
    define string ListItem(string title, any value, any index) ``{
	string summary = "";
	summary = Summary::AddHeader(summary, title);
	if (is(value,list) && value != nil && value != []) {
	    summary = Summary::OpenList(summary);
	    foreach (any d, (list) value, ``{
		string entry = "";
		if (is(d,map))
		    entry = ((map)d)[index]:"???";
		else if (is (d,list))
		    entry = ((list)d)[(integer)index]:"???";
		else
		    entry = (string) d;

		summary = Summary::AddListItem(summary, entry);
	    });
	    summary = Summary::CloseList(summary);
	} else {
	    summary = Summary::AddLine(summary,Summary::NotConfigured ());
	}
	return summary;
    }

    /**
     * Summary
     * @return string with summary of configuration
     */
    global define string Summary ()
	``{
	// TODO: use widget captions, strip sho&rtcuts

	string agent = "";
	if (mta == `sendmail)
	     agent = "sendmail";
	else if (mta == `postfix)
	    agent = "postfix";
	else
	    agent = "other";


	string con_type = "";
	if (connection_type == `permanent)
	    con_type = "Permanent";
	else if (connection_type == `dialup)
	    con_type = "Dialup";
	else
	    con_type = "None";

	string nc = Summary::NotConfigured ();
	string summary = "";
	// summary header; mail transfer agent
	summary = Summary::AddHeader(summary, _("MTA"));
	summary = Summary::AddLine(summary, agent);
	// summary header
	summary = Summary::AddHeader(summary, _("Connection Type"));
	summary = Summary::AddLine(summary,con_type );

	// summary header
	summary = Summary::AddHeader(summary, _("Outgoing Mail Server"));
	summary = Summary::AddLine(summary, (outgoing_mail_server != "") ? outgoing_mail_server : nc);

	// summary header; the "From: foo@bar.com" mail header
	summary = Summary::AddHeader(summary, _("From Header"));
	summary = Summary::AddLine(summary, (from_header != "") ? from_header : nc);

	// summary item
	summary = summary + ListItem (_("Local Domains"), local_domains, nil);
	// summary item
	summary = summary + ListItem (_("Masquerade Other Domains"), masquerade_other_domains, nil);
	// summary item
	summary = summary + ListItem (_("Masquerade Users"), masquerade_users, "user");
	// summary header
	summary = Summary::AddHeader(summary, _("Accept remote SMTP connections"));
	// summary item
	summary = Summary::AddLine(summary, (listen_remote) ? _("Yes") : _("No"));
	// summary header
	summary = Summary::AddHeader(summary, _("Use AMaViS"));
	// summary item
	summary = Summary::AddLine(summary, (use_amavis) ? _("Yes") : _("No"));
	// summary item
	summary = summary + ListItem (_("Fetchmail"), fetchmail, "server");
	// summary item
	summary = summary + ListItem (_("Aliases"), MailAliases::MergeRootAlias (MailAliases::aliases), "alias");
	// summary item
	summary = summary + ListItem (_("Virtual Users"), virtual_users, "alias");
	// summary item
	summary = summary + ListItem (_("Authentication"), smtp_auth, "server");
	return summary;
    }

	/**
	* Return required packages for auto-installation
    * @return map of packages to be installed and to be removed
    */
    global define map AutoPackages() ``{
	     return ($["install": required_packages, "remove": []]);

    }


}
