2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
38 /*********************************************************************************
41 ********************************************************************************/
42 //find all mailboxes of type bounce.
45 * Retrieve the attached error report for a bounced email if it exists.
50 function retrieveErrorReportAttachment($email)
53 $query = "SELECT description FROM notes WHERE file_mime_type = 'messsage/rfc822' AND parent_type='Emails' AND parent_id = '".$email->id."' AND deleted=0";
54 $rs = $GLOBALS['db']->query($query);
55 while ($row = $GLOBALS['db']->fetchByAssoc($rs))
56 $contents .= $row['description'];
62 * Create a bounced log campaign entry
66 * @param string $email_description
69 function createBouncedCampaignLogEntry($row,$email, $email_description)
71 $GLOBALS['log']->debug("Creating bounced email campaign log");
72 $bounce = new CampaignLog();
73 $bounce->campaign_id=$row['campaign_id'];
74 $bounce->target_tracker_key=$row['target_tracker_key'];
75 $bounce->target_id= $row['target_id'];
76 $bounce->target_type=$row['target_type'];
77 $bounce->list_id=$row['list_id'];
78 $bounce->marketing_id=$row['marketing_id'];
80 $bounce->activity_date=$email->date_created;
81 $bounce->related_type='Emails';
82 $bounce->related_id= $email->id;
84 //do we have the phrase permanent error in the email body.
85 if (preg_match('/permanent[ ]*error/',$email_description))
87 $bounce->activity_type='invalid email';
88 markEmailAddressInvalid($email);
91 $bounce->activity_type='send error';
93 $return_id=$bounce->save();
98 * Given an email address, mark it as invalid.
100 * @param $email_address
102 function markEmailAddressInvalid($email_address)
104 if(empty($email_address))
106 $sea = new SugarEmailAddress();
107 $rs = $sea->retrieve_by_string_fields( array('email_address_caps' => trim(strtoupper($email_address))) );
109 $sea->AddUpdateEmailAddress($email_address, 1,0);
113 * Get the existing campaign log entry by tracker key.
115 * @param string Target Key
116 * @return array Campaign Log Row
118 function getExistingCampaignLogEntry($identifier)
121 $targeted = new CampaignLog();
122 $where="campaign_log.activity_type='targeted' and campaign_log.target_tracker_key='{$identifier}'";
123 $query=$targeted->create_new_list_query('',$where);
124 $result=$targeted->db->query($query);
125 $row=$targeted->db->fetchByAssoc($result);
131 * Scan the bounced email searching for a valid target identifier.
133 * @param string Email Description
134 * @return array Results including matches and identifier
136 function checkBouncedEmailForIdentifier($email_description)
139 $identifiers = array();
141 //Check if the identifier is present in the header.
142 if(preg_match('/X-CampTrackID: [a-z0-9\-]*/',$email_description,$matches))
144 $identifiers = preg_split('/X-CampTrackID: /',$matches[0],-1,PREG_SPLIT_NO_EMPTY);
146 $GLOBALS['log']->debug("Found campaign identifier in header of email");
148 else if( preg_match('/index.php\?entryPoint=removeme&identifier=[a-z0-9\-]*/',$email_description, $matches) )
150 $identifiers = preg_split('/index.php\?entryPoint=removeme&identifier=/',$matches[0],-1,PREG_SPLIT_NO_EMPTY);
152 $GLOBALS['log']->debug("Found campaign identifier in body of email");
155 return array('found' => $found, 'matches' => $matches, 'identifiers' => $identifiers);
158 function campaign_process_bounced_emails(&$email, &$email_header)
160 global $sugar_config;
161 $emailFromAddress = $email_header->fromaddress;
162 $email_description = $email->raw_source;
164 //if raw_source is empty, try using the description instead
165 if (empty($email_description)){
166 $email_description = $email->description;
169 $email_description .= retrieveErrorReportAttachment($email);
171 if (preg_match('/MAILER-DAEMON|POSTMASTER/i',$emailFromAddress))
173 $email_description=quoted_printable_decode($email_description);
176 //do we have the identifier tag in the email?
177 $identifierScanResults = checkBouncedEmailForIdentifier($email_description);
179 if ( $identifierScanResults['found'] )
181 $matches = $identifierScanResults['matches'];
182 $identifiers = $identifierScanResults['identifiers'];
184 if (!empty($identifiers))
186 //array should have only one element in it.
187 $identifier = trim($identifiers[0]);
188 $row = getExistingCampaignLogEntry($identifier);
193 //do not create another campaign_log record is we already have an
194 //invalid email or send error entry for this tracker key.
195 $query_log = "select * from campaign_log where target_tracker_key='{$row['target_tracker_key']}'";
196 $query_log .=" and (activity_type='invalid email' or activity_type='send error')";
197 $targeted = new CampaignLog();
198 $result_log=$targeted->db->query($query_log);
199 $row_log=$targeted->db->fetchByAssoc($result_log);
203 $return_id = createBouncedCampaignLogEntry($row, $email, $email_description);
208 $GLOBALS['log']->debug("Warning: campaign log entry already exists for identifier $identifier");
214 $GLOBALS['log']->info("Warning: skipping bounced email with this tracker_key(identifier) in the message body: ".$identifier);
220 $GLOBALS['log']->info("Warning: Empty identifier for campaign log.");
226 $GLOBALS['log']->info("Warning: skipping bounced email because it does not have the removeme link.");
232 $GLOBALS['log']->info("Warning: skipping bounced email because the sender is not MAILER-DAEMON.");