Jul 312012
 

Third and final PHP script for Android GCM. This script is for device registration. After a device has registered itself for GCM, the registration id is sent to the third-party application server for storage.

Also see http://skipstechtalk.net/2012/07/20/my-google-cloud-messaging-gcm-php-script/
and
http://skipstechtalk.net/2012/07/31/my-php-script-for-storing-gcm-user-registration/

<?php
/*
saveRegId.php
 
Script for receiving GCM device registrations. The script 
  expects an HTTP GET with the following parameters:
&phone_name=my_friendly_phone_name. This is edited by the user in 
  the global settings.
&regid=my_gcm_registration_id. This is the registration id that the 
  phone receives when it calls the GCM registration intent
&device_id=my_device_id. Normally this is the IMEI number, but it can 
  be other numbers, depending on the phone and OS. It is easily
  hacked, so it isn't bulletproof. I just use it to try and prevent
  multiple device registrations.
&username=my_username. The same username used to register here
&password=my_password. The same password used to register here
$message_type=gcm or c2dm. All recent versions of PHCA send gcm
 
A working example of this is (all on one line)
https://phc-a.net/saveRegId.php?phone_name=Skips%20phone
  &regid=a1b2c3d4f51234567890abcdef
  &device_id=9ab8c7d6e5f1234567890abcdef
  &username=me
  &password=fluffypants
  &message_type=gcm
*/
$dbuser = 'mydbusername';
$dbname = 'phcadb';
$dbpasswd = 'mydbpassword';
$dbhost = 'my.dbhost.net';
 
$dbh = @mysql_connect($dbhost, $dbuser, $dbpasswd);
$verbose = "";
 
if (!$dbh) {
	echo "Could not connect to MySQL server on " . $dbhost;
	die();
} else {
	$verbose = mysql_real_escape_string($_REQUEST[verbose]);
}
 
if (!@mysql_select_db($dbname, $dbh)) {
	if ($verbose==true) echo "Could not connect to database " . $dbname;
	die();
}
 
 
// store the url, just for debugging purposes.
//$incomingurl = curPageURL();
//$sql = "INSERT INTO incomingsql (sqlx) VALUES ('$incomingurl')";
//$result = query($sql);
 
$salt = GetSalt();
$storedEncryptedPassword = GetStoredEncryptedPassword();
 
$encryptedPassword=hash('sha512', $salt.$_REQUEST[password]);
for($i=0; $i<5000; $i++){
   $encryptedPassword=hash('sha512', $salt.$encryptedPassword);
}
 
if ($storedEncryptedPassword == $encryptedPassword) {
	StoreRegistrationId($verbose);
}
else
{
	header('HTTP/1.0 401 Unauthorized', true, 401);
	die("Bad Username/Password");
}
 
 
function StoreRegistrationId($verbose) {
	$phone_name = mysql_real_escape_string($_REQUEST[phone_name]);
	$regid = mysql_real_escape_string($_REQUEST[regid]);
	$device_id = mysql_real_escape_string($_REQUEST[device_id]);
	$username = mysql_real_escape_string($_REQUEST[username]);
	$password= mysql_real_escape_string($_REQUEST[password]);
	$message_type = mysql_real_escape_string($_REQUEST[message_type]);
 
	// first check to see if we already have this device registered
	if ($verbose==true) echo "Checking if we have seen this device before<br />";
	$sql = "SELECT * FROM userdevices WHERE device_id='$_REQUEST[device_id]'";
	if ($verbose==true) echo "$sql <br />";
	$result = query($sql);
	if (mysql_num_rows($result) == 0) // it's a new device, so INSERT it
	{
		$sql = "INSERT INTO userdevices (
			registration_id, 
			device_id, 
			username, 
			is_sending, 
			message_type, 
			phone_name
			) VALUES (
			'$regid', 
			'$device_id', 
			'$username', 
			1, 
			'$message_type', 
			'$phone_name'
			)";
	}
	else
	{
		// we do have this phone registered already. Check to see if it is with the same user.
		// We don't want people signing up with multiple accounts.
		$row = mysql_fetch_array($result);
		if ($_REQUEST[username] == $row[username])
		{
			$sql = "UPDATE userdevices SET 
			registration_id='$regid', 
			is_sending=1, 
			message_type='$message_type', 
			phone_name='$phone_name' 
			WHERE username = '$username' AND device_id='$device_id'";
		}
		else
		{
			header('HTTP/1.0 550 Unauthorized', true, 550);
			die("This phone is already registered with username '" . $row[username] . "'");
		}
	}
 
	if ($verbose==true) echo "$sql <br />";
	$result = query($sql);
	if (!$result) {
		header('HTTP/1.0 500 Internal Server Error');
		die("Internal Error");
	} else {
		//header("Status: 200");
		//echo "OK<br />";
		// send a test message to the phone to be sure everything is working.
		$url = "https://phc-a.net/send.php?username=".$username."&password=".$password."&varname=".$device_id."&varvalue=".$device_id;
		if ($verbose==true) echo "Sending test message to GCM server. The url is $url<br />";
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_TIMEOUT, '10');
		$data = curl_exec($ch);
		if(curl_errno($ch) && $verbose==true){
		    echo 'Curl error: ' . curl_error($ch);
		}
		if ($verose==true) print_r(curl_getinfo($ch));
		if ($verbose==true) echo "Execute complete. Here's the data: $data";
		curl_close($ch);
		//exit;
	}
}
 
function curPageURL() {
 $pageURL = 'http';
 if ($_SERVER["HTTPS"] == "on") {$pageURL .= "s";}
 $pageURL .= "://";
 if ($_SERVER["SERVER_PORT"] != "80") {
  $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
 } else {
  $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
 }
 return $pageURL;
}
 
 
function GetSalt() {
	$sql = "SELECT * FROM user WHERE username = '$_REQUEST[username]'";
	$result = query($sql);
	if (mysql_num_rows($result) == 0) // bad username
	{
		header('HTTP/1.0 401 Unauthorized');
		die("Bad Username/Password");
	}
	while ($row = mysql_fetch_assoc($result)) {
	    return $row["salt"];
	}
}
 
function GetStoredEncryptedPassword() {
	$sql = "SELECT * FROM user WHERE username = '$_REQUEST[username]'";
	$result = query($sql);
	if (mysql_num_rows($result) == 0) // bad username, should not happen here
	{
		header('HTTP/1.0 401 Unauthorized');
		die("Bad Username/Password");
	}
	while ($row = mysql_fetch_assoc($result)) {
	    return $row["passwordHash"];
	}
}
 
function query($query) {
if(!($result = mysql_query($query)))
	{
		die ( "MySQL Reports: " . mysql_error());
	}
return $result;
}
?>
 Posted by at 2:48 pm
Jul 312012
 

Next in the series of GCM scripts that I use, here is the script that stores registration information on the server. I have this saved as my index.php file so it is served up whenever someone navigates to my server. See http://skipstechtalk.net/2012/07/20/my-google-cloud-messaging-gcm-php-script/ for the script for actually sending GCM messages. There will also be one more script for saving device registrations.

<?php
$action = $_REQUEST['action'];
 
if ($action=='formsubmitted' || $action =='reg')
{
	$salt = getSalt();
	$encryptedPassword=hash('sha512', $salt.$_REQUEST[password]);
	for($i=0; $i<5000; $i++){
	   $encryptedPassword=hash('sha512', $salt.$encryptedPassword);
	}
 
	$dbuser = 'mydbusername';
	$dbname = 'phcadb';
	$dbpasswd = 'mydbpassword';
	$dbhost = 'my.dbhost.net';
 
	$dbh = @mysql_connect($dbhost, $dbuser, $dbpasswd);
	if (!$dbh) {
		echo "Could not connect to MySQL server on " . $dbhost;
		die();
	}
 
	if (!@mysql_select_db($dbname, $dbh)) {
		echo "Could not connect to database " . $dbname;
		die();
	}
 
	if ($_REQUEST[username] == "" | $_REQUEST[password] == "")
	{
		die ('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><br />' .
		'<html>' .
		'<head>' .
		'</head>' .
		'<body>' .
		'<h1>PowerHome Connector for Android</h1><br />' .
		"Username and password may not be blank.<br /><a href='https://phc-a.net'>Click here to try again.</a>" .
		'</body>' .
		'</html>' );
	}
 
	//first see if the user already exists
	$sql = "SELECT * FROM user WHERE username = '$_REQUEST[username]'";
	$result = query($sql);
 
	if (mysql_num_rows($result) == 0)
	{
		$sql = "INSERT INTO user (username,passwordHash,salt,registration_date,email) VALUES ('$_REQUEST[username]','$encryptedPassword','$salt', curdate(), '$_REQUEST[email]')";
		$result = query($sql);
 
		echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><br />';
		echo "<html>\n";
		echo "<head>\n";
		echo "</head>\n";
		echo "<body>\n";
		echo "<h1>PowerHome Connector for Android</h1><br />\n";
		echo "username = " . $_REQUEST[username] . "<br />";
		echo "password = " . $_REQUEST[password] . "<br />";
		echo "salt = $salt<br />";
		echo "encrypted password = $encryptedPassword<br />";
		echo 'All done!<br />Now you can setup GCM notifications on your Android phone with the PowerHome Connector for Android app.<br />See <a href="http://skipstechtalk.net/powerhome-connector-for-android-usage-instructions-and-screenshots/">skipstechtalk.net</a> for a tutorial for setting up your PowerHome formulas.';
		echo '</body>\n';
		echo '</html>\n';
	}
	else
	{
		header('x', true, 401);
		echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><br />';
		echo '<html>';
		echo '<head>';
		echo '</head>';
		echo '<body>';
		echo '<h1>PowerHome Connector for Android</h1><br />';
		echo "Sorry, but the username $_REQUEST[username] has already been used. Please try a different username.<br /><a href='https://phc-a.net'>Click here to try again.</a>";
		echo '</body>';
		echo '</html>';
	}
}
else
{
	echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><br />';
	echo "<html>\n";
	echo "<head>\n";
	echo "</head>\n";
	echo "<body>\n";
	echo "<h1>PowerHome Connector for Android</h1><br />\n";
	echo 'Create an account so you can use the PowerHome Connector for Android app on your Android phone.<br /><br />' .
	     'Please use a username and password that you are not using anywhere else. ' .
	     'Passwords are stored in a database on this server, but are hashed 5000 times with a random salt, so it should be secure, but you never know. ' .
	     'That is why I suggest you use a username and password that you do not use anywhere else. ' .
	     'There is no personal information stored in the database. Just your login information and a registration id which routes the messages to your phone. ' .
	     'If someone does figure out how to copy the database, even though they will not have access to any personal information, you do not want them having your paypal password. ' .
	     'So, please, go the safe route and use a different password from any other website. <br/><br />' .
	     'Do remember your username and password because you will need them to send messages from PowerHome and you will need to enter it into your phone too.<br /><br />' .
	     'You may also include your email address which will be used to help you recover your username and password should you forget them. I promise I will not use it for anything else.<br /><br />' .
	     'You may register as many phones as you want with this account. This will allow you to send messages to each phone. You cannot select some messages for one phone and some messages for another. ' .
	     'It is an all or nothing deal.<br /><br />' .
	     '<b>None of the messages sent from your PowerHome application to this server are saved on this server</b>.<br /><br />' .
	     'For instructions on installing and using the PowerHome Connector for Android App, please see <a href="http://skipstechtalk.net/powerhome-connector-for-android/">http://skipstechtalk.net/powerhome-connector-for-android/</a><br /><br />';
	echo '<FORM METHOD="POST" ACTION="?action=formsubmitted">';
	echo 'Username (required): <INPUT TYPE="text" NAME="username" SIZE="30"><br />';
	echo 'Password (Required): <INPUT TYPE="text" NAME="password" SIZE="30"><br />';
	echo 'Email (optional): <INPUT TYPE="text" NAME="email" SIZE="30"><br />';
	echo '<INPUT TYPE="submit">';
	echo '</body>';
	echo '</html>';
}
 
//function md5crypt($password){
function getSalt(){
    // create a salt that ensures crypt creates an md5 hash
    $base64_alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                    .'abcdefghijklmnopqrstuvwxyz0123456789+/';
    $salt='';
    for($i=0; $i<20; $i++){
        $salt.=$base64_alphabet[rand(0,63)];
    }
    return $salt;
}
 
function query($query) {
		if(!($result = mysql_query($query)))
        	{
            		//can't execute query
            		echo ( "Couldn't query table!<br>\n");
            		echo ( "MySQL Reports: " . mysql_error() . "<br>\n");
            		exit();
        	}
 
		return $result;
	}
?>
 Posted by at 11:51 am
Jul 202012
 

This is the script that I have on my GCM server. I am posting it here in the hopes that I can get some feedback on it, and perhaps also help some other people setting up GCM servers.

The PHP script for users to register themselves is here: http://skipstechtalk.net/2012/07/31/my-php-script-for-storing-gcm-user-registration/

The PHP script for to store device registration id’s is here: http://skipstechtalk.net/2012/07/31/my-php-script-for-gcm-device-registration/

<?php
/*
Third-party Application server for the PowerHome Connector for Android
C2DM and GCM applications. Takes an incoming HTTP GET request and
sends the correctly encoded message to the Google server, for ultimate
passing to the registered devices.
 
The requests should be sent as (all on one line, of course)
https://phc-a.net/send.php?username=myusername
	&password=mypass&var1=var1value&var2=var2value
 
additionally, requests may be sent as
https://phc-a.net/send.php?username=myusername
	&password=mypass&varname=myvar&varvalue=myvarvalue
but this format only supports a single variable/value pair.
 
The app looks at the returned message from the google server to determine if 
any changes need to be made, such as deleting registration id's
*/
 
$dbuser = 'mydbusername';
$dbname = 'phcadb';
$dbpasswd = 'mydbpassword';
$dbhost = 'my.dbhost.net';
 
// hold the data for the JSON object sent to the google server
$devices = array();
$data = array();
 
// php keeps track of the db connection. Each sql query here uses this 
// connection
$dbh = @mysql_connect($dbhost, $dbuser, $dbpasswd);
if (!$dbh) {
	echo "Could not connect to MySQL server on " . $dbhost;
	die();
}
 
if (!@mysql_select_db($dbname, $dbh)) {
	echo "Could not connect to database " . $dbname;
	die();
}
 
// used to determine whether to print debugging information
$verbose = $_REQUEST[verbose];
 
if ($verbose==true) {
	echo "<a href='help.html'>See this page for help</a><br />\n";
}
 
// for debugging, store each url in a database
//	$sql = "LOCK TABLE incomingsql WRITE";
//	query($sql);
//	$url = curPageURL();
//	$sql = "INSERT INTO incomingsql (sqlx) VALUES ('$url')";
//	$result = query($sql);
//	$sql = "UNLOCK TABLES";
//	query($sql);
 
if ($verbose==true) echo "The url was: $url<br />";
 
// check the password
$salt = GetSalt();
 
// get the stored password for the username passed in
$storedEncryptedPassword = GetStoredEncryptedPassword();
 
// hash the password that was passed in
$encryptedPassword=hash('sha512', $salt.$_REQUEST[password]);
for($i=0; $i<5000; $i++){
   $encryptedPassword=hash('sha512', $salt.$encryptedPassword);
}
 
// If the passwords match, then check for variable names and values 
// to be sent
if ($storedEncryptedPassword == $encryptedPassword) {
	if ($verbose==true) {
		echo "Username/Password match. Continue to send C2DM message.<br />";
	}
 
	// load up the $data array with the variable names and values
	foreach($_REQUEST as $key => $value) {
		switch ($key) {
			case "verbose":
			case "password":
			case "varvalue":
			case "username":
				break;
			case "varname":
				if ($verbose==true) {
					echo "Using varname/varvalue syntax<br />";
				}
				$varname = mysql_real_escape_string($value);
				$varvalue = $_REQUEST[varvalue];
 
				// prevent sql injection
				$varvalue = addslashes($varvalue);
				$varvalue = strip_tags($varvalue);
				if ($verbose==true) echo "Varname: " . $varname 
					. "; VarValue: " . $varvalue . "<br />";
				$data[$varname]=$varvalue;
				UpdateLastMessageSentTime($verbose);
				break;
			default:
				if ($verbose==true) {
					echo "Using direct variable syntax<br />";
				}
				$value = mysql_real_escape_string( $value );
 
				// prevent sql injection
				$value = addslashes($value);
				$value = strip_tags($value);
				if ($verbose==true) {
					echo "Varname: " . $key . "; VarValue: " . $value 
						. "<br />";
				}
				$data[$key]=$value;
				UpdateLastMessageSentTime($verbose);
				break;
		}
	}
}
else
{
	header('x', true, 401);
	die("Bad Username/Password");
}
 
if ($verbose==true) {
	echo "The data array is:<br />";
	print_r($data);
	echo "<br />";
}
 
// get all of the user's GCM devices
$sql = "SELECT * FROM userdevices WHERE username = "
           . "'$_REQUEST[username]' AND message_type='GCM'";
$result = query($sql);
if ($verbose==true) echo "Sending to the following GCM devices:<br />";
$devicecount = mysql_num_rows($result);
if ($devicecount==0) {
	if ($verbose==true) echo "--none--<br />";
}
while ($row = mysql_fetch_array($result)) {
	array_push($devices, $row[registration_id]);
	if ($verbose==true) echo $row[phone_name] . "<br />";
}
 
$devicecount = mysql_num_rows($result);
if ($devicecount>0) {
	if ($verbose==true) echo "There are $devicecount GCM devices<br />";
	SendGCM($verbose, $devices, $data);
}
 
// reset devices for C2DM
$devices = array();
 
// get all of the user's C2DM devices
$sql = "SELECT * FROM userdevices WHERE username = "
     . "'$_REQUEST[username]' AND message_type='C2DM'";
$result = query($sql);
$devicecount = mysql_num_rows($result);
if ($verbose==true) echo "There are $devicecount C2DM devices<br />";
if ($verbose==true) echo "Sending to the following C2DM devices:<br />";
if ($devicecount==0) {
	if ($verbose==true) echo "--none--<br />";
}
while ($row = mysql_fetch_array($result)) {
	array_push($devices, $row[registration_id]);
	if ($verbose==true) echo $row[phone_name] . "<br />";
}
 
foreach ($devices as $device) {
	SendC2DM($verbose, $device, $data);
}
 
// All done!
 
//
//
// FUNCTIONS go below here
//
//
function SendGCM ($verbose, $devices, $data) {
	if ($verbose==true) {
		echo "The data array is:<br />";
		print_r($data);
		echo "<br />";
		echo "The GCM devices array is:<br />";
		print_r($devices);
		echo "<br />";
	}
	$message = json_encode(array("registration_ids"=>$devices, 
		"data"=>$data));
	if ($verbose==true) echo "JSON message:<br />";
	if ($verbose==true) echo "$message" . "<br />";
	if ($verbose==true) echo "Sending over GCM<br />";
 
	// My GCM API key. KEEP SECRET!!!!!
	$phcaApiKey = "AIzaSAbCdEfGhIjKlnjkyXp-DCUMnOpQra8"; // SECRET!!!
 
	$url = "https://android.googleapis.com/gcm/send";
 
	$headers = array('Authorization: key=' . $phcaApiKey, 
		"Content-Type: application/json");
 
	if ($verbose==true) echo "Sending POST to GCM server. <br />";
 
	$x = curl_init($url); 
	curl_setopt($x, CURLOPT_HTTPHEADER, $headers);
	curl_setopt($x, CURLOPT_HEADER, 1); 
	curl_setopt($x, CURLOPT_POST, 1); 
	curl_setopt($x, CURLOPT_POSTFIELDS, $message); 
	curl_setopt($x, CURLOPT_RETURNTRANSFER, 1); 
	curl_setopt($x, CURLOPT_SSL_VERIFYPEER, false);
	$response = curl_exec($x); 
	$http_code = curl_getinfo($x, CURLINFO_HTTP_CODE);
 
	// request has been sent. Now get the response. The response 
	// is in the form of a JSON object, formatted as
	/*
		{ "multicast_id": 216,
		  "success": 3,
		  "failure": 3,
		  "canonical_ids": 1,
		  "results": [
		    { "message_id": "1:0408" },
		    { "error": "Unavailable" },
		    { "error": "InvalidRegistration" },
		    { "message_id": "1:1516" },
		    { "message_id": "1:2342", "registration_id": "32" },
		    { "error": "NotRegistered"}
		  ]
		}
	*/
 
	$header_size = curl_getinfo($x,CURLINFO_HEADER_SIZE);
	$body = substr( $response, $header_size );
 
	$json_response = json_decode($body);
	$results = $json_response->results;
 
	// first, print some debugging information
	if ($verbose==true) {
		echo "The response from the C2DM server was:<br />";
		print_r ($response);
		echo "<br />";
		$header = substr($response, 0, $header_size);
		echo "Header: $header<br />\n";
		echo "Body: $body<br />\n";
		echo "JSON decoded response:<br />";
		print_r($json_response);
		echo "<br />";
		echo "Results:<br />";
		print_r($results);
		echo "<br />";
	}
 
	$device_number = 0;
	foreach ($results as $device_response) {
		if (is_array($device_response)) {
			// only happens when a registration id needs to be updated.
			$old_id = $devices->$device_number;
			$new_id = $device_response->registration_id;
			if ($verbose==true) {
				echo "Updating the registration id from "
				   . "$old_id to $new_id.<br />\n";
			}
			$sql = "userdevices SET registration_id = "
			     . "'$new_id' WHERE registration_id='$old_id'";
			if ($verbose==true) echo $sql . "<br />";
			$result2 = query($sql);
		} else {
			if ($device_response->error == "NotRegistered") {
				// delete the device from the userdevices table
				if ($verbose==true) {
					echo "Deleting this entry from the devices table. "
					. "It has been unregistered.<br />\n";
				}
				$sql = "LOCK TABLE userdevices WRITE";
				query($sql);
				$sql = "DELETE FROM userdevices WHERE registration_id='"
 				   . $devices[$device_number] . "'";
				if ($verbose==true) echo $sql . "<br />";
				$result2 = query($sql);
				if ($verbose==true) echo "The result was: ";
				if ($verbose==true) print_r ($result2);
				$sql = "UNLOCK TABLES";
				query($sql);
			}
			if ($device_response->error == "InvalidRegistration") {
				// Not very likely. Possible corruption in the database???
				if ($verbose==true) {
					echo "ERROR!! Invalid Registration should never "
					. "happen. Please inform the developer and perhaps "
					. "try unregistering and re-registering your device.";
				}
			}
			if ($device_response->error == "Unavailable") {
				// GCM Service is temporarily down. Try again later
				if ($verbose==true) {
					echo "Google GCM dervice is temporarily down. Try "
					. "sending again later.";
				}
			}
		}
		$device_number++;
	}
 
	// close the curl object
	curl_close($x); 
 
	if ($verbose==true) {
		echo "You should not see any errors in the response. If there "
		. "are no errors, you should see an updated variable in the "
		. "status text on your phone.<br />";
	}
}
 
function SendC2DM($verbose, $device, $data) {
 
	if ($verbose==true) echo "Sending over C2DM<br />";
	/*
	Ultimately, this is how the request should be formatted:
	curl --header "Authorization: GoogleLogin auth=your_authenticationid" \
	"https://android.apis.google.com/c2dm/send" \
	-d registration_id=your_registration \
	-d "data.payload=This data will be send to your application as payload" \
	-d collapse_key=0
	*/
	$googleAuth = "A-VERY-LONG-STRING-WITH-A-BUNCH-OF-LETTERS-AND-NUMBERS";
	/*
	$google Auth can be obtained using curl like this:
	(all on one line, begin below this line)
	C:\>curl https://www.google.com/accounts/ClientLogin 
		-d Email=skip.morrow.mobile@gmail.com 
		-d "Passwd=kklirctpnucvnelf" 
		-d accountType=GOOGLE 
		-d source=Google-cURL-Example -d service=ac2dm -k
	(end of command)
 
	Note that the password is a one-time use, application specific password.
 
	*/
 
	$url = "https://android.apis.google.com/c2dm/send";
	//$regid = GetRegId();
	$data_msg .= urlencode($key) ."=". urlencode($value); 
	$size=strlen($data_msg);
 
	$data_msg = "";
 
	if ($verbose==true) echo "<br />Registration_id = " 
		. $device . "<br />";
 
	// add "data." to all of the keys in the $data array
 
	foreach($data as $key => $value) {
		$data["data." . $key] = $value;
		unset($data[$key]);
	}
	// $data has all of the variables. Just add the registration_id 
	// and collapse_key and it will be ready to send
	$data["registration_id"]=$device;
	$collapse_key = date("Hi");
 
	$data["collapse_key"]=$collapse_key;
 
	if ($verbose==true) print_r ($data);
	if ($verbose==true) echo "<br />";
	//echo "Headers: ";
	$headers = array('Authorization: GoogleLogin auth=' . $googleAuth);
	//print_r($headers);
	if ($verbose==true) echo "Sending POST to C2DM server. <br />";
 
	$x = curl_init("https://android.apis.google.com/c2dm/send"); 
	if ($headers) curl_setopt($x, CURLOPT_HTTPHEADER, $headers);
	curl_setopt($x, CURLOPT_HEADER, 1); 
	curl_setopt($x, CURLOPT_POST, 1); 
	curl_setopt($x, CURLOPT_POSTFIELDS, $data); 
	curl_setopt($x, CURLOPT_RETURNTRANSFER, 1); 
	curl_setopt($x, CURLOPT_SSL_VERIFYPEER, false);
	$data = curl_exec($x); 
	curl_close($x); 
	$response = $data;
	if ($verbose==true) {
		echo "The response from the C2DM server was:<br />";
		print_r ($data);
		echo "<br />";
		echo "<br />";
		echo "<br />\n";
		echo "You should not see any errors in the response. "
			. "If there are no errors, you should see an updated "
			. "variable in the status text on your phone.<br />";
	}
}
 
function curPageURL() {
 $pageURL = 'http';
 if ($_SERVER["HTTPS"] == "on") {$pageURL .= "s";}
 $pageURL .= "://";
 if ($_SERVER["SERVER_PORT"] != "80") {
  $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"]
	.$_SERVER["REQUEST_URI"];
 } else {
  $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
 }
 return $pageURL;
}
 
function GetRegId() {
	$sql = "SELECT * FROM user WHERE username = '$_REQUEST[username]'";
	$result = query($sql);
	if (mysql_num_rows($result) == 0) // bad username
	{
		header('x', true, 401);
		die("Bad Username");
	}
	while ($row = mysql_fetch_assoc($result)) {
	    return $row["regId"];
	}
}
 
 
function GetSalt() {
	$sql = "SELECT * FROM user WHERE username = '$_REQUEST[username]'";
	$result = query($sql);
	if (mysql_num_rows($result) == 0) // bad username
	{
		header('x', true, 401);
		die("Bad Username");
	}
	while ($row = mysql_fetch_assoc($result)) {
	    return $row["salt"];
	}
}
 
function GetStoredEncryptedPassword() {
	$sql = "SELECT * FROM user WHERE username = '$_REQUEST[username]'";
	$result = query($sql);
	if (mysql_num_rows($result) == 0) // bad username, should not happen
	{
		header('x', true, 401);
		die("Bad Username");
	}
	while ($row = mysql_fetch_assoc($result)) {
	    return $row["passwordHash"];
	}
}
 
function UpdateLastMessageSentTime() {
	$sql = "LOCK TABLE user WRITE";
	query($sql);
	$sql = "UPDATE user SET last_msg_timestamp=now() WHERE username = "
	. "'$_REQUEST[username]'";
	$result = query($sql);
	$sql = "UNLOCK TABLES";
	query($sql);
}
 
function query($query) {
if(!($result = mysql_query($query)))
	{
		//can't execute query
		if ($verbose==true) echo ( "Couldn't query table!<br>\n");
		if ($verbose==true) {
			echo ( "MySQL Reports: " . mysql_error() . "<br>\n");
		}
		exit();
	}
return $result;
}
?>

Here’s the SQL scripts to create the database tables that I am using:

 
-- phpMyAdmin SQL Dump
-- version 3.3.10.4
-- http://www.phpmyadmin.net
--
-- Host: my.dbhost.net
-- Generation Time: Jul 20, 2012 at 11:07 AM
-- Server version: 5.1.39
-- PHP Version: 5.2.17
 
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
 
--
-- Database: `phcadb`
--
 
-- --------------------------------------------------------
 
--
-- Table structure for table `incomingsql`
--
 
CREATE TABLE IF NOT EXISTS `incomingsql` (
  `index` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `sqlx` VARCHAR(1024) NOT NULL,
  PRIMARY KEY (`index`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
-- --------------------------------------------------------
 
--
-- Table structure for table `user`
--
 
CREATE TABLE IF NOT EXISTS `user` (
  `username` VARCHAR(64) NOT NULL,
  `passwordHash` VARCHAR(1024) NOT NULL,
  `salt` VARCHAR(26) NOT NULL,
  `registration_date` DATE NOT NULL,
  `email` VARCHAR(60) DEFAULT NULL,
  `last_msg_timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
-- --------------------------------------------------------
 
--
-- Table structure for table `userdevices`
--
 
CREATE TABLE IF NOT EXISTS `userdevices` (
  `registration_id` VARCHAR(256) NOT NULL,
  `username` VARCHAR(64) NOT NULL,
  `device_id` VARCHAR(128) NOT NULL,
  `is_sending` tinyint(1) NOT NULL,
  `message_type` enum('C2DM','GCM') DEFAULT NULL COMMENT 'Either C2DM or GCM',
  `phone_name` VARCHAR(64) DEFAULT NULL,
  PRIMARY KEY (`registration_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 Posted by at 5:54 pm
Jun 292011
 

Are the users of the application that you developed using the latest version? If your application doesn’t have automatic checking built in, you have to rely on the users to periodically check the site where you host the software for updates, or else you have to email the users or otherwise notify them. Certainly, the very best solution is to have your application self check itself to see if a newer version is available. I will present a simple solution here that uses a small php script placed on a web server. Data about my applications is stored in a mysql database. Then I will show you how I have a C# application check for updates. It’s a really easy solution and anyone with a little programming experience will be able to do it. First, here is the php code that is placed on the server where I host my applications for download.

<!--?php
/*
* To use, place this on a web server of your choice.  Next, create a mysql database
* where you will store information about your applications, such as the latest version.
* From now on, when you update an application, you will also need to update the
* information in the database about that application. Configure the database with the
* following columns: app_name, latest_version, build_date, url
*
* Then, in your application, when it checks for updates, have it send a request to the
* web server formed like this:
* http://myserver.com/app_updates.php?app_name=MyAppName
* where MyAppName above is exactly the same as the app_name stored in the mysql
* database. If MyAppName is found in the database, then it will return a small
* xml string with the data for that record.  If MyAppName is not found, it will
* return nothing. The xml output will look like this
*
* <?xml version="1.0"?-->
*
*   SevereWeatherGrowler
*   1.2.0
*   20110629
*   http://skipstechtalk.net/severe-weather-growler/
*
*
*
* It is up to your application to decide what to do with the returned data. For
* instance, one way to check if an update is available is to always store the build
* date in your application. Then when your application checks the sever, it can
* compare its build data to the build date for the latest version. If they are
* different, then an update is required. You can then direct the user to the url
* provided to get the update.
*/
$dbuser = 'my_database_user_name';
$dbname = 'my_database_name';
$dbpasswd = 'my_super_secret_password';
$dbhost = 'my_db_host'; // might be 'localhost'
$table_name = 'app_updates';
 
// test the connection
$dbh = @mysql_connect($dbhost, $dbuser, $dbpasswd);
if (!$dbh)
{
  echo "Could not connect to MySQL server on " . $dbhost;
  die();
}
 
// get the app_name from the url
$app_name = $_REQUEST['app_name'];
 
// get the data from the database for that app_name
$query = "SELECT * FROM $dbname.$table_name WHERE app_name = '$app_name'";
$result = query($query);
$row = mysql_fetch_object($result);
//print_r ($row);
 
// build the xml output
if ($row != null)
{
  $xmlstr = "<!--?xml version='1.0' ?-->\n";
  $xml = new SimpleXMLElement($xmlstr);
  $xml->addChild('app_name', $row->app_name);
  $xml->addChild('latest_version', $row->latest_version);
  $xml->addChild('build_date', $row->build_date);
  $xml->addChild('url', $row->url);
  //print_r($xml);
  echo $xml->asXml();
}
 
function query($query) {
  if(!($result = mysql_query($query)))
  {
    //can't execute query
    echo ( "Couldn't query table!\n");
    echo ( "MySQL Reports: " . mysql_error() . "\n" . "Query was: " . $query);
    exit();
  }
  return $result;
}
 
?&gt;

 

 

The code is pretty well documented (in my opinion). As it says in the comments, just have your application check http://yourwebserver.com/app_updates.php?app_name=MyAppName. The server will then return a small xml string that can be parsed by your application.  Here’s how I do it with C#

 

public const string UPDATE_CHECKER_URL = "http://my_webserver.com/app_updates.php";
public const int BUILD_DATE = 20110629;
 
public static void appUpdateTimer_Elapsed(object sender, ElapsedEventArgs e)
{
  try
  {
    WebClient wc = new WebClient();
    string updateCheckerUrl = UPDATE_CHECKER_URL + "?app_name=" +
         System.Windows.Forms.Application.ProductName;
    string updateText = wc.DownloadString(updateCheckerUrl);
    XmlDocument myXmlDoc = new XmlDocument();
    myXmlDoc.LoadXml(updateText);
 
    string latestVersion =
       myXmlDoc.SelectSingleNode("/appdata/latest_version").InnerText;
    string build_date =
       myXmlDoc.SelectSingleNode("/appdata/build_date").InnerText;
    string updateUrl =
       myXmlDoc.SelectSingleNode("/appdata/url").InnerText;
    int result = 0;
    Int32.TryParse(build_date, out result);
 
    if (BUILD_DATE != result)
    {
      MessageBox.Show("There is an update available. Please check " +
           updateUrl + " for details.");
    }
  }
  catch
  {
    //do nothing.
  }
}

Then all that’s left is to set a timer to periodically call this function.  Put this in your Main() or someplace appropriate.

appUpdateTimer.Interval = 4 * 60 * 60 * 1000; //check every four hours
appUpdateTimer.AutoReset = true;
appUpdateTimer.Elapsed += new ElapsedEventHandler(appUpdateTimer_Elapsed);
appUpdateTimer.Start();

 

 Posted by at 1:40 pm
Mar 142011
 
Liftmaster312HM

Many times as I sit in my car about to drive out my driveway I wonder “did I arm my security system?” In the past I would either pull out my phone, browse to my PowerHome website and check to see if it is armed, and if not I would arm it, or I would walk back into the house to check. I’ve got a homelink remote in my car which works great for opening and closing my garage door, so I wondered what it would take to have one of the unused buttons in my car arm my system. I don’t want to use any of the buttons to disarm my system for security reasons, but I don’t see any risk in setting it up to arm the elk. The steps below will tell you how I did it. It is a pretty easy install. Anyone that installed an Elk should be able to do this very easily.

Receiver. I did a little research and found that I could get LiftMaster receivers pretty cheaply (less than $50 depending on whether you need any remotes). There are a lot to choose from, so here’s some guidance. First of all, I got the LiftMaster 312HM, available in many places–search online. This model comes with an external antenna and operates at 315MHz. I believe you must get a receiver that operates at this frequency for the homelink to work. There are receivers out there that use 390MHz, so avoid those. The way the receiver works is that when a signal is received that has been learned into the receiver, the receiver electrically closes (shorts) the two output terminals for one second. This is perfect for integrating with an Elk. We can tie those two terminals to a zone input, set that zone to unsupervised, normally open, and write some rules for it to do the magic when the zone is “violated”. Perfect. Note that the 312HM is a single-channel receiver. There are dual- and three-channel receivers that allow you to program several remotes to it and close different terminals depending on which remote was pushed.

Mounting. I mounted my remote right next to my garage door inside my garage. I chose this location because I already had wiring run from that area back to my elk. This wiring was because I installed a magnetic sensor there to tell me if my door was closed. If you don’t have a sensor installed on your door, this might be a good time to do it. You don’t need the sensors for this project, but if you have to crawl under your house, why not kill two birds, right? I suppose mounting the receiver close to the door will also maximize the reception because the receiver will be physically closer to the car when someone pushes the button. You will also need to power the receiver. You could tap off the 12V outputs from your garage door, or you can buy the wall-wart transformer from Liftmaster model 85LM, which is what I did. I’m sure that other transformers would work if you found the right voltage and amperage.

Wiring. I suggest that you run four-strand wire from your mounting location back to a zone input on your elk. That way if you do decide that you need to connect something else, you will have a pair of wires ready.

Initial Elk programming. First set up the zone. Make it unsupervised, normally open. I also made it a fast-loop because the relay only closes for a second, and I wanted to be sure that the elk recognized it that fast. Probably not necessary, but I did it anyway. I also set it up as a type 16, non-alarm and chose to enable the chime, and I named it “Remote Arm”. Next, set up a simple rule so that you can easily tell if everything is working. Something like WHENEVER Remote Arm (Zn XX) BECOMES NOT SECURE THEN CHIRP THE OUTSIDE SIREN 1 TIME. You could have the elk say something, or just listen for the chime. Also, if you are close to the receiver, you will hear the relay click.

Receiver programming. I happened to have two remotes for my garage door–one for me and one for my wife. Since I had programmed the homelink in my car to open the garage door, I had a spare transmitter. The way I will do this is have one transmitter paired with the garage door. My wife keeps that transmitter in her car. I have that same transmitter learned into my homelink in my car. If you already had both transmitters paired with the garage door opener, you will need to unlearn all the transmitters and then relearn just the one transmitter. Now the other remote will be for remote arming through the 312HM, so we need the 312HM to learn that remote. To program the 312HM, press and release the learn button inside the receiver, and then press and hold the transmitter for a couple of seconds. Release the transmitter button. Press the transmitter button again and you should hear the relay click and your elk rule should fire. You are done programming the receiver… for now. I had to do some extra programming to get it to work with my homelink, so don’t put the cover on just yet.

HomeLink programming. I think of the homelink as being similar to those universal IR remotes where you had to learn new commands by putting the factory remote head-to-head with the universal remote. You then press a special key sequence on the universal and then the key on the factory that you want to learn into the universal. The universal just repeats what it learned. So, for the homelink programming, on my 2011 Acadia, I press and hold the outside two buttons for 20 seconds. The HomeLink indicator light flasshes fast to tell you it is in learn more. Press and hold the Homelink button you want to associate with the remote arming transmitter, and press and hold the remote arming transmitter. Watch the indicator light flash slow at first and then flash fast. At this point the homelink has learned the new transmitter. Press the new homelink button that you just programmed and hold it for about a second. With any luck, the 312HM will recognize the newly trained transmitter and do its mogic. In my case, it did not work. So I had to go to the backup programming instructions. Back on the 312HM, press and release the learn button. Then back in your car (you only have 30 seconds to do this, so you may need help), press and hold the newly trained homelink button for two seconds. If the receiver still does not respond, press and hold the homelink button again for two seconds. In my case, the 312HM always responded to the second attempt. I guess the 312HM uses some kind of encoding that is a little different than that of my garage door opener. The HomeLink system cycles through the different encoding methods each time you press it. Supposedly you may have to do this press and hold for two seconds exercise up to four times before the right encoding is found. At this point, you should be completely paired with one handheld transmitter and the homelink system. And your elk should be responding each time either of these are pressed.

Final elk programming. I wrote two rules for the elk. One, if the remote is pressed and the alarm is not armed, and two, if the remote is pressed and the alarm is already armed. First, set an unused output to help prevent both of these rules from firing at the same time. You see, with the Elk, so many things can happen in near simultaneousness, that you have to disable the second rule or it may actually fire at the wrong time. So I set an unused output to prevent the second case from running. It will all make sense when you read the code

Case number one
WHENEVER Remote Arm (Zn XX) BECOMES NOT SECURE
AND Home (Area 1) ARM STATE = DISARMED
AND EXIT DELAY, ANY AREA IS NOT ACTIVE
THEN TURN auto arm delay (Out XX) ON FOR FIVE SECS
THEN ARM AREA(S) 1 TO AWAY IMMEDIATELY
THEN CHIRP THE OUTSIDE SIREN 1 TIME

Case number two
WHENEVER Remote Arm (Zn XX) BECOMES NOT SECURE
AND Home (Area 1) IS ARMED AWAY
AND EXIT DELAY, ANY AREA IS NOT ACTIVE
AND auto arm delay (Out XX) STATE IS OFF
THEN CHIRP THE OUTSIDE SIREN 2 TIMES

So now, if I press the remote after the system is armed (like if I arm the system on the way out using the keypad but forgot by the time I got to the car. I’m old, it happens), the outside siren will chirp twice telling me it is already armed. I found that if I didn’t use that output to block the second case, both rules would fire when I pressed the remote button. I think I can do without the checks for the exit delays being acive, but for now, I do have them in.

So now I have both handheld remotes in my wife’s car, and I have both remotes programmed into the homelink in my car. In my car, I can press one homelink button to operate the garage door, and I can press another button to arm my security system. The third button is unused. One thing I have noticed is that with the homelink system, you do have to press and hold the button for a full second or two before it seems to do anything. WIth the handheld remotes, they respond much faster. So I have trained myself to press and hold the buttons when I want it to do something.

Nov 142010
 

Very basic post here.  This is for my mom, who has been having a hard time backing up files.  Mom, all you have to do is copy your files and then paste them in your backup drive.  It really is that simple.  You don’t have to use a backup program, but it can be nice to have such a program that automates the backup.  But if you just want to copy files from one drive to another, then just watch the video here.  This method is good for any type of file copying.  That’s all a backup is–just a copy.  There’s nothing magical about a backup versus a copy.  They are both the same.  So, if you need to make a one time, annual copy of all the images that you shot in one year, then you can certainly follow this method.  You with me?  Good.  Now, just watch the video.

http://skipstechtalk.net/copy-and-paste-video/

Wordpress advanced ticket system