TableEditor: don't use REPLACE to update the content of a row.
[platal.git] / classes / plmailer.php
index e587b91..24479f6 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /***************************************************************************
- *  Copyright (C) 2003-2006 Polytechnique.org                              *
+ *  Copyright (C) 2003-2010 Polytechnique.org                              *
  *  http://opensource.polytechnique.org/                                   *
  *                                                                         *
  *  This program is free software; you can redistribute it and/or modify   *
@@ -21,8 +21,6 @@
 
 require_once('smarty/libs/Smarty.class.php');
 
-// {{{ class PlMail
-
 /** Classe de mail avec corps en templates.
  */
 class PlMail extends Smarty
@@ -30,19 +28,17 @@ class PlMail extends Smarty
     private $tpl;
     private $mailer = null;
 
-    // {{{ constructor
-    
-    function PlMail($mailer, $tpl)
+    function __construct($tpl)
     {
         global $globals;
         $this->tpl           = $tpl;
-        $this->mailer        = $mailer;
         $this->caching       = false;
         $this->compile_check = true;
 
         $this->template_dir  = $globals->spoolroot . "/templates/";
-        $this->compile_dir   = $globals->spoolroot . "/spool/templates_c/";
+        $this->compile_dir   = $globals->spoolroot . "/spool/mails_c/";
         $this->config_dir    = $globals->spoolroot . "/configs/";
+        array_unshift($this->plugins_dir, $globals->spoolroot."/plugins/");
 
         $this->register_outputfilter(Array($this, 'mail_format'));
         $this->register_function('from',    Array($this, 'setFrom'));
@@ -51,30 +47,36 @@ class PlMail extends Smarty
         $this->register_function('bcc',     Array($this, 'addBcc'));
         $this->register_function('subject', Array($this, 'setSubject'));
         $this->register_function('add_header', Array($this, 'addHeader'));
+        $this->assign_by_ref('globals', $globals);
     }
 
-    // }}}
-    // {{{ function run()
-
-    function run($html)
+    public static function &get(&$mailer, $tpl)
     {
-        $this->assign('html_version', $html);
-        return $this->fetch($this->tpl);
+        static $plmail;
+        if (!isset($plmail) || $plmail->tpl != $tpl) {
+            $plmail = new PlMail($tpl);
+        }
+        $plmail->mailer =& $mailer;
+        return $plmail;
     }
 
-    // }}}
-    // {{{ function mail_format()
+    public function run($version)
+    {
+        $this->assign('mail_part', $version);
+        $text = $this->fetch($this->tpl);
+        if ($version == 'text') {
+            return wordwrap($text, 78);
+        }
+        return $text;
+    }
 
     /** used to remove the empty lines due to {from ...}, {to ...} ... functions */
-    static function mail_format($output, &$smarty)
+    static public function mail_format($output, &$smarty)
     {
-        return wordwrap("\n".trim($output)."\n",75);
+        return "\n".trim($output)."\n";
     }
 
-    // }}}
-    // {{{ function format_addr()
-
-    static function format_addr(&$params)
+    static protected function format_addr(&$params)
     {
         if (isset($params['full'])) {
             return $params['full'];
@@ -85,120 +87,103 @@ class PlMail extends Smarty
         }
     }
 
-    // }}}
-    // {{{ function setFrom()
-
     /** template function : from.
      * {from full=...} for an already formatted address
      * {from addr=... [text=...]} else
      */
-    function setFrom($params, &$smarty)
+    public function setFrom($params, &$smarty)
     {
-        $smarty->mailer->setFrom($this->format_addr($params));
+        $smarty->mailer->setFrom(PlMail::format_addr($params));
     }
 
-    // }}}
-    // {{{ function setTo()
-
     /** template function : to.
      * {to full=...} for an already formatted address
      * {to addr=... [text=...]} else
      */
-    function addTo($params, &$smarty)
+    public function addTo($params, &$smarty)
     {
-        $smarty->mailer->addTo($this->format_addr($params));
+        $smarty->mailer->addTo(PlMail::format_addr($params));
     }
 
-    // }}}
-    // {{{ function setCc()
-
     /** template function : cc.
      * {cc full=...} for an already formatted address
      * {cc addr=... [text=...]} else
      */
-    function addCc($params, &$smarty)
+    public function addCc($params, &$smarty)
     {
-        $smarty->mailer->addCc($this->format_addr($params));
+        $smarty->mailer->addCc(PlMail::format_addr($params));
     }
 
-    // }}}
-    // {{{ function setBcc()
-
     /** template function : bcc.
      * {bcc full=...} for an already formatted address
      * {bcc addr=... [text=...]} else
      */
-    function addBcc($params, &$smarty)
+    public function addBcc($params, &$smarty)
     {
-        $smarty->mailer->addBcc($this->format_addr($params));
+        $smarty->mailer->addBcc(PlMail::format_addr($params));
     }
 
-    // }}}
-    // {{{ function setSubject()
-
     /** template function : subject.
-     * {subject text=...} 
+     * {subject text=...}
      */
-    function setSubject($params, &$smarty)
+    public function setSubject($params, &$smarty)
     {
         $smarty->mailer->setSubject($params['text']);
     }
 
-    // }}}
-    // {{{ function addHeader()
-
     /** template function : add_header.
      * {add_header name=... value=...}
      */
-    function addHeader($params, &$smarty)
+    public function addHeader($params, &$smarty)
     {
         $smarty->mailer->addHeader($params['name'], $params['value']);
     }
-
-    // }}}
 }
-// }}}
-
 
 require_once('Mail.php');
 require_once('Mail/mime.php');
 
-// {{{ class PlMailer
 /** Class for sending inline or multipart-emails.
+ * Based on Diogenes' HermesMailer
  */
 class PlMailer extends Mail_Mime {
 
     private $mail;
     private $page    = null;
     private $charset;
-    // {{{ constructor
+    private $wiki    = null;
 
-    function PlMailer($tpl = null, $charset = "ISO-8859-15")
+    function __construct($tpl = null, $charset = "UTF-8")
     {
         $this->charset = $charset;
         $this->Mail_Mime("\n");
-        $this->mail =& Mail::factory('sendmail', Array('sendmail_args' => '-oi'));
+        $this->mail = Mail::factory('sendmail', Array('sendmail_args' => '-oi'));
         if (!is_null($tpl)) {
-            $this->page = new PlMail($this, $tpl);
+            $this->page =& PlMail::get($this, $tpl);
         }
     }
 
-    // }}}
-    // {{{ function correct_emails()
+    static private function formatUser(PlUser $user)
+    {
+        return '"' . $user->fullName() . '" <' . $user->bestEmail() . '>';
+    }
 
     /**
      * converts all : Foo Bar Baz <quux@foobar.org> into "Foo Bar Baz" <quux@foobar.org> which is RFC compliant
      */
-
     private function correct_emails($email)
     {
-        return preg_replace('!(^|, *)([^<"][^<"]*[^< "]) *(<[^>]*>)!', '\1"\2" \3', $email);
+        if ($email instanceof PlUser) {
+            $email = self::formatUser($email);
+        }
+        $email = preg_replace('!(^|, *)([^<"]+?) *(<[^>]*>)!ue',
+                              '\1 "\2" \3', $email);
+        return preg_replace('/"([^<]+)"/e',
+                            '"\\"" . PlMailer::encodeStringQP("\1") . "\\""',
+                            $email);
     }
 
-    // }}}
-    // {{{ function addTo()
-
-    function addTo($email)
+    public function addTo($email)
     {
         $email = $this->correct_emails($email);
         if (isset($this->_headers['To'])) {
@@ -208,34 +193,48 @@ class PlMailer extends Mail_Mime {
         }
     }
 
-    // }}}
-    // {{{ function addCc()
+    public function setTo($email)
+    {
+        $email = $this->correct_emails($email);
+        $this->_headers['To'] = $email;
+    }
 
-    function addCc($email)
+    public function addCc($email)
     {
         return parent::addCc($this->correct_emails($email));
     }
 
-    // }}}
-    // {{{ function addBcc()
-
-    function addBcc($email)
+    public function addBcc($email)
     {
         return parent::addBcc($this->correct_emails($email));
     }
 
-    // }}}
-    // {{{ function setFrom()
-
-    function setFrom($email)
+    public function setFrom($email)
     {
         return parent::setFrom($this->correct_emails($email));
     }
 
-    // }}}
-    // {{{ function addHeader()
-    
-    function addHeader($hdr,$val)
+    static function encodeStringQP($string)
+    {
+        if (!preg_match('/^[\x20-\x7e]*$/', $string)) {
+            $string = '=?UTF-8?Q?' . preg_replace('/[^\x21-\x3C\x3e\x40-\x7e]/e', 'PlMailer::encodeQP("\0")', $string)
+                     . '?=';
+        }
+        return $string;
+    }
+
+
+    static function encodeQP($char)
+    {
+        return sprintf('=%02X', ord($char));
+    }
+
+    public function setSubject($subject)
+    {
+        return parent::setSubject(self::encodeStringQP($subject));
+    }
+
+    public function addHeader($hdr,$val)
     {
         switch($hdr) {
             case 'From':
@@ -262,89 +261,122 @@ class PlMailer extends Mail_Mime {
         }
     }
 
-    // }}}
-    // {{{ function assign()
+    public function addUploadAttachment(PlUpload &$upload, $name)
+    {
+        $encoding = $upload->isType('text') ? 'quoted-printable' : 'base64';
+        $this->addAttachment($upload->getContents(), $upload->contentType(), $name, false, $encoding);
+    }
 
-    function assign($var, $value)
+    public function assign($var, $value)
     {
         if (!is_null($this->page)) {
             $this->page->assign($var, $value);
         }
     }
-    
-    // }}}
-    // {{{ function assign_by_ref()
-    
-    function assign_by_ref($var, &$value)
+
+    public function assign_by_ref($var, &$value)
     {
         if (!is_null($this->page)) {
             $this->page->assign_by_ref($var, $value);
         }
     }
 
-    // }}}
-    // {{{ function register_modifier()
-
-    function register_modifier($var, $callback)
+    public function register_modifier($var, $callback)
     {
         if (!is_null($this->page)) {
             $this->page->register_modifier($var, $callback);
         }
     }
-    
-    // }}}
-    // {{{ function register_function()
 
-    function register_function($var, $callback)
+    public function register_function($var, $callback)
     {
         if (!is_null($this->page)) {
             $this->page->register_function($var, $callback);
         }
     }
-    
-    // }}}
-    // {{{ function processPage()
+
+    public function setWikiBody($wiki)
+    {
+        $this->wiki = $wiki;
+    }
 
     private function processPage($with_html = true)
     {
         if (!is_null($this->page)) {
-            $this->setTxtBody($this->page->run(false));
-            if ($with_html) {
-                $html = trim($this->page->run(true));
-                if (!empty($html)) {
-                    $this->setHtmlBody($html);
+            global $globals;
+            if (!($globals->debug & DEBUG_SMARTY)) {
+                $level = error_reporting(0);
+            }
+            $this->page->assign_by_ref('globals', $globals);
+            $this->page->run('head'); // process page headers
+            $this->wiki = trim($this->page->run('wiki')); // get wiki
+            if (!$this->wiki) {
+                $this->setTxtBody($this->page->run('text'));
+                if ($with_html) {
+                    $html = trim($this->page->run('html'));
+                    if (!empty($html)) {
+                        $this->setHtmlBody($html);
+                    }
                 }
             }
+            if (!($globals->debug & DEBUG_SMARTY)) {
+                error_reporting($level);
+            }
+        }
+        if ($this->wiki) {
+            $this->setTxtBody(MiniWiki::WikiToText($this->wiki, false, 0, 78));
+            if ($with_html) {
+                $this->setHtmlBody('<html><body>' . MiniWiki::WikiToHtml($this->wiki, true) . '</body></html>');
+            }
         }
     }
 
-    // }}}
-    // {{{ function send()
+    public function sendTo(PlUser &$user)
+    {
+        $this->setTo($user);
+        $this->assign_by_ref('user', $user);
+        return $this->send($user->isEmailFormatHtml());
+    }
 
-    function send($with_html = true)
+    public function send($with_html = true)
     {
         $this->processPage($with_html);
-        if (S::v('forlife')) {
-            $this->addHeader('X-Org-Mail', S::v('forlife') . '@polytechnique.org');
+        if (S::user()) {
+            $this->addHeader('X-Org-Mail', S::user()->forlifeEmail());
         }
         $addrs = Array();
-        foreach(Array('To', 'Cc', 'Bcc') as $hdr) {
-            if(isset($this->_headers[$hdr])) {
+        foreach (Array('To', 'Cc', 'Bcc') as $hdr) {
+            if (isset($this->_headers[$hdr])) {
                 require_once 'Mail/RFC822.php';
-                $addrs = array_merge($addrs, Mail_RFC822::parseAddressList($this->_headers[$hdr]));
+                $parsed = @Mail_RFC822::parseAddressList($this->_headers[$hdr]);
+                if (is_array($parsed)) {
+                    $addrs = array_merge($addrs, $parsed);
+                }
             }
         }
-        if(empty($addrs)) {
+        if (empty($addrs)) {
             return false;
         }
-    
+
         $dests = Array();
-        foreach($addrs as $a) {
+        foreach ($addrs as $a) {
             $dests[] = "{$a->mailbox}@{$a->host}";
         }
-    
+
+        // Support for a "catch-all" email address, to be used by developers.
+        // This mode can only be activated when the working copy is in restricted
+        // mode, to ensure that production plat/al copies are never affected.
+        global $globals;
+        if ($globals->email_catchall && $globals->core->restricted_platal) {
+            require_once 'Mail/RFC822.php';
+            if (@Mail_RFC822::isValidInetAddress($globals->email_catchall)) {
+                $dests = array($globals->email_catchall);
+            }
+        }
+
         // very important to do it in THIS order very precisely.
         $body = $this->get(array('text_charset' => $this->charset,
+                                 'text_encoding' => '8bit',
                                  'html_charset' => $this->charset,
                                  'head_charset' => $this->charset));
         $hdrs = $this->headers();
@@ -354,10 +386,7 @@ class PlMailer extends Mail_Mime {
         }
         return $this->mail->send($dests, $hdrs, $body);
     }
-
-    // }}}
 }
 
-// }}}
-
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
 ?>