<?php

require_once dirname(__FILE__) . '/accesscheck.php';

if (defined('PHPMAILER_PATH') and PHPMAILER_PATH != '') {
    #require_once '/usr/share/php/libphp-phpmailer/class.phpmailer.php'
    require_once PHPMAILER_PATH;
} else {
    //https://github.com/PHPMailer/PHPMailer
    require_once dirname(__FILE__) . '/PHPMailer/PHPMailerAutoload.php';
}

class PHPlistMailer extends PHPMailer
{
    public $WordWrap = 75;
    public $encoding = 'base64';
    public $messageid = 0;
    public $destinationemail = '';
    public $estimatedsize = 0;
    public $mailsize = 0;
    private $inBlast = false;
    public $image_types = array(
        'gif' => 'image/gif',
        'jpg' => 'image/jpeg',
        'jpeg' => 'image/jpeg',
        'jpe' => 'image/jpeg',
        'bmp' => 'image/bmp',
        'png' => 'image/png',
        'tif' => 'image/tiff',
        'tiff' => 'image/tiff',
        'swf' => 'application/x-shockwave-flash',
    );

    public $LE = "\n";
    public $Hello = '';
    public $timeStamp = '';

    public function PHPlistMailer($messageid, $email, $inBlast = true, $exceptions = false)
    {
        parent::__construct($exceptions);
        parent::SetLanguage('en', dirname(__FILE__) . '/PHPMailer/language/');
        $this->addCustomHeader('X-phpList-version: ' . VERSION);
        $this->addCustomHeader("X-MessageID: $messageid");
        $this->addCustomHeader("X-ListMember: $email");

        ## amazon SES doesn't like this
        /*
         * http://mantis.phplist.com/view.php?id=15562
         * Interesting, https://mantis.phplist.com/view.php?id=16688
         * says Gmail wants it. Everyone's confused.
         *
         * Also, are we "Precedence: bulk, or Precedence: list"
         *
         * I guess this should become configurable, to leave the choice up to the installation,
         * but what would be our default?
         *
              if (!USE_AMAZONSES) {
        #        $this->addCustomHeader("Precedence: bulk");
              }
              *
              * ok, decided:
        */

        if (!USE_AMAZONSES && USE_PRECEDENCE_HEADER) {
            $this->addCustomHeader('Precedence: bulk');
        }

        $newwrap = getConfig('wordwrap');
        if ($newwrap) {
            $this->WordWrap = $newwrap;
        }
        if (defined('SMTP_TIMEOUT')) {
            $this->Timeout = sprintf('%d', SMTP_TIMEOUT);
        }

        $this->destinationemail = $email;
        $this->SingleTo = false;
        $this->CharSet = 'UTF-8';# getConfig("html_charset");
        $this->inBlast = $inBlast;
        ### hmm, would be good to sort this out differently, but it'll work for now
        ## don't send test message using the blast server
        if (isset($_GET['page']) && $_GET['page'] == 'send') {
            $this->inBlast = false;
        }
        $this->Helo = getConfig('domain');

        if ($this->inBlast && defined('PHPMAILERBLASTHOST') && defined('PHPMAILERBLASTPORT') && PHPMAILERBLASTHOST != '') {
            $this->Host = PHPMAILERBLASTHOST;
            $this->Port = PHPMAILERBLASTPORT;
            if (isset($GLOBALS['phpmailer_smtpuser']) && $GLOBALS['phpmailer_smtpuser'] != ''
                && isset($GLOBALS['phpmailer_smtppassword']) && $GLOBALS['phpmailer_smtppassword']
            ) {
                $this->Username = $GLOBALS['phpmailer_smtpuser'];
                $this->Password = $GLOBALS['phpmailer_smtppassword'];
                $this->SMTPAuth = true;
            }
            $this->Mailer = 'smtp';
        } elseif (!$this->inBlast && defined('PHPMAILERTESTHOST') && PHPMAILERTESTHOST != '') {
            if (defined('PHPMAILERPORT')) {
                $this->Port = PHPMAILERPORT;
            }
            //logEvent('Sending email via '.PHPMAILERHOST);
            $this->Host = PHPMAILERTESTHOST;
            if (isset($GLOBALS['phpmailer_smtpuser']) && $GLOBALS['phpmailer_smtpuser'] != ''
                && isset($GLOBALS['phpmailer_smtppassword']) && $GLOBALS['phpmailer_smtppassword']
            ) {
                $this->Username = $GLOBALS['phpmailer_smtpuser'];
                $this->Password = $GLOBALS['phpmailer_smtppassword'];
                $this->SMTPAuth = true;
            }
            $this->Mailer = 'smtp';
        } elseif (defined('PHPMAILERHOST') && PHPMAILERHOST != '') {
            if (defined('PHPMAILERPORT')) {
                $this->Port = PHPMAILERPORT;
            }
            //logEvent('Sending email via '.PHPMAILERHOST);
            $this->Host = PHPMAILERHOST;
            if (isset($GLOBALS['phpmailer_smtpuser']) && $GLOBALS['phpmailer_smtpuser'] != ''
                && isset($GLOBALS['phpmailer_smtppassword']) && $GLOBALS['phpmailer_smtppassword']
            ) {
                $this->Username = $GLOBALS['phpmailer_smtpuser'];
                $this->Password = $GLOBALS['phpmailer_smtppassword'];
                $this->SMTPAuth = true;
            }
            $this->Mailer = 'smtp';
        } else {
            $this->isMail();
        }
        if (empty($_SERVER['SERVER_NAME']) || empty($this->Hostname)) {
            $this->Hostname = getConfig('domain');
        }

        if (defined('PHPMAILER_SECURE') && PHPMAILER_SECURE) {
            $this->SMTPSecure = PHPMAILER_SECURE;
        }
        $this->SMTPAutoTLS = true;

        if (isset($GLOBALS['phpmailer_smtpoptions'])) {
            $this->SMTPOptions = $GLOBALS['phpmailer_smtpoptions'];
        }

        if ($GLOBALS['message_envelope']) {
            $this->Sender = $GLOBALS['message_envelope'];
            $this->addCustomHeader('Bounces-To: ' . $GLOBALS['message_envelope']);

## one to work on at a later stage
#        $this->addCustomHeader("Return-Receipt-To: ".$GLOBALS["message_envelope"]);
        }
        ## when the email is generated from a webpage (quite possible :-) add a "received line" to identify the origin
        if (!empty($_SERVER['REMOTE_ADDR'])) {
            $this->add_timestamp();
        }
        $this->messageid = $messageid;
    }

    public function add_html($html, $text = '', $templateid = 0)
    {
        $this->Body = $html;
        $this->IsHTML(true);
        if ($text) {
            $this->add_text($text);
        }
        $this->Encoding = HTMLEMAIL_ENCODING;
        $this->find_html_images($templateid);
    }

    public function add_timestamp()
    {
        #0013076:
        # Add a line like Received: from [10.1.2.3] by website.example.com with HTTP; 01 Jan 2003 12:34:56 -0000
        # more info: http://www.spamcop.net/fom-serve/cache/369.html
        $ip_address = $_SERVER['REMOTE_ADDR'];
        if (!empty($_SERVER['REMOTE_HOST'])) {
            $ip_domain = $_SERVER['REMOTE_HOST'];
        } else {
            $ip_domain = gethostbyaddr($ip_address);
        }
        if ($ip_domain != $ip_address) {
            $from = "$ip_domain [$ip_address]";
        } else {
            $from = "[$ip_address]";
        }
        $hostname = hostName();
        $request_time = date('r', $_SERVER['REQUEST_TIME']);
        $sTimeStamp = "from $from by $hostname with HTTP; $request_time";
        $this->addTimeStamp($sTimeStamp);
    }

    public function AddTimeStamp($sTimeStamp)
    {
        $this->timeStamp = $sTimeStamp;
    }

    public function add_text($text)
    {
        if (!$this->Body) {
            $this->IsHTML(false);
            $this->Body = html_entity_decode($text, ENT_QUOTES, 'UTF-8'); #$text;
        } else {
            $this->AltBody = html_entity_decode($text, ENT_QUOTES, 'UTF-8');#$text;
        }
    }

    public function append_text($text)
    {
        if ($this->AltBody) {
            $this->AltBody .= html_entity_decode($text, ENT_QUOTES, 'UTF-8');#$text;
        } else {
            $this->Body .= html_entity_decode($text . "\n", ENT_QUOTES, 'UTF-8');#$text;
        }
    }

    public function build_message()
    {
    }

    public function CreateHeader()
    {
        $parentheader = parent::CreateHeader();
        if (!empty($this->timeStamp)) {
            $header = 'Received: ' . $this->timeStamp . $this->LE . $parentheader;
        } else {
            $header = $parentheader;
        }

        return $header;
    }

    public function CreateBody()
    {
        $body = parent::CreateBody();
        /*
              if ($this->ContentType != 'text/plain') {
                foreach ($GLOBALS['plugins'] as $plugin) {
                  $plreturn =  $plugin->mimeWrap($this->messageid,$body,$this->header,$this->ContentTypeHeader,$this->destinationemail);
                  if (is_array($plreturn) && sizeof($plreturn) == 3) {
                    $this->header = $plreturn[0];
                    $body = $plreturn[1];
                    $this->ContentTypeHeader = $plreturn[2];
                  }
                }
              }
        */
        return $body;
    }

    public function compatSend(
        $to_name = '',
        $to_addr,
        $from_name,
        $from_addr,
        $subject = '',
        $headers = '',
        $envelope = ''
    ) {
        if (!empty($from_addr) && method_exists($this, 'SetFrom')) {
            $this->SetFrom($from_addr, $from_name);
        } else {
            $this->From = $from_addr;
            $this->FromName = $from_name;
        }
        if (!empty($GLOBALS['developer_email'])) {
            # make sure we are not sending out emails to real users
            # when developing
            $this->AddAddress($GLOBALS['developer_email']);
            if ($GLOBALS['developer_email'] != $to_addr) {
                $this->Body = 'X-Originally to: ' . $to_addr . "\n\n" . $this->Body;
            }
        } else {
            $this->AddAddress($to_addr);
        }
        $this->Subject = $subject;
        if ($this->Body) {
            ## allow plugins to add header lines
            foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                #    print "Checking Destination for ".$plugin->name."<br/>";
                $pluginHeaders = $plugin->messageHeaders($this);
                if ($pluginHeaders && count($pluginHeaders)) {
                    foreach ($pluginHeaders as $headerItem => $headerValue) {
                        ## @@TODO, do we need to sanitise them?
                        $this->addCustomHeader($headerItem . ': ' . $headerValue);
                    }
                }
            }
            if (!parent::Send()) {
                logEvent(s('Error sending email to %s', $to_addr) . ' ' . $this->ErrorInfo);

                return 0;
            }#
        } else {
            logEvent(s('Error, empty message-body sending email to %s', $to_addr));

            return 0;
        }

        return 1;
    }

    public function Send()
    {
        if (!parent::Send()) {
            return 0;
        }

        return 1;
    }

    public function add_attachment($contents, $filename, $mimetype)
    {
        ## phpmailer 2.x
        if (method_exists($this, 'AddStringAttachment')) {
            $this->AddStringAttachment($contents, $filename, 'base64', $mimetype);
        } else {
            ## old phpmailer
            // Append to $attachment array
            $cur = count($this->attachment);
            $this->attachment[$cur][0] = base64_encode($contents);
            $this->attachment[$cur][1] = $filename;
            $this->attachment[$cur][2] = $filename;
            $this->attachment[$cur][3] = $this->encoding;
            $this->attachment[$cur][4] = $mimetype;
            $this->attachment[$cur][5] = false; // isStringAttachment
            $this->attachment[$cur][6] = 'attachment';
            $this->attachment[$cur][7] = 0;
        }
    }

    public function find_html_images($templateid)
    {
        #if (!$templateid) return;
        ## no template can be templateid 0, find the powered by image
        $templateid = sprintf('%d', $templateid);

        // Build the list of image extensions
        $extensions = array();
        while (list($key) = each($this->image_types)) {
            $extensions[] = $key;
        }
        $html_images = array();
        $filesystem_images = array();

        ## addition for external images
        if (defined('EMBEDEXTERNALIMAGES') && EMBEDEXTERNALIMAGES) {
            $external_images = array();
            $matched_images = array();

            preg_match_all('~="(http[s]?://(?:(?!' . $_SERVER['SERVER_NAME'] . '))([^"]+\.(' . implode('|',
                    $extensions) . '))([\\?/][^"]+)?)"~Ui', $this->Body, $matched_images); //

            for ($i = 0; $i < count($matched_images[1]); ++$i) {
                if ($this->external_image_exists($matched_images[1][$i])) {
                    $external_images[] = $matched_images[1][$i] . '~^~' . basename($matched_images[2][$i]) . '~^~' . strtolower($matched_images[3][$i]);
                }
            }

            if (!empty($external_images)) {
                $external_images = array_unique($external_images);

                for ($i = 0; $i < count($external_images); ++$i) {
                    $external_image = explode('~^~', $external_images[$i]);

                    if ($image = $this->get_external_image($external_image[0])) {
                        $content_type = $this->image_types[$external_image[2]];
                        $cid = $this->add_html_image($image, $external_image[1], $content_type);

                        if (!empty($cid)) {
                            $this->Body = str_replace($external_image[0], 'cid:' . $cid, $this->Body);
                        }
                    }
                }
            }
        }
        ## end addition

        preg_match_all('/"([^"]+\.(' . implode('|', $extensions) . '))"/Ui', $this->Body, $images);

        for ($i = 0; $i < count($images[1]); ++$i) {
            if ($this->image_exists($templateid, $images[1][$i])) {
                $html_images[] = $images[1][$i];
                $this->Body = str_replace($images[1][$i], basename($images[1][$i]), $this->Body);
            }
            ## addition for filesystem images
            if (EMBEDUPLOADIMAGES) {
                if ($this->filesystem_image_exists($images[1][$i])) {
                    $filesystem_images[] = $images[1][$i];
                    $this->Body = str_replace($images[1][$i], basename($images[1][$i]), $this->Body);
                }
            }
            ## end addition
        }
        if (!empty($html_images)) {
            // If duplicate images are embedded, they may show up as attachments, so remove them.
            $html_images = array_unique($html_images);
            sort($html_images);
            for ($i = 0; $i < count($html_images); ++$i) {
                if ($image = $this->get_template_image($templateid, $html_images[$i])) {
                    $content_type = $this->image_types[strtolower(substr($html_images[$i],
                        strrpos($html_images[$i], '.') + 1))];
                    $cid = $this->add_html_image($image, basename($html_images[$i]), $content_type);
                    if (!empty($cid)) {
                        $this->Body = str_replace(basename($html_images[$i]), "cid:$cid", $this->Body);
                    }
                }
            }
        }
        ## addition for filesystem images
        if (!empty($filesystem_images)) {
            // If duplicate images are embedded, they may show up as attachments, so remove them.
            $filesystem_images = array_unique($filesystem_images);
            sort($filesystem_images);
            for ($i = 0; $i < count($filesystem_images); ++$i) {
                if ($image = $this->get_filesystem_image($filesystem_images[$i])) {
                    $content_type = $this->image_types[strtolower(substr($filesystem_images[$i],
                        strrpos($filesystem_images[$i], '.') + 1))];
                    $cid = $this->add_html_image($image, basename($filesystem_images[$i]), $content_type);
                    if (!empty($cid)) {
                        $this->Body = str_replace(basename($filesystem_images[$i]), "cid:$cid", $this->Body);#@@@
                    }
                }
            }
        }
        ## end addition
    }

    public function add_html_image($contents, $name = '', $content_type = 'application/octet-stream')
    {
        ## in phpMailer 2 and up we cannot use AddStringAttachment, because that doesn't use a cid
        ## we can't write to "attachment" either, because it's private

        /* one way to do it, is using a temporary file, but that'll have
         * quite an effect on performance and also isn't backward compatible,
         * because EncodeFile would need to be reverted to the default

        file_put_contents('/tmp/'.$name,base64_decode($contents));
        $cid = md5(uniqid(time()));
        $this->AddEmbeddedImage('/tmp/'.$name, $cid, $name,'base64', $content_type);
        */

        /* So, for now the only way to get this working in phpMailer 2 or up is to make
         * the attachment array public or add the AddEmbeddedImageString method
         * we need to add instructions how to patch phpMailer for that.
         * find out here whether it's been done and give an error if not
         *
         * it's been added to phpMailer 5.2.2
         * http://code.google.com/a/apache-extras.org/p/phpmailer/issues/detail?id=119
         *
         *
         */

        /* @@TODO additional optimisation:
         *
         * - we store the image base64 encoded
         * - then we decode it to pass it back to phpMailer
         * - when then encodes it again
         * - best would be to take out a step in there, but that would require more modifications
         * to phpMailer
         */

        #$cid = md5(uniqid(time()));
        $cid = md5(mt_rand() . $name . uniqid(time(), true)); ##17603 better random CID value on Windows
        if (method_exists($this, 'AddEmbeddedImageString')) {
            $this->AddEmbeddedImageString(base64_decode($contents), $cid, $name, $this->encoding, $content_type);
        } elseif (method_exists($this, 'AddStringEmbeddedImage')) {
            ## PHPMailer 5.2.5 and up renamed the method
            ## https://github.com/PHPMailer/PHPMailer/issues/42#issuecomment-16217354
            $this->AddStringEmbeddedImage(base64_decode($contents), $cid, $name, $this->encoding, $content_type);
        } elseif (isset($this->attachment) && is_array($this->attachment)) {
            // Append to $attachment array
            $cur = count($this->attachment);
            $this->attachment[$cur][0] = base64_decode($contents);
            $this->attachment[$cur][1] = '';#$filename;
            $this->attachment[$cur][2] = $name;
            $this->attachment[$cur][3] = 'base64';
            $this->attachment[$cur][4] = $content_type;
            $this->attachment[$cur][5] = true; // isStringAttachment
            $this->attachment[$cur][6] = 'inline';
            $this->attachment[$cur][7] = $cid;
        } else {
            logEvent('phpMailer needs patching to be able to use inline images from templates');
            print Error('phpMailer needs patching to be able to use inline images from templates');

            return;
        }

        return $cid;
    }

    ## addition for filesystem images

    public function filesystem_image_exists($filename)
    {
        ##  find the image referenced and see if it's on the server
        $imageroot = getConfig('uploadimageroot');
#      cl_output('filesystem_image_exists '.$docroot.' '.$filename);

        $elements = parse_url($filename);
        $localfile = basename($elements['path']);

        $localfile = urldecode($localfile);
        #     cl_output('CHECK'.$localfile);

        if (defined('UPLOADIMAGES_DIR')) {
            #  print $_SERVER['DOCUMENT_ROOT'].$localfile;
            return
                is_file($_SERVER['DOCUMENT_ROOT'] . '/' . UPLOADIMAGES_DIR . '/image/' . $localfile)
                || is_file($_SERVER['DOCUMENT_ROOT'] . '/' . UPLOADIMAGES_DIR . '/' . $localfile)
                ## commandline
                || is_file($imageroot . '/' . $localfile);
        } else {
            return
                is_file($_SERVER['DOCUMENT_ROOT'] . $GLOBALS['pageroot'] . '/' . FCKIMAGES_DIR . '/image/' . $localfile)
                || is_file($_SERVER['DOCUMENT_ROOT'] . $GLOBALS['pageroot'] . '/' . FCKIMAGES_DIR . '/' . $localfile)
                ## commandline
                || is_file('../' . FCKIMAGES_DIR . '/image/' . $localfile)
                || is_file('../' . FCKIMAGES_DIR . '/' . $localfile);
        }
    }

    public function get_filesystem_image($filename)
    {
        ## get the image contents
        $localfile = basename(urldecode($filename));
#      cl_output('get file system image'.$filename.' '.$localfile);
        if (defined('UPLOADIMAGES_DIR')) {
            #       print 'UPLOAD';
            $imageroot = getConfig('uploadimageroot');
            if (is_file($imageroot . $localfile)) {
                return base64_encode(file_get_contents($imageroot . $localfile));
            } else {
                if (is_file($_SERVER['DOCUMENT_ROOT'] . $localfile)) {
                    ## save the document root to be able to retrieve the file later from commandline
                    SaveConfig('uploadimageroot', $_SERVER['DOCUMENT_ROOT'], 0, 1);

                    return base64_encode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . $localfile));
                } elseif (is_file($_SERVER['DOCUMENT_ROOT'] . '/' . UPLOADIMAGES_DIR . '/image/' . $localfile)) {
                    SaveConfig('uploadimageroot', $_SERVER['DOCUMENT_ROOT'] . '/' . UPLOADIMAGES_DIR . '/image/', 0, 1);

                    return base64_encode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/' . UPLOADIMAGES_DIR . '/image/' . $localfile));
                } elseif (is_file($_SERVER['DOCUMENT_ROOT'] . '/' . UPLOADIMAGES_DIR . '/' . $localfile)) {
                    SaveConfig('uploadimageroot', $_SERVER['DOCUMENT_ROOT'] . '/' . UPLOADIMAGES_DIR . '/', 0, 1);

                    return base64_encode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/' . UPLOADIMAGES_DIR . '/' . $localfile));
                }
            }
        } elseif (is_file($_SERVER['DOCUMENT_ROOT'] . $GLOBALS['pageroot'] . '/' . FCKIMAGES_DIR . '/' . $localfile)) {
            $elements = parse_url($filename);
            $localfile = basename($elements['path']);

            return base64_encode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . $GLOBALS['pageroot'] . '/' . FCKIMAGES_DIR . '/' . $localfile));
        } elseif (is_file($_SERVER['DOCUMENT_ROOT'] . $GLOBALS['pageroot'] . '/' . FCKIMAGES_DIR . '/image/' . $localfile)) {
            return base64_encode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . $GLOBALS['pageroot'] . '/' . FCKIMAGES_DIR . '/image/' . $localfile));
        } elseif (is_file('../' . FCKIMAGES_DIR . '/' . $localfile)) {   ## commandline
            return base64_encode(file_get_contents('../' . FCKIMAGES_DIR . '/' . $localfile));
        } elseif (is_file('../' . FCKIMAGES_DIR . '/image/' . $localfile)) {
            return base64_encode(file_get_contents('../' . FCKIMAGES_DIR . '/image/' . $localfile));
        }

        return '';
    }

    ## end addition

    ## addition for external images

    public function external_image_exists($filename)
    {
        // Check for a http(s) address excluding this host
        if ((strpos($filename, 'http') === 0) && (strpos($filename, '://' . $_SERVER['SERVER_NAME'] . '/') === false)) {
            $extCacheDir = $GLOBALS['tmpdir'] . '/external_cache';

            // Create cache directory
            if (!file_exists($extCacheDir)) {
                @mkdir($extCacheDir);
            }

            if (file_exists($extCacheDir) && is_writable($extCacheDir)) {
                // Remove old files in cache directory
                if (defined('EXTERNALIMAGE_MAXAGE') && EXTERNALIMAGE_MAXAGE && ($extCacheDirHandle = @opendir($extCacheDir))) {
                    while (false !== ($cacheFile = @readdir($extCacheDirHandle))) {
                        if ((strlen($cacheFile) > 0) && (substr($cacheFile, 0, 1) != '.')) {
                            $cacheFileMTime = @filemtime($extCacheDir . '/' . $cacheFile);

                            if (is_numeric($cacheFileMTime) && ($cacheFileMTime > 0) && ((time() - $cacheFileMTime) > EXTERNALIMAGE_MAXAGE)) {
                                @unlink($extCacheDir . '/' . $cacheFile);
                            }
                        }
                    }

                    @closedir($extCacheDirHandle);
                }

                // Generate local filename
                //$cacheFile = $extCacheDir.'/'.$this->messageid.'_'.hash('sha256', $filename);
                $cacheFile = $extCacheDir . '/' . $this->messageid . '_' . preg_replace(array(
                        '~[\.][\.]+~Ui',
                        '~[^\w\.]~Ui'
                    ), array('', '_'), $filename);

                // Download and cache file
                if (!file_exists($cacheFile)) {
                    $cacheFileContent = '';
                    $downloadTimeout = (defined('EXTERNALIMAGE_TIMEOUT') ? EXTERNALIMAGE_TIMEOUT : 30);

                    // Try downloading using cURL
                    if (function_exists('curl_init')) {
                        $cURLHandle = curl_init($filename);

                        if ($cURLHandle !== false) {
                            //curl_setopt($cURLHandle, CURLOPT_URL, $filename);
                            curl_setopt($cURLHandle, CURLOPT_HTTPGET, true);
                            curl_setopt($cURLHandle, CURLOPT_HEADER, 0);
                            curl_setopt($cURLHandle, CURLOPT_BINARYTRANSFER, true);
                            curl_setopt($cURLHandle, CURLOPT_RETURNTRANSFER, true);
                            //curl_setopt($cURLHandle, CURLOPT_FILE, $cacheFileHandle);
                            curl_setopt($cURLHandle, CURLOPT_TIMEOUT, $downloadTimeout);
                            curl_setopt($cURLHandle, CURLOPT_FOLLOWLOCATION, true);
                            curl_setopt($cURLHandle, CURLOPT_MAXREDIRS, 10);
                            curl_setopt($cURLHandle, CURLOPT_SSL_VERIFYPEER, false);
                            curl_setopt($cURLHandle, CURLOPT_FAILONERROR, true);

                            $cacheFileContent = curl_exec($cURLHandle);

                            $cURLErrNo = curl_errno($cURLHandle);
                            $cURLInfo = curl_getinfo($cURLHandle);

                            curl_close($cURLHandle);

                            if ($cURLErrNo != 0) {
                                $cacheFileContent = 'CURL_ERROR_' . $cURLErrNo;
                            }
                            if ($cURLInfo['http_code'] >= 400) {
                                $cacheFileContent = 'HTTP_CODE_' . $cURLInfo['http_code'];
                            }
                        }
                    }

                    // Try downloading using file_get_contents 
                    if ($cacheFileContent == '') {
                        $remoteURLContext = stream_context_create(array(
                            'http' => array(
                                'method' => 'GET',
                                'timeout' => $downloadTimeout,
                                'max_redirects' => '10',
                            ),
                        ));

                        $cacheFileContent = file_get_contents($filename, false, $remoteURLContext);
                        if ($cacheFileContent === false) {
                            $cacheFileContent = 'FGC_ERROR';
                        }
                    }

                    // Limit size
                    if (defined('EXTERNALIMAGE_MAXSIZE') && EXTERNALIMAGE_MAXSIZE && (strlen($cacheFileContent) > EXTERNALIMAGE_MAXSIZE)) {
                        $cacheFileContent = 'MAX_SIZE';
                    }

                    // Write cache file
                    //file_put_contents($cacheFile, $cacheFileContent, LOCK_EX);
                    $cacheFileHandle = @fopen($cacheFile, 'wb');
                    if ($cacheFileHandle !== false) {
                        if (flock($cacheFileHandle, LOCK_EX)) {
                            fwrite($cacheFileHandle, $cacheFileContent);
                            fflush($cacheFileHandle);
                            flock($cacheFileHandle, LOCK_UN);
                        }
                        fclose($cacheFileHandle);
                    }
                }

                if (file_exists($cacheFile) && (@filesize($cacheFile) > 64)) {
                    return true;
                }
            }
        }
    }

    public function get_external_image($filename)
    {
        $extCacheDir = $GLOBALS['tmpdir'] . '/external_cache';
        //$cacheFile = $extCacheDir.'/'.$this->messageid.'_'.hash('sha256', $filename);
        $cacheFile = $extCacheDir . '/' . $this->messageid . '_' . preg_replace(array('~[\.][\.]+~Ui', '~[^\w\.]~Ui'),
                array('', '_'), $filename);

        if (file_exists($cacheFile) && (@filesize($cacheFile) > 64)) {
            return base64_encode(file_get_contents($cacheFile));
        }
    }

    ## end addition

    public function image_exists($templateid, $filename)
    {
        if (basename($filename) == 'powerphplist.png' || strpos($filename, 'ORGANISATIONLOGO') === 0) {
            $templateid = 0;
        }
        $req = Sql_Query(sprintf('select * from %s where template = %d and (filename = "%s" or filename = "%s")',
            $GLOBALS['tables']['templateimage'], $templateid, $filename, basename($filename)));

        return Sql_Affected_Rows();
    }

    public function get_template_image($templateid, $filename)
    {
        if (basename($filename) == 'powerphplist.png' || strpos($filename, 'ORGANISATIONLOGO') === 0) {
            $templateid = 0;
        }
        $req = Sql_Fetch_Row_Query(sprintf('select data from %s where template = %d and (filename = "%s" or filename = "%s")',
            $GLOBALS['tables']['templateimage'], $templateid, $filename, basename($filename)));

        return $req[0];
    }

    public function EncodeFile($path, $encoding = 'base64')
    {
        # as we already encoded the contents in $path, return $path
        return chunk_split($path, 76, $this->LE);
    }

    public function AmazonSESSend($messageheader, $messagebody)
    {
        $messageheader = preg_replace('/' . $this->LE . '$/', '', $messageheader);
        $messageheader .= $this->LE . 'Subject: ' . $this->EncodeHeader($this->Subject) . $this->LE;

        #print nl2br(htmlspecialchars($messageheader));      exit;

        $date = date('r');
        $aws_signature = base64_encode(hash_hmac('sha256', $date, AWS_SECRETKEY, true));

        $requestheader = array(
            'Host: ' . parse_url(AWS_POSTURL, PHP_URL_HOST),
            'Content-Type: application/x-www-form-urlencoded',
            'Date: ' . $date,
            'X-Amzn-Authorization: AWS3-HTTPS AWSAccessKeyId=' . AWS_ACCESSKEYID . ',Algorithm=HMACSHA256,Signature=' . $aws_signature,
        );

        /*
         *    using the SendEmail call
              $requestdata = array(
                'Action' => 'SendEmail',
                'Source' => $this->Sender,
                'Destination.ToAddresses.member.1' => $this->destinationemail,
                'Message.Subject.Data' => $this->Subject,
                'Message.Body.Text.Data' => $messagebody,
              );
        */
        #     print '<hr/>Rawmessage '.nl2br(htmlspecialchars($messageheader. $this->LE. $this->LE.$messagebody));

        $rawmessage = base64_encode($messageheader . $this->LE . $this->LE . $messagebody);
        #   $rawmessage = str_replace('=','',$rawmessage);

        $requestdata = array(
            'Action' => 'SendRawEmail',
            'Source' => $GLOBALS['message_envelope'],
            'Destinations.member.1' => $this->destinationemail,
            'RawMessage.Data' => $rawmessage,
        );

        $header = '';
        foreach ($requestheader as $param) {
            $header .= $param . $this->LE;
        }

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, AWS_POSTURL);
        curl_setopt($curl, CURLOPT_TIMEOUT, 30);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $requestheader);
        #    print('<br/>Sending header '.htmlspecialchars($header).'<hr/>');

        curl_setopt($curl, CURLOPT_HEADER, 1);
        curl_setopt($curl, CURLOPT_DNS_USE_GLOBAL_CACHE, true);
        curl_setopt($curl, CURLOPT_USERAGENT, NAME . ' (phpList version ' . VERSION . ', http://www.phplist.com/)');
        curl_setopt($curl, CURLOPT_POST, 1);

        ## this generates multipart/form-data, and that crashes the API, so don't use
        #      curl_setopt($curl, CURLOPT_POSTFIELDS, $parameters);

        $data = '';
        foreach ($requestdata as $param => $value) {
            $data .= $param . '=' . urlencode($value) . '&';
        }
        $data = substr($data, 0, -1);
        #    print('Sending data '.htmlspecialchars($data).'<hr/>');
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

        $res = curl_exec($curl);
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        #    print('Curl status '.$status);
        if ($status != 200) {
            $error = curl_error($curl);
            logEvent('Amazon SES status ' . $status . ' ' . strip_tags($res) . ' ' . $error);
        }
        curl_close($curl);
        #     print('Got remote admin response '.htmlspecialchars($res).'<br/>');
        return $status == 200;
    }

    public function MailSend($header, $body)
    {
        $this->mailsize = strlen($header . $body);

        ## use Amazon, if set up, @@TODO redo with latest PHPMailer
        ## https://github.com/PHPMailer/PHPMailer/commit/57b183bf6a203cb69231bc3a235a00905feff75b

        if (USE_AMAZONSES) {
            $header .= 'To: ' . $this->destinationemail . $this->LE;

            return $this->AmazonSESSend($header, $body);
        }

        ## we don't really use multiple to's so pass that on to phpmailer, if there are any
        if (!$this->SingleTo || !USE_LOCAL_SPOOL) {
            return parent::MailSend($header, $body);
        }
        if (!is_dir(USE_LOCAL_SPOOL) || !is_writable(USE_LOCAL_SPOOL)) {
            ## if local spool is not set, send the normal way
            return parent::MailSend($header, $body);
        }
        $fname = tempnam(USE_LOCAL_SPOOL, 'msg');
        file_put_contents($fname, $header . "\n" . $body);
        file_put_contents($fname . '.S', $this->Sender);

        return true;
    }
}
