Ed’s blog

Latest updates from what I’m doing now

Whats this?

Eddie's techie blog. Mostly concerned with ColdFusion, PHP, Asterisk, C++, games , Flex amongst others.

Hi all,

Scrolling on my Mac was annoying me so I had a look into how you can scroll properly and of course I found out the very easy way to scroll using just two fingers. In the process I’ve also found out you can do a whole bunch more using the multi-touch.
Check it out here.

Also I’ve had a lot of missing songs in my iTunes and haven’t found a very easy way to remove them. Some quick Googl’ing found that a guy called Doug has made a handy Applescript to remove dead songs. You can get it at Doug’s site here.
Very easy to install just put it in the iTunes script folder in /Users/username/Library/iTunes/Scripts folder and it will appear in iTunes as a weird scroll shaped menu. I tried it, it works a treat. Removed over 270 missing files from my library, hurray!

PHP AGI Hangup

October 1st, 2008

Hi all,

Hangups are handled by PHP AGI by registering a sig_handler function. Whenever a user hangs up then this function is called. However as I found out recently you cannot use the agi within the hangup handler. You can use the verbose command and see what it outputs to the system/error log as the verbose command outputs to the default php error log as well as to the Asterisk CLI. Here is some simple code to register your hangup handler:


function sig_handler($signo)
{	//Do some stuff in here
	exit(0);
}

//Register the hangup handler
if (function_exists('pcntl_signal'))
{
      pcntl_signal(SIGHUP,  "sig_handler");
} 

I’ve no idea what pcntl_signal is but its installed alongside Asterisk by default I believe so you shouldn’t need to do anything extra to get it working.

If you are stuck, the approach we took is to set some variables in the dialplan and have some conditional statements within the dialplan to check these variables. There is a special extension ‘h’ which is called when a hangup occurs. The conditional Gotoif checks for a boolean statement which if true will attempt to go to the first extension, if false it will go to the second extension.


exten => h,1,GotoIf($[${EXISTS(${aVar})}]?2:4)
exten => h,2,DoSomething()
exten => h,3,GotoIf(${aVar} > 0 ? 4:5)
exten => h,4,Goto(some_extension)
exten => h,5,Goto(some_extension)

PS. I’ve noted that labels don’t appear to be working(for me at least) in the dialplan. By labels I mean:


exten => h(myLabel),2,DoSomething()

According to the docs I should be able either of below within a GotoIf:


exten => h,1,GotoIf($[${EXISTS(${aVar})}]?myLabel:4)
exten => h,1,GotoIf($[${EXISTS(${aVar})}]?2(myLabel):4)

Both of these fail for me, telling me that the extension doesn’t exist.

Hey all,

Very quick post, very handy little entry on Lifehacker - how to watch videos on Hulu from anywhere. For someone like me who wants good quality TVs without searching around for ages then Hulu is for you. I’ve tried it on my Mac (Leopard) and it works a dream.

Lifehacker link

Going to be a short blog post as Leopard has made this process very simple as PHP5 has been included as standard with the new OSX.

First thing you need to do is open terminal and edit your http.conf file by typing:

nano -sw /etc/apache2/httpd.conf

Firstly you need to enable the php module. Search for ‘php5_mod’ by typing it in when you press ctrl+w and press enter. This will bring you to a line with a hash before it….

#LoadModule php5_module libexec/apache2/libphp5.so

Remove this hash to enable php5.

Next you need to setup your document root, i.e. the location your webserver will default to when you go to http://localhost. Search for ‘DocumentRoot’ within nano again by pressing Ctrl+w and hitting return. Alter the location (I think its Library/WebServer or something by default) to be whatever you wish your webserver root to be. I set it to be the Sites folder of the User I setup for Leopard.

Next you need to enable Apache, this is exceedingly easy. Go to System Preferences and then Sharing. Click the checkbox next to Web Sharing and apache should be ready to do. Put a sample html or php file(including is always a good way to test if php is working) in the document root and go to http://localhost and you should see the sample page.

Next you need to get MySQL. I got it here: http://mirrors.sunsite.dk/mysql/downloads/mysql/5.0.html#macosx-dmg.
Follow the very easy steps in the package and you should be hunky-dorey!

Hi all,
I’m back after a nice holiday and have begun to setup my brand new spanking macbook pro. I downloaded the Gmail Notifier and I need it to notify me on several accounts but it only supports a single account by default. I found this nice workaround here: http://www.macosxhints.com/article.php?story=20061117161341318

Works nicely!

As the title says, I’m going to finish up blogging on techie stuff for the moment due to the travelling! you can keep up to date with the travels at http://travel.edmundlong.com

Hi all haven’t posted in a while have a couple of posts to catch up on :)

I’ve recently have the need to perform locking in php on the server level. Basically I want just a single php script to execute a piece of code at any time. This requirement was brought about by race conditions whereby two php scripts would go and execute the same piece of code which would consequently conflict with one another. I needed to stop this. The solution is nice and easy and works a treat.

Whenever a php script comes in and requests a lock on a piece of code it attempts to create a directory using phps mkdir with a specific name myLock1. If the directory already exists then the mkdir returns false and the locking fails. If the mkdir completes then the php script has locked the section of code. Now when another seperate php script comes in and tries to create the directory mkdir will return false and the locking will fail.

When the php script is finished with a piece of code then it unlocks by deleting the directory using rmdir. Now a new php script is free to come in lock the piece of code again.

This locking works across all php scripts (i.e. is server wide) and provides named locks as you can give the directories different names. So one section of code may be locking a myLock1 directory and another part be locking myLock2 directory but these won’t interfere with one another. I’ve attached the code for this simple locking below and highlighed how it would be used to lock a piece of code.


class FLock
	{	private $files = array();
		private $oUUID = "";

		function __construct()
		{	$this->oUUID = new UUID();
		}	

		function __destruct()
		{	foreach($this->files as $file)
			{
				fclose($this->files['$file']);
			}
		}

		private function saveFileHandle($filepath)
		{
			if(!array_key_exists("$filepath",$this->files))
			{
				$this->files["$filepath"] = array();
				$this->files["$filepath"]['directory'] = $filepath;
				$this->files["$filepath"]['locked'] = false;

			}

		}

		public function attemptFileLock($filepath=false, $lockFile=true)
		{
			global $fileLockingLocation; 

		    $this->saveFileHandle($filepath);

			$canLock = false;

		    if($lockFile == true)
			{	if(!$this->files["$filepath"]['locked'])
				{	try
					{
						$canLock = mkdir("$filepath");
						if($canLock)
						{
							$this->files["$filepath"]['locked'] = true;

						}

					}
					catch(Exception $ex)
					{
						LogError($ex);
					}
				}
			}
			else
			{	if($this->files["$filepath"]['locked'])
				{
					rmDir("$filepath");

					$this->files["$filepath"]['locked'] = false;

					//TODO - Fix
					return true;

				}
			}

		    return $canLock;
		} 

		public function removeAllFileLocks()
		{	foreach($this->files as $file)
			{	rmDir($file['directory']);
				$this->files[$file['directory']]['locked'] = false;
			}
		}
	} 

To lock the some code you need to just do like what I’ve done below.


$this->oFL = new fLock();
$fileLockResult = $this->oFL->attemptFileLock("/fileLocks/".$lockName."_lock",true);
if($fileLockResult)
{
      //Perform mySQL inserts,deletes etc.
      $fileLockResult = $this->oFL->attemptFileLock("/fileLocks/".$lockName."_lock",false);
}
else
{
     //Sleep between 0.5 - 1 second
     usleep(500000+(mt_rand(1,10))*500000);
}

I’ve had to perform php redirects of late also, its nice and easy but as Michael found out, even when you put a redirect in a chunk of code, the rest of the script gets parsed and the redirection doesn’t occur straight away. So you may want to put in a die() or something after the redirect if you wish to abort the rest of the page’s processing.


header( 'Location: http://www.yoursite.com/new_page.html' ) ;

Performing error logging with PHP is another task I’ve wanted to do of late. Basically I’ve created a php wrapper page that takes in a script location from the Asterisk dial plan and executes the passed in script within a try catch block. Wrapping the script execution within the try provides a simple error logging mechanism. Anywhere I want to log errors that are fatal to the application I can just perform a throw within a php script and the top-most wrapper will always catch and log the error.


#!/usr/bin/php -q
<?php

	require_once $phpScriptPath.'common/phpagi.php';
	require_once ("utility/logging.php");

	global $agi;
	try
	{	if(!isset($agi))
		{	$agi = new AGI();
		}
		$scriptName = $agi->get_variable("scriptName");
		$agi->exec("AGI",$scriptName['data']);

	}
	catch(Exception $ex)
	{	if(!isset($agi))
		{	$agi = new AGI();
		}

		$errorFileName = 'error-'."date-".date("dmy_His");
		$errorInfo = "Error message: ".$ex->getMessage();
		$errorCode = "Error code: ".$ex->getCode();
		$errorFile = "File: ".$ex->getFile();
		$errorLineNo = "Line number: ".$ex->getLine();

		if(!isset($agi))
		{
			$errorCode = $errorCode." - AGI cannot be created.";
			$errorFile = $errorFile." - applicationWrapper.php catch block.";
		}

		//Log to error log directory
		writeVariableToFile($errorInfo,$errorFileName);
		writeVariableToFile($errorCode,$errorFileName,true);
		writeVariableToFile($errorFile,$errorFileName,true);
		writeVariableToFile($errorLineNo,$errorFileName,true);

		if(isset($agi))
		{	//Log to terminal if possible
			$agi->verbose($errorInfo);
			$agi->verbose($errorCode);
			$agi->verbose($errorFile);
			$agi->verbose($errorLineNo);
			$agi->text2wav($ex->getMessage());
		}

		//Log to php error log
		error_log($errorInfo);
		error_log($errorCode);
		error_log($errorFile);
		error_log($errorLineNo);

		exit(0);

	}

	exit(0);

?>

The error_log is a built in php error logging system. You can specify where PHP logs errors to within you php.ini config file, I set it up to log to the system log by uncommenting the error_log = syslog line but you can easily setup your own location for logging errors.

From the Asterisk dial plan i.e. the extensions.conf you basically have something like

exten => s,n,Set(scriptName=/my/Path/To/File/myfile.php)
exten => s,n,AGI(/path/to/wrapper/applicationWrapper.php)
exten => s,n,Hangup()

Hi again,

I’ve been delving into the wonderful world of Farcry CMS. I needed to create a custom type and having never created one before didn’t really know where to start. Luckily for me I’m working with one of the ex-developers of Farcry Michael and he stepped me through how to create it. This process is for the latest beta version of Farcry 5 so may be different for other versions of the CMS.

First and foremost you need to need to create a new type cfc in you /webroot/farcry/projects/#projectName#/packages/types directory. I created a CFC called Products(although I think you may be better of leaving it all lower case, more on that later) which contains several properties. The properties contained in this CFC define what is to be contained in the request stObj whenever someone hits/calls the new types page. The products cfc extends the farcry core types cfc and so inherits all the generic stuff like status verison, object id etc properties that are common across all farcry objects. Next I began to include my own custom ’stuff’ that I want specific to that object. I created a title, display method and product type. The title is just a string and the display method goes off and creates a drop down list of all objects in the products directory in the webskin that begin with ‘displayPage’, again more on that later. The product type is a list of allowed types of products. I specified this as a string but it can easily be replaced by a query or you can pull in other Farcry objects. Finally we need an edit function that tells farcry where to look when editing the object in the back-end. I specified that farcry should look in the _Products directory and for edit.cfm. So whenever we are editing an object in the back end Farcry will load up edit.cfm and this page essentially becomes a regular form style page with a little difference as you need to save the changes back to the Farcry object & DB when completed.


<cfcomponent displayname="Products" extends="farcry.core.packages.types.types" output="false" buseintree="true">
<cfproperty ftSeq="1" ftFieldset="General Details" name="title" type="string"hint="Product title." required="yes" default="" blabel="true" ftlabel="Product Title" />
<cfproperty ftSeq="2" ftFieldset="General Details" name="displayMethod" type="string" hint="Display method to render this HTML object with." required="yes" ftLabel="Display Method" ftType="webskin" ftPrefix="displayPage">
<cfproperty ftSeq="3" ftFieldSet="General Details"  name="productType" type="string" hint="The product type" required="true" default="Type1" ftLabel="Product Type" ftType="list" ftList="Type1:Type1,Type2:Type2,Type3:Type3,Type4:Type4">
<!--- Object Methods --->
<cffunction name="edit" access="public">
	<cfargument name="objectid" required="yes" type="UUID">

	<!--- getData for object edit --->
	<cfset stObj = this.getData(arguments.objectid)>
	<cfinclude template="_Products/edit.cfm">
</cffunction>

</cfcomponent>

The edit.cfm is quite long but I’ve edited it a bit and included it anyway.


<cfsetting enablecfoutputonly="yes" />

<!--- import tag libraries --->
<cfimport taglib="/farcry/core/tags/navajo/" prefix="nj" />
<cfimport taglib="/farcry/core/tags/widgets" prefix="widgets">

<!--- determine where the edit handler has been called from to provide the right return url --->
<cfparam name="url.ref" default="sitetree" type="string">
<cfif url.ref eq "typeadmin">
	<!--- typeadmin redirect --->
	<cfset cancelCompleteURL = "#application.url.farcry#/content/dmProducts.cfm">
<cfelse>
	<!--- site tree redirect --->
	<cfset cancelCompleteURL = "#application.url.farcry#/edittabOverview.cfm?objectid=#stObj.ObjectID#">
</cfif>

<!--- default form elements --->
<cfparam name="form.title" default="">
<cfparam name="form.productType" default="">

<!--- local variables --->
<cfparam name="errormessage" default="">
<cfset lProductTypes = "Type1, Type2, Type3, Type4">

<!------------------------------------------------
	Form Action
	 - self posting form
------------------------------------------------->
<!--- action: cancel --->
<cfif isDefined("form.cancel")>
	<!--- cancel content item lock --->
	<cfset setlock(locked="false")>
	<cflocation url="#cancelCompleteURL#" addtoken="no">
</cfif>

<cfif isDefined("form.update")>
	<!--- action: update --->
	<cfset stProperties = structNew()>
	<cfset stProperties.objectid = stObj.ObjectID>
	<cfset stProperties.label = form.title>
 	<cfset stProperties.title = form.title>
	<cfset stProperties.datetimelastupdated = Now()>
	<cfset stProperties.lastupdatedby = session.dmSec.authentication.userlogin>
	<cfset stProperties.displayMethod = form.displayMethod>	 

	<!--- update the OBJECT if no error occured and reloacte--->
	<cfif NOT len(errormessage)>
		<!--- remove content item lock --->
		<cfset setlock(locked="false")>
		<!--- update content item --->
		<cfset setData(stProperties=stProperties)>

		<!--- if not typeadmin edit then refresh JS tree data --->
		<cfif url.ref neq "typeadmin">
			<!--- get parent to update site js tree --->
			<nj:treeGetRelations typename="#stObj.typename#" objectId="#stObj.ObjectID#" get="parents" r_lObjectIds="ParentID" bInclusive="1">
			<!--- update site js tree --->
			<nj:updateTree objectId="#parentID#">
			<!--- relocate iframes for tree and edit areas using JS --->
			<cfoutput>
			<script type="text/javascript">
			if(parent['sidebar'].frames['sideTree'])
				parent['sidebar'].frames['sideTree'].location= parent['sidebar'].frames['sideTree'].location;
				parent['content'].location.href = "#cancelCompleteURL#"
			</script>
			</cfoutput>
			<cfabort>

		<cfelse>
			<cflocation url="#cancelCompleteURL#" addtoken="no">
		</cfif>

	<cfelse>
		<!--- show error --->
		<cfoutput><p id="fading1" class="fade"><span class="error">#errormessage#</span></p></cfoutput>
	</cfif>

<!--- set default values for form--->
<cfelse>
	<!--- Lock content item for editing--->
	<cfset setlock(locked="true")>

	<cfset title = stObj.title>
	<cfset productType = stObj.productType>

</cfif>

<!------------------------------------------------
	Form Display
------------------------------------------------->
<!--- output form UI --->
<cfoutput>
<form action="#cgi.script_name#?#cgi.query_string#" class="f-wrap-1 wider f-bg-medium" enctype="multipart/form-data" name="fileForm" method="post">
	<fieldset>
<h3>#application.rb.getResource("generalInfo")#: <span class="highlight">#stObj.title#</span></h3>
		<label for="title"><b>#application.rb.getResource("titleLabel")#</b>
			<input type="text" name="title" id="title" value="#title#" maxlength="255" size="45" /><br />
		</label>

		<!--- <widgets:fileUpload fileFieldPrefix="css" fieldLabel="Upload CSS:" uploadType="file" fieldValue="#stObj.filename#" previewURL="/css/" bShowPreview="0"> --->
		<widgets:displayMethodSelector typeName="Products" prefix="displayPage">

		<label for="productType"><b>Product Type:</b>
			<select name="productType" id="productType" multiple="true"><cfloop index="iProduct" list="#lProductTypes#">
				<option value="#iProduct#"<cfif ListFindNoCase(productType,iProduct)> selected="selected"</cfif>>#iProduct#</option></cfloop>
			</select><br />
		</label>
	</fieldset>
	<div class="f-submit-wrap">
	<input type="submit" name="update" value="OK" class="f-submit" />
	<input type="submit" name="cancel" value="Cancel" class="f-submit" />
	</div>
</form>
</cfoutput>
<cfsetting enablecfoutputonly="no" />

Now you’ve created your products.cfc log into the farcry backend and go to the Admin tab. Select COAPI management from the dropdown and then select types on the left hand nav. You should now see your newly created products object here ready to be deployed. You can select deploy and your products should be available in the backend. However you need to do one more thing before you can begin to create products in the backend and view them on the front end, you need to define how products are to be displayed on the front end. To do this create a products directory in the /webroot/farcry/products/#appName#/webskin/ and within this create a page called displayPageStandard. Whenever this page is called the stObj struct is passed into the page and you can now access all the properties of this specific product and display it as you wish. So within this page I just dumped the stObj and then created a simple table of the object title and type. Obviously you can do much more complex stuff than this but this is really just a tutorial on how to do it.

Now you should be able to create product objects select a display method and edit them according to your own custom edit.cfm. One problem I never encountered on my Mac but did in staging was that I had created the products cfc with a large “P” and had use the same casing everywhere around the project. No problem there but in staging an error occurred saying that the table ‘products’ couldn’t be found. Looks like Farcry LCASEs the products name when creating the table, Mac’s NFS filesystem was forgiving but on Linux the problem reared it’s head. Had to rename the table manually and it all worked fine again. Perhaps this is just a bug in the beta that will be fixed come the release date.

I wrote this based entirely off my memory do hope I haven’t left anything out!!!

Hi all haven’t posted in a while.

Been doing a fair bit of PHP recently and have come across some useful PHP snippets.

I was looking to perform multiple MySQL queries but the plain old mysql_query doesn’t allow it unfortunately. After doing a little bit of digging around I found that you need to use mysqli_multi_query to perform multi-queries. Of course you need to connect to your MySQL database using mysqli_connect(and I believe you need to have enabled a seperate mysqli module for php).

I’ve also been doing quite a bit of string manipulation with php. I was looking for a similar function to Coldfusion’s ListContainsNoCase whereby I needed to search a comma seperated list for a specific value. Didn’t find anything especially useful so learnt that using the explode function converts a string separated by a specified delimiter into an array which can then be searched using the in_array function. The strict flag is especially useful if you require an element of the array to match exactly the original term you’re looking for in the list.

String comparison was another sticking point last week. I had a string containing a single character, “*” and attempted compare this string variable using the double equality operator(i.e. $myVar == “*”) but was getting a false match every time. After printing out the variable many times and ensuring that it did indeed have the right value I began to think something else was wrong and had a look at the php documentation. After trying out the Strcasecmp function I began getting the matches I expected. strcasecmp is a binary safe case-insensitive string comparison. A binary-safe function is essentially one that treats its input as a raw stream of data without any specific format. It should thus work with all 256 possible values that a character can take (assuming 8-bit characters). Most functions are not binary safe when using any special or markup characters, such as escape codes or those that expect null-terminated strings. A possible exception would be a function whose explicit purpose is to search for a certain character in a binary string.(thank you Wikipedia). As I was working with a special character my string comparison was crapping out, now I know why.

Creating a singleton class in PHP is something that is a very common operation and after looking at a handy tutorial I came up with a handy singleton class which is used to obtain and instantiate singleton objects. Further work needs to be done on this to allow for multiple objects to be instantiated but it works pretty well for me.(sorry about the indentation, Wordpress screwed it up on pasting it in)


<?php

class Singleton
// ensure that only a single instance exists for each class.
{
	function getDBConnection()
	{	static $db = null;
		global $dbLocation;
		global $dbusername;
		global $dbpassword;
		global $cstdatabase;

		if($db == null)
		{	$db = mysql_connect($dbLocation,$dbusername,$dbpassword);
			mysql_select_db($cstdatabase) or die( "Unable to select database");
		}

		return $db;

	}

	//TODO: Make this more flexible for parameters into inited functions, possibly an array
    public function &getInstance ($class, $classPath=null,$arg1=null)
    // implements the 'singleton' design pattern.
    {	$lowerClassName = strtolower($class);
        static $instances = array();  // array of instance names

        if (array_key_exists($lowerClassName, $instances)) {
            // instance exists in array, so use it
            $instance =& $instances[$lowerClassName];

        } else {
            // load the class file (if not already loaded)
            if (!class_exists($lowerClassName)) {
                if($classPath)
				{
					require_once "$classPath/$class".".php";
				}
				else
				{
					switch ($lowerClassName) {
	                    case 'object1':
	                        require_once 'object1/Object1.php';
	                        break;

	                    case 'object2':
	                        require_once 'object2/Object2.php';
	                        break;
	                    default:
	                        require_once "$class".".php";
	                        break;
	                } // switch
				}

            } // if

            // instance does not exist, so create it
            $newClass = new $class();

	    $instances[$lowerClassName] = $newClass;	

	    if(method_exists($newClass,"init"))
	    {	  if($arg1 != null)
		 {	$newClass->init($arg1);
		 }
		 else
		 {
			$newClass->init();
		 }
	   }

	   $instance =& $newClass;
        } // if

        return $instance;

    } // getInstance

} // singleton

?>

A couple of links for iTouch stuff again. Theres a hand-recognition application out for the iTouch, it works…….kinda. Check out the demo at Gizmodo.

I saw today that its possible to setup Voip on the iTouch. I’ve not tried it yet but looks cool. You need a mic addon to get it working but doesn’t look especially hard to get going. Touchmods.

Recently I’ve had the need to setup an Asterisk machine to connect to Voxbone via IAX. So basically a person would dial a DID from a normal phone, Voxbone picks this up and then through the URIs you setup on their site maps from the DID to your own Asterisk box. When a call comes in the call gets routed according to your Asterisk’s iax.conf to a context you setup in your extensions.conf Below are the conf files that we used to get Voxbone working with Asterisk there are still some issues with calls not getting through on occasion but hopefully that will be fixed soon.

Within the Voxbone menu create a new URI of type IAX(Configure->URIs->Create or modify URI). The uri value I set to be : {E164}@xxx.xxx.xxx.xx - where the X’s should be replaced by the IP of your Asterisk machine.
After that go to Configure->DIDs&Trunks. You should see a list of DIDs you own and map a DID to the URI you just setup. Next go to POP and setup the POP to be the closest location to your own location. I selected US-LA but changed again to US-NY after that stopped working. Once you’ve selected and updated your POP then go and get a list of the host IPs for that region on the same page as the POP update. Get the list of IPs and add then to your IAX conf file in the host string like what I’ve done below.

Iax.conf


[general]
bandwidth=low
jitterbuffer=no
tos=lowdelay

autokill=yes
[voxbone]
disallow=all
allow=ulaw
allow=gsm
canreinvite=no&no&no&no&no&no&no&no&no&no&no&no&no
context=default
host=81.201.84.21&81.201.84.22&81.201.84.24&81.201.84.25&81.201.84.26&81.201.84$
insecure=very&very&very&very&very&very&very&very&very&very&very&very&very
type=friend

extensions.conf


[default]
exten => DID_ASSIGNED_BY_VOXBONE,1,Goto(menu,1,1)

When a call comes in from one of the IPs listed in the host string the call is routed to the default context which in turn routes the call to an internal menu system.