Skip to content

Commit b3a9ba3

Browse files
committed
Fixed email tracking code not injected to actual message sent to user
1 parent f85ea88 commit b3a9ba3

10 files changed

Lines changed: 136 additions & 127 deletions

htsrv/anon_unsubscribe.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
.' '.T_('For security reasons the link is only valid for your current session (by means of your session cookie).')
6060
."\n\n"
6161
.T_('If it was not you that requested this, simply ignore this email.');
62+
$message = add_email_tracking( $message, '$mail_log_ID$', '$email_key$' );
6263

6364
if( send_mail( $anon_email, NULL, T_('Confirm opt-out for emails through message form'), $message ) )
6465
{

inc/_core/_misc.funcs.php

Lines changed: 69 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3756,17 +3756,6 @@ function send_mail( $to, $to_name, $subject, $message, $from = NULL, $from_name
37563756

37573757
global $debug, $app_name, $app_version, $current_locale, $current_charset, $evo_charset, $locales, $Debuglog, $Settings, $demo_mode, $mail_log_insert_ID;
37583758

3759-
$message_data = $message;
3760-
if( is_array( $message_data ) && isset( $message_data['full'] ) )
3761-
{ // If content is multipart
3762-
$message = $message_data['full'];
3763-
}
3764-
elseif( is_string( $message_data ) )
3765-
{ // Convert $message_data to array
3766-
$message_data = array( 'full' => $message );
3767-
}
3768-
3769-
37703759
// Memorize email address
37713760
$to_email_address = $to;
37723761

@@ -3792,12 +3781,6 @@ function send_mail( $to, $to_name, $subject, $message, $from = NULL, $from_name
37923781
$from_name = user_get_notification_sender( $user_ID, 'name' );
37933782
}
37943783

3795-
// Pass these data for SMTP mailer
3796-
$message_data['to_email'] = $to;
3797-
$message_data['to_name'] = empty( $to_name ) ? NULL : $to_name;
3798-
$message_data['from_email'] = $from;
3799-
$message_data['from_name'] = empty( $from_name ) ? NULL : $from_name;
3800-
38013784
$return_path = $Settings->get( 'notification_return_path' );
38023785

38033786
// Add real name into $from...
@@ -3847,6 +3830,27 @@ function send_mail( $to, $to_name, $subject, $message, $from = NULL, $from_name
38473830
// COMPACT HEADERS:
38483831
$headerstring = get_mail_headers( $headers, $NL );
38493832

3833+
// Create initial email log with empty message
3834+
$email_key = generate_random_key();
3835+
mail_log( $user_ID, $to_email_address, $clear_subject, NULL, $headerstring, 'ready_to_send', $email_key );
3836+
3837+
$message = str_replace( array( '$email_key$', '$mail_log_ID$' ), array( $email_key, $mail_log_insert_ID ), $message );
3838+
$message_data = $message;
3839+
if( is_array( $message_data ) && isset( $message_data['full'] ) )
3840+
{ // If content is multipart
3841+
$message = $message_data['full'];
3842+
}
3843+
elseif( is_string( $message_data ) )
3844+
{ // Convert $message_data to array
3845+
$message_data = array( 'full' => $message );
3846+
}
3847+
3848+
// Pass these data for SMTP mailer
3849+
$message_data['to_email'] = $to;
3850+
$message_data['to_name'] = empty( $to_name ) ? NULL : $to_name;
3851+
$message_data['from_email'] = $from;
3852+
$message_data['from_name'] = empty( $from_name ) ? NULL : $from_name;
3853+
38503854
// Set an additional parameter for the return path:
38513855
switch( $Settings->get( 'sendmail_params' ) )
38523856
{
@@ -3872,67 +3876,54 @@ function send_mail( $to, $to_name, $subject, $message, $from = NULL, $from_name
38723876
$additional_parameters = '';
38733877
}
38743878

3875-
// Create initial email log with empty message:
3876-
$email_key = generate_random_key();
3877-
mail_log( $user_ID, $to_email_address, $clear_subject, NULL, $headerstring, 'ready_to_send', $email_key );
3879+
// Remove markers from message that will be sent to actual email
3880+
$message_data = str_replace( array( '$email_key_start$', '$email_key_end$', '$secret_content_start$', '$secret_content_end$' ), '', $message_data );
38783881

3879-
if( ! empty( $mail_log_insert_ID ) )
3880-
{
3881-
// fp>erwin : not super urgent but I don't like it that you add tracking twice (waste of resources). Is it possible to optimize by changing order of code?
3882-
$message = add_email_tracking( $message, $mail_log_insert_ID, $email_key );
3883-
$message_data['full'] = add_email_tracking( $message_data['full'], $mail_log_insert_ID, $email_key );
3884-
$message_data = str_replace( array( '$email_key_start$', '$email_key_end$', '$secret_content_start$', '$secret_content_end$' ), '', $message_data );
3882+
if( mail_is_blocked( $to_email_address ) )
3883+
{ // Check if the email address is blocked
3884+
$Debuglog->add( 'Sending mail to «'.htmlspecialchars( $to_email_address ).'» FAILED, because this email marked with spam or permanent errors.', 'error' );
38853885

3886-
if( mail_is_blocked( $to_email_address ) )
3887-
{ // Check if the email address is blocked
3888-
$Debuglog->add( 'Sending mail to «'.htmlspecialchars( $to_email_address ).'» FAILED, because this email marked with spam or permanent errors.', 'error' );
3886+
//mail_log( $user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'blocked', $email_key );
3887+
update_mail_log( $mail_log_insert_ID, 'blocked', $message );
38893888

3890-
//mail_log( $user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'blocked', $email_key );
3891-
update_mail_log( $mail_log_insert_ID, 'blocked', $message );
3889+
return false;
3890+
}
38923891

3893-
return false;
3894-
}
3892+
if( $email_send_simulate_only )
3893+
{ // The email sending is turned on simulation mode, Don't send a real message:
3894+
$send_mail_result = true;
3895+
}
3896+
else
3897+
{ // Send email message on real mode:
3898+
$send_mail_result = evo_mail( $to, $subject, $message_data, $headers, $additional_parameters );
3899+
}
3900+
3901+
if( ! $send_mail_result )
3902+
{ // The message has not been sent successfully
3903+
if( $debug > 1 )
3904+
{ // We agree to die for debugging...
3905+
//mail_log( $user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'error', $email_key );
3906+
update_mail_log( $mail_log_insert_ID, 'error', $message );
38953907

3896-
if( $email_send_simulate_only )
3897-
{ // The email sending is in simulation mode, Don't send a real message:
3898-
$send_mail_result = true;
3908+
debug_die( 'Sending mail from «'.htmlspecialchars($from).'» to «'.htmlspecialchars($to).'», Subject «'.htmlspecialchars($subject).'» FAILED.' );
38993909
}
39003910
else
3901-
{ // Send email message on real mode:
3902-
$send_mail_result = evo_mail( $to, $subject, $message_data, $headers, $additional_parameters );
3903-
}
3911+
{ // Soft debugging only....
3912+
$Debuglog->add( 'Sending mail from «'.htmlspecialchars($from).'» to «'.htmlspecialchars($to).'», Subject «'.htmlspecialchars($subject).'» FAILED.', 'error' );
39043913

3905-
if( ! $send_mail_result )
3906-
{ // The message has not been sent successfully
3907-
if( $debug > 1 )
3908-
{ // We agree to die for debugging...
3909-
//mail_log( $user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'error', $email_key );
3910-
update_mail_log( $mail_log_insert_ID, 'error', $message );
3914+
//mail_log( $user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'error', $email_key );
3915+
update_mail_log( $mail_log_insert_ID, 'error', $message );
39113916

3912-
debug_die( 'Sending mail from «'.htmlspecialchars($from).'» to «'.htmlspecialchars($to).'», Subject «'.htmlspecialchars($subject).'» FAILED.' );
3913-
}
3914-
else
3915-
{ // Soft debugging only....
3916-
$Debuglog->add( 'Sending mail from «'.htmlspecialchars($from).'» to «'.htmlspecialchars($to).'», Subject «'.htmlspecialchars($subject).'» FAILED.', 'error' );
3917-
3918-
//mail_log( $user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'error', $email_key );
3919-
update_mail_log( $mail_log_insert_ID, 'error', $message );
3920-
3921-
return false;
3922-
}
3917+
return false;
39233918
}
3919+
}
39243920

3925-
$Debuglog->add( 'Sent mail from «'.htmlspecialchars($from).'» to «'.htmlspecialchars($to).'», Subject «'.htmlspecialchars($subject).'».' );
3921+
$Debuglog->add( 'Sent mail from «'.htmlspecialchars($from).'» to «'.htmlspecialchars($to).'», Subject «'.htmlspecialchars($subject).'».' );
39263922

3927-
//mail_log( $user_ID, $to_email_address, $clear_subject, $message, $headerstring, ( $email_send_simulate_only ? 'simulated' : 'ok' ), $email_key );
3928-
update_mail_log( $mail_log_insert_ID, ( $email_send_simulate_only ? 'simulated' : 'ok' ), $message );
3923+
//mail_log( $user_ID, $to_email_address, $clear_subject, $message, $headerstring, ( $email_send_simulate_only ? 'simulated' : 'ok' ), $email_key );
3924+
update_mail_log( $mail_log_insert_ID, ( $email_send_simulate_only ? 'simulated' : 'ok' ), $message );
39293925

3930-
return true;
3931-
}
3932-
else
3933-
{
3934-
return false;
3935-
}
3926+
return true;
39363927
}
39373928

39383929

@@ -4145,8 +4136,6 @@ function mail_template( $template_name, $format = 'auto', $params = array(), $Us
41454136
$template_message .= $template_headers[ $format ]."\n\n";
41464137
}
41474138

4148-
$template_message .= '$message_body_'.$format.'_start$';
4149-
41504139
// Get mail template
41514140
ob_start();
41524141
emailskin_include( $template_name.$ext, $params );
@@ -4192,10 +4181,9 @@ function mail_template( $template_name, $format = 'auto', $params = array(), $Us
41924181
$formated_message = str_replace( '$name$', $params['anonymous_recipient_name'], $formated_message );
41934182
}
41944183

4184+
$formated_message = add_email_tracking( $formated_message, '$mail_log_ID$', '$email_key$', $format );
41954185
$template_message .= $formated_message;
41964186

4197-
$template_message .= '$message_body_'.$format.'_end$';
4198-
41994187
if( isset( $template_contents ) )
42004188
{ // Multipart content
42014189
$template_contents[ $format ] = $formated_message;
@@ -8546,4 +8534,16 @@ function php_to_jquery_date_format( $php_format )
85468534

85478535
return $js_format;
85488536
}
8537+
8538+
8539+
/**
8540+
* Check if given string is HTML
8541+
*
8542+
* @param string String to check if HTML
8543+
* @return boolean True if string is HTML
8544+
*/
8545+
function is_html( $string )
8546+
{
8547+
return $string != strip_tags( $string ) ? true : false;
8548+
}
85498549
?>

inc/email_campaigns/campaigns.ctrl.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@
251251

252252
case 'test':
253253
// Send test email
254+
global $track_email_click_html, $track_email_click_plain_text;
255+
256+
$track_email_click_html = param( 'track_test_email_click_html', 'boolean', 0 );
257+
$track_email_click_plain_text = param( 'track_test_email_click_plain_text', 'boolean', 0 );
254258

255259
// Check that this action request is not a CSRF hacked request:
256260
$Session->assert_received_crumb( 'campaign' );

inc/email_campaigns/views/_campaigns_send.form.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@
4949
echo '<div class="floatleft" style="width:50%">';
5050
echo '<p><b>'.T_('HTML message').':</b></p>';
5151
$html_mail_template = mail_template( 'newsletter', 'html', array( 'message_html' => $edited_EmailCampaign->get( 'email_html' ), 'include_greeting' => false ), $current_User );
52+
$html_mail_template = str_replace( array( '$email_key$', '$mail_log_ID$', '$email_key_start$', '$email_key_end$' ), array( '***email-key***', '', '', '' ), $html_mail_template );
5253
// Clear all html tags that may break styles of main html page:
5354
$html_mail_template = preg_replace( '#</?(html|head|meta|body)[^>]*>#i', '', $html_mail_template );
54-
$html_mail_template = str_replace( array( '$message_body_html_start$', '$message_body_html_end$' ), '', $html_mail_template );
5555
echo '<div style="overflow:auto">'.$html_mail_template.'</div>';
5656
echo '</div>';
5757

5858
echo '<div class="floatright" style="width:49%">';
5959
echo '<p><b>'.T_('Plain-text message').':</b></p>';
6060
$text_mail_template = mail_template( 'newsletter', 'text', array( 'message_text' => $edited_EmailCampaign->get( 'email_plaintext' ), 'include_greeting' => false ), $current_User );
61-
$text_mail_template = str_replace( array( '$message_body_text_start$', '$message_body_text_end$' ), '', $text_mail_template );
61+
$text_mail_template = str_replace( array( '$email_key$', '$mail_log_ID$', '$email_key_start$', '$email_key_end$' ), array( '***email-key***', '', '', '' ), $text_mail_template );
6262
echo '<div style="font-family:monospace;overflow:auto">'.nl2br( $text_mail_template ).'</div>';
6363
echo '</div>';
6464
echo '</div>';
@@ -85,7 +85,7 @@
8585
$Form->checklist( array(
8686
array( 'track_email_click_html', 1, T_('track clickthroughs in HTML version'), 1 ),
8787
array( 'track_email_click_plain_text', 1, T_('track clickthroughs in plain text version'), 1 )
88-
), 'track_email', T_('Track email') );
88+
), 'track_email', T_('Track email opens') );
8989
if( $Settings->get( 'email_campaign_send_mode' ) == 'cron' )
9090
{ // Asynchronous sending mode:
9191
if( $edited_EmailCampaign->get_Cronjob() )
@@ -114,6 +114,10 @@
114114
{ // User must has a permission to edit emails
115115

116116
$Form->begin_fieldset( T_('Send test email').get_manual_link( 'campaign-send-test-panel' ) );
117+
$Form->checklist( array(
118+
array( 'track_test_email_click_html', 1, T_('track clickthroughs in HTML version'), 1 ),
119+
array( 'track_test_email_click_plain_text', 1, T_('track clickthroughs in plain text version'), 1 )
120+
), 'track_test_email', T_('Track email opens') );
117121
$Form->text_input( 'test_email_address', $Session->get( 'test_campaign_email' ), 30, T_('Email address'), T_('Fill your email address and press button "Send test email" if you want to test this list'), array( 'maxlength' => 255 ) );
118122
$test_button = array( array( 'name' => 'actionArray[test]', 'value' => T_('Send test email'), 'class' => 'SaveButton btn btn-primary' ) );
119123
$Form->buttons_input( $test_button );

inc/tools/model/_email.funcs.php

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ function mail_log( $user_ID, $to, $subject, $message, $headers, $result, $email_
305305
* @var integer|NULL This global var stores ID of the last inserted mail log
306306
*/
307307
// TODO fp>erwin: why do we need a global below? Why don't we just return $DB->insert_id; ?
308+
// erwin>fp: I agree that we should just return $DB->insert_id but this is how it was done before and I never changed it for fear of breaking something somewhere.
308309
global $mail_log_insert_ID;
309310
$mail_log_insert_ID = NULL;
310311

@@ -1195,7 +1196,7 @@ function php_email_sending_test()
11951196
* @param string Email key
11961197
* @return string Message with email tracking
11971198
*/
1198-
function add_email_tracking( $message, $email_ID, $email_key )
1199+
function add_email_tracking( $message, $email_ID, $email_key, $content_type = 'auto' )
11991200
{
12001201
global $track_email_click_html, $track_email_click_plain_text;
12011202

@@ -1211,42 +1212,48 @@ function add_email_tracking( $message, $email_ID, $email_key )
12111212
debug_die( 'No email key specified.' );
12121213
}
12131214

1214-
// Plain text content
1215-
if( ! isset( $track_email_click_plain_text ) || $track_email_click_plain_text == 1 )
1215+
if( $content_type == 'auto' )
12161216
{
1217-
preg_match( '/(?<=\$message_body_text_start\$)(?s)(.*)(?=\$message_body_text_end\$)/', $message, $matches );
1218-
$plain_text_content = $matches[0];
1219-
1220-
// Add link click tracking
1221-
//$re = '/(<a\b.+\bhref=")([^"]*)(")/';
1222-
$re = '#(\$secret_content_start\$|\b)\s*(https?://[^,\s()<>]+(?:\([\w\d]+\)|(?:[^,[:punct:]\s]?|/)))(\$secret_content_end\$)?#';
1223-
$callback = new EmailTrackingHelper( 'link', $email_ID, $email_key, 'plain_text' );
1224-
$plain_text_content = preg_replace_callback( $re, array( $callback, 'callback' ), $plain_text_content );
1225-
1226-
$message = preg_replace( '/(?<=\$message_body_text_start\$)(?s)(.*)(?=\$message_body_text_end\$)/', $plain_text_content, $message );
1217+
if( is_html( $message ) )
1218+
{
1219+
$content_type = 'html';
1220+
}
1221+
else
1222+
{
1223+
$content_type = 'text';
1224+
}
12271225
}
12281226

1229-
// HTML content
1230-
preg_match( '/(?<=\$message_body_html_start\$)(?s)(.*)(?=\$message_body_html_end\$)/', $message, $matches );
1231-
$html_content = $matches[0];
1232-
1233-
// Add email open tracking to first image
1234-
$re = '/(<img\b.+\bsrc=")([^"]*)(")/';
1235-
$callback = new EmailTrackingHelper( 'img', $email_ID, $email_key );
1236-
$html_content = preg_replace_callback( $re, array( $callback, 'callback' ), $html_content, 1 );
1237-
1238-
if( ! isset( $track_email_click_html ) || $track_email_click_html == 1 )
1227+
switch( $content_type )
12391228
{
1240-
// Add link click tracking
1241-
$re = '/(<a\b.+\bhref=")([^"]*)(")/';
1242-
$callback = new EmailTrackingHelper( 'link', $email_ID, $email_key );
1243-
$html_content = preg_replace_callback( $re, array( $callback, 'callback' ), $html_content );
1244-
}
1229+
case 'text':
1230+
// Add link click tracking
1231+
if( ! isset( $track_email_click_plain_text ) || $track_email_click_plain_text == 1 )
1232+
{
1233+
$re = '#(\$secret_content_start\$|\b)\s*(https?://[^,\s()<>]+(?:\([\w\d]+\)|(?:[^,[:punct:]\s]?|/)))(\$secret_content_end\$)?#';
1234+
$callback = new EmailTrackingHelper( 'link', $email_ID, $email_key, 'plain_text' );
1235+
$message = preg_replace_callback( $re, array( $callback, 'callback' ), $message );
1236+
}
1237+
return $message;
1238+
1239+
case 'html':
1240+
// Add email open tracking to first image
1241+
$re = '/(<img\b.+\bsrc=")([^"]*)(")/';
1242+
$callback = new EmailTrackingHelper( 'img', $email_ID, $email_key );
1243+
$message = preg_replace_callback( $re, array( $callback, 'callback' ), $message, 1 );
12451244

1246-
$message = preg_replace( '/(?<=\$message_body_html_start\$)(?s)(.*)(?=\$message_body_html_end\$)/', $html_content, $message );
1245+
if( ! isset( $track_email_click_html ) || $track_email_click_html == 1 )
1246+
{
1247+
// Add link click tracking
1248+
$re = '/(<a\b.+\bhref=")([^"]*)(")/';
1249+
$callback = new EmailTrackingHelper( 'link', $email_ID, $email_key );
1250+
$message = preg_replace_callback( $re, array( $callback, 'callback' ), $message );
1251+
}
1252+
return $message;
12471253

1248-
$message = str_replace( array( '$message_body_text_start$', '$message_body_text_end$', '$message_body_html_start$', '$message_body_html_end$' ), '', $message );
1254+
default:
1255+
debug_die( 'Invalid content type' );
1256+
}
12491257

1250-
return $message;
12511258
}
12521259
?>

inc/tools/views/_email_sent_details.view.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
{ // Display Plain Text content
5454
$plain_text_content = preg_replace( '~\$secret_content_start\$.*?\$secret_content_end\$~', '***secret-content-removed***', $mail_contents['text']['content'] );
5555
$plain_text_content = preg_replace( '~\$email_key_start\$(.*?)\$email_key_end\$~', '***prevent-tracking-through-log***$1', $plain_text_content );
56-
$plain_text_content = str_replace( array( '$message_body_text_start$', '$message_body_text_end$' ), '', $plain_text_content );
5756

5857
$Form->info( T_('Text content'), $mail_contents['text']['type']
5958
.'<pre class="email_log_scroll"><span>'.htmlspecialchars( $plain_text_content ).'</span></pre>' );
@@ -64,7 +63,6 @@
6463

6564
$html_content = preg_replace( '~\$secret_content_start\$.*?\$secret_content_end\$~', '***secret-content-removed***', $mail_contents['html']['content'] );
6665
$html_content = preg_replace( '~\$email_key_start\$(.*?)\$email_key_end\$~', '***prevent-tracking-through-log***$1', $html_content );
67-
$html_content = str_replace( array( '$message_body_html_start$', '$message_body_html_end$' ), '', $html_content );
6866

6967
if( ! empty( $mail_contents['html']['head_style'] ) )
7068
{ // Print out all styles of email message
@@ -78,7 +76,6 @@
7876
}
7977
$emlog_message = preg_replace( '~\$secret_content_start\$.*?\$secret_content_end\$~', '***secret-content-removed***', $MailLog->emlog_message );
8078
$emlog_message = preg_replace( '~\$email_key_start\$(.*?)\$email_key_end\$~', '***prevent-tracking-through-log***$1', $emlog_message );
81-
$emlog_message = str_replace( array( '$message_body_text_start$', '$message_body_text_end$', '$message_body_html_start$', '$message_body_html_end$' ), '', $emlog_message );
8279
$Form->info( T_('Raw email source'), '<pre class="email_log_scroll"><span>'.htmlspecialchars( $emlog_message ).'</span></pre>' );
8380

8481
$Form->end_form();

0 commit comments

Comments
 (0)