Пример #1
0
 /**
  * @param Campaign $campaign
  * @param Subscriber $subscriber
  * @param bool $is_test_mail
  * @param array $forwardedby
  * @return bool
  */
 public static function sendEmail($campaign, $subscriber, $is_test_mail = false, $forwardedby = array())
 {
     $get_speed_stats = Config::VERBOSE && Config::get('getspeedstats', false) !== false && Timer::get('process_queue') != null;
     $sql_count_start = phpList::DB()->getQueryCount();
     ## for testing concurrency, put in a delay to check if multiple send processes cause duplicates
     #usleep(rand(0,10) * 1000000);
     if ($get_speed_stats) {
         phpList::log()->debug('sendEmail start ' . Timer::get('process_queue')->interval(1), ['page' => 'preparecampaign']);
     }
     #0013076: different content when forwarding 'to a friend'
     if (Config::FORWARD_ALTERNATIVE_CONTENT) {
         $forwardContent = sizeof($forwardedby) > 0;
     } else {
         $forwardContent = 0;
     }
     if (!Cache::isCampaignCached($campaign)) {
         if (!PrepareCampaign::precacheCampaign($campaign, $forwardContent)) {
             phpList::log()->notice('Error loading campaign ' . $campaign->id . '  in cache');
             return false;
         }
     } else {
         #  dbg("Using cached {$cached->fromemail}");
         if (Config::VERBOSE) {
             phpList::log()->debug('Using cached campaign', ['page' => 'preparecampaign']);
         }
     }
     $cached_campaign = Cache::getCachedCampaign($campaign);
     if (Config::VERBOSE) {
         phpList::log()->debug(s('Sending campaign %d with subject %s to %s', $campaign->id, $cached_campaign->subject, $subscriber->getEmailAddress()), ['page' => 'preparecampaign']);
     }
     ## at this stage we don't know whether the content is HTML or text, it's just content
     $content = $cached_campaign->content;
     if ($get_speed_stats) {
         phpList::log()->debug('Load subscriber start', ['page' => 'preparecampaign']);
     }
     #0011857: forward to friend, retain attributes
     if ($subscriber->uniqid == 'forwarded' && Config::KEEPFORWARDERATTRIBUTES) {
         $subscriber = Subscriber::getSubscriberByEmailAddress($forwardedby['email']);
     }
     $subscriber_att_values = $subscriber->getCleanAttributes();
     $html = $text = array();
     if (stripos($content, '[LISTS]') !== false) {
         $lists = MailingList::getListsForSubscriber($subscriber->id);
         if (!empty($lists)) {
             foreach ($lists as $list) {
                 $html['lists'] .= '<br/>' . $list->name;
                 $text['lists'] .= "\n" . $list->name;
             }
         } else {
             $html['lists'] = s('strNoListsFound');
             $text['lists'] = s('strNoListsFound');
         }
     }
     if ($get_speed_stats) {
         phpList::log()->debug('Load subscriber end', ['page' => 'preparecampaign']);
     }
     if ($cached_campaign->subscriberpecific_url) {
         if ($get_speed_stats) {
             phpList::log()->debug('fetch personal URL start', ['page' => 'preparecampaign']);
         }
         ## Fetch external content, only if the URL has placeholders
         //TODO: I changed can_fetchUrl to can_fetch_url -> make sure it's changed everywhere
         if (Config::get('can_fetch_url') && preg_match('/\\[URL:([^\\s]+)\\]/i', $content, $regs)) {
             while (isset($regs[1]) && strlen($regs[1])) {
                 $url = $regs[1];
                 if (!preg_match('/^http/i', $url)) {
                     $url = 'http://' . $url;
                 }
                 $remote_content = Util::fetchUrl($url, $subscriber);
                 # @@ don't use this
                 #      $remote_content = includeStyles($remote_content);
                 if ($remote_content) {
                     $content = str_replace($regs[0], $remote_content, $content);
                     $cached_campaign->htmlformatted = strip_tags($content) != $content;
                 } else {
                     phpList::log()->notice('Error fetching URL: ' . $regs[1] . ' to send to ' . $subscriber->getEmailAddress());
                     return 0;
                 }
                 preg_match('/\\[URL:([^\\s]+)\\]/i', $content, $regs);
             }
         }
         if ($get_speed_stats) {
             phpList::log()->debug('fetch personal URL end', ['page' => 'preparecampaign']);
         }
     }
     if ($get_speed_stats) {
         phpList::log()->debug('define placeholders start', ['page' => 'preparecampaign']);
     }
     //TODO: can't we precache parts of the urls and the just use string concatenation for better performance
     $unsubscribe_url = Config::get('unsubscribeurl');
     ## https://mantis.phplist.com/view.php?id=16680 -> the "sep" should be & for the text links
     $sep = strpos($unsubscribe_url, '?') === false ? '?' : '&';
     $html['unsubscribe'] = sprintf('<a href="%s%suid=%s">%s</a>', $unsubscribe_url, htmlspecialchars($sep), $subscriber->uniqid, s('Unsubscribe'));
     $text['unsubscribe'] = sprintf('%s%suid=%s', $unsubscribe_url, $sep, $subscriber->uniqid);
     $text['jumpoff'] = sprintf('%s%suid=%s&jo=1', $unsubscribe_url, $sep, $subscriber->uniqid);
     $html['unsubscribeurl'] = sprintf('%s%suid=%s', $unsubscribe_url, htmlspecialchars($sep), $subscriber->uniqid);
     $text['unsubscribeurl'] = sprintf('%s%suid=%s', $unsubscribe_url, $sep, $subscriber->uniqid);
     $text['jumpoffurl'] = sprintf('%s%suid=%s&jo=1', $unsubscribe_url, $sep, $subscriber->uniqid);
     #0013076: Blacklisting posibility for unknown subscribers
     $blacklist_url = Config::get('blacklisturl');
     $sep = strpos($blacklist_url, '?') === false ? '?' : '&';
     $html['blacklist'] = sprintf('<a href="%s%semail=%s">%s</a>', $blacklist_url, htmlspecialchars($sep), $subscriber->getEmailAddress(), s('Unsubscribe'));
     $text['blacklist'] = sprintf('%s%semail=%s', $blacklist_url, $sep, $subscriber->getEmailAddress());
     $html['blacklisturl'] = sprintf('%s%semail=%s', $blacklist_url, htmlspecialchars($sep), $subscriber->getEmailAddress());
     $text['blacklisturl'] = sprintf('%s%semail=%s', $blacklist_url, $sep, $subscriber->getEmailAddress());
     #0013076: Problem found during testing: campaign part must be parsed correctly as well.
     if (sizeof($forwardedby) && isset($forwardedby['email'])) {
         $html['unsubscribe'] = $html['blacklist'];
         $text['unsubscribe'] = $text['blacklist'];
         $html['forwardedby'] = $forwardedby['email'];
         $text['forwardedby'] = $forwardedby['email'];
     }
     $subscribe_url = Config::get('subscribeurl');
     //$sep = strpos($subscribe_url, '?') === false ? '?' : '&';
     $html['subscribe'] = sprintf('<a href="%s">%s</a>', $subscribe_url, s('this link'));
     $text['subscribe'] = sprintf('%s', $subscribe_url);
     $html['subscribeurl'] = sprintf('%s', $subscribe_url);
     $text['subscribeurl'] = sprintf('%s', $subscribe_url);
     $forward_url = Config::get('forwardurl');
     $sep = strpos($forward_url, '?') === false ? '?' : '&';
     $html['forward'] = sprintf('<a href="%s%suid=%s&amp;mid=%d">%s</a>', $forward_url, htmlspecialchars($sep), $subscriber->uniqid, $campaign->id, s('this link'));
     $text['forward'] = sprintf('%s%suid=%s&mid=%d', $forward_url, $sep, $subscriber->uniqid, $campaign->id);
     $html['forwardurl'] = sprintf('%s%suid=%s&amp;mid=%d', $forward_url, htmlspecialchars($sep), $subscriber->uniqid, $campaign->id);
     $text['forwardurl'] = $text['forward'];
     $html['campaignid'] = $text['campaignid'] = sprintf('%d', $campaign->id);
     # make sure there are no newlines, otherwise they get turned into <br/>s
     $html['forwardform'] = sprintf('<form method="get" action="%s" name="forwardform" class="forwardform"><input type="hidden" name="uid" value="%s" /><input type="hidden" name="mid" value="%d" /><input type="hidden" name="p" value="forward" /><input type=text name="email" value="" class="forwardinput" /><input name="Send" type="submit" value="%s" class="forwardsubmit"/></form>', $forward_url, $subscriber->uniqid, $campaign->id, Config::get('strForward'));
     $text['signature'] = "\n\n-- powered by phpList, www.phplist.com --\n\n";
     $preferences_url = Config::get('preferencesurl');
     $sep = strpos($preferences_url, '?') === false ? '?' : '&';
     $html['preferences'] = sprintf('<a href="%s%suid=%s">%s</a>', $preferences_url, htmlspecialchars($sep), $subscriber->uniqid, s('this link'));
     $text['preferences'] = sprintf('%s%suid=%s', $preferences_url, $sep, $subscriber->uniqid);
     $html['preferencesurl'] = sprintf('%s%suid=%s', $preferences_url, htmlspecialchars($sep), $subscriber->uniqid);
     $text['preferencesurl'] = sprintf('%s%suid=%s', $preferences_url, $sep, $subscriber->uniqid);
     $confirmation_url = Config::get('confirmationurl');
     $sep = strpos($confirmation_url, '?') === false ? '?' : '&';
     $html['confirmationurl'] = sprintf('%s%suid=%s', $confirmation_url, htmlspecialchars($sep), $subscriber->uniqid);
     $text['confirmationurl'] = sprintf('%s%suid=%s', $confirmation_url, $sep, $subscriber->uniqid);
     #historical, not sure it's still used
     $html['userid'] = $text['userid'] = $subscriber->uniqid;
     $html['website'] = $text['website'] = Config::get('website');
     # Your website's address, e.g. www.yourdomain.com
     $html['domain'] = $text['domain'] = Config::get('domain');
     # Your domain, e.g. yourdomain.com
     if ($subscriber->uniqid != 'forwarded') {
         $text['footer'] = $cached_campaign->textfooter;
         $html['footer'] = $cached_campaign->htmlfooter;
     } else {
         #0013076: different content when forwarding 'to a friend'
         if (Config::FORWARD_ALTERNATIVE_CONTENT) {
             $text['footer'] = stripslashes($campaign->forwardfooter);
         } else {
             $text['footer'] = Config::get('forwardfooter');
         }
         $html['footer'] = $text['footer'];
     }
     /*
       We request you retain the signature below in your emails including the links.
       This not only gives respect to the large amount of time given freely
       by the developers  but also helps build interest, traffic and use of
       phpList, which is beneficial to it's future development.
     
       You can configure how the credits are added to your pages and emails in your
       config file.
     
       Michiel Dethmers, phpList Ltd 2003 - 2013
     */
     if (!Config::EMAILTEXTCREDITS) {
         $html['signature'] = Config::get('PoweredByImage');
         #'<div align="center" id="signature"><a href="http://www.phplist.com"><img src="powerphplist.png" width=88 height=31 title="Powered by PHPlist" alt="Powered by PHPlist" border="0" /></a></div>';
         # oops, accidentally became spyware, never intended that, so take it out again :-)
         $html['signature'] = preg_replace('/src=".*power-phplist.png"/', 'src="powerphplist.png"', $html['signature']);
     } else {
         $html['signature'] = Config::get('PoweredByText');
     }
     #  $content = $cached->htmlcontent;
     if ($get_speed_stats) {
         phpList::log()->debug('define placeholders end', ['page' => 'preparecampaign']);
     }
     ## Fill text and html versions depending on given versions.
     if ($get_speed_stats) {
         phpList::log()->debug('parse text to html or html to text start', ['page' => 'preparecampaign']);
     }
     if ($cached_campaign->htmlformatted) {
         if (empty($cached_campaign->textcontent)) {
             $textcontent = String::HTML2Text($content);
         } else {
             $textcontent = $cached_campaign->textcontent;
         }
         $htmlcontent = $content;
     } else {
         if (empty($cached_campaign->textcontent)) {
             $textcontent = $content;
         } else {
             $textcontent = $cached_campaign->textcontent;
         }
         $htmlcontent = PrepareCampaign::parseText($content);
     }
     if ($get_speed_stats) {
         phpList::log()->debug('parse text to html or html to text end', ['page' => 'preparecampaign']);
     }
     $defaultstyle = Config::get('html_email_style');
     $adddefaultstyle = 0;
     if ($get_speed_stats) {
         phpList::log()->debug('merge into template start', ['page' => 'preparecampaign']);
     }
     if ($cached_campaign->template) {
         # template used
         $htmlcampaign = str_replace('[CONTENT]', $htmlcontent, $cached_campaign->template);
     } else {
         # no template used
         $htmlcampaign = $htmlcontent;
         $adddefaultstyle = 1;
     }
     $textcampaign = $textcontent;
     if ($get_speed_stats) {
         phpList::log()->debug('merge into template end', ['page' => 'preparecampaign']);
     }
     ## Parse placeholders
     if ($get_speed_stats) {
         phpList::log()->debug('parse placeholders start', ['page' => 'preparecampaign']);
     }
     /*
       var_dump($html);
       var_dump($subscriberdata);
       var_dump($subscriber_att_values);
       exit;
     */
     #print htmlspecialchars($htmlcampaign);exit;
     ### @@@TODO don't use forward and forward form in a forwarded campaign as it'll fail
     if (strpos($htmlcampaign, '[FOOTER]') !== false) {
         $htmlcampaign = str_ireplace('[FOOTER]', $html['footer'], $htmlcampaign);
     } elseif ($html['footer']) {
         $htmlcampaign = PrepareCampaign::addHTMLFooter($htmlcampaign, '<br />' . $html['footer']);
     }
     if (strpos($htmlcampaign, '[SIGNATURE]') !== false) {
         $htmlcampaign = str_ireplace('[SIGNATURE]', $html['signature'], $htmlcampaign);
     } else {
         # BUGFIX 0015303, 2/2
         //    $htmlcampaign .= '<br />'.$html['signature'];
         $htmlcampaign = PrepareCampaign::addHTMLFooter($htmlcampaign, '
            ' . $html['signature']);
     }
     # END BUGFIX 0015303, 2/2
     if (strpos($textcampaign, '[FOOTER]')) {
         $textcampaign = str_ireplace('[FOOTER]', $text['footer'], $textcampaign);
     } else {
         $textcampaign .= "\n\n" . $text['footer'];
     }
     if (strpos($textcampaign, '[SIGNATURE]')) {
         $textcampaign = str_ireplace('[SIGNATURE]', $text['signature'], $textcampaign);
     } else {
         $textcampaign .= "\n" . $text['signature'];
     }
     ### addition to handle [FORWARDURL:Campaign ID:Link Text] (link text optional)
     while (preg_match('/\\[FORWARD:([^\\]]+)\\]/Uxm', $htmlcampaign, $regs)) {
         $newforward = $regs[1];
         $matchtext = $regs[0];
         if (strpos($newforward, ':')) {
             ## using FORWARDURL:campaignid:linktext
             list($forwardcampaign, $forwardtext) = explode(':', $newforward);
         } else {
             $forwardcampaign = sprintf('%d', $newforward);
             $forwardtext = 'this link';
         }
         if (!empty($forwardcampaign)) {
             $sep = strpos($forward_url, '?') === false ? '?' : '&';
             $forwardurl = sprintf('%s%suid=%s&mid=%d', $forward_url, $sep, $subscriber->uniqid, $forwardcampaign);
             $htmlcampaign = str_replace($matchtext, '<a href="' . htmlspecialchars($forwardurl) . '">' . $forwardtext . '</a>', $htmlcampaign);
         } else {
             ## make sure to remove the match, otherwise, it'll be an eternal loop
             $htmlcampaign = str_replace($matchtext, '', $htmlcampaign);
         }
     }
     ## the text campaign has to be parsed seperately, because the line might wrap if the text for the link is long, so the match text is different
     while (preg_match('/\\[FORWARD:([^\\]]+)\\]/Uxm', $textcampaign, $regs)) {
         $newforward = $regs[1];
         $matchtext = $regs[0];
         if (strpos($newforward, ':')) {
             ## using FORWARDURL:campaignid:linktext
             list($forwardcampaign, $forwardtext) = explode(':', $newforward);
         } else {
             $forwardcampaign = sprintf('%d', $newforward);
             $forwardtext = 'this link';
         }
         if (!empty($forwardcampaign)) {
             $sep = strpos($forward_url, '?') === false ? '?' : '&';
             $forwardurl = sprintf('%s%suid=%s&mid=%d', $forward_url, $sep, $subscriber->uniqid, $forwardcampaign);
             $textcampaign = str_replace($matchtext, $forwardtext . ' ' . $forwardurl, $textcampaign);
         } else {
             ## make sure to remove the match, otherwise, it'll be an eternal loop
             $textcampaign = str_replace($matchtext, '', $textcampaign);
         }
     }
     #  $result = Sql_Query(sprintf('select filename,data from %s where template = %d',
     #    Config::getTableName('templateimage'),$cached->templateid));
     if (Config::ALWAYS_ADD_USERTRACK) {
         if (stripos($htmlcampaign, '</body>')) {
             $htmlcampaign = str_replace('</body>', '<img src="' . Config::get('public_scheme') . '://' . Config::get('website') . Config::PAGEROOT . '/ut.php?u=' . $subscriber->uniqid . '&amp;m=' . $campaign->id . '" width="1" height="1" border="0" /></body>', $htmlcampaign);
         } else {
             $htmlcampaign .= '<img src="' . Config::get('public_scheme') . '://' . Config::get('website') . Config::PAGEROOT . '/ut.php?u=' . $subscriber->uniqid . '&amp;m=' . $campaign->id . '" width="1" height="1" border="0" />';
         }
     } else {
         ## can't use str_replace or str_ireplace, because those replace all, and we only want to replace one
         $htmlcampaign = preg_replace('/\\[USERTRACK\\]/i', '<img src="' . Config::get('public_scheme') . '://' . Config::get('website') . Config::PAGEROOT . '/ut.php?u=' . $subscriber->uniqid . '&amp;m=' . $campaign->id . '" width="1" height="1" border="0" />', $htmlcampaign, 1);
     }
     # make sure to only include subscribertrack once, otherwise the stats would go silly
     $htmlcampaign = str_ireplace('[USERTRACK]', '', $htmlcampaign);
     $html['subject'] = $text['subject'] = $cached_campaign->subject;
     $htmlcampaign = PrepareCampaign::parsePlaceHolders($htmlcampaign, $html);
     $textcampaign = PrepareCampaign::parsePlaceHolders($textcampaign, $text);
     if ($get_speed_stats) {
         phpList::log()->debug('parse placeholders end', ['page' => 'preparecampaign']);
     }
     if ($get_speed_stats) {
         phpList::log()->debug('parse subscriberdata start', ['page' => 'preparecampaign']);
     }
     $subscriberdata = array();
     foreach (Subscriber::$DB_ATTRIBUTES as $key) {
         $subscriberdata[$key] = $subscriber->{$key};
     }
     $htmlcampaign = PrepareCampaign::parsePlaceHolders($htmlcampaign, $subscriberdata);
     $textcampaign = PrepareCampaign::parsePlaceHolders($textcampaign, $subscriberdata);
     //CUT 2
     $destinationemail = '';
     if (is_array($subscriber_att_values)) {
         // CUT 3
         $htmlcampaign = PrepareCampaign::parsePlaceHolders($htmlcampaign, $subscriber_att_values);
         $textcampaign = PrepareCampaign::parsePlaceHolders($textcampaign, $subscriber_att_values);
     }
     if ($get_speed_stats) {
         phpList::log()->debug('parse subscriberdata end', ['page' => 'preparecampaign']);
     }
     if (!$destinationemail) {
         $destinationemail = $subscriber->getEmailAddress();
     }
     # this should move into a plugin
     if (strpos($destinationemail, '@') === false && Config::get('expand_unqualifiedemail', false) !== false) {
         $destinationemail .= Config::get('expand_unqualifiedemail');
     }
     if ($get_speed_stats) {
         phpList::log()->debug('pass to plugins for destination email start', ['page' => 'preparecampaign']);
     }
     /*TODO: enable plugins
             foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
             #    print "Checking Destination for ".$plugin->name."<br/>";
                 $destinationemail = $plugin->setFinalDestinationEmail($campaign->id, $subscriber_att_values, $destinationemail);
             }
             if ($getspeedstats) {
                 phpList::log()->debug('pass to plugins for destination email end', ['page' => 'preparecampaign']);;
             }
     
             foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                 $textcampaign = $plugin->parseOutgoingTextCampaign($campaign->id, $textcampaign, $destinationemail, $subscriberdata);
                 $htmlcampaign = $plugin->parseOutgoingHTMLCampaign($campaign->id, $htmlcampaign, $destinationemail, $subscriberdata);
             }*/
     ## click tracking
     # for now we won't click track forwards, as they are not necessarily subscribers, so everything would fail
     if ($get_speed_stats) {
         phpList::log()->debug('click track start', ['page' => 'preparecampaign']);
     }
     if (Config::CLICKTRACK && $subscriber->uniqid != 'forwarded') {
         $urlbase = '';
         # let's leave this for now
         /*
         if (preg_match('/<base href="(.*)"([^>]*)>/Umis',$htmlcampaign,$regs)) {
           $urlbase = $regs[1];
         } else {
           $urlbase = '';
         }
                 #    print "URLBASE: $urlbase<br/>";
         */
         # convert html campaign
         #preg_match_all('/<a href="?([^> "]*)"?([^>]*)>(.*)<\/a>/Umis',$htmlcampaign,$links);
         preg_match_all('/<a (.*)href=["\'](.*)["\']([^>]*)>(.*)<\\/a>/Umis', $htmlcampaign, $links);
         # to process the Yahoo webpage with base href and link like <a href=link> we'd need this one
         #preg_match_all('/<a href=([^> ]*)([^>]*)>(.*)<\/a>/Umis',$htmlcampaign,$links);
         $clicktrack_root = sprintf('%s://%s/lt.php', Config::get('public_scheme'), Config::get('website') . Config::PAGEROOT);
         for ($i = 0; $i < count($links[2]); $i++) {
             $link = Util::cleanUrl($links[2][$i]);
             $link = str_replace('"', '', $link);
             if (preg_match('/\\.$/', $link)) {
                 $link = substr($link, 0, -1);
             }
             //$linkid = 0;
             $linktext = $links[4][$i];
             ## if the link is text containing a "protocol" eg http:// then do not track it, otherwise
             ## it will look like Phishing
             ## it's ok when the link is an image
             $linktext = strip_tags($linktext);
             $looks_like_phishing = stripos($linktext, 'https://') !== false || stripos($linktext, 'http://') !== false;
             if (!$looks_like_phishing && (preg_match('/^http|ftp/', $link) || preg_match('/^http|ftp/', $urlbase)) && stripos($link, 'www.phplist.com') === false && !strpos($link, $clicktrack_root)) {
                 # take off personal uids
                 $url = Util::cleanUrl($link, array('PHPSESSID', 'uid'));
                 #$url = preg_replace('/&uid=[^\s&]+/','',$link);
                 #if (!strpos('http:',$link)) {
                 #   $link = $urlbase . $link;
                 #}
                 $linkid = PrepareCampaign::clickTrackLinkId($campaign->id, $subscriber->id, $url, $link);
                 $masked = "H|{$linkid}|{$campaign->id}|" . $subscriber->id ^ Config::get('XORmask');
                 $masked = base64_encode($masked);
                 ## 15254- the encoding adds one or two extraneous = signs, take them off
                 $masked = preg_replace('/=$/', '', $masked);
                 $masked = preg_replace('/=$/', '', $masked);
                 $masked = urlencode($masked);
                 if (Config::get('CLICKTRACK_LINKMAP', false) === false) {
                     $newlink = sprintf('<a %shref="%s://%s/lt.php?id=%s" %s>%s</a>', $links[1][$i], Config::get('public_scheme'), Config::get('website') . Config::PAGEROOT, $masked, $links[3][$i], $links[4][$i]);
                 } else {
                     $newlink = sprintf('<a %shref="%s://%s%s" %s>%s</a>', $links[1][$i], Config::get('public_scheme'), Config::get('website') . Config::get('CLICKTRACK_LINKMAP'), $masked, $links[3][$i], $links[4][$i]);
                 }
                 $htmlcampaign = str_replace($links[0][$i], $newlink, $htmlcampaign);
             }
         }
         # convert Text campaign
         # first find occurances of our top domain, to avoid replacing them later
         # hmm, this is no point, it's not just *our* topdomain, but any
         if (0) {
             preg_match_all('#(https?://' . Config::get('website') . '/?)\\s+#mis', $textcampaign, $links);
             #preg_match_all('#(https?://[a-z0-9\./\#\?&:@=%\-]+)#ims',$textcampaign,$links);
             #preg_match_all('!(https?:\/\/www\.[a-zA-Z0-9\.\/#~\?+=&%@-_]+)!mis',$textcampaign,$links);
             for ($i = 0; $i < count($links[1]); $i++) {
                 # not entirely sure why strtolower was used, but it seems to break things http://mantis.phplist.com/view.php?id=4406
                 #$link = strtolower(cleanUrl($links[1][$i]));
                 $link = Util::cleanUrl($links[1][$i]);
                 if (preg_match('/\\.$/', $link)) {
                     $link = substr($link, 0, -1);
                 }
                 $linkid = 0;
                 if (preg_match('/^http|ftp/', $link) && stripos($link, 'www.phplist.com') === false && !strpos($link, $clicktrack_root)) {
                     $url = Util::cleanUrl($link, array('PHPSESSID', 'uid'));
                     phpList::DB()->query(sprintf('INSERT IGNORE INTO %s (campaignid, userid, url, forward)
                              VALUES(%d, %d, "%s", "%s")', Config::getTableName('linktrack'), $campaign->id, $subscriber->id, $url, $link));
                     $result = phpList::DB()->query(sprintf('SELECT linkid FROM %s
                             WHERE campaignid = %s
                             AND userid = %d
                             AND forward = "%s"', Config::getTableName('linktrack'), $campaign->id, $subscriber->id, $link));
                     $linkid = $result->fetchColumn(0);
                     $masked = "T|{$linkid}|{$campaign->id}|" . $subscriber->id ^ Config::get('XORmask');
                     $masked = urlencode(base64_encode($masked));
                     $newlink = sprintf('%s://%s/lt.php?id=%s', Config::get('public_scheme'), Config::get('website') . Config::PAGEROOT, $masked);
                     $textcampaign = str_replace($links[0][$i], '<' . $newlink . '>', $textcampaign);
                 }
             }
         }
         #now find the rest
         # @@@ needs to expand to find complete urls like:
         #http://user:password@www.web-site.com:1234/document.php?parameter=something&otherpar=somethingelse#anchor
         # or secure
         #https://user:password@www.website.com:2345/document.php?parameter=something%20&otherpar=somethingelse#anchor
         preg_match_all('#(https?://[^\\s\\>\\}\\,]+)#mis', $textcampaign, $links);
         #preg_match_all('#(https?://[a-z0-9\./\#\?&:@=%\-]+)#ims',$textcampaign,$links);
         #preg_match_all('!(https?:\/\/www\.[a-zA-Z0-9\.\/#~\?+=&%@-_]+)!mis',$textcampaign,$links);
         ## sort the results in reverse order, so that they are replaced correctly
         rsort($links[1]);
         $newlinks = array();
         for ($i = 0; $i < count($links[1]); $i++) {
             $link = Util::cleanUrl($links[1][$i]);
             if (preg_match('/\\.$/', $link)) {
                 $link = substr($link, 0, -1);
             }
             $linkid = 0;
             if (preg_match('/^http|ftp/', $link) && stripos($link, 'www.phplist.com') === false) {
                 # && !strpos($link,$clicktrack_root)) {
                 $url = Util::cleanUrl($link, array('PHPSESSID', 'uid'));
                 $linkid = PrepareCampaign::clickTrackLinkId($campaign->id, $subscriber->id, $url, $link);
                 $masked = "T|{$linkid}|{$campaign->id}|" . $subscriber->id ^ Config::get('XORmask');
                 $masked = base64_encode($masked);
                 ## 15254- the encoding adds one or two extraneous = signs, take them off
                 $masked = preg_replace('/=$/', '', $masked);
                 $masked = preg_replace('/=$/', '', $masked);
                 $masked = urlencode($masked);
                 if (Config::get('CLICKTRACK_LINKMAP', false) === false) {
                     $newlinks[$linkid] = sprintf('%s://%s/lt.php?id=%s', Config::get('public_scheme'), Config::get('website') . Config::PAGEROOT, $masked);
                 } else {
                     $newlinks[$linkid] = sprintf('%s://%s%s', Config::get('public_scheme'), Config::get('website') . Config::get('CLICKTRACK_LINKMAP'), $masked);
                 }
                 #print $links[0][$i] .' -> '.$newlink.'<br/>';
                 $textcampaign = str_replace($links[1][$i], '[%%%' . $linkid . '%%%]', $textcampaign);
             }
         }
         foreach ($newlinks as $linkid => $newlink) {
             $textcampaign = str_replace('[%%%' . $linkid . '%%%]', $newlink, $textcampaign);
         }
     }
     if ($get_speed_stats) {
         phpList::log()->debug('click track end', ['page' => 'preparecampaign']);
     }
     ## if we're not tracking clicks, we should add Google tracking here
     ## otherwise, we can add it when redirecting on the click
     if (!Config::CLICKTRACK && !empty($cached_campaign->google_track)) {
         preg_match_all('/<a (.*)href=["\'](.*)["\']([^>]*)>(.*)<\\/a>/Umis', $htmlcampaign, $links);
         for ($i = 0; $i < count($links[2]); $i++) {
             $link = Util::cleanUrl($links[2][$i]);
             $link = str_replace('"', '', $link);
             ## http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=55578
             $trackingcode = 'utm_source=emailcampaign' . $campaign->id . '&utm_medium=phpList&utm_content=HTMLemail&utm_campaign=' . urlencode($cached_campaign->subject);
             ## take off existing tracking code, if found
             if (strpos($link, 'utm_medium') !== false) {
                 $link = preg_replace('/utm_(\\w+)\\=[^&]+&/U', '', $link);
             }
             if (strpos($link, '?')) {
                 $newurl = $link . '&' . $trackingcode;
             } else {
                 $newurl = $link . '?' . $trackingcode;
             }
             #   print $link. ' '.$newurl.' <br/>';
             $newlink = sprintf('<a %shref="%s" %s>%s</a>', $links[1][$i], $newurl, $links[3][$i], $links[4][$i]);
             $htmlcampaign = str_replace($links[0][$i], $newlink, $htmlcampaign);
         }
         preg_match_all('#(https?://[^\\s\\>\\}\\,]+)#mis', $textcampaign, $links);
         rsort($links[1]);
         $newlinks = array();
         for ($i = 0; $i < count($links[1]); $i++) {
             $link = Util::cleanUrl($links[1][$i]);
             if (preg_match('/\\.$/', $link)) {
                 $link = substr($link, 0, -1);
             }
             if (preg_match('/^http|ftp/', $link) && stripos($link, 'www.phplist.com') !== 0) {
                 # && !strpos($link,$clicktrack_root)) {
                 //$url = Util::cleanUrl($link, array('PHPSESSID', 'uid'));
                 $trackingcode = 'utm_source=emailcampaign' . $campaign->id . '&utm_medium=phpList&utm_content=textemail&utm_campaign=' . urlencode($cached_campaign->subject);
                 ## take off existing tracking code, if found
                 if (strpos($link, 'utm_medium') !== false) {
                     $link = preg_replace('/utm_(\\w+)\\=[^&]+/', '', $link);
                 }
                 if (strpos($link, '?')) {
                     $newurl = $link . '&' . $trackingcode;
                 } else {
                     $newurl = $link . '?' . $trackingcode;
                 }
                 $newlinks[$i] = $newurl;
                 $textcampaign = str_replace($links[1][$i], '[%%%' . $i . '%%%]', $textcampaign);
             }
         }
         foreach ($newlinks as $linkid => $newlink) {
             $textcampaign = str_replace('[%%%' . $linkid . '%%%]', $newlink, $textcampaign);
         }
         unset($newlinks);
     }
     #print htmlspecialchars($htmlcampaign);exit;
     #0011996: forward to friend - personal campaign
     if (Config::FORWARD_PERSONAL_NOTE_SIZE && $subscriber->uniqid == 'forwarded' && !empty($forwardedby['personalNote'])) {
         $htmlcampaign = nl2br($forwardedby['personalNote']) . '<br/>' . $htmlcampaign;
         $textcampaign = $forwardedby['personalNote'] . "\n" . $textcampaign;
     }
     if ($get_speed_stats) {
         phpList::log()->debug('cleanup start', ['page' => 'preparecampaign']);
     }
     ## allow fallback to default value for the ones that do not have a value
     ## delimiter is %% to avoid interfering with markup
     preg_match_all('/\\[.*\\%\\%([^\\]]+)\\]/Ui', $htmlcampaign, $matches);
     for ($i = 0; $i < count($matches[0]); $i++) {
         $htmlcampaign = str_ireplace($matches[0][$i], $matches[1][$i], $htmlcampaign);
     }
     preg_match_all('/\\[.*\\%\\%([^\\]]+)\\]/Ui', $textcampaign, $matches);
     for ($i = 0; $i < count($matches[0]); $i++) {
         $textcampaign = str_ireplace($matches[0][$i], $matches[1][$i], $textcampaign);
     }
     ## remove any remaining placeholders
     ## 16671 - do not do this, as it'll remove conditional CSS and other stuff
     ## that we'd like to keep
     //$htmlcampaign = preg_replace("/\[[A-Z\. ]+\]/i","",$htmlcampaign);
     //$textcampaign = preg_replace("/\[[A-Z\. ]+\]/i","",$textcampaign);
     #print htmlspecialchars($htmlcampaign);exit;
     # check that the HTML campaign as proper <head> </head> and <body> </body> tags
     # some readers fail when it doesn't
     if (!preg_match("#<body.*</body>#ims", $htmlcampaign)) {
         $htmlcampaign = '<body>' . $htmlcampaign . '</body>';
     }
     if (!preg_match("#<head.*</head>#ims", $htmlcampaign)) {
         if (!$adddefaultstyle) {
             $defaultstyle = "";
         }
         $htmlcampaign = '<head>
     <meta content="text/html;charset=' . $cached_campaign->html_charset . '" http-equiv="Content-Type">
     <title></title>' . $defaultstyle . '</head>' . $htmlcampaign;
     }
     if (!preg_match("#<html.*</html>#ims", $htmlcampaign)) {
         $htmlcampaign = '<html>' . $htmlcampaign . '</html>';
     }
     ## remove trailing code after </html>
     $htmlcampaign = preg_replace('#</html>.*#msi', '</html>', $htmlcampaign);
     ## the editor sometimes places <p> and </p> around the URL
     $htmlcampaign = str_ireplace('<p><!DOCTYPE', '<!DOCTYPE', $htmlcampaign);
     $htmlcampaign = str_ireplace('</html></p>', '</html>', $htmlcampaign);
     if ($get_speed_stats) {
         phpList::log()->debug('cleanup end', ['page' => 'preparecampaign']);
     }
     #  $htmlcampaign = compressContent($htmlcampaign);
     # print htmlspecialchars($htmlcampaign);exit;
     if ($get_speed_stats) {
         phpList::log()->debug('build Start ' . Config::get('processqueue_timer')->interval(1), ['page' => 'preparecampaign']);
     }
     # build the email
     $mail = new phpListMailer($campaign->id, $destinationemail);
     if ($forwardedby) {
         $mail->add_timestamp();
     }
     $mail->addCustomHeader("List-Help: <" . $text['preferences'] . ">");
     $mail->addCustomHeader("List-Unsubscribe: <" . $text['jumpoffurl'] . ">");
     $mail->addCustomHeader("List-Subscribe: <" . Config::get("subscribeurl") . ">");
     $mail->addCustomHeader("List-Owner: <mailto:" . Config::get("admin_address") . ">");
     list($dummy, $domaincheck) = explode('@', $destinationemail);
     $text_domains = explode("\n", trim(Config::get("alwayssendtextto")));
     if (in_array($domaincheck, $text_domains)) {
         $subscriber->htmlemail = 0;
         if (Config::VERBOSE) {
             phpList::log()->debug(s('sendingtextonlyto') . " {$domaincheck}", ['page' => 'preparecampaign']);
         }
     }
     /*TODO: enable plugins
       foreach (Config::get('plugins') as $pluginname => $plugin) {
           #$textcampaign = $plugin->parseOutgoingTextCampaign($campaign->id,$textcampaign,$destinationemail, $subscriberdata);
           #$htmlcampaign = $plugin->parseOutgoingHTMLCampaign($campaign->id,$htmlcampaign,$destinationemail, $subscriberdata);
           $plugin_attachments = $plugin->getCampaignAttachment($campaign->id, $mail->Body);
           if (!empty($plugin_attachments[0]['content'])) {
               foreach ($plugins_attachments as $plugin_attachment) {
                   $mail->add_attachment(
                       $plugin_attachment['content'],
                       basename($plugin_attachment['filename']),
                       $plugin_attachment['mimetype']
                   );
               }
           }
       }*/
     # so what do we actually send?
     switch ($cached_campaign->sendformat) {
         case "PDF":
             # send a PDF file to subscribers who want html and text to everyone else
             /*TODO: enable plugins
               foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                   $plugin->processSuccesFailure($campaign->id, 'astext', $subscriberdata);
               }*/
             if ($subscriber->htmlemail) {
                 if (!$is_test_mail) {
                     $campaign->aspdf += 1;
                     $campaign->update();
                 }
                 $pdffile = PrepareCampaign::createPdf($textcampaign);
                 if (is_file($pdffile) && filesize($pdffile)) {
                     $fp = fopen($pdffile, "r");
                     if ($fp) {
                         $contents = fread($fp, filesize($pdffile));
                         fclose($fp);
                         unlink($pdffile);
                         $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
           <html>
           <head>
             <title></title>
           </head>
           <body>
           <embed src="campaign.pdf" width="450" height="450" href="campaign.pdf"></embed>
           </body>
           </html>';
                         #$mail->add_html($html,$textcampaign);
                         #$mail->add_text($textcampaign);
                         $mail->addAttachment($contents, "campaign.pdf", "application/pdf");
                     }
                 }
                 PrepareCampaign::addAttachments($campaign, $mail, "HTML");
             } else {
                 if (!$is_test_mail) {
                     phpList::DB()->query("UPDATE {Config::getTableName('campaign')} SET astext = astext + 1 WHERE id = {$campaign->id}");
                 }
                 $mail->add_text($textcampaign);
                 PrepareCampaign::addAttachments($campaign, $mail, "text");
             }
             break;
         case "text and PDF":
             /*TODO: enable plugins
               foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                   $plugin->processSuccesFailure($campaign->id, 'astext', $subscriberdata);
               }*/
             # send a PDF file to subscribers who want html and text to everyone else
             if ($subscriber->htmlemail) {
                 if (!$is_test_mail) {
                     phpList::DB()->query("UPDATE {Config::getTableName('campaign')} SET astextandpdf = astextandpdf + 1 WHERE id = {$campaign->id}");
                 }
                 $pdffile = PrepareCampaign::createPDF($textcampaign);
                 if (is_file($pdffile) && filesize($pdffile)) {
                     $fp = fopen($pdffile, "r");
                     if ($fp) {
                         $contents = fread($fp, filesize($pdffile));
                         fclose($fp);
                         unlink($pdffile);
                         $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
           <html>
           <head>
             <title></title>
           </head>
           <body>
           <embed src="campaign.pdf" width="450" height="450" href="campaign.pdf"></embed>
           </body>
           </html>';
                         #           $mail->add_html($html,$textcampaign);
                         $mail->add_text($textcampaign);
                         $mail->add_attachment($contents, "campaign.pdf", "application/pdf");
                     }
                 }
                 PrepareCampaign::addAttachments($campaign, $mail, "HTML");
             } else {
                 if (!$is_test_mail) {
                     phpList::DB()->query("UPDATE {Config::getTableName('message')} SET astext = astext + 1 WHERE id = {$campaign->id}");
                 }
                 $mail->add_text($textcampaign);
                 PrepareCampaign::addAttachments($campaign, $mail, "text");
             }
             break;
         case "text":
             # send as text
             /*TODO: enable plugins
               foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                   $plugin->processSuccesFailure($campaign->id, 'astext', $subscriberdata);
               }*/
             if (!$is_test_mail) {
                 phpList::DB()->query("UPDATE {Config::getTableName('message')} SET astext = astext + 1 WHERE id = {$campaign->id}");
             }
             $mail->add_text($textcampaign);
             PrepareCampaign::addAttachments($campaign, $mail, "text");
             break;
         case "both":
         case "text and HTML":
         case "HTML":
         default:
             $handled_by_plugin = 0;
             /*TODO: enable plugins
               if (!empty($GLOBALS['pluginsendformats'][$cached_campaign->sendformat])) {
                   # possibly handled by plugin
                   $pl = $GLOBALS['plugins'][$GLOBALS['pluginsendformats'][$cached_campaign->sendformat]];
                   if (is_object($pl) && method_exists($pl, 'parseFinalCampaign')) {
                       $handled_by_plugin = $pl->parseFinalCampaign(
                           $cached_campaign->sendformat,
                           $htmlcampaign,
                           $textcampaign,
                           $mail,
                           $campaign->id
                       );
                   }
               }
               */
             if (!$handled_by_plugin) {
                 # send one big file to subscribers who want html and text to everyone else
                 if ($subscriber->htmlemail) {
                     if (!$is_test_mail) {
                         phpList::DB()->query("update {Config::getTableName('message')} set astextandhtml = astextandhtml + 1 where id = {$campaign->id}");
                     }
                     /*TODO: enable plugins
                       foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                           $plugin->processSuccesFailure($campaign->id, 'ashtml', $subscriberdata);
                       }*/
                     #  dbg("Adding HTML ".$cached->templateid);
                     if (Config::WORDWRAP_HTML) {
                         ## wrap it: http://mantis.phplist.com/view.php?id=15528
                         ## some reports say, this fixes things and others say it breaks things https://mantis.phplist.com/view.php?id=15617
                         ## so for now, only switch on if requested.
                         ## it probably has to do with the MTA used
                         $htmlcampaign = wordwrap($htmlcampaign, Config::WORDWRAP_HTML, "\r\n");
                     }
                     $mail->add_html($htmlcampaign, $textcampaign, $cached_campaign->templateid);
                     PrepareCampaign::addAttachments($campaign, $mail, "HTML");
                 } else {
                     if (!$is_test_mail) {
                         phpList::DB()->query("UPDATE {Config::getTableName('message')} SET astext = astext + 1 WHERE id = {$campaign->id}");
                     }
                     /*TODO: enable plugins
                       foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                           $plugin->processSuccesFailure($campaign->id, 'astext', $subscriberdata);
                       }*/
                     $mail->add_text($textcampaign);
                     #$mail->setText($textcampaign);
                     #$mail->Encoding = TEXTEMAIL_ENCODING;
                     PrepareCampaign::addAttachments($campaign, $mail, "text");
                 }
             }
             break;
     }
     #print htmlspecialchars($htmlcampaign);exit;
     if (!Config::TEST) {
         if ($subscriber->uniqid != 'forwarded' || !sizeof($forwardedby)) {
             $fromname = $cached_campaign->fromname;
             $fromemail = $cached_campaign->fromemail;
             $subject = $cached_campaign->subject;
         } else {
             $fromname = '';
             $fromemail = $forwardedby['email'];
             $subject = s('Fwd') . ': ' . $cached_campaign->subject;
         }
         if (!empty($cached_campaign->replytoemail)) {
             $mail->AddReplyTo($cached_campaign->replytoemail, $cached_campaign->replytoname);
         }
         if ($get_speed_stats) {
             phpList::log()->debug('build End ' . Timer::get('PQT')->interval(1), ['page' => 'preparecampaign']);
         }
         if ($get_speed_stats) {
             phpList::log()->debug('send Start ' . Timer::get('PQT')->interval(1), ['page' => 'preparecampaign']);
         }
         if (DEBUG) {
             $destinationemail = PHPLIST_DEVELOPER_EMAIL;
         }
         if (!$mail->compatSend('', $destinationemail, $fromname, $fromemail, $subject)) {
             #if (!$mail->send(array($destinationemail),'spool')) {
             /*TODO: enable plugins
               foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                   $plugin->processSendFailed($campaign->id, $subscriberdata, $isTestMail);
               }*/
             phpList::log()->debug(sprintf(s('Error sending campaign %d (%d/%d) to %s (%s) '), $campaign->id, 0, 0, $subscriber->getEmailAddress(), $destinationemail), ['page' => 'preparecampaign']);
             return false;
         } else {
             ## only save the estimated size of the campaign when sending a test campaign
             if ($get_speed_stats) {
                 phpList::log()->debug('send End ' . Timer::get('PQT')->interval(1), ['page' => 'preparecampaign']);
             }
             //TODO: find solution for send process id global var which currently is definded in CampaignQueue
             if (!isset($GLOBALS['send_process_id'])) {
                 if (!empty($mail->mailsize)) {
                     $name = $subscriber->htmlemail ? 'htmlsize' : 'textsize';
                     $campaign->setDataItem($name, $mail->mailsize);
                 }
             }
             $sqlCount = phpList::DB()->getQueryCount() - $sql_count_start;
             if ($get_speed_stats) {
                 phpList::log()->debug('It took ' . $sqlCount . '  queries to send this campaign', ['page' => 'preparecampaign']);
             }
             /*TODO:enable plugins
               foreach ($GLOBALS['plugins'] as $pluginname => $plugin) {
                   $plugin->processSendSuccess($campaign->id, $subscriberdata, $isTestMail);
               }*/
             #   logEvent("Sent campaign $campaign->id to $subscriber->getEmailAddress() ($destinationemail)");
             return true;
         }
     }
     return false;
 }