<?PHP
# Mailing list subscription manager
#
# Assumes sudo access as user 'mailman' to run various commands in ~mailman/bin
# Needs find_member, add_members, and remove_members
#
# In /etc/sudoers, this means:
#   Cmnd_Alias MAILMAN = /usr/lib/mailman/bin/find_member,
#   /usr/lib/mailman/bin/add_members, /usr/lib/mailman/bin/remove_members
#
#   %mailman    ALL=(mailman) NOPASSWD: MAILMAN
#
#   And be sure that requiretty is commented out or otherwise disabled for
#   the mailman group
#
# In /etc/group:
#   the apache user needs to be added to the mailman group

class Mailman extends Plugin
{
    function Register()
    {
        $this->Name = "Mailman subscription management";
        $this->Version = "1.0.2";
        $this->Description = "Links a CWIS site with a mailman installation"
            ." to provide user-friendly mailing list subscription"
            ." and transparent updates to ML subscriptions when a user's"
            ." email address is changed";
        $this->Author = "Internet Scout";
        $this->Url = "http://scout.wisc.edu/cwis/";
        $this->Email = "scout@scout.wisc.edu";
        $this->Requires = array(
            "CWISCore" => "2.0.3"
            );
        $this->CfgSetup["MailmanPrefix"] = array(
            "Type" => "Text",
            "Label" => "Mailman Prefix",
            "Help" => "Installation prefix of the Mailman software"
            );
    }

    function Install()
    {
        $DB = new Database();
        $DB->Query("CREATE TABLE MailmanLists (".
                   "ListName VARCHAR(64) UNIQUE"
                   .");");
        $this->ConfigSetting("MailmanPrefix","");
    }

    function Upgrade($PreviousVersion)
    {
        $DB = new Database();

        switch ($PreviousVersion)
        {
        default:
            $DB->Query("CREATE TABLE MailmanLists (".
                       "ListName VARCHAR(64) UNIQUE"
                       .");");
            $DB->Query("CREATE TABLE MailmanConfig (K TEXT, V TEXT);");
            $DB->Query(
                "INSERT INTO MailmanConfig (K,V) VALUES ('MailmanPrefix','')"
                );

        case "1.0.0":
            $Pfx = $DB->Query(
                "SELECT V FROM MailmanConfig where K='MailmanPrefix'", "V");
            $DB->Query("DROP TABLE MailmanConfig");

            $this->ConfigSetting("MailmanPrefix", $Pfx);
        }
    }

    function DeclareEvents()
    {
        return array(
            "MAILMAN_EVENT_GET_SUBS"
              => ApplicationFramework::EVENTTYPE_FIRST,
            "MAILMAN_EVENT_CHANGE_SUBS"
              => ApplicationFramework::EVENTTYPE_DEFAULT
            );
    }

    function HookEvents()
    {
        return array(
            "MAILMAN_EVENT_GET_SUBS"    => "GetUserSubscriptions",
            "MAILMAN_EVENT_CHANGE_SUBS" => "ChangeSubscription",
            "EVENT_USER_EMAIL_CHANGED"  => "UpdateUserEmail",
            "EVENT_SYSTEM_ADMINISTRATION_MENU" => "SysAdminMenu",
            );
    }

    function SysAdminMenu()
    {
        return array("ConfigureMailman" => "Edit Displayed Mailing Lists");
    }

    /**
     * Generate a temp file containing an email address.
     */
    function CreateEmailFile($Email)
    {
        $rc = tempnam('','');
        $FH = fopen($rc, "w");
        fwrite($FH, $Email."\n");
        fclose($FH);
        chmod($rc, 0644);
        return $rc;
    }

    /**
     * Pull the user subscriptions out of the mailman db.
     * @return Array of lists to display, keys are list names,
     *    values are bools indicating subscription or not
     */
    function GetUserSubscriptions()
    {
        global $User;

        $Lists = array();

        $MmPfx = $this->ConfigSetting("MailmanPrefix");
        if ($MmPfx !== NULL)
        {
            $DB = new Database();
            $DB->Query("SELECT ListName FROM MailmanLists");

            while ($Row = $DB->FetchRow())
            {
                $ListName = $Row["ListName"];
                $Lists[$ListName] = False;
            }

            $Out = array();
            exec('sudo -u mailman '.$MmPfx.'/bin/find_member -w '
                 .$User->Get("EMail"), $Out);
            foreach ($Out as $Line)
            {
                $Matches = array();
                if( preg_match( "/^ +([A-z-]+)/", strtolower($Line), $Matches) )
                {
                    $Lists [ $Matches[1] ] = True;
                }
            }

        }
        return $Lists;
    }

    /**
     * Toggle subscription status for a given list.
     */
    function ChangeSubscription($List,$Subscribe)
    {
        global $User;

        $MmPfx = $this->ConfigSetting("MailmanPrefix");
        if ($MmPfx !== NULL)
        {
            $Addr= $this->CreateEmailFile($User->Get("EMail"));

            if ($Subscribe)
            {
                exec('sudo -u mailman '.$MmPfx.'/bin/add_members -r '
                     .$Addr.' '.escapeshellarg($List));
            }
            else
            {
                exec('sudo -u mailman '.$MmPfx.'/bin/remove_members -f '
                     .$Addr.' '.escapeshellarg($List));
            }

            unlink($Addr);
        }
    }

    /**
     * Update a user's subscriptions on email change.  This doesn't
     * send the "welcome to" or "goodbye" messages to the user,
     * just removes their old address from the ML and adds the new one.
     */
    function UpdateUserEmail($UserId, $OldEmail, $NewEmail)
    {
        $DB = new Database();

        $MmPfx = $this->ConfigSetting("MailmanPrefix");
        if ($MmPfx !== NULL)
        {
            $AddrOld = $this->CreateEmailFile($OldEmail);
            $AddrNew = $this->CreateEmailFile($NewEmail);

            $SubscribedLists = array();
            exec('sudo -u mailman '.$MmPfx.'/bin/find_member -w '
                 .$OldEmail, $SubscribedLists);

            foreach ($SubscribedLists as $ListLine)
            {
                $List = preg_match("/^ +([^ ]*)/", $ListLine, $Matches);

                if (isset($Matches[1]))
                {
                    exec('sudo -u mailman '.$MmPfx.'/bin/remove_members -n -f '
                         .$AddrOld.' '.escapeshellarg($Matches[1]));
                    exec('sudo -u mailman '.$MmPfx.'/bin/add_members -w n -r '
                        .$AddrNew.' '.escapeshellarg($Matches[1]));
                }

            }

            unlink($AddrOld);
            unlink($AddrNew);
        }
    }
}
