6

I have been tracking emails for years using a "beacon" image and for those clients that allow the images to download it has worked great to track how many people have opened the email.

I came across the service "DidTheyReadIt" which shows how long the client actually read the email, I tested it with their free service and it is actually pretty close to the times I opened the email.

I am very curious in how they achieve the ability to track this, I am certain that whatever solution is chosen it will put a lot of load on the server / database and that many of the community will reply with "Stop, No and Dont" but I do want to investigate this and try it out, even if its just enough for me to run a test on the server and say "hell no".

I did some googling and found this article which has a basic solution http://www.re-cycledair.com/tracking-email-open-time-with-php

I made a test using sleep() within the beacon image page:

<?php 

set_time_limit(300); //1000 seconds
ignore_user_abort(false);  

$hostname_api = "*";
$database_api = "*";
$username_api = "*";
$password_api = "*";

$api = mysql_pconnect($hostname_api, $username_api, $password_api) or     trigger_error(mysql_error(),E_USER_ERROR);
mysql_select_db($database_api, $api);

$fileName = "logo.png";

$InsertSQL = "INSERT INTO tracker (FileName,Time_Start,Time_End) VALUES   ('$fileName',Now(),Now()+1)";
mysql_select_db($database_api, $api);
$Result1 = mysql_query($InsertSQL, $api) or die(mysql_error());
$TRID = mysql_insert_id();

//Open the file, and send to user.

$fp = fopen($fileName, "r");
header("Content-type: image/png");
header('Content-Length: ' . filesize($fileName));
readfile($fileName);

set_time_limit(60);
$start = time();

for ($i = 0; $i < 59; ++$i) {

// Update Read Time

$UpdateSQL = "UPDATE tracker SET Time_End = Now()  WHERE TRID = '$TRID'";
mysql_select_db($database_api, $api);
$Result1 = mysql_query($UpdateSQL, $api) or die(mysql_error());

time_sleep_until($start + $i + 1);
}

?>

The problem with the code above (other than updating the database every second) is that once the script runs it continues to run even if the user disconnects (or moves to another email in this case).

I added "ignore_user_abort(false);", however as there is no connection to the mail client and the headers are already written I dont think the "ignore_user_abort(false);" can fire.

I looked at the post Track mass email campaigns and one up from the bottom "Haragashi" says:

"You can simply build a tracking handler which returns the tracking image byte by byte. After every byte flush the response and sleep for a period of time.

If you encounter a stream closed exception the client has closed the e-mail (deleted or changed to another e-mail who knows).

At the time of the exception you know how long the client 'read' the e-mail."

Does anyone know how I could "simply build a tracking handler" like this or know of a solution I can implement into my code that will force the code to stop running when the user disconnects?

Community
  • 1
  • 1
jdublu
  • 187
  • 1
  • 4
  • 9

2 Answers2

1

I think the problem is that you aren't doing a header redirect every so often. The reason that it is necessary is because once a script starts executing in PHP+Apache, it basically disregards the client until finished. If you force a redirect every X seconds, it makes the server re-evaluate if the client is still connected. If the client isn't connected, it can't force the redirect, and therefore stops tracking the time.

When I played around with this stuff, my code looked like:

header("Content-type: image/gif");
while(!feof($fp)) {
    sleep(2);
    if(isset($_GET['clientID'])) {
        $redirect = $_SERVER['REQUEST_URI'];
    } else {
        $redirect = $_SERVER['REQUEST_URI'] . "&clientID=" . $clientID;
    }
    header("Location: $redirect");
    exit;
}

If the client id was set, then above this block of code I would log this attempt at reading the beacon in the database. It was easy to simply increment the time on email column by 2 seconds every time the server forced a redirect.

Jack Slingerland
  • 2,651
  • 5
  • 34
  • 56
  • Clients don't follow redirects that much, so this is more a theoretical answer than something that makes sense practically. Also this is not what the OP quoted. – hakre Jul 01 '12 at 00:57
0

Would you not do something more like this:

<?php
// Time the request
$time = time(); 

// Ignore user aborts and allow the script
// to run forever
ignore_user_abort(true);
set_time_limit(0);

// Run a pointless loop that sometime 
// hopefully will make us click away from 
// page or click the "Stop" button.
while(1)
{
    // Did the connection fail?
    if(connection_status() != CONNECTION_NORMAL)
    {
        break;
    }

    // Sleep for 1 seconds
    sleep(1);
}

// Connention is now terminated, so insert the amount of seconds since start
$duration = time() - $time;
Petah
  • 45,477
  • 28
  • 157
  • 213
  • Thanks for posting the code above. I tried it and when I click stop on the browser the insert does not take place when I put it after the $duration = $time() - $time; I tried putting the db insert after the sleep(1); and it will update every second but if I stop the page the script keeps going. – jdublu Jun 29 '12 at 18:46
  • Ignore user abort is set to false which makes sense it won't stop until timeout which is also not set. – Luc Franken Jun 30 '12 at 12:58
  • 1
    also send a byte each time, otherwise PHP won't notice if the connection closed. Create some traffic. – hakre Jul 01 '12 at 00:58
  • @hakre could you give me some more details on how to send a byte each time?, I have tested the solution above with the redirects and after 20 redirects or 20 seconds both firefox and chrome bomb close the connection as the number of redirects is exceeded. I think running the loop in the php script and then sending the image a few bytes at a time is probably the best solution for tracking email opens however after a lot of googling I cant find how to do this. – jdublu Jul 01 '12 at 04:10
  • 1
    @jdublu: Well, I find it morally questionable what you do, so I don't want to go further here. Please respect privacy. – hakre Jul 01 '12 at 10:20