Talking to iChat
The InstantMessaging framework in 10.4 is useful for getting a list of all of your buddies and getting their email address, picture, or capabilities etc, but how can you send a file to a buddy? What about getting a list of the groups a buddy is in, or even simply creating a new chat window? The IM framework doesn’t do these things. You can do some of these through Applescript of course, but I’d rather gag on the proverbial fork.
So what can you do? Well, after poking around for a short while, I figured out (it’s not that hard) there’s a connection between iChat and another program called iChatAgent. iChat controls most everything dealing with service connections, buddies, and all of that by telling iChatAgent to do it. The iChat status menu item is separate from iChat.app and talks to iChatAgent also. This is why iChat.app doesn’t have to be open and you can still be connected to AIM or Jabber or whatnot. So how can you talk to iChatAgent directly? You do so by creating a connection to it using NSConnection.
{
if (!theConnection) {
theConnection = [NSConnection connectionWithRegisteredName:@"com.apple.iChatAgent" host:nil];
fzdaemon = [[theConnection rootProxy] retain];
[fzdaemon setProtocolForProxy:@protocol(FZDaemon)];
}
}
The FZDaemon protocol and that for related objects can be found by using a class-dump on iChatAgent.app which is inside of the InstantMessaging framework bundle. When you dump it, you’ll see a bunch of methods in protocols starting with the “FZ” prefix that have return types of “oneway void”. This is a dead giveaway of the use of distributed objects. So basically, anything with the FZ prefix is what we can use to talk to iChatAgent.
- (void)providePiggyback;
- (void)addListener:(id)fp8 signature:(id)fp12 capabilities:(int)fp16;
- (void)removeListener:(id)fp8;
- (id)serviceDefaults;
- (void)writeServiceDefaults:(id)fp8;
- (id)loginDefaults;
- (void)setActiveAccount:(id)fp8;
- (id)activeAccount;
- (id)accountsForLoginID:(id)fp8;
- (id)accountDefaults:(id)fp8;
- (void)writeAccount:(id)fp8 defaults:(id)fp12;
- (id)name;
- (id)shortName;
- (id)internalName;
- (id)serviceIconURL;
- (id)addressBookProperty;
- (id)defaultAddressBookLabel;
- (id)emoteString;
- (id)emailDomains;
- (int)IDSensitivity;
- (unsigned int)capabilities;
- (unsigned int)publicCapabilities;
- (BOOL)handlesChatInvites;
- (BOOL)hasCapability:(unsigned int)fp8;
- (void)holdBuddyUpdates;
- (void)resumeBuddyUpdates;
- (int)acceptableMessageFormats;
- (id)defaultBuddyIconURLs;
- (id)loginID;
- (oneway void)autoLogin;
- (oneway void)login;
- (oneway void)logout;
- (BOOL)loginIfAvailable;
- (int)serviceLoginStatus;
- (int)serviceDisconnectReason;
- (id)serviceLoginStatusMessage;
- (void)serviceCertificatesChanged;
- (void)enableSecureIM:(BOOL)fp8;
- (void)disableCertificatesForPerson:(id)fp8;
- (id)allBuddies;
- (id)buddyProperties;
- (BOOL)requestProperty:(id)fp8 ofPerson:(id)fp12;
- (BOOL)setValue:(id)fp8 ofProperty:(id)fp12 ofPerson:(id)fp16;
- (id)buddyPictures;
- (id)pictureOfBuddy:(id)fp8;
- (id)groups;
- (BOOL)renameGroup:(id)fp8 to:(id)fp12;
- (BOOL)addBuddies:(id)fp8 toGroups:(id)fp12;
- (BOOL)removeBuddies:(id)fp8 fromGroups:(id)fp12;
- (id)createChatForIMsWith:(id)fp8 isDirect:(BOOL)fp12;
- (id)createChatWith:(id)fp8 invitation:(id)fp12 named:(id)fp16;
- (id)goToChatNamed:(id)fp8;
- (BOOL)invitePerson:(id)fp8 toChat:(id)fp12 withMessage:(id)fp16;
- (BOOL)respond:(BOOL)fp8 toInvitationToChat:(id)fp12;
- (BOOL)sendMessage:(id)fp8 toChat:(id)fp12;
- (BOOL)leaveChat:(id)fp8;
- (BOOL)setPerson:(id)fp8 isIgnored:(BOOL)fp12 inChat:(id)fp16;
- (CDAnonymousStruct1)validityOfChatRoomName:(id)fp8;
- (BOOL)infoForChat:(id)fp8 status:(out int *)fp12 isChatRoom:(out char *)fp16 members:(out id *)fp20;
- (BOOL)sendFile:(id)fp8 toPerson:(id)fp12;
- (id)currentShareUploads;
- (BOOL)requestShareDirectoryListing:(id)fp8 ofBuddy:(id)fp12;
- (id)getSharedFile:(id)fp8 ofBuddy:(id)fp12;
- (void)setVCCapabilities:(unsigned int)fp8 withMask:(unsigned int)fp12;
- (void)requestVCWithPerson:(id)fp8 properties:(id)fp12;
- (void)respondToVCInvitationWithPerson:(id)fp8 properties:(id)fp12;
- (void)cancelVCRequestWithPerson:(id)fp8;
- (void)sendCounterProposalToPerson:(id)fp8 properties:(id)fp12;
- (void)sendVCUpdate:(id)fp8 toPerson:(id)fp12;
- (void)sendVCOOBToPerson:(id)fp8 action:(unsigned long)fp12 param:(unsigned long)fp16;
- (void)blockMessages:(BOOL)fp8 fromID:(id)fp12;
- (void)setBlockList:(id)fp8;
- (id)blockList;
- (void)setAllowList:(id)fp8;
- (id)allowList;
- (void)setBlockingMode:(int)fp8;
- (int)blockingMode;
- (BOOL)blockIdleStatus;
- (void)setBlockIdleStatus:(BOOL)fp8;
- (BOOL)setPresenceStatus:(BOOL)fp8 forPerson:(id)fp12 requestID:(id)fp16;
- (BOOL)requestPresenceStatusFromPerson:(id)fp8;
@end
After getting the fzdaemon object, you can do just about anything you want. There’s a bit of guessing and gdb (if guessing doesn’t work) to figure out what parameter and return types are for some methods, but mostly it’s pretty obvious.
For starters, you can call [fzdaemon allServices] to get an array of service objects, and on each service object you can call name, allBuddies, buddyProperties, buddyPictures… to get the different values you want. You can even send a file to a person by using the -sendFile:toPerson: method. There are all kinds of things you can do.
So what have I done? Well, I had an idea for a project which I might not get around to for a long time, but I started hacking together a window that looks similar to iChat’s buddy list.
That’s about that.







Comments(0)