2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import "AIAccountController.h"
18 #import "AIContactController.h"
19 #import "AIContentController.h"
20 #import "AIInterfaceController.h"
21 #import "AdiumURLHandling.h"
22 #import "XtrasInstaller.h"
23 #import <AIUtilities/AIStringAdditions.h>
24 #import <AIUtilities/ESURLAdditions.h>
25 #import <Adium/AIAccount.h>
26 #import <Adium/AIContentMessage.h>
27 #import <Adium/AIService.h>
29 @interface AdiumURLHandling(PRIVATE)
30 + (void)_setHelperAppForKey:(ConstStr255Param)key withInstance:(ICInstance)ICInst;
31 + (void)_openChatToContactWithName:(NSString *)name onService:(NSString *)serviceIdentifier withMessage:(NSString *)body;
32 + (void)_openAIMGroupChat:(NSString *)roomname onExchange:(int)exchange;
35 @implementation AdiumURLHandling
37 + (void)registerURLTypes
40 * Prompt the user to change Adium to be the protocol handler for aim:// and/or yahoo:// if we aren't already. Give them the option to agree, disagree, or disagree and never be asked again.
45 //Start Internet Config, passing it Adium's creator code
46 Err = ICStart(&ICInst, 'AdiM');
48 //Bracket multiple calls with ICBegin() for efficiency as per documentation
49 ICBegin(ICInst, icReadWritePerm);
51 //Configure the protocols we want.
52 [self _setHelperAppForKey:(kICHelper "aim") withInstance:ICInst]; //AIM, official
53 [self _setHelperAppForKey:(kICHelper "ymsgr") withInstance:ICInst]; //Yahoo!, official
54 [self _setHelperAppForKey:(kICHelper "yahoo") withInstance:ICInst]; //Yahoo!, unofficial
55 [self _setHelperAppForKey:(kICHelper "xmpp") withInstance:ICInst]; //Jabber, official
56 [self _setHelperAppForKey:(kICHelper "jabber") withInstance:ICInst]; //Jabber, unofficial
57 [self _setHelperAppForKey:(kICHelper "icq") withInstance:ICInst]; //ICQ, unofficial
58 [self _setHelperAppForKey:(kICHelper "msn") withInstance:ICInst]; //MSN, unofficial
61 [self _setHelperAppForKey:(kICHelper "adiumxtra") withInstance:ICInst];
63 //End whatever it was that ICBegin() began
66 //We're done with Internet Config, so stop it
69 //How there could be an error stopping Internet Config, I don't know.
71 NSLog(@"Error stopping InternetConfig. Error code: %d", Err);
74 NSLog(@"Error starting InternetConfig. Error code: %d", Err);
78 + (void)handleURLEvent:(NSString *)eventString
80 NSURL *url = [NSURL URLWithString:eventString];
81 AIAdium *sharedAdium = [AIObject sharedAdiumInstance];
84 NSString *scheme, *newScheme;
87 //make sure we have the // in ://, as it simplifies later processing.
88 if (![[url resourceSpecifier] hasPrefix:@"//"]) {
89 eventString = [NSString stringWithFormat:@"%@://%@", [url scheme], [url resourceSpecifier]];
90 url = [NSURL URLWithString:eventString];
93 scheme = [url scheme];
95 //map schemes to common aliases (like jabber: for xmpp:).
96 static NSDictionary *schemeMappingDict = nil;
97 if (!schemeMappingDict) {
98 schemeMappingDict = [[NSDictionary alloc] initWithObjectsAndKeys:
103 newScheme = [schemeMappingDict objectForKey:scheme];
104 if (newScheme && ![newScheme isEqualToString:scheme]) {
106 eventString = [NSString stringWithFormat:@"%@:%@", scheme, [url resourceSpecifier]];
107 url = [NSURL URLWithString:eventString];
110 static NSDictionary *schemeToServiceDict = nil;
111 if (!schemeToServiceDict) {
112 schemeToServiceDict = [[NSDictionary alloc] initWithObjectsAndKeys:
121 if ((serviceID = [schemeToServiceDict objectForKey:scheme])) {
122 NSString *host = [url host];
123 if ([host caseInsensitiveCompare:@"goim"] == NSOrderedSame) {
124 // aim://goim?screenname=tekjew
125 NSString *name = [[[url queryArgumentForKey:@"screenname"] stringByDecodingURLEscapes] compactedString];
128 [self _openChatToContactWithName:name
130 withMessage:[[url queryArgumentForKey:@"message"] stringByDecodingURLEscapes]];
133 } else if ([host caseInsensitiveCompare:@"addbuddy"] == NSOrderedSame) {
134 // aim://addbuddy?screenname=tekjew
135 // aim://addbuddy?listofscreennames=screen+name1,screen+name+2&groupname=buddies
136 NSString *name = [[[url queryArgumentForKey:@"screenname"] stringByDecodingURLEscapes] compactedString];
137 AIService *service = [[sharedAdium accountController] firstServiceWithServiceID:serviceID];
140 [[sharedAdium contactController] requestAddContactWithUID:name
144 NSString *listOfNames = [url queryArgumentForKey:@"listofscreennames"];
145 NSArray *names = [listOfNames componentsSeparatedByString:@","];
146 NSEnumerator *enumerator;
148 enumerator = [names objectEnumerator];
149 while ((name = [enumerator nextObject])) {
150 NSString *decodedName = [[name stringByDecodingURLEscapes] compactedString];
151 [[sharedAdium contactController] requestAddContactWithUID:decodedName
156 } else if ([host caseInsensitiveCompare:@"sendim"] == NSOrderedSame) {
157 // ymsgr://sendim?tekjew
158 NSString *name = [[[url query] stringByDecodingURLEscapes] compactedString];
161 [self _openChatToContactWithName:name
166 } else if ([host caseInsensitiveCompare:@"gochat"] == NSOrderedSame) {
167 // aim://gochat?RoomName=AdiumRocks
168 NSString *roomname = [[url queryArgumentForKey:@"roomname"] stringByDecodingURLEscapes];
169 NSString *exchangeString = [url queryArgumentForKey:@"exchange"];
172 if (exchangeString) {
173 exchange = [exchangeString intValue];
176 [self _openAIMGroupChat:roomname onExchange:(exchange ? exchange : 4)];
179 } else if ([url queryArgumentForKey:@"openChatToScreenName"]) {
180 // aim://openChatToScreenname?tekjew [?]
181 NSString *name = [[[url queryArgumentForKey:@"openChatToScreenname"] stringByDecodingURLEscapes] compactedString];
184 [self _openChatToContactWithName:name
189 //Default to opening the host as a name.
191 NSString *user = [url user];
192 NSString *host = [url host];
194 if (user && [user length]) {
195 // jabber://tekjew@jabber.org
196 // msn://jdoe@hotmail.com
197 name = [NSString stringWithFormat:@"%@@%@",[url user],[url host]];
203 [self _openChatToContactWithName:[name compactedString]
208 } else if ([scheme isEqualToString:@"adiumxtra"]) {
209 //Installs an adium extra
210 // adiumxtra://www.adiumxtras.com/path/to/xtra.zip
212 [[XtrasInstaller installer] installXtraAtURL:url];
217 + (void)_setHelperAppForKey:(ConstStr255Param)key withInstance:(ICInstance)ICInst
224 TheSize = sizeof(Spec);
226 // Get the current aim helper app, to fill the Spec and TheSize variables
227 Err = ICGetPref(ICInst, key, &Junk, &Spec, &TheSize);
229 //Set the name and creator codes
230 if (Spec.fCreator != 'AdIM') {
231 Spec.name[0] = sprintf((char *) &Spec.name[1], "Adium.app");
232 Spec.fCreator = 'AdIM';
234 //Set the helper app to Adium
235 Err = ICSetPref(ICInst, key, kICAttrNoChange, &Spec, TheSize);
239 + (void)_openChatToContactWithName:(NSString *)UID onService:(NSString *)serviceID withMessage:(NSString *)message
241 AIListContact *contact;
242 AIAdium *sharedAdium = [AIObject sharedAdiumInstance];
244 contact = [[sharedAdium contactController] preferredContactWithUID:UID
245 andServiceID:serviceID
246 forSendingContentType:CONTENT_MESSAGE_TYPE];
248 //Open the chat and set it as active
249 [[sharedAdium interfaceController] setActiveChat:[[sharedAdium chatController] openChatWithContact:contact]];
251 //Insert the message text as if the user had typed it after opening the chat
252 NSResponder *responder = [[[NSApplication sharedApplication] keyWindow] firstResponder];
253 if (message && [responder isKindOfClass:[NSTextView class]] && [(NSTextView *)responder isEditable]) {
254 [responder insertText:message];
262 + (void)_openAIMGroupChat:(NSString *)roomname onExchange:(int)exchange
265 NSEnumerator *enumerator;
267 //Find an AIM-compatible online account which can create group chats
268 enumerator = [[[[AIObject sharedAdiumInstance] accountController] accounts] objectEnumerator];
269 while ((account = [enumerator nextObject])) {
270 if ([account online] &&
271 [[account serviceClass] isEqualToString:@"AIM-compatible"] &&
272 [[account service] canCreateGroupChats]) {
277 if (roomname && account) {
278 [[[AIObject sharedAdiumInstance] chatController] chatWithName:roomname
280 chatCreationInfo:[NSDictionary dictionaryWithObjectsAndKeys:
282 [NSNumber numberWithInt:exchange], @"exchange",