Keeping an eye on things with a cheap wireless IP camera

Categories: Personal

Many of you may know that the wife and I have our first baby due this Spring. One the things I've been researching since I found out the wife was pregnant has been IP cameras. I really like the idea of being able to check in on the baby at any time to make sure she's ok—without actually walking in and risking waking her up.

I looked at a lot of various wireless cameras, from baby-specific video monitors to IP cameras, but the features that were key for me were:

  • Night vision—this is a must to check in on a sleeping baby
  • Viewable from my iPhone—the wife and I both have iPhones and this makes the perfect remote video monitor
  • Remote pan/tilt
  • Audio monitoring (2-way audio would be a bonus)
  • A way for the Grandparents to view in on the baby

After looking at some baby video monitors (which didn't meet all the above requirements anyway) I realized that they're all over priced and most got pretty poor reviews. I was leaning towards getting an IP camera anyway, so this made the decision easier.

After looking at many different models, I ended up purchasing the Foscam IP Wireless/Wired Camera (Model: FI8908W). I looked at a few Axis cameras, which are great cameras, but really more expensive than I wanted to pay and really way more than I needed. I also seriously considered getting Astak Mole IP Camera—it met all my requirements, but just was a little more than I wanted to spend. Then just last weekend I stumbled across the Foscam camera. It met all my needs and I could get it shipped for around $95.

After playing around with the camera a bit last night, here's my findings:

  • The camera uses MJPEG for streaming video—which doesn't natively support audio. There is an ASF streaming URL you can access (which supposedly includes audio,) but I haven't found the necessary codec to view it with Windows Media Player. Supposedly VLC will play the stream fine, but I don't want to install another app if I don't have to.
  • Terrific night vision—I was really impressed with the quality in a pitch black room.
  • Pretty straight forward setup. While I found it easy/quick to setup, someone without good gadget skills might struggle a bit.
  • Decent integrate web-interface. While it has lots of options, it's not particularly user friendly. Fortunately, you can give out URLs straight to the stream for family members.
  • Multi-user configuration. You can have up to 8 separate user accounts. Users can either be an Administrator, Operator or Visitor. An Operator can do everything but change configuration—including changing the camera pan/tilt. A Visitor can only view the picture.
  • The picture quality is a little washed out, but seems to do well in low lighting. I've heard that replacing the lens can improve the coloring, but I think it's sufficient for monitoring purposes—I don't need exact color matching.

I would definitely say the camera is well worth the $100. I'm still playing around with it, but there's a nice iPhone app called Foscam IP Control for $1.99 that allows you to remote view the camera and alter the pan/tilt (it does lack a full screen mode.) Since I'm still not sure how I'm going to expose the camera to the world, I may end up changing apps but it works well.

If you decide this camera, you must watch out for Foscam clones. Apparently there are a slew of online resellers selling clones of the Foscam (same box, same design) that use a different firmware. Foscam has a list of authorized resellers on their site. I ended up using an eBay reseller named usahitec. I ordered the camera on Saturday for $95.60 (w/free priority shipping) and it got here yesterday (Wednesday.)

If you want more information on the Foscam camera, check out Gadget Victims has several really insight blog entries:

So far this seems like an excellent way to monitor my sleeping baby and give the out-of-town grandparents a way to keep up with the baby too!

Samsung TV randomly power off and on

Categories: Personal, Technology

For Christmas this year, the big family gift was a new Samsung PN58B860 58" Plasma TV. Our old TV worked fine, but with the baby on the way I we wanted try and reclaim some living room space—much of which was being taken up by our old Mitsubishi 55" rear projection TV.

We absolutely love the picture, but from day one we started running into a problem with the TV in which it would just randomly power off and then immediately back on. I went through a battery of tests trying to determine if it was a power issue, cable issue, etc. I tried replacing the HDMI cable, hooking the TV up directly to the cable, changing outlets, etc. Nothing I did resolved the issue. Since the problem was random (on average, probably happened once an hour, but we might go a couple of hours with no issues or it might happen several times within a few minutes.) It didn't matter what we were watching—could be cable TV, a DVD, playing XBOX, etc. The TV would just turn off and then turn right back on.

After talking to Samsung and HH Gregg everyone seemed to think it was the power supply on the TV and since the TV was less than a week old, HH Gregg delivered a replacement TV. However, the new TV was having the same problem. So, once again I started going through my battery of tests trying to figure out what was wrong.

After testing tons of things, I finally found the root case—the Internet connection.

Newer TVs are now coming with Internet connections. Being the tech guy I am, I immediately hooked my TV up to the Internet so I could get firmware updates, view Flickr photos, etc. To get up and running quickly, I had plugged my TV into the wireless bridge I was using for my XBOX 360—which I had the IP configured as the DMZ in my firewall (which I had done to resolve some issues with online play.)

Since now the TV was acting as a DMZ, all unrecognized traffic was being routed to the TV. I suspect what was happening is various ping and exploit attacks on my IP address was causing problems with the TV, so the TV would just shut down and power up again. I just never thought that the Internet connection would be the root casue of the TV rebooting.

Anyway, if you have a Samsung TV that's recycling the power, try unplugging your Internet connection and see if that improves the situation. Also, never run a device as a DMZ unless you know what you're doing!

Dead Milwaukee Battery, might not be so dead afterall…

Categories: Potpourri

Several years ago, I bought a set of Milwaukee power tools. I love the tools, but one of the batteries seemed to die after a few years (but very little usage.) The batteries are like $80 and since I don't use the tools very often, I've never bothered replacing the one that seemed to be dead (since I have 2 batteries.)

However, today I came across a very cool tip.

Apparently these "dead" batteries are a known issue with Milwaukee and often the batteries aren't dead, but just need to be jump started. To awaken one of these dead batteries, place a standard 9-volt battery against the leads on your dead Milwuakee battery for 30 seconds (single lead on your Milwaukee is positive, the double lead is negative.)

Sure enough after doing this little tip, I was able to plug my battery in to the charger and it started charging it!

ColdFusion 8 Search Server service (Verity) reporting Error 1067 on startup

Categories: HTML/ColdFusion

Yesterday I rebooted my personal dev server and much to my chagrin, when the server started back up the ColdFusion 8 Search Server service (aka Verity) would not start. Any attempt to start the service would result in the following error:

Error 1067: The process terminated unexpectedly.

I looked around ColdFusion's log files, but couldn't find anything useful so I decided to try and start up Verity from the command line. Running from command line resulted in:

K2Admin - Verity, Inc. Version 5.5.0 (Build 20050601)
Resolved Entity [admin.dtd] to DTD file [C:\ColdFusion8\verity\k2\common\admin.dtd]
D:\ColdFusion8\verity\Data\host\admin\admin8.xml parsed in 16 ms
Status: Starting K2Admin Version 5.5.0 (Build 20050601 Sep  4 2006 14:41:49)
Status: Service Alias: ColdFusionK2 (Host:localhost Port:9951)
Status: Master Administration Server: localhost:9951
Status: Creating Backup Service - 2 threads
Status: Creating Broker Service - 1 threads
*** FATAL Application Error ***
Exiting ...

I hunted around trying to find the root cause for the fatal application error, but I couldn't find anything on Google or any of the forums I checked. Since I wasn't getting anywhere, I decided to try uninstalling and reinstalling the Verity services.

Inside the .\ColdFusion8\verity folder, there's two files called verity-uninstall.bat and verity-install.bat which you can use to uninstall and install the Verity service.

I ran the verity-uninstall.bat batch to uninstall the service and once the service was correctly uninstalled, I went ahead and ran the verity-install.bat file to re-install the service.

Fortunately this fixed the issue for me. I had to re-create all my collection, but that wasn't a big deal (time consuming, but painless.)

So, if your Verity service isn't starting up and nothing else you've tried is working, you might just trying uninstalling and reinstalling the service.

Flash suddenly stops working in Firefox v3

Categories: Flex/Flash

Sometime in the last week or so, Flash stopped working me on many sites. It seemed to stop working around the time of the last Microsoft Vista updates, but I can't be sure. The strange thing is, it worked on some sites (like Adobe) but wasn't working on a lot of the sites I was visiting. I tried reinstalling Flash, but that didn't fix it. Since it wasn't a high priority for me, I haven't had time to look into it until today.

Today I was doing some QA on our site and noticed one of the Flash components was no longer working. Since Flash seemed to break for me around the time of the last Windows Update, I thought I should probably look into things to see if this is a wide spread issue.

I quickly fired up a couple of different versions of Firefox (and on different PCs) and they were all working fine. This seemed to indicate the problem might be local to my installation.

Next, I decided to actually examine the source code in Firebug—which is when I noticed something odd. The <object /> tag was grayed out like it was hidden or being ignored. So, I fired up the same page in another Firefox install and the <object /> tag was being shown as expected.

NOTE:
At this point I also checked some of the sites I remembered where Flash was working and sure enough they were using the <embed > tag instead of the <object /> tag.

Since I thought this was extremely odd, I decided to fire up a brand new Profile to see if things would work in an empty Profile. Dropping to a command prompt, I used firefox –P to create a brand new profile called "test". Next I used firefox -P test -no-remote to open up the new profile (the –no-remote argument allows you to open a separate instance of Firefox using a different profile.)

When using the new Profile, Flash was worked as I expected. This told me something was wrong with my normal Profile, but what?

In order to track things down, I first tried disabling add-ons that looked like primary suspects (Firebug, Web Developer, AdBlock Plus, etc.) Nothing changed. I next tried uninstalling these add-ons. Flash still not working. I finally just uninstalled all the add-ons. And what do you know? Flash started working again.

In order to try to determine which add-on was causing my Flash problems, I tried installing each add-on individually. Unfortunately (at least from a troubleshooting standpoint) after re-installing all the add-ons Flash is still working correctly.

So I obviously had a problem with my profile that was caused by one of the add-ons I had installed. Unfortunately, I do not know which add-on was the culprit. I do know that after removing all my add-ons and reinstalling them Flash is now working properly again.

UPDATE:

Here are a list of the add-ons I had installed, all of which have been reinstalled:

Adblock Plus 1.1.1
Clear Cache Button 0.9b
ColorZilla 2.0.2
Download Statusbar 0.9.6.5
Firebug 1.4.3
Firesizer 0.92
Free Download Manager 1.3.4
Html Validator 0.8.5.8
IE View 1.4.4
Microsoft .NET Framework Assistant 1.1
Organize Status Bar 0.6.3
Page Speed 1.3
Print Context Menu 1.2
QuickFrame 0.5.1
QuickProxy 2009.07.19
Web Developer 1.1.8
XMarks 3.3.2  (NOTE: I didn't actually uninstall this add-on, it was the one exception of an add-on that I did not uninstall.)

Stupid HTML Tricks: Add a non-selectable option to a select element

Categories: HTML/ColdFusion

Occasionally I've run into a situation where I've wanted to add some whitespace between two options in a <select /> element. The most common solution I've seen people use is to include an empty <option></option> tag pair. While this works, it adds a blank entry that the user can actually select—which means you have to add some code to handle the selection of what is designed to just be used for whitespace.

A better solution is to use an empty <optgroup /> that is disabled:

<select>
  <option>Value 1</option>
  <!---// add a non-selectable space between options, the margin-top is for FF spacing //--->
  <optgroup disabled="disabled" style="margin-top: 1em;"></optgroup>
  <option>Value 2</option>
</select>

This will add a non-selectable blank line between "Value 1" and "Value 2". Here's an example:

I use this technique often before normal <optgroup /> tags as it helps make the menu look a little cleaner by adding some separation between the options and works with all modern browsers. Best of all, you don't have to worry about handling those empty <option /> tags!

Custom jQuery selector for finding usable elements

Categories: jQuery

While working on some code today, I faced a problem I've run into a few times in the past where I needed to find the first usable input element within a specific context. What I mean by "usable" is an element which is visible to the user and not disabled.

Finding the first usable input element can be handy when you're using tabbed form, as when the user changes tabs, you can place the focus in the first visible input element. In my use case, I was implement a "reset" on a form where the fields being displayed could vary. I wanted to automatically place the focus in the first visible field after the user triggered a "reset" of the form.

If you're using jQuery v1.3 (or higher) this custom selector is very simple, just add the following to your page:

// finds elements that are usable (i.e. visible on the screen and are not disabled)
$.expr[":"].usable = function (node, index, prop, nodes){
  var $n = $(node);
  // return if the element is viewable or not            
  return ($n.attr("disabled") !== true) && $n.is(":visible");
};

If you're using jQuery v1.2 (or lower) the code is slightly more complex because the ":visible" selector doesn't check with the parent elements to make sure they are all visible. So, in order to make our code work with jQuery v1.2 we'll need to first create a custom selector for which will find elements that are truly visible:

// finds elements that are viewable
$.expr[":"].viewable = function (node, index, prop, nodes){
  var r = false;

  function viewable(n){
    var $n = $(n);
    return (($n.css("display") !== "none") && ($n.css("visibility") !== "hidden"));
  }
  
  // if not usable, stop processing
  if( !viewable(node) ) return false;

  $(node).parents().each(function (){
    r = viewable(this);
    // if not viewable, stop processing chain
    return r;
  });

  // return if the element is usable or not            
  return r;
};

The new ":viewable" selector should pretty much behave the same way that the ":visible" selector does in jQuery v1.3.

The only difference in this version of the ":usable" selector is that we're going to use our custom ":viewable" selector instead of the ":visible" selector:

// finds elements that are usable (i.e. visible on the screen and are not disabled)
$.expr[":"].usable = function (node, index, prop, nodes){
  var $n = $(node);
  // return if the element is viewable or not            
  return ($n.attr("disabled") !== true) && $n.is(":viewable");
};

The ":usable" selector can be used in several ways:

// find all usable form elements
$(":input:usable")

// find the first usable form element
$(":input:usable:first")

While the most common usage of the selector will probably be :input:usable:first, I can see where occasionally you might want to get all the input elements visible to the user.

Hope this helps someone!

Vote now to have ColdFusion provide better XSS protection

Categories: HTML/ColdFusion

Today I submitted the ColdFusion Enhancement Request #80336 to add better tools for preventing XSS attacks. More and more it's becoming common for developers to need to implement some type of WYSIWYG editor, but with that comes the increased risk of XSS attacks.

We've all heard about attacks on MySpace and other major sites and this is because it's actually a pretty difficult problem to solve correctly (since there are so many attack vectors.)

My suggestion would be for Adobe to integrate the OWASP AntiSamy project into ColdFusion. This is an excellent tool for cleaning up input to prevent XSS attacks and is highly configurable via XML.

If you're interested in solving this problem now, see my Using AntiSamy to protect your CFM pages from XSS hacks blog entry for details on implementing AntiSamy right now.

Rounding to the nearest fraction (i.e. specific decimal place)

Categories: HTML/ColdFusion, JavaScript, Java, SQL, Flex/Flash

I'm working on some "star rating" code and I really wanted to round numbers of to the nearest fraction. While the math is really basic, my math skills are rusty and this took me longer to solve than I care to admit. So I thought I'd just blog the solution for anyone else that might find it useful:

round( value/fraction ) * fraction

Like I said, it's very basic math but is very handy when you want to round to something other than to the closest integer.

From the worst day ever, to the best…

Categories: Personal

Today started off horribly. But before I get into today, I have to backtrack a bit.

While I haven't blogged about this yet (because it's still very early,) my wife is pregnant. Getting pregnant has been a long process for us and something our family and close friends have known about. Because so many people have known what we've been going through, we announced it to close friends and family a little sooner than we'd probably have liked.

Last Monday at 5am, Jenn woke me up and she had passed a clot. From the look of things, I was really afraid things were bleak. Jenn continued to spot all last week, so I was really worried that something went wrong w/the pregnancy. The doctor's didn't seem overly concerned and we just needed to wait out our appointment which was scheduled for today at 10a.

Needless to say, last week really sucked. We were trying to stay optimistic, but I was preparing for the worst.

This morning at 4:45am (a week almost to the minute) Jenn work me up again. This time the bleeding was really bad (I even had to clean up the path on the floor to the bathroom.) The clot she passed was about the size of a racket ball. As much as I thought last week was bad, this was much worse. The morning was really tough on us. The morning was a long one waiting for our scheduled ultrasound (which I was really assuming the worse had happened.)

When we saw the doctor we explained what had happened. He didn't seem overly shocked (because we had kept him a breast of the spotting) but I was surprised he didn't seem as concerned as we were.

So when the ultrasound started, I was really trying to figure out how to comfort Jenn, just fearing the worse.

Much to my surprise, I saw a similar black blob as we did on our ultrasound on 9/10, but I was still hesitant to get my hopes too high. However, within seconds a little peanut shape emerged from the blackness and once again we saw the little fluttering of a heart.

After basically preparing myself of the worst for the past week and then just feeling like my thoughts were confirmed this morning, I can't even begin to explain the emotions I was feeling--to say it was a relief doesn't even begin to explain it.

We even got to hear the heart for the very first time. The heartbeat was very healthy and was over the healthy 150 beats per minute they like to hear.

While we're still not sure what's causing Jenn's spotting, the doctor didn't find any lesions--only another smaller clot and he doesn't feel like it's threatening the baby.

So everything looks to be going great. The bleeding is still a concern, but thankfully the baby appears healthy.
It's funny how quickly your life can change with just the lubb-dub sound of a heartbeat. I've always heard how life change on a heartbeat, now I truly understand.

ultrasound_20090921

Create iPhone-style buttons with the iButton jQuery Plug-in

Categories: jQuery

At work we just released another jQuery plug-in called the iButton jQuery Plug-in (which brings the total of open source jQuery plug-ins we've released to four.) This plug-in allows you to generate iPhone-style buttons from checkbox and radio elements. While there are several libraries out there that generated iPhone-style buttons, we couldn't find one that did everything we needed, so I wrote one!

The users of our application are very keyboard centric, so it was very important that we supported keyboard entry. Keyboard support often seems to be overlooked in most UI plug-ins—developers get so focused on the mouse interaction, they forget completely about keyboard entry. So we always make keyboard support a key feature in the jQuery plug-ins we write.

Anyway, here's a list of the key features:

  • Works with checkboxes or radio elements
  • Full keyboard support — use the [TAB] key to move from field to field and use the spacebar to toggle the status of the iButton (or use the arrow keys for radio buttons)
  • Custom event handlers
  • Detach iButton behavior from the element
  • Metadata support — when used with the jQuery Metadata Plug-in, you can define the properties for your button completely within the class attribute of your input elements
  • Enable/disable drag support — while the dragging behavior is intuitive on touch-based devices, it's not always be the best or expected UI behavior and may cause some mouse users problems (NOTE: In order to help differentiate between an intended mouse click or an actual drag event, we're developed the clickOffset option. If the time (in milliseconds) is under this value (120ms by default) it's assumed the user was attempting to click the button and not drag the handle.)
  • Enable/disable animation
  • Single sprite image — easily change the look of your button by just replacing the image sprite
  • Customizable labels — use any labels you want for your buttons
  • Support for disabled buttons
  • Easing support for animations
  • iPhone support

You can see a demo of the plug-in on the Giva Labs - iButton Example Page.

We're pretty happy with the end result and are planning on using it in a few locations in our application. If you like plug-in, don't forget to Digg it!

Disabling "The Publisher Could Not Be Verified" when running program under Vista

Categories: Technology

Ever since upgrading to Windows Vista, I've had a probably with a program that I run infrequently where I'd get a message of "The Publisher Could Not Be Verified" and have to click on a box to verify I want to run the program. Since I don't use the application very frequently, the annoyance was minor.

Today I found myself needing to frequently re-load the application, so this message became really annoying. Windows Vista has an "Unblock" function in the file properties (right-click on the file from Windows Explorer and select "Properties"—it's on the general tab,) but this was not working for me.

I could click the "Unblock" button and apply the change, but the change wouldn't ever take. Since this program is my Program Files (x86) folder, I suspected this was the culprit. While I tried a few things to try to make the change as an admin (such as changing the file attributes—which forces you to grant admin privileges) the thing that end up working for me was to copy the files to another folder (like the desktop) go through the "Unblock" procedure again and then copy the files back to it's sub-folder in the Program Files (x86) folder. This seemed to do the trick.

So, while I'm sure there's a better way to get this to work, if you're getting the message "The Publisher Could Not Be Verified" every time you try to run a program and just using the "Unblock" function doesn't work, try moving the file to a folder that doesn't need administration privileges to modify the file.

SVN reporting "Working copy text base is corrupt"

Categories: HTML/ColdFusion, Java

This morning started off to a rough start. I came in to realize the commit of a merge I made at the end of the day yesterday had failed. Whenever I would attempt to commit the merge changes, it would go through the entire process and then finally fail with a error message of:

org.tigris.subversion.javahl.ClientException: Working copy text base is corrupt

After trying a "Cleanup" and several other steps to rectify the problem, I finally did hit Google to try and find a solution. I came across chris' subversion checksum mismatch - easy workaround which offered several various solutions to the problem.

I tried several of the solutions, but wasn't haven't much success until I came across Michael Sparer's comment:

Thanks for the explanation, which shed some light on how svn manages its working copy. My problem seemed to stem from a bogus file under .svn/text-base, which didn't match the file actually on the server.
- server file (.../x.java): OK
- orig copy from server (.svn/text-base/x.java.svn-base): BOGUS (not same as server)
- checksum (in .svn/entries): matches server, but not server copy

If I can make the bogus copy match what's on the server, then it will also match the checksum, and everyone will be happy.
What I did (i'll call the working dir with the corrupt file "orig-dir":
1. Fresh checkout of svn dir matching orig-dir into /tmp/blah
2. Copy /tmp/blah/.svn/text-base/x.java.svn-base into orig-dir/.svn/text-base
3. Check in successfully

I wish svn would let you refresh a given file from the server...maybe there's a command and I just haven't found it or my svn is too old.

In the end, this ended up being my problem as well. The copy of me .svn/text-base/filename.ext.svn-base was out of sync with the actually copy on the server.

To resolve this, I checked out a clean copy from the server to a tmp folder, then I just replaced the copy in my working folder. After doing this, I was able to check in the file without incident.

NOTE:
I also noticed I had a copy of the template in the .svn/tmp/text-base/ folder. I made a copy of this file and then removed the file this directory before committing. I'm not sure if this step is necessary, but I wanted my local working copy to mirror as closely as possible a fresh working copy.
I could have just wiped my local copy altogether, but I wanted to know the root problem incase it every happens again. Since I have a pretty large repository it takes a while to checkout from SVN, so at least know I have an option to try if I ever run into the issue again that won't require me checking out the entire working directory structure.

Using MSSQL to output time in days, hours and minutes

Categories: SQL

I was working on some code today where I wanted to output a time span in the format 1d 4h 36m (i.e. "1 day, 4 hours and 36 minutes.") I wanted a pure SQL solution so that I didn't have to worry about formatting later. Here's some example SQL that will format a time span by days, hours and minutes.

declare @startTime datetime
set @startTime = '2009-07-11 14:19:40.000'

declare @endTime datetime
set @endTime = getUtcDate()

select 
    convert(varchar(40), dateDiff(mi, @startTime, @endTime)/(24*60)) + 'd ' 
  + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%(24*60)/60) + 'h '
  + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60) + 'm' as Format1
  , case
      when (((dateDiff(mi, @startTime, @endTime)/(24*60))) > 0) then
          convert(varchar(40), dateDiff(mi, @startTime, @endTime)/(24*60)) + 'd ' 
        + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%(24*60)/60) + 'h '
        + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60) + 'm'
      when (((dateDiff(mi, @startTime, @endTime)%(24*60)/60)) > 0) then
          convert(varchar(40), dateDiff(mi, @startTime, @endTime)%(24*60)/60) + 'h '
        + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60) + 'm'
      else
          convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60) + 'm'
    end as Format2
  , convert(varchar(40), dateDiff(mi, @startTime, @endTime)/(24*60)) + ':' 
  + right('00' + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%(24*60)/60), 2) + ':'
  + right('00' + convert(varchar(40), dateDiff(mi, @startTime, @endTime)%60), 2) as Format3
NOTE:
Make sure to keep the dateDiff() time in minutes. If you switch to hours (or days) you'll have rounding issues you'll have to work around. By sticking to using minutes for the equations, you avoid the rounding issues.

When you run this query in Microsoft SQL Server, you'll see that it outputs 3 columns—Format1, Format2 and Format3.

Format1 always outputs the time span in format 0d 0h 0m, even when the days or hours are 0. In my case, I really wanted to ignore days if no days have passed and hours if we're still under 60 minutes. This lead me to create Format2.

Format2 still uses the 0d 0h 0m format, but it will drop off days if less than 24 hours old and drop hours if less than 60 minutes has passed. This leaves you strings like "6d 4h 52m", "4h 10m" or "3m". In my case, this was the best formatting for my uses.

Format3 is an alternative format that places the time span in the format d:hh:mm. While I'm not using that formatting currently, some people may find it useful. This also shows off how you can force the hours and minutes to output as a digit value (i.e. 0:02:01 = 0 days, 2 hours and 1 minute.)

Hopefully this will prove useful to some of you.

CF9 can protect SQL statements stored as string from SQL injection

Categories: HTML/ColdFusion

One of the many new features in ColdFusion 9 is the ability to use every tag within a <cfscript /> block. Developers seem to either love or hate <cfscript /> and I've always falling into the love (well, maybe not love but I'm extremely fond of it.) I just find <cfscript /> a better method for writing business logic. So for me, the addition of being able to access any CF tag via <cfscript /> will be really nice. However, I wanted to point out one specific use case where the addition of being able to script any tag really comes in handy.

One of issue I've run into in the past with ColdFusion is that there's no good way to protect a SQL statement from SQL injections if your SQL is coming from a string variable. In most cases you're going to build dynamic SQL statements within a <cfquery /> block, occasionally I've found situations were this isn't ideal.

In these cases I've wanted to use a UDF to generate a block of SQL for me, because I can easily re-use the function to re-use common blocks of SQL.

The problem with building the statement via a UDF is that when your SQL is generated as a string, there's you can't use <cfqueryparam /> to secure your data (unless you were to save the string to disk, include it in the query and then clean up the temp file.)

This is where CF9's new functionality of being able to script any tag will become handy. ColdFusion 9 introduces a way to bind a token to a SQL parameter. This would allow you to build a SQL statement completely from a string and then bind tokens in the string to parameters.

The following example code comes from John Whish's cfquery in cfml with parameters post:

// create a query
q = new Query();

// don't need to set datasource if using this.datasource in Application.cfc 
q.setDatasource( "cfartgallery" );

// build the SQL statement 
q.setSQL( "select * from Art where artistid = :artistid and issold = :issold" );

// this is the equivalent of cfqueryparam 
q.addParam( name="artistid", value=2, cfsqltype="CF_SQL_INTEGER" );
q.addParam( name="issold", value=1, cfsqltype="CF_SQL_BIT" );

// run the query and get a query object
result = q.execute();

// dump query object
writeDump( result );

As you can see from the code, the setSQL() method takes a string of SQL to execute. You'll also notice two unique tokens in the SQL ":artistid" and ":issold". When you invoke the q.addParam() method, you're replacing the tokens with a binding SQL variable—which protects you from SQL injections.

So, the addition to the new Query() object ends up solving a problem that I've run into in the past to which there's never been a really good solution for in the past.

I've been meaning to post this since the CF9 beta became public, but work kept getting in the way. :)