So I finally wrote a proper Cocoa app to accomplish a specific task! Here it is:
Ok, so its lack of actual UI makes it somewhat unimpressive. The "specific task" part is true, though. After running across a bunch of old AIM chat logs from various sources in my recent hard-drive-trawling, I decided to put them all in a DB for no discernable benefit, because that's the sort of thing I do in my spare time.
The AIM+ and two styles of gaim chats were easy, and I expect the Adium ones to be not too bad, but iChat gave me a little trouble. It's apparently used two formats over time, both of white are Apple-proprietary formats. I decided to focus on the older format, since most of the chat logs were circa-2002/2003.
After doing a little digging, I found that the files are in the format used by NSArchiver and NSUnarchiver, which appear to be the Cocoa method of object serialization. I pointed an NSUnarchiver at one of the chats and... it complained about not having the InstantMessage class. Fair enough. After finding that Apple's InstantMessage framework is just meant for looking at your buddy list and doing AV stuff and not parsing logs, I grabbed the source of the horrifically-named chat browser Logorrhea. After I plunked its iChat data type classes into my project, all was well.
I found that the files consist of an NSArray (well, NSCFArray) with a mix of children representing chat attributes and the chat lines itself. I looped through that, checked the class of each one, and dealt with each appropriately. "Appropriately" in this case meant "converting to XML", so the agents in my database could read it back in properly. I haven't yet taken a whack at converting the body text from an NSAttributedString to HTML, so for now it's just the plain-text version.
It's rickety and I'm sure I did tons of stuff wrong, but it works! I ended up with a folder full of XML versions of the chats, ready to be imported next to the other logs. There's not much better to get your feet wet with a new environment than a clearly-defined basic task, I say.
For the morbidly curious, the pertinent code is here: