Skip to content

Instantly share code, notes, and snippets.

@jystewart
Created June 9, 2010 11:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jystewart/431351 to your computer and use it in GitHub Desktop.
Save jystewart/431351 to your computer and use it in GitHub Desktop.
A slightly saner interface for JangoMail Sync than the one they supply
<?php
/**
* A slightly saner interface for JangoMail Sync than the one they supply
*
* This is a demonstration of how to produce a more security-aware interface for JangoMail's
* web database synchronisation system. It is not guaranteed in any way and is not meant
* to be seen as an example of best practice on any level. It does, however, demonstrate how
* the flaws in that system can be worked around to build a reasonable JSON-based interface
* when working with recent versions of PHP.
*
* See http://jystewart.net/process/2010/06/jangomail-lackadaisical-security-and-a-workaround/
* for more.
*
* @author James Stewart <james@ketlai.co.uk>
* @date 9th June 2010
*/
/**
* Very crude way to handle errors
*/
function return_error($code_and_message) {
header('HTTP/1.1 ' . $code_and_message);
echo $code_and_message;
exit;
}
/**
* Do our basic mysql connection. In a function to make sure we only call it when we need it.
*/
function make_mysql_connection() {
mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
}
/**
* Make sure this is a real email address and give a 406 if not
*/
function check_email($address) {
$email = filter_var($address, FILTER_VALIDATE_EMAIL);
if (! $email) {
return_error('406 Unacceptable');
}
return $email;
}
/**
* Handle the 'massmail' request, returning a list of active email addresses
*
* @return String
*/
function retrieve_addresses() {
$fields = array('name', 'email');
$output = implode(',', $fields) . FIELDNAME_DELIMITER;
make_mysql_connection();
$query = "SELECT `name`, `email` FROM `email_subscribers`
WHERE `unsubscribed` = 0 AND bouncing = 0";
$rs = mysql_query($query);
while ($arr_row = mysql_fetch_row($rs)) {
$output .= implode(COL_DELIMITER, $arr_row) . ROW_DELIMITER;
}
return $output;
}
/**
* Handle the 'unsubscribe' action - mark our record for the address as unsubscribed
*
* @param stdClass
* @return boolean | resource
*/
function handle_unsubscribe($params) {
$email = check_email($params->email);
make_mysql_connection();
$query = sprintf("UPDATE `email_subscribers` SET `unsubscribed` = 1 WHERE `email` = '%s'",
mysql_real_escape_string($email));
return mysql_query($query);
}
/**
* Handle the 'bounce' action - mark our record for the address as bouncing
*
* @param stdClass
* @return boolean | resource
*/
function handle_bounce($params) {
$email = check_email($params->email);
make_mysql_connection();
$query = sprintf("UPDATE `email_subscribers` SET `bouncing` = 1 WHERE `email` = '%s'",
mysql_real_escape_string($email));
return mysql_query($query);
}
/**
* Handle the 'change' action - updating our email address for the user
*
* @param stdClass
* @return boolean | resource
*/
function handle_change($params) {
$new_email = check_email($params->new_email);
$old_email = check_email($params->old_email);
make_mysql_connection();
$query = sprintf("UPDATE `email_subscribers` SET `email` = '%s' WHERE `email` = '%s'",
mysql_real_escape_string($new_email), mysql_real_escape_string($old_email));
return mysql_query($query);
}
/* Rather crude way to check IP address is in range 209.173.141.193 - 209.173.141.255 */
preg_match('/(.+?)\.(\d+)$/', $_SERVER['REMOTE_ADDR'], $ip_parts);
if ($ip_parts[1] != '209.173.141' || $ip_parts[2] < 193 || $ip_parts[2] > 255) {
return_error('403 Forbidden');
}
set_time_limit(1200);
define('USERNAME', 'sample');
define('PASSWORD', 'sample');
define('DB_HOST', 'localhost');
define('DB_USER', 'user');
define('DB_PASSWORD', 'password');
define('ROW_DELIMITER', 'WG0ROWWG0');
define('COL_DELIMITER', 'WG0COLWG0');
define('FIELDNAME_DELIMITER', '___ASDF---BREAK');
define('JANGO_EOF', 'WANGO-ENDOFDATASTREAM');
/* Process input */
$temp_params = $_POST;
function clean_params($param) {
return str_replace('ABC-WANGOMAIL-ABC', chr(0), $param);
}
array_walk($temp_params, 'clean_params');
$params = array();
$strFieldNameDelimiter = '___ASDF---BREAK';
$params['action'] = $temp_params['action'];
$params['username'] = $temp_params['username'];
$params['password'] = $temp_params['password'];
$params['data'] = json_decode($temp_params['querystring']);
/* Authenticate */
if ($params['username'] != USERNAME || $params['password'] != PASSWORD) {
return_error('403 Forbidden');
}
/* Dispatch actions */
switch ($params['action']) {
# massmail actually means 'get list of users for mass mailing'
case 'massmail':
echo retrieve_addresses();
break;
case 'unsubscribe':
if (handle_unsubscribe($params['data'])) {
echo 'unsubscribe-sync-success';
} else {
echo 'unsubscribe-sync-failure';
}
break;
case 'bounce':
if (handle_bounce($params['data'])) {
echo 'bounce-sync-success';
} else {
echo 'bounce-sync-failure';
}
break;
case 'change':
if (handle_change($params['data'])) {
echo 'change-sync-success';
} else {
echo 'change-sync-failure';
}
break;
case 'view':
case 'click':
case 'sent':
case 'job':
case 'action':
case 'forward':
case 'test':
# We don't care about any of these in our system so pretend they worked
echo $action . 'sync-success';
break;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment