IcyBee needs a new maintainer

The IcyBee project is a client for the ICB chat network with a graphical user interface and written in the Java language for cross-platform portability. The current version of the software has several years of stability behind it and few user requests, but there is always more work to do. The project is currently hosted on SourceForge and all of the source is already available.

I’m looking for a maintainer that will take the reins of the project from me and give the project some much needed love as time and platforms march on. If interested, drop a comment on this post and we can discuss the possibility.

Connecting to Apple Push Notification Services using Python & Twisted

One of the exciting features of the upcoming iPhone 3.0 operating system is the ability for a device to receive a notification message from your own application that is relayed by Apple’s network cloud. This feature allows the iPhone to channel all out-of-band communication into a narrow channel that is low impact on the device from a power, security and usability point-of-view.

As an application developer, you are responsible for two pieces to make this new notification service work: the actual application that runs on the iPhone OS, and a provider service that both communicates with the device and directly to Apple’s cloud.

In a typical flow, the application on an iPhone device issues an API call to request notifications for the identifier that the developer has assigned to the application. In response to that API call, Apple’s servers provide the device with a binary token value that uniquely identifies the device & installation combination (for security reasons, the device token is reset if the device is reset, so it is not a one-to-one mapping to the actual hardware). Next, the application must connect to the developer’s provider servers and provide it with the device token it has received from apple. The provider can now use the device token when communicating back to the Apple Push Notification Service (APNS) cloud.

A provider implementation has very little work to do to inform APNS of a device update. Most of the effort in a provider will be in the actual framework of the application itself, and managing what information should be sent to a device. A common implementation will be for a provider to both communicate with the APNS server cloud and also to the application’s own network services and effectively become a protocol translator. Such a heavily network based application like that is a perfect match for the Python language and the Twisted framework.

A provider to APNS link is very simple: a secure-socket connection (SSL) is made to a APNS server and then simple messages are sent by the provider whenever a notification needs to be sent to the device. The APNS server does not respond or acknowledge the message. Every so often (Apple recommends once an hour), the provider server should connect to a web server that will dump a list of device tokens that are no longer valid (as in the device has uninstalled the application). This is the provider’s only feedback mechanism outside of communicating directly with a device.

The first step is to setup an SSL connection to the APNS servers. By following the directions on Apple’s developer portal, you can create a certificate and private key that are assigned to your application identifier. This certificate & private key must then be used when connecting to the APNS servers as an authentication mechanism.

One caveat  - the Mac OS X Keychain Access application does not directly export certificates and private keys in Private Enhanced Mail (.pem)  format, which is what the OpenSSL implementation we use with Twisted will want, but luckily there’s an easy mechanism to convert if you export the files as Personal Information Exchange (.p12) format. The following two commands can be used to convert the .p12 files into .pem files using the built-in openssl command on Mac OS X or most Linux distributions:

openssl pkcs12 -in cred.p12 -out certkey.pem -nodes -clcerts
openssl pkcs12 -in pkey.p12 -out pkey.pem -nodes -clcerts

Now that we have our SSL files in the proper format, we can build our ClientContextFactory object that will be used by Twisted to initiate a connection to the APNS servers:

class APNSClientContextFactory(ClientContextFactory):
    def __init__(self):
        self.ctx = SSL.Context(SSL.SSLv3_METHOD)
        self.ctx.use_certificate_file(APNS_SSL_CERTIFICATE_FILE)
        self.ctx.use_privatekey_file(APNS_SSL_PRIVATE_KEY_FILE)
 
def getContext(self):
    return self.ctx

We’ll also need a ClientFactory object that will be used to build the Protocol object whenever a connection is established to a server:

class APNSClientFactory(ClientFactory):
    def buildProtocol(self, addr):
        print "Connected to APNS Server %s:%u" % (addr.host, addr.port)
        return APNSProtocol()
 
    def clientConnectionLost(self, connector, reason):
        print "Lost connection. Reason: %s" % reason
 
    def clientConnectionFailed(self, connector, reason):
        print "Connection failed. Reason: %s" % reason

Next, we’ll need the actual Protocol object that will format messages and queue them to the server. For APNS, we don’t receive any responses so that half of the protocol goes unused.

class APNSProtocol(Protocol):
    def sendMessage(self, deviceToken, payload):
        # notification messages are binary messages in network order
        # using the following format:
        # <1 byte command> <2 bytes length> <2 bytes length>
 
        fmt = "!cH32sH%ds" % len(payload)
        command = '\x00'
        msg = struct.pack(fmt, command, 32, deviceToken,
                          len(payload), payload)
        self.transport.write(msg)

And finally, we’ll need the main code to kick everything off for the test:

if __name__ == '__main__':
    reactor.connectSSL(APNS_SERVER_HOSTNAME,
                       APNS_SERVER_PORT,
                       APNSClientFactory(),
                       APNSClientContextFactory())
    reactor.run()

From here, the Python and Twisted combination can be easily extended to handle multiple protocols and convert between them as needed. For example, one of the commonly expected use cases of the new push feature will be instant messaging (IM) clients that will need to provide a proxy between the device and the IM servers and the APNS servers in order to notify a device of outstanding messages.

The full Python script containing the above excerpts can be downloaded here:

test-apns-blog

One Year at Zenoss

Yesterday marked my one-year anniversary at Zenoss. When I decided to take the job at Zenoss I knew it would be a challenging position, and time has proven this assumption correct. The new (to me, anyway) technology, the remote work environment and the realities of a startup company with limited resources have all added their own challenges to the job.

I found the Zenoss product itself to be more mature than I had expected. While it still has plenty of rough edges, especially in the user interface, it is a rather feature-rich application with a very large user base in both the free community and paid enterprise versions. There is a tremendous amount of work left to do on the application, but with each version we see a significant step forward and an expanded user interest because of the changes.

When I came to Zenoss, my new co-workers and I brought with us a culture of using agile development methodologies and processes to the company. We expected, and were asked by company management, to implement those processes at Zenoss. Over the past year, we have seen the right things happen from this shift in engineering culture:

  • development time has become more predictable
  • planning and priority selection take a much more important role than ever before, and not just in engineering (in fact, most importantly not in engineering, but at the executive level)
  • quality assurance is now a fundamental part of the engineering process, not a luxury for later
  • code and design quality has improved dramatically as the engineering team begins to understand how much control they really do have over the process

There still remain many challenges, but these are the same ones I have seen with all teams that struggle with the discipline needed for agile methodologies. Effectively breaking down large development tasks into two-week iterations remains a hurdle; I suspect at a fundamental level many of the developers do not fully believe this is possible, which is a long, long discussion. There was also a reluctance, until recently, to fully engage with teammates on performing effective code and design reviews. These will both improve over time as the team matures.

When I first started at Zenoss there were 5 developers in Annapolis, 2 developers in Austin and me , usually working remote in Houston. Since then, the team dynamics have changed so that I and one other developer are remote most of the time, 3 developers are full-time in Austin and 1 in Annapolis. This team dynamic change has made the infrastructure needed to support remote development much more important, but at the same time it has made the remote developers somewhat more isolated since a bit more now happens only in one location. Since I travel to Austin frequently I can avoid the worst of this unavoidable trend, but it will likely always remain a challenge as long as we have remote employees.

On a personal level, working remotely full-time for this long has been much more difficult than I expected. I last did this much remote work in the mid-90s, and it was difficult to do then, as well. On the surface, I spend all of my work hours focused on some problem on the computer, but in reality much of my job is very social and without face time it starts to wear on me very quickly. Frequent visits to Austin help tremendously, but in reality this will likely always be an issue until I relocate.

I’ve encountered almost all of the traditional problems people have while working at home, but especially keeping focus and balancing the time spent between work and home life. Focus is purely a self-discipline issue, but it is remarkable how much of this is provided by the routine of commuting to an office. Maintaining a good work-life balance is also tremendously hard when working at home, unless you dedicate a specific place in the house for work. It took a few months to really learn this lesson but eventually I bought a second desk and made myself an office separate from my normal home office. Before then, I found it way too easy to bring my laptop into the living room and I’d continue to check e-mail and work on problems throughout the evening. Sometimes, I still do, but not at least it is not a mindless occurrence.

Zenoss is a very small company, and naturally we have very limited resources. Of course, today’s economic environment makes this true for almost every single company. But more specifically, I find ourselves working around what effectively is a lack of resources in our IT department. I often have to build my own virtual machines at home, or buy little widgets to help productivity because I am remote. At some level, I don’t mind this, since it’s nice being able to actually work somewhere you can do this, rather than being told you can’t.

After a year, I’m still not a huge fan of the Python programming language, but I’ve come to accept it and I can be productive with it. Working with it feels a whole lot like I stepped back in time 15 years, and saying that is a great way to start an argument, so I’ll just leave it at that.

One great surprise from the past year is that I became the guru for Zenoss’s Windows monitoring technology. We have a custom implementation of MS-RPC that is based upon the Samba 4 source tree. On top of this layer, we have provided our own implementations of the DCOM, WMI, and Windows registry interfaces so we can call these services directly on any Windows device. This is a great feature, as it allows us to communicate directly to Windows devices from our Linux based product, without requiring a Windows device to be running our software just to provide Windows-based connectivity, unlike some other systems management products.

Over the next few months I will be taking on more and more of the implementation of the new Zenoss user interface, which is a great project that should dramatically improve the user acceptance of quality of the application. It also gives me more time to work on user interfaces, which is always an area of software development I enjoy but rarely get a chance to do.

Secure Windows Monitoring with Zenoss

Starting with version 2.3.x, Zenoss can monitor computers running Microsoft Windows with a variety of data collection protocols: SNMP, WMI over DCOM/MS-RPC and Perfmon over MS-RPC.

In Zenoss Core, the status of Windows services and the Windows Event Log are monitored using Windows Management Instrumentation (WMI) queries over the DCOM/MS-RPC protocol. In the implementation of MS-RPC that Zenoss is based upon, authentication credentials are sent to the remote server using the Windows Challenge / Response (NTLM) mechanism. Using this authentication mechanism, the actual password is never sent across the network, but rather the server produces a “challenge” value that the client must calculate using the password rather than sending it across the network.

NTLM authentication is the same mechanism that Windows devices themselves use for client/server communications, such as file sharing and remote administration.

Starting with version 2.3.x, Zenoss Enterprise gathers Perfmon data using the remote Windows registry API over the MS-RPC protocol. This technique is both more efficient and secure than the previous one. The same authentication mechanism used by Zenoss’s WMI library is used here, providing the same level of security.

Prior to version 2.3.x, Zenoss Enterprise used a different mechanism to collect Perfmon data from Windows devices. This mechanism used a utility known as winexe to remotely execute commands on the Windows device (in this case, the typeperf.exe Windows utility). Unfortunately, the winexe utility sends the username and password used for authentication across the network in clear text, providing a less than ideal configuration for security.

Zenoss users monitoring Windows devices should be running version 2.3.3 or newer for the best possible security when communicating with those devices.

new MacBook setup

I bought another Mac today, a nice 2.4 GHz 13-inch unibody MacBook. I had planned on buying a 17-inch unibody MacBook Pro, and very nearly did, but luckily sanity won out and I remembered how much of a hassle it was to carry around those giant things, even if they are only “only” 6.6 lbs.

I had a 2.0 GHz 13-inch MacBook a couple of years ago when the first Intel-based models came out and I do remember the screen resolution, while not abundant, was more than adequate for browsing, e-mail and even development. And, the unibody model is only 4.5 lbs, so 2 lbs lighter will be a lot nicer to carry around. I also sprung for a spare battery to try and help get a little closer to the awesome battery life of the 17-inch model.

Of course, the obvious question is why buy another laptop when I’ve already got a nice 15-inch MacBook Pro that work provides? The answer there is easy: I don’t want to do anything personal, even development, on the work provided machine.

Now, on to the actual system setup, documented here for posterity.

  1. After account creation, run software update and get all the latest updates installed first.
  2. Create an applecare account with an easy, but secure, password. This way the Apple store geeks can have that account should there need to be any repair work done.
  3. Change the battery lifetime display with Show -> Time.
  4. Secure the screensaver by using System Preferences -> Security -> General and checking the Require password to wake this computer from sleep or screen saver option.
  5. Disable the Front Row remote by using System Preferences -> Security -> General and checking the Disable remote control infrared receiver option.
  6. Disable the Front Row keyboard shortcut by using System Preferences -> Keyboard & Mouse -> Keyboard Shortcuts and disabling the Hide and show Front Row shortcut.
  7. Enable full keyboard shortcuts by checking the All controls option in the bottom of the same Keyboard Shortcuts screen.
  8. Enable the Use secure virtual memory option in System Preferences -> Security -> General.
  9. Encrypt my home directory with System Preferences -> Security -> FileVault.
  10. Install Growl 1.1.4 from http://growl.info/
    1. Install the GrowlSafari extra package.
    2. Install the HardwareGrowler extra package.
      1. Drag HardwareGrowler.app to /Applications
      2. Disable the HardwareGrowler dock icon by following the instructions at http://growl.info/documentation/hardwaregrowler.php
      3. Add HardwareGrowler to the start at login list by using System Preferences -> Accounts -> Login Items and dragging HardwareGrowler to the list.
    3. Enable Growl starting at login with System Preferences -> Growl and enabling the Start Growl at login option.
  11. Remove unused printer drivers by deleting the appropriate folders in /Library/Printers folder (everything but Brother, hp and PPDs in my case).
  12. Install the XcodeTools package from the Installation DVD’s Optional Installs directory.
  13. Drag Xcode to the dock by going to /Developer/Applications and dragging the icon to the dock.
  14. Add Activity Monitor to the dock by going to /Applications/Utilities and dragging the icon to the dock. Seondary-click on the icon and enable Open at Login.
  15. Add Terminal to the dock by going to /Applications/Utilities and dragging the icon to the dock.
    1. Change the default Terminal settings by starting Terminal.app, selecting Preferences (Cmd-,) and then changing the “new window with settings” to Pro.
    2. Select the Pro scheme in the Settings tab and click default.
    3. Choose the Window tab with the Pro scheme selected, click the Background color chooser and set the opacity level to 90%.
    4. Change the window size to 80 columns and 36 rows.
  16. Customize vim by creating ~/.vimrc with the following content:

    :color elflord
    :syntax enable
    :set shiftwidth=4
    :set expandtab
    :set autoindent
    :set cindent
    :set enc=utf-8
    :set nu
    :set showmatch
    :set laststatus=2
    :set nocompatible
    :set gfn=Monaco:h15:a
  17. Enable color highlighting for ls by adding the following lines to /etc/bashrc:

    alias ls='ls -CFG'
    alias dir='ls -FGlas'
  18. Install the Safari 4 beta from http://www.apple.com/safari/download
  19. Install Firefox 3 from http://getfirefox.com/ and drag it to the dock.
  20. Install iStat pro from http://www.islayer.com/apps/istatpro/
  21. Install MySQL 5.1 x86 community edition from http://dev.mysql.com/downloads/mysql/5.1.html be sure to install the StartupItem package as well as the preference pane.
  22. Add MySQL to the shell profile by appending the following to /etc/bashrc:

    export PATH=/usr/local/mysql/bin:$PATH
  23. Install EverNote from http://www.evernote.com/
  24. Install DropBox from http://www.getdropbox.com/
  25. Install the Windows Media Components for QuickTime from http://www.microsoft.com/windows/windowsmedia/player/wmcomponents.mspx
  26. Install Twitterrific from http://iconfactory.com/software/twitterrific
  27. Disable automatic synchronization for iPhones and iPods since this won’t be the primary iTunes machine by going to iTunes Preferences and enabling Disable automatic syncing for iPhones and iPods on the Devices tab.
  28. Install the iPhone SDK from http://developer.apple.com/
  29. Party! Or maybe just nap.