No route matches “…” with {:method=>:get}

I configured Ruby on Rails to run with Apache, because I’m not too worried about speed and didn’t want to mess with proxies. I also configured the app to run in a subdirectory, using Apache’s Alias directive to point to the app’s public directory. I’ll point out this is the first time I’ve ever looked at Ruby in my life, and my first time working with any MVC framework, although I’ve looked into them a bit.

I was getting the dreaded No route matches "/subdirectory/" with {:method=>:get} error and it seemed pretty clear what the problem was. The app didn’t know it was in a subdirectory; I’d probably need to edit the routes to tell it so. It seems this is the last thing the people in Google-land were needing to do, but I eventually figured it out. I’d need to do something like this with the routes:

map.connect 'subdirectory/:controller/:action/:id'

So I took a look at routes.rb and it was using resources, not traditional routes. So what do I do with that?

It took hours of searching before I found the answer to my problem — a testament to the quality of Rails’ documentation I suppose. The answer is path_prefix

map.resources :groups, :path_prefix => 'subdirectory/'

You can also use it for the root as well.

map.root :controller => 'start', :path_prefix => 'subdirectory/'

Now I just have to fix the fact that the author of the app hard coded all sorts of stuff with the assumption that the app wouldn’t be in a directory. Grrr.

Update: Turns out it’s even easier than that. I didn’t have to change routes.rb at all.

config.action_controller.relative_url_root = '/subdirectory'

This has the added advantage of fixing things like linked stylesheets and stuff as well.

Configuring Unison on Windows

Unison is a free file sync utility that runs on Windows, OS X, or Linux. As with most of these things, Windows support is a bit of an afterthought, and can be tricky to work with. Here’s a step-by-step list of what I did to get it working. Note that I’m only setting things up for mirroring (ie one-way sync) so I only installed the SSH server and Unison text client on the “client” and the remainder of the stuff on the “server.” If you’re doing full 2-way sync, perform all the installations on both machines.

  1. Install GTK for Windows. I tried a couple of installers that didn’t work, but found an installer as part of this project that worked great.
  2. Install an SSH server. freeSSHd is a great little program, and it’s a tiny download. Make sure to install as a Windows service when it asks you.
  3. Configure the SSH server. Starting up the freeSSHd GUI actually tries to open another SSH server, even though you configured one to run as a service. This means that changes you make in the GUI won’t take effect until you restart the service. The only thing you’ll need to do is create a user, with password stored as SHA1 hash, that is allowed to use the shell.
  4. Configure environment variables. Unison expects to find an environment variable called HOME and will complain if it doesn’t find it. I set mine to C: and it’s happy with that.
  5. Get a fake SSH client. Unison expects that it can say ssh and something will answer. So first download PuTTY and Plink and make sure they’re in your path. Then download the Unison-ssh wrapper and make sure it’s in your path. It will redirect any calls for ssh.exe to plink.exe
  6. Put the Unison client in place. Windows binaries are available here. Rename the binaries something simple like unison-gui and unison. I suggest naming the text version unison as this is the name that Unison expects the client to have. Make sure the binaries are also in your path.
  7. Test your SSH. At this point you should be able to go to a command prompt and do this:
    ssh -l username -pw password server "unison -version"
    and get something like this:
    unison version 2.27.57
  8. Test your Unison. Assuming that worked, try testing Unison with the -testserver parameter:
    unison -testserver -sshargs "-l username -pw password" c: ssh://server//
    which should give you something like this:
    Connected [//server1/c:/ -> //server2/c:/]
  9. Set up your files. Congratulations, the servers are talking to each other! I suggest running the next step in the GUI, as there may be a lot of initial file syncing to be done:
    unison-gui -sshargs "-l username -pw password" c:directory ssh://server//directory
    It’s pretty self-explanatory, arrows point in the direction of updates, click “Go” when you’ve got it set up how you want it.

After the initial setup, you can continue using the GUI, or start using the text mode. There are a number of preferences that make things easier, which you can learn about in the documentation. As I mentioned, I’m using this for mirroring, so I use the -force parameter to make sure the “server” always takes priority over the “client.”

Creating a FreePBX module

The process of creating a module for FreePBX is, in theory, documented in a number of wiki pages, but these are worse than nothing. Full of outdated information — some of it labeled as such, some of it not — as well as broken links and vague promises of information “to be determined,” this collection of pages does more harm than good. Here, then, are some pointers to get a module working, presented as a list of files you need to have in your module’s directory.

module.xml
One of the few well documented pages in the wiki, this determines how the module is displayed within the FreePBX menu structure, as well as information on version, change logs, etc. that will be displayed in the module administration page.
install.php
Any code that needs to be executed when the module is installed should be placed here. This includes database setup; the wiki will tell you that install.sql fulfills this purpose, but it doesn’t. Within this file you have access to the $db object, which is a PEAR::DB object. Note that this file gets executed during upgrades as well. So for example if you add a column to an existing FreePBX database table, make sure to catch any errors that might happen if the column already exists from a previous install.
uninstall.php
Similarly, this file is used to clean up after the module is uninstalled. Again, uninstall.sql is not processed properly so all database cleanup should be in this file.
functions.inc.php
This file is necessary if you want to interact with other FreePBX modules; for example, to add a drop-down box while editing an extension. How is this done? During the initialisation stage of the page, before anything is output, config.php runs through the list of installed modules and includes all their functions.inc.php files. It then checks for a function called modulename_configpageinit and executes it if found.
Within this function you can call addguifunc to specify a function to be run when the page displays, and addprocessfunc for one that is run after the page has been submitted for changes. The simple act of adding a drop-down box to a page requires you to prepare a list of items at the initialisation stage (using addoptlist and addoptlistitem) and then refer to that list during page output in your GUI function to actually output the drop-down box (using the gui_selectbox object.) To save the value, your process function is called, and you are given the chance to do what you like with the POST request.
page.modulename.php
This is one of the many things in the wiki marked as “deprecated” with no mention of what replaces it. So I’ve continued to use it for the module’s own page. Unlike other modules’ pages, when you’re creating your own module’s page it is largely included as-is, meaning you can write it like most any PHP page. If you want to do anything “AJAX” in your pages, you’ll need to exit from the page when you return an XML or JSON value; this will prevent FreePBX from tacking it’s own stuff onto the output.

WMI error 80041010 on performance counters

I recently was having problems with my WMI queries. Following some (bad) advice I rebuilt the repository. It didn’t solve my problem, and afterwards all the performance counter classes had disappeared. Win32_PerfRawData_* and Win32_PerfFormattedData_* were gone, reporting error 0x80041010 [“Invalid class”] (Instead of an error 0x80041010, MS says you might get error 0x80041002 [“Object could not be found”] or error 0x80041006 [“Insufficient memory”] when trying to connect to a nonexistent class.) All the rebuilding and troubleshooting and searching MOF files gave me nothing.

The answer? winmgmt /resyncperf rebuilds the performance counter classes in the repository. To be extra safe, winmgmt /clearadap clears the old data first.

Continue reading “WMI error 80041010 on performance counters”

ATT00000.txt files in Outlook

This information is for people who create email messages in programming languages, not Outlook users.

The separator (defined in the Content-Type header) is used to start a new part of the multipart MIME message. Standard practice (not sure if it’s RFC behaviour or not) is to place an instance of the separator at the end of the message. Outlook sees this as the start of a new attachment. Because it has no Content-Disposition information it names it automatically, and of course there’s no content so it’s an empty file. So by not placing the separator at the end of the message, you avoid the empty attachment.

Searching for the answer today I have seen loads of people asking about this online, nobody came up with an answer. Part of the problem is the frequency with which Outlook generates these ATT*.txt files; some people were seeing their attachments replaced with these empty text files, some were getting blank email bodies, but with attachments and the empty text files, etc.

This just came to me after spending the day trying to figure it out, and it works. The logic of the first paragraph is entirely guesswork on my part.

Quotes

It sometimes seems that Einstein was the point at which physics broke away from common sense.

Jad Abumrad, Radiolab

They show [Hitchcock’s The Lady Vanishes] very often in Paris; sometimes I see it twice in one week. Since I know it by heart, I tell myself each time that I’m going to ignore the plot, to examine the train and see if it’s really moving, or to look at the transparencies, or to study the camera movements inside the compartments. But each time I become so absorbed by the characters and the story that I’ve yet to figure out the mechanics of that film.

François Truffaut

Set list for Tori’s 1996 Vancouver show

I went to see Tori Amos back in the day – 19 July 1996 at the Orpheum in Vancouver – with a Dutch girl I was crazy about, as well as my future wife and her boyfriend. Here’s the set list, because I needed somewhere to put it:

  • Beauty Queen/Horses
  • Yes Anastasia
  • Blood Roses
  • Little Amsterdam
  • Cornflake Girl
  • Doughnut Song
  • Pretty Good Year
  • China
  • Precious Things
  • Leather
  • Caught A Lite Sneeze
  • Talula (Tornado Mix – interrupted because of tech problems)
  • Happy Phantom
  • Me And A Gun
  • Encore 1
    • Putting The Damage On
    • A Case Of You
  • Encore 2
    • Butterfly
    • Tear In Your Hand
    • Sister Janet (harmonium organ)

See also here.

PHP fatal errors in imagepng()

I upgraded to PHP 5.2 from 4.3 recently and came across a couple of error messages: php[7028], PHP Warning: imagepng(): gd-png: fatal libpng error: zlib error in … followed by: php[7028], PHP Warning: imagepng(): gd-png error: setjmp returns error condition in …

Turns out the paramters for imagepng changed in PHP 5.1.3, and I’m not sure what the third argument used to be, but where I had imagepng($image, null, 100) it died, because the third argument (quality) is supposed to be 0 to 9 now.

I came across postings saying to replace DLL files and all this nonsense, but all I needed to do was change the 100 to a 9.

Driver for PC card from a Linksys WET-11 bridge

The Linksys WET-11 bridge, if taken apart, contains a PCMCIA (PC Card) wireless adapter with an external antenna connection, which could be very useful.

Unfortunately it has no helpful identifying marks. On one side there is an FCC ID (PKW-WM11); the other side has a model number (?) of WM302-IJR10, a serial number, and a date code. Do a search for that FCC ID and you’ll get tons of people asking where they can find drivers, and if anyone knows what kind of card it is, etc.

Apparently nobody thought to actually put it into a PC, which I did. Windows can’t find a driver for it, but it does identify itself as a PCMCIA-11M_WLAN_CARD_V3.0. So I did a search on that string, and found that the drivers for a ZyAIR B-100 made by Zyxel will work just fine for everything from Windows 95 to Windows XP.

The software is available here: http://www.zyxeltech.de/zyairb100.htm and I’ve tested the older version in Windows 95, and the newer version in Windows XP. Both work great.

Hopefully Google will lead some weary travellers here. If this has made your day, visit my website and click on an ad! http://mike.eire.ca/

Using PHP to interface with WMI

Windows Management Instrumentation (WMI) is a Windows derivative of the WBEM standard allowing centralized management of a wide number of Windows functions. There is almost no mention of how to use it from PHP, although combined together they provide a powerful method of web-based management. This example shows how to connect to a remote server, update a single DNS record, then flush the DNS cache.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
< ?php
$host = 'www';
$ip = '192.168.1.1';
$domain = 'example.com';
$query = "SELECT * FROM MicrosoftDNS_AType WHERE DomainName='$domain' AND OwnerName='$host.$domain'"; 
try {
//create the object
	$rpc = new COM('WbemScripting.SWbemLocator');
//update DNS
	$wmi = $rpc->ConnectServer($rpchost, 'Root/MicrosoftDNS', $user, $pass);
	$hosts = $wmi->ExecQuery($query);
	foreach($hosts as $host) {
		echo "Updating $host->OwnerName from $host->IPAddress to $ip.";
		flush(); ob_flush();
		$result = new Variant(null);
		$host->Modify(null, $ip, $result);
	}
//flush the DNS cache by restarting the dnscache service
	$query = "SELECT * FROM Win32_Service WHERE Name='Dnscache'";
	$wmi = $rpc->ConnectServer($rpchost, 'Root/cimv2', $user, $pass);
	$services = $wmi->ExecQuery($query);
	foreach ($services as $service) {
		$service->StopService();
		sleep(2);
		$service->StartService();
	}
}
catch(Exception $e) {
	echo $e;
	exit;
}?>

A couple of points to note:

  1. this is PHP 5 code, it will not work in version 4.
  2. This code uses the COM functions, only available in Windows-based PHP installs.
  3. notice that even though I only pulled one record from the WMI server, I still have to use foreach to iterate through the result set. Like the query itself, the result set is treated the same as one from a database.
  4. I needed to reconnect after the DNS update to use a new namespace; where the DNS server management classes are in the Root/MicrosoftDNS namespace, the service management classes are in the default Root/cimv2 namespace.
  5. Microsoft’s WMI documentation is here. All the code samples are VBScript, but using the example above you should be able to figure things out.

Upgrading Blackberry device software

Your cellular carrier likely isn’t making the most recent version of your Blackberry’s software available to you.  Usually they don’t want to support a newer version, or don’t want subscribers to have access to new features until they find a way to make money from them.  However, if any carrier has a newer version of software, you can have it too.  Here’s how it works:

  1. Google for this: “software download for” welcome site:blackberry.com to find the most recent version of software for your phone.  This may take some searching, as there’s about 50 different carriers.  I have a 7290, and Cincinnati Bell had version 4.1.0.379, much better than Rogers’ 4.0.2
  2. Download and install the software
  3. Open the file C:Program FilesCommon FilesResearch In MotionAppLoaderVendor.xml in Notepad
  4. Search for the name of the provider whose software you downloaded.  In my case, I searched for “Cincinnati” to find the right section: <vendor id=”0x87″ Name=”Cincinnati Bell”>
  5. Inside that provider’s section of the file (the section ends at the </vendor> line,) find the subsection corresponding to the version you downloaded.  Mine was: <bundle id=”System” version=”4.1.0.379″>
  6. Copy that “bundle” subsection in its entirety, which should be 3 lines, like my example:
    <bundle id="System" version="4.1.0.379">
    <devicehwid> 0x94000903 0x9c000503</devicehwid>
    </bundle>

    If you want to double check that this is really for your device, the file C:Program FilesCommon FilesResearch In MotionAppLoaderdevice.xml has a list of all the hardware IDs with model numbers. My device ID is 0x9c000503, which I can see is included in the line above.

  7. Search for your actual provider.  I searched for Rogers and found this: <vendor id=”0x6B” Name=”Rogers”>
  8. Paste the “bundle” subsection into your provider’s section (above the </vendor> line)
  9. Start the desktop software and plug in.  Your handheld should be upgraded shortly

That XML file keeps track of which carrier is allowed to have a given version of the Blackberry OS.  All I did was say “A handheld from Rogers is allowed to have version 4.1.0.379 if it connects.”

Configure a Cisco 7960 phone for use with Asterisk

This one’s sure to get some hits from Google.

First, if you need to upgrade the phone’s firmware, follow the directions on this page at Cisco. The phones come from Cisco configured to use SCCP instead of SIP, and you’ll almost always want to change the firmware. If you know what you’re doing, you’ll have that done in an hour. I read all these posts from people getting confused and agonizing over the firmware upgrade; 90% of their problems could have been avoided by following that document to the letter.

Anyway, once the phone’s firmware is upgraded you should do a factory reset on the phone. To do that, reboot it by pushing * + Settings + 6 together. As soon as the reboot starts, hold down the # key. After a second it will come up with a message about the factory reset key being detected. Type 123456789*0# and it will ask if you want to keep network settings. Push 2 for no.

Now your phone is wiped out. Most of the configuration files are detailed extensively elsewhere, but the important ones are SIPDefault.cnf and SIP<mac address>.cnf where <mac address> is of course the MAC address of your phone in uppercase. Here’s what you can get away with as a bare minimum (note that these directives can be put in either file, as far as I know):

image_version: "P0S3-07-4-00" ; your firmware image file
proxy1_address: "asterisk.example.com" ; you can put an ip address in as well
proxy_register: 1 ; you can't take incoming calls if you aren't registered
nat_enable: 1 ; obviously don't use this if you're not behind NAT
nat_received_processing: 1 ; same as the last one
telnet_level: 2 ; to get access to all functions in the telnet shell

line1_name: "username" ; your asterisk username from sip.conf
line1_authname: "username" ; this will be the same 99% of the time
line1_password: "secret" ; also from sip.conf
user_info: "none" ; I'm not 100% certain this is necessary

Something important to note is that these values get saved in the phone. That’s why we did the reset. If you don’t explicitly set a value to be empty (“”) it will use the value already in the phone, not the default value.

I spent a day getting “401 Unauthorized” messages back from Asterisk before I added those 2 NAT settings. I’m not sure what the problem was, because Asterisk was getting registration requests from the phone, but for some reason it was responding with 401 until I changes settings on the phone.

I’ll add for the record that getting into the phone via telnet can be very useful. The default password is “cisco” and debug sip-messages followed by register 1 1 is your friend. Also, erase protflash will remove all the setting stored in flash memory, forcing the phone to reget the settings from TFTP. Much better than doing a reboot to reload the config files.

bad code

Today I replaced this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<tr>
  <td height="1">
    <table width="347" height="1" border="0" cellpadding="0" cellspacing="0" background="CCorner/images/dot_hor.gif">
      <tr>
        <td width="347" height="1"><img src="images/en/spacer.gif" width="1" height="1"/></td>
      </tr>
    </table>
  </td>
  <td>
    <table width="1" height="22" border="0" cellpadding="0" cellspacing="0" background="CCorner/images/dot_vert.gif">
      <tr>
        <td width="1" height="22"><img src="images/en/spacer.gif" width="1" height="1" alt=""/></td>
      </tr>
    </table>
  </td>
</tr>

with this:

1
2
3
4
5
6
<tr>
  <td height="1" width="347" style="background:url(CCorner/images/dot_hor.gif)"></td>
</tr>
<tr>
  <td width="1" height="22" style="background:url(../CCorner/images/dot_vert.gif)"></td>
</tr>

But I still feel icky.

More sortable tables

That script again. I needed to be able to trigger a sort onload. Posted this to the aforementioned blog as well, but I’ll keep it here for myself.

1
2
3
4
5
6
7
8
9
10
11
12
//this function is passed the ID of one of the TH elements, and triggers a sort without a click
function trigger_Sort(id) {
	var fakeEvent=new Object;
	fakeEvent.currentTarget=document.getElementById(id).firstChild;
//comment out this block for ascending sort
	for (var i=0; i<fakeevent .currentTarget.childNodes.length; i++) {
		if (fakeEvent.currentTarget.childNodes[i].tagName) {
			fakeEvent.currentTarget.childNodes[i].setAttribute('sortdir','down');
		}
	}
	ts_resortTable(fakeEvent);
}