Technology Musings

March 10, 2010

Platforms / Facebook FBJS problems on FireFox

JB

I've been banging my head against a wall for the past day trying to figure out various FBJS problems in facebook.  Turns out the problem was..... FireBug!  By simply turning Firebug off, (most of) my problems went away.  I have no idea what the issue is, but it took me forever to realize it because I always have FireBug on, and don't even realize it is there.  It's never been a problem before.  But, I noticed that one of my coworkers was using my app, with Firefox, with no problem, and I quickly realized that the problem was with FireBug.

There's still another issue with fb:js-string on profile widgets that's killing me, but hopefully that will be resolved soon.  I _think_ they changed how they were evaluating profile javascript, to make it more secure - but in doing so made all references to the variable created by fb:js-string out-of-scope.  Developing javascripty apps for facebook is truly painful.

January 27, 2010

Platforms / IE6 Retiring Soon!

JB

Looking at their lifecycle page, it looks like Microsoft will be retiring IE 6 this year!  For anyone who works in IT, this is certainly a cause for celebration.

January 16, 2010

Platforms / Forcing HTTP Basic Authentication on the iPhone

JB

Here's my setup.  I have a RESTful Rails app.  Going to http://whatever.com/contacts gives the search screen for the contacts.  Going to http://whatever.com/contacts.xml gives the full list of contacts as an XML file.  For normal users, I use cookie-based authentication.  However, I also allow, for API users, HTTP basic authentication.  There is no access difference between the two.  However, this is a problem for the iPhone, because, although NSURL supports usernames and passwords, it will not present them for authentication without being challenged!  This is somewhat understandable because it doesn't know what authentication mechanism is being used (basic or digest).  However, it is EXTREMELY annoying to program for.

So, after spending about 2 hours digging through stuff, here is what I found out.

First of all, Basic authentication relies upon Base64 encoding, which isn't available on the iPhone.  Thankfully, someone posted public-domain Objective-C iPhone code for this here and someone else gave an almost-working-but-not-quite version of the request code here.  The Base64 code left out some things (like the class wrapper and header file), and the request code assumed a Base64 method existed which didn't.  In any case, here is the Base64 header file, Base64.h:

#import <Foundation/Foundation.h>


@interface Base64 : NSObject {
}
+ (void) initialize;
+ (NSString*) encode:(const uint8_t*) input length:(NSInteger) length;
+ (NSString*) encode:(NSData*) rawBytes;
+ (NSData*) decode:(const char*) string length:(NSInteger) inputLength;
+ (NSData*) decode:(NSString*) string;

@end

Here is the body of the code, Base64.m:

#import "Base64.h"


@implementation Base64

/* Copied from http://www.cocoadev.com/index.pl?BaseSixtyFour (cyrus.najmabadi@gmail.com) */

#define ArrayLength(x) (sizeof(x)/sizeof(*(x)))

static char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static char decodingTable[128];

+ (void) initialize {
if (self == [Base64 class]) {
memset(decodingTable, 0, ArrayLength(decodingTable));
for (NSInteger i = 0; i < ArrayLength(encodingTable); i++) {
decodingTable[encodingTable[i]] = i;
}
}
}


+ (NSString*) encode:(const uint8_t*) input length:(NSInteger) length {
NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t* output = (uint8_t*)data.mutableBytes;

for (NSInteger i = 0; i < length; i += 3) {
NSInteger value = 0;
for (NSInteger j = i; j < (i + 3); j++) {
value <<= 8;

if (j < length) {
value |= (0xFF & input[j]);
}
}

NSInteger index = (i / 3) * 4;
output[index + 0] = encodingTable[(value >> 18) & 0x3F];
output[index + 1] = encodingTable[(value >> 12) & 0x3F];
output[index + 2] = (i + 1) < length ? encodingTable[(value >> 6) & 0x3F] : '=';
output[index + 3] = (i + 2) < length ? encodingTable[(value >> 0) & 0x3F] : '=';
}

return [[[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding] autorelease];
}


+ (NSString*) encode:(NSData*) rawBytes {
return [self encode:(const uint8_t*) rawBytes.bytes length:rawBytes.length];
}


+ (NSData*) decode:(const char*) string length:(NSInteger) inputLength {
if ((string == NULL) || (inputLength % 4 != 0)) {
return nil;
}

while (inputLength > 0 && string[inputLength - 1] == '=') {
inputLength--;
}

NSInteger outputLength = inputLength * 3 / 4;
NSMutableData* data = [NSMutableData dataWithLength:outputLength];
uint8_t* output = data.mutableBytes;

NSInteger inputPoint = 0;
NSInteger outputPoint = 0;
while (inputPoint < inputLength) {
char i0 = string[inputPoint++];
char i1 = string[inputPoint++];
char i2 = inputPoint < inputLength ? string[inputPoint++] : 'A'; /* 'A' will decode to \0 */
char i3 = inputPoint < inputLength ? string[inputPoint++] : 'A';

output[outputPoint++] = (decodingTable[i0] << 2) | (decodingTable[i1] >> 4);
if (outputPoint < outputLength) {
output[outputPoint++] = ((decodingTable[i1] & 0xf) << 4) | (decodingTable[i2] >> 2);
}
if (outputPoint < outputLength) {
output[outputPoint++] = ((decodingTable[i2] & 0x3) << 6) | decodingTable[i3];
}
}

return data;
}


+ (NSData*) decode:(NSString*) string {
return [self decode:[string cStringUsingEncoding:NSASCIIStringEncoding] length:string.length];
}


@end

Okay, so now how do we make the request?  It's pretty evil, but if you wrap it in a function it's not too bad.  Don't forget to import Base64.h!  Anyway, here is the code:

+ (NSData *) loadDataFromURLForcingBasicAuth:(NSURL *url) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *authString = [Base64 encode:[[NSString stringWithFormat:@"%@:%@",[url user], [url password]] dataUsingEncoding:NSUTF8StringEncoding]];
[request setValue:[NSString stringWithFormat:@"Basic %@", authString] forHTTPHeaderField:@"Authorization"];
return [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
}

Ugly, ain't it?  You can use this with asynchronous connections as well, or extend it to handle the responses/errors from sendSynchronchronousRequest, but that is an exercise for the reader.

 

 

 

 

 

 

November 11, 2009

Platforms / Debugging EXC_BAD_ACCESS on the iPhone

JB

I've been working on my first real iPhone app.  It's a very interesting way of coding.  Objective-C is an interesting language.  It seems to shoot for the middle ground between something out-of-control like C++, and something so-object-oriented-it-hurts like Java.  Personally, I like Ruby best, but that's neither here nor there.

Memory management on the iPhone is a tough cookie for someone who has not done Objective-C development before.  If you are new to the iPhone, the first thing you should do is read the memory management contract for Cocoa, as well as the links that link out from there.

I was having a problem getting EXC_BAD_ACCESS.  I finally used breakpoints to find where the problem was occurring, and it was an NSArray that was causing it.

I did a google search, and found this really cool method for getting a little more information about objects which have been released before they were accessed.

Eventually, I found out that the problem came from the fact that [NSArray arrayWithObjects:], because it doesn't have "init" or "copy" or "alloc" in the message name for object creation, autoreleases the object when it gives it to me!  Therefore, as soon as the present event was done, it trashed the object.  If you want to keep an object that has been autoreleased, you have to send it a retain message.

Doing the retain fixed my problem.  Yay!

October 06, 2009

General / Laps for Little Ones

JB

Support the Little Light House by sponsoring us in Laps for Little Ones

The Little Light House is one of the best ministries I've ever been involved with.  They are a Christian, private, tuition-free school for special-needs kids.  That's right, the kids who go there don't have to pay anything at all.

This isn't day-care - it's an intensive, customized program for each child.  The school day lets out at 1PM, and the staff spends the rest of the day planning each child's next day.  When a child gets to school, they have a card of things that they are going to work on that day.  It's both extremely fun and extremely helpful for the children -- and the parents.

While our oldest son, Danny, was alive, he attended the Little Light House.  His world expanded so much while he was there.  His ability to play with others and interact and do new things hinged upon the teachers at the Little Light House and their love and their help.  Danny had to be fed through a tube, received many, many, many medications at specially-timed intervals, and, if everyone was lucky, he only threw up three times a day.  Yet the Little Light House had no problems seeing to his every need while he was there, and providing every manner of therapy.  At the Little Light House, they have physical therapy, speech therapy, occupational therapy, and probably a lot of other therapies I'm not so familiar with.  And everything is done in a specifically Christian way.

Isaac had the same genetic defect that Danny had, and, had he lived long enough, would have enjoyed the services of the Little Light House as well.  As soon as we discovered his condition, we reserved him a spot there, because we knew that their help was the difference between night and day for us.

Below are pictures of Danny learning at the Little Light House.  Also, for those of you who didn't get to know Danny or Isaac, I pasted their memorial videos below.  In any case, please consider helping out the Little Light House - they have been a huge blessing to us, and to many, many, many other children.

You can donate now by going here.

Here is Danny's Memorial Video:

Isaac's Memorial Video:

A few pictures of Danny at the Little Light House if you don't have time for the video:

The picture below might look like playtime to you, but this was actually crucial for Danny.  He had problems touching a variety of surfaces - many different textures made him cry and gag and puke (yes, really).  The Little Light House worked with him to help him adjust his senses to be able to touch and play with a huge variety of textures.

July 05, 2009

Platforms / Migrating to Phusion Passenger

JB

Spent the evening migrating one of my Ruby on Rails servers to Phusion Passenger.  It was a nice upgrade, but not the trouble-free upgrade they promise.  Here were the gotchas that I ran into:

  1. Remember to install all of the gems into your new enterprise ruby that were on your regular ruby.  This one is pretty obvious, but its easy to miss some
  2. Passenger preloads your application.  This means that if there were models that you never used (and thus never got loaded into Rails before), Passenger will load them for you, and if they are broken, your app will not start.  This also affected gem loading, as there was one gem which was loaded by a file that was unused, and therefore I didn't have it installed.  But Passenger required that I install the gem.
  3. Passenger basically works by default as a SetUID program.  It looks like whatever user owns the application is the user that Passenger will run it as.  This can cause all sorts of problems.  It can cause problems accessing log files and session files.  When I finally got my gem issues sorted out, I kept getting 500 errors and I found the following in my log file: file /tmp/ruby_sess.97c076be86e7dd90 not readable - This was because it was running as my development user rather than as Apache.  I had to use BOTH of the directives "PassengerUserSwitching off" and "PassengerDefaultUser apache" to get it to work.  
  4. Then I had to find and fix all of the badly-created permissions that had accrued while it was running as the wrong user
If I have any additional problems, I'll post them here.

June 26, 2009

Snippets / What's Taking So Long?

JB

There are a lot of performance analysis tools for Rails logs.  However, sometimes you just need something quick and dirty.  This one helped me out.  1-line perl script to show all Rails log lines which took more that 100milliseconds to produce:

perl -n -e 'print if(m/\((\d+\.\d+)ms\)/ && $1 > 100);' log/development.log

UPDATE - Here's another handy one:

 tail -20000 log/production.log|grep 'Completed in'|cut -d" " -f3,11|sort -n

June 01, 2009

Amusing / Risk Management Fail

JB

From failblog:

fail owned pwned pictures

May 18, 2009

General / FTP Problems with Extended Passive Mode (EPSV)

JB

I just went through many hours of trying to figure out wierd FTP problems.  It looks like Peer1 has a weird firewall which gets confused on EPSV FTP connections.  The way that the problem presented was that after login, when the FTP client attempted a directory listing (or any other command), after giving the EPSV command it would just hang.

For those who don't know, EPSV is "extended passive mode", and is a newer extension to FTP's historical passive mode (PASV), which is used to make FTP work in firewalled environments.  I really don't know much about EPSV beyond that, although I think it was developed for enhanced future compatibility with IPv6.  

Anyway, the problem is that most recent FTP clients attempt EPSV first, and then only use the traditional PASV if it fails.  Also, most recent FTP servers support EPSV.  Therefore, even if the firewall is blocking EPSV, the client will think that the command is successful, because the server is trying to do it, not knowing that it is being blocked.

I use vsftpd.  The only way I found to prevent EPSV mode is to use their command whitelisting feature, and whitelist everything except EPSV and EPRT (the extended version of the FTP PORT command).  So, here's what I added to my vsftpd.conf file, and it seems to work so far:

cmds_allowed=ABOR,CWD,DELE,LIST,MDTM,MKD,NLST,PASS,PASV,PORT,PWD,QUIT,RETR,RMD,RNFR,RNTO,SITE,SIZE,STOR,TYPE,USER,CDUP,HELP,MODE,NOOP,STAT,STOU,STRU

So now when a client attempts an EPSV command, it will respond with "550 Permission denied." and the client will usually fall back to regular PASV mode.  I wish I could have blacklisted the command rather than whitelisting all other commands, but oh well.

If you have access to the client side of the connection, and don't/can't mess with the server side, you can usually turn off extended passive mode there as well.  With curl, you need to add --disable-epsv.  With regular ftp, you need to issue the command "epsv4" after connecting.

 

 

February 10, 2009

Amusing / This Friday is the Geek Apocalypse

JB

See here.  On Friday, UNIX time will reach 1234567890.  This is, like, the end of the world :)