Monthly Archive for August, 2007

Boy, Is My Face Red

So then I discovered that the functionality I was trying to add to bsflite was kinda, sorta, maybe already there. Way to read the entire help output, smart guy.

That left only a couple of features I really wish bsflite had. One is more robust tab completion, such that screennames could be partially completed out to the next difference, the way bash does it. Another is deleting the last word on the line with Ctrl-W, also the way bash does it. I’m in love with bash, you know.

Since I’m about as good at C programming as Stevie Wonder would be at tennis, I thought that if I wanted to tackle one of those features, I’d have to go with the easier one. So, Ctrl-W word deletion it is!

I jumped the gun on the thing and had a semi-working prototype up and running, which I posted to the project page on Sourceforge just before realizing that it is full of bugs. Back to the drawing board I went, writing a test harness and experimenting with pointers and strings until I was really pretty darn sure it would work.

At which point it did not work.

As it turns out, the third time is the charm, and I really think I’ve gotten it to a workable stage. I was able to make it segfault once, but I haven’t yet figured out what caused it to do that, so for what it’s worth, here it is. Without further ado…

The bsflite 0.82 word delete patch

The Joys of AIM

AOL Instant Messenger is really the last remaining shred of AOL’s former monopoly. Once folks realized that they didn’t need AOL to get on the Internet, and that the Internet itself, through search engines and portals, could give them basically the same stuff they got from AOL for at least half the price, they dumped that service like a ton of bricks. In their scramble to rebuild the empire they once enjoyed, AOL introduced AOL Instant Messenger, which has now become completely entrenched in the world of instant messaging.

Despite the alternatives, including the big names like Yahoo!, MSN, and Google, not to mention Jabber, etc., most people mean AIM when they say “IM.” That being the case, huge numbers of third-party clients have been released to give access to the AIM network from other platforms and using different interfaces. In OS X, Adium takes the cake. For Windows there’s Trillian. In Linux, of course, gAIM. But if you want to use a console client (because it makes you three times as 1337), you have a choice.

The forerunner in the console AIM client race is nAIM (I believe it is short for ncurses AIM, invoking the name of the console input/output library it uses, ncurses). But I have had spotty luck with it myself, either because it’s in a development rut or because of my particular system setup. nAIM crashes a bit for me and so on. On top of that, it’s very friendly and colorful, but that doesn’t work for everyone in every situation.

So I found “bsflite,” a very lightweight client written in C that compiles on most *NIX platforms (check the bsflite Sourceforge page for more). I started using it and I like it very much. It’s fast, it’s simple, it gets the job done. Except for one thing…

The basic interface concept of bsflite is a simple prompt with all other messages and status information dumped into the screen from bottom to top. In order to send messages and do other things, you enter a single-character command followed by any parameters. For example, to send an IM, you enter “m” followed by the user’s screenname. There is some tab-completion, but it only works once you’ve typed enough of a name to be unique and on top of that, if you are talking with just one person, you have to type at least the first few letters of their name over and over.

So I came up with a pretty simple solution that allows you to press tab on an empty line to pop up the command to send a message to the last person you sent a message to. In other words, if you enter “mfavoritefriend How do you do?” and then press tab on a blank line, you will get “mfavoritefriend” with a space after it so you can enter another message.

The change was really simple, so if anyone uses bsflite and wants to hook it up, here is the patch

This patch is for bsflite 0.82. Other versions may not be quite the same and the patch is for the main file, bsf.c, so be wary of that.

Getting Your Web 2.0 on, iPhone Style

Long has it been since a post was made here; my apologies. Last week I totally snapped and bought an iPhone despite the modest litany of reasons I thought I shouldn’t (locked to one carrier, not standard GSM, battery life qualms, questionable extensibility) and my overall reaction is do it. If you are a smart and resourceful person, as I am sure you are since you are reading this blog, you will have no trouble bending the iPhone to your whims. This is one such report.

Lately, I’ve been Twittering (yes, it’s a verb now, too). For those of you who aren’t in the know, Twitter is a service that allows you to send a text message reporting, simply, what you are doing. That text message is then displayed on Twitter’s site (if you so desire), but is also relayed via SMS to any other Twitter users who are “following” you. Likewise, you receive text messages from Twitter when people you are “following” send in their updates.

The whole thing is a little bit silly, but the reported effect is to have a better peripheral idea of your friends’ daily lives and circumstances without having to hassle them synchronously (a little Web 2.0 lingo for you there). Thus, when you run into so-and-so, you may remember that they recently crashed their car or that they saw some movie you wanted to see, and so on. It’s not important information, but it’s neat.

So there I was, Twittering my heart out, when I realized that more of my friends read Facebook on a daily basis than participate in Twitter, and that Facebook provides an interface for updating your “status,” which is printed next to your picture on your profile page and disseminated through the “news feed.” Obviously, I could not stand idly by and let Twitter be updated but not Facebook! Ruby to the rescue.

The first step was to build an interface to Facebook’s status updating system, which is not supported in their public API (for shame). Because there is no programming interface for it, I had to make my script access the page through normal HTTP methods, forge the login security of the browser and cookies, and post the update that way. Here is the code:

require 'net/http'
require 'net/https'
require 'cgi'

def StatusFacebook(status,opt)
   if not opt.is_a? Hash or (not opt.has_key? 'user' or not opt.has_key? 'pass')
      return false
   end

   ua = opt.has_key?('ua') ? opt['ua'] : 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060612 Firefox/1.5.0.4 Flock/0.7.0.17.1'
   post_headers = {
      'Content-Type' => 'application/x-www-form-urlencoded',
      'User-Agent' => ua
   }
   get_headers = {
      'User-Agent' => ua
   }

   http = Net::HTTP.new('www.facebook.com')
   response = http.request_get('/login.php', get_headers)

   challenge = response.body.match(/<input[^<]*?name="challenge".*?>/)[0].match(/value="(.*?)"/)[1]

   loginform = "email="+CGI.escape(opt['user'])+"&pass="+CGI.escape(opt['pass'])+"&challenge=#{challenge}&md5pass=&next="

   cookies = []
   response.get_fields('Set-Cookie').each do |c|
      co,k,v = c.match(/^(.*?)=(.*?);/).to_a
      if ['test_cookie', 'login', 'reg_fb_ref', 'reg_fb_gate'].include? k
         cookies << co
      end
   end

   post_headers['Cookie']  = cookies.join(' ')
   get_headers['Cookie']      = cookies.join(' ')

   http = Net::HTTP.new('login.facebook.com', 443)
   http.use_ssl = true
   http.verify_mode = OpenSSL::SSL::VERIFY_NONE

   response = http.request_post('/login.php', loginform, post_headers)

   if response.code == '302'
      cookies = []
      response.get_fields('Set-Cookie').each do |c|
         co,k,v = c.match(/^(.*?)=(.*?);/).to_a
         if ['test_cookie', 'login', 'login_x', 'xs', 'c_user', 'h_user'].include? k
            cookies << co
         end
      end

      post_headers['Cookie']  = cookies.join(' ')
      get_headers['Cookie']      = cookies.join(' ')

      get_headers['Referer'] = 'https://login.facebook.com/login.php'
      get_headers.delete('Referer')
      http = Net::HTTP.new('www.facebook.com')

      response = http.request_get('/home.php?', get_headers)

      if response.code == '200'
         post_form_id = response.body.match(/<input[^<]*?id="post_form_id".*?>/)[0].match(/value="(.*?)"/)[1]

         if not post_form_id.empty?
            statusform = 'status=' + CGI.escape(status) + '&post_form_id=' + post_form_id

            post_headers['Referer'] = 'http://www.facebook.com/home.php'
            response = http.request_post('/updatestatus.php', statusform, post_headers)

            if response.body.match(/#{status}/)
               return true
            else
               return false
            end
         end
      end
   end

   false
end  

Feel free to pick through it. The important part to notice is the handling of the authentication cookies. I used the “Live HTTP Headers” Firefox Add-In to trace the headers during a Facebook login, took note of the cookies required, and selected only those cookies to be transmitted. Facebook has some pretty slick security measures as well, including a “challenge” code that is generated on the login form and checked along with your authentication information, and a “post_form_id” value that appears within every link and form post on the front page of the site, presumably to further validate the session integrity. There are a couple of neat .match() lines to capture those.

Step two was to access Twitter. Twitter does have an API, which is accessed through REST (for Twitter’s interface, that’s just a fancy shmancy term for “post a form”). Here is the Ruby function for updating Twitter:

require 'net/http'
require 'cgi'

def StatusTwitter(status,opt)
   if not opt.is_a? Hash or (not opt.has_key? 'user' or not opt.has_key? 'pass')
      return false
   end

   ua = opt.has_key?('ua') ? opt['ua'] : 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060612 Firefox/1.5.0.4 Flock/0.7.0.17.1'
   post_headers = {
      'Content-Type' => 'application/x-www-form-urlencoded',
      'User-Agent' => ua
   }
   get_headers = {
      'User-Agent' => ua
   }

   response = Net::HTTP.post_form(URI.parse('http://'+CGI.escape(opt['user'])+':'+CGI.escape(opt['pass'])+'@twitter.com/statuses/update.xml'),
                                          { 'status' => status } )

   return true if response.code == '200'
   return false
end  

Then there is bootstrap code that calls those two functions. Hooray. So what about the iPhone, then?

Well, thanks to the iPhone’s nearly complete and rather robust support for e-mail, I decided to create a way for my home mail server to receive a message and use it to update the status on those sites. From there, the possibilities would be endless. All I needed was a little procmail recipe to check that the message was addressed to my status-update address and a little script to process the full e-mail message and run the update. Here is the procmail destination file that bootstraps the updates:

#!/usr/bin/env ruby

BASEPATH = __FILE__.match(/^.*\//)[0]

require "#{BASEPATH}status_twitter"
require "#{BASEPATH}status_facebook"
require 'net/smtp'

$stdin.each do |line|
   if subject = line.match(/^Subject:(.*?)$/)

      # Format the subject.
      status_fb = subject[1].strip
      status_tw = subject[1].strip
      # No initial cap for Facebook.
      status_fb[0] = status_fb[0].chr.downcase
      # Initial cap for Twitter.
      status_tw[0] = status_tw[0].chr.upcase

      ret_fb = StatusFacebook(status_fb, {
         'user' => 'your_user_name',
         'pass' => 'your_password'
      })
      ret_tw = StatusTwitter(status_tw, {
         'user' => 'your_user_name',
         'pass' => 'your_password'
      })

      ret_fb = (ret_fb) ? 'Success' : 'Failure'
      ret_tw = (ret_tw) ? 'Success' : 'Failure'

msg = <<EOF
From: Status <my.status.email@my.domain>
To: iPhone <my.iphone.acct@my.domain>
Subject: Update report

I have submitted your update, "#{status_tw}"

Facebook: #{ret_fb}
Twitter:  #{ret_fb}
EOF
      
      Net::SMTP.start('my.mail.server') do |smtp|
         smtp.send_message msg,
                        'my.status.email@my.domain',
                        'my.iphone.acct@my.domain'
      end
      exit 0
   end
end

exit 0  

I just set procmail to deliver the message to that script if it matched my updater address (by using :0, no flags). That means I never receive e-mail to that address in my actual mail client, which is good. The script also returns an e-mail to my phone to let me know it succeeded because it would take me too long to figure out if it did or not through mobile Safari.

One of the cool things that this script also does is to capitalize the first letter of the update for the Twitter version and lowercase the first letter of the Facebook version. This is done because Facebook builds a sentence like “Aaron is “, while Twitter simply posts the sentence it is entirety. Being a slut for grammar, I require that capitalization and punctuation flow like a river. One also must keep in mind the point-of-view and temporal sense in which the update is written.

I have a plan to make the system handle Facebook updates even more cleverly by replacing “my” with “his” and things like that, though I might be biting off more than I can chew with that one.

Cool, huh?