Merged [14875]:
[adiumx.git] / Source / AdiumURLHandling.m
bloba20d6d1c146d20bb0edb8842257cc4e2e67a0e7a
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
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.
8  * 
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.
12  * 
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.
15  */
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;
33 @end
35 @implementation AdiumURLHandling
37 + (void)registerURLTypes
39         /* TODO:
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. 
41          */
42         ICInstance ICInst;
43         OSErr Err = noErr;
45         //Start Internet Config, passing it Adium's creator code
46         Err = ICStart(&ICInst, 'AdiM');
47         if (Err == noErr) {
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
59          
60                 //Adium xtras
61                 [self _setHelperAppForKey:(kICHelper "adiumxtra") withInstance:ICInst];
63                 //End whatever it was that ICBegin() began
64                 ICEnd(ICInst);
66                 //We're done with Internet Config, so stop it
67                 Err = ICStop(ICInst);
68                 
69                 //How there could be an error stopping Internet Config, I don't know.
70                 if (Err != noErr) {
71                         NSLog(@"Error stopping InternetConfig. Error code: %d", Err);
72                 }
73         } else {
74                 NSLog(@"Error starting InternetConfig. Error code: %d", Err);
75         }
78 + (void)handleURLEvent:(NSString *)eventString
80         NSURL   *url = [NSURL URLWithString:eventString];
81         AIAdium *sharedAdium = [AIObject sharedAdiumInstance];
83         if (url) {
84                 NSString        *scheme, *newScheme;
85                 NSString        *serviceID;
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];
91                 }
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:
99                                 @"ymsgr", @"yahoo",
100                                 @"xmpp", @"jabber",
101                                 nil];
102                 }
103                 newScheme = [schemeMappingDict objectForKey:scheme];
104                 if (newScheme && ![newScheme isEqualToString:scheme]) {
105                         scheme = newScheme;
106                         eventString = [NSString stringWithFormat:@"%@:%@", scheme, [url resourceSpecifier]];
107                         url = [NSURL URLWithString:eventString];
108                 }
110                 static NSDictionary     *schemeToServiceDict = nil;
111                 if (!schemeToServiceDict) {
112                         schemeToServiceDict = [[NSDictionary alloc] initWithObjectsAndKeys:
113                                 @"AIM",    @"aim",
114                                 @"Yahoo!", @"ymsgr",
115                                 @"Jabber", @"xmpp",
116                                 @"ICQ",    @"icq",
117                                 @"MSN",    @"msn",
118                                 nil];
119                 }
120                 
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];
127                                 if (name) {
128                                         [self _openChatToContactWithName:name
129                                                                                    onService:serviceID 
130                                                                                  withMessage:[[url queryArgumentForKey:@"message"] stringByDecodingURLEscapes]];
131                                 }
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];
138                                 
139                                 if (name) {
140                                         [[sharedAdium contactController] requestAddContactWithUID:name
141                                                                                                                                 service:service];
142                                         
143                                 } else {
144                                         NSString                *listOfNames = [url queryArgumentForKey:@"listofscreennames"];
145                                         NSArray                 *names = [listOfNames componentsSeparatedByString:@","];
146                                         NSEnumerator    *enumerator;
147                                         
148                                         enumerator = [names objectEnumerator];
149                                         while ((name = [enumerator nextObject])) {
150                                                 NSString        *decodedName = [[name stringByDecodingURLEscapes] compactedString];
151                                                 [[sharedAdium contactController] requestAddContactWithUID:decodedName
152                                                                                                                                         service:service];
153                                         }
154                                 }
156                         } else if ([host caseInsensitiveCompare:@"sendim"] == NSOrderedSame) {
157                                 // ymsgr://sendim?tekjew
158                                 NSString *name = [[[url query] stringByDecodingURLEscapes] compactedString];
159                                 
160                                 if (name) {
161                                         [self _openChatToContactWithName:name
162                                                                                    onService:serviceID
163                                                                                  withMessage:nil];
164                                 }
165                                 
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"];
170                                 if (roomname) {
171                                         int exchange = 0;
172                                         if (exchangeString) {
173                                                 exchange = [exchangeString intValue];   
174                                         }
175                                         
176                                         [self _openAIMGroupChat:roomname onExchange:(exchange ? exchange : 4)];
177                                 }
179                         } else if ([url queryArgumentForKey:@"openChatToScreenName"]) {
180                                 // aim://openChatToScreenname?tekjew  [?]
181                                 NSString *name = [[[url queryArgumentForKey:@"openChatToScreenname"] stringByDecodingURLEscapes] compactedString];
182                                 
183                                 if (name) {
184                                         [self _openChatToContactWithName:name
185                                                                                    onService:serviceID
186                                                                                  withMessage:nil];
187                                 }
188                         } else {
189                                 //Default to opening the host as a name.
191                                 NSString        *user = [url user];
192                                 NSString        *host = [url host];
193                                 NSString        *name;
194                                 if (user && [user length]) {
195                                         // jabber://tekjew@jabber.org
196                                         // msn://jdoe@hotmail.com
197                                         name = [NSString stringWithFormat:@"%@@%@",[url user],[url host]];
198                                 } else {
199                                         // aim://tekjew
200                                         name = host;
201                                 }
202                                 
203                                 [self _openChatToContactWithName:[name compactedString]
204                                                                            onService:serviceID
205                                                                          withMessage:nil];
206                         }
207                         
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];
213                 }
214         }
217 + (void)_setHelperAppForKey:(ConstStr255Param)key withInstance:(ICInstance)ICInst
219         OSErr           Err;
220         ICAppSpec       Spec;
221         ICAttr          Junk;
222         long            TheSize;
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);
236         }
239 + (void)_openChatToContactWithName:(NSString *)UID onService:(NSString *)serviceID withMessage:(NSString *)message
241         AIListContact   *contact;
242         AIAdium                 *sharedAdium = [AIObject sharedAdiumInstance];
243         
244         contact = [[sharedAdium contactController] preferredContactWithUID:UID
245                                                                                                                                                          andServiceID:serviceID 
246                                                                                                                                         forSendingContentType:CONTENT_MESSAGE_TYPE];
247         if (contact) {
248                 //Open the chat and set it as active
249                 [[sharedAdium interfaceController] setActiveChat:[[sharedAdium chatController] openChatWithContact:contact]];
250                 
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];
255                 }
257         } else {
258                 NSBeep();
259         }
262 + (void)_openAIMGroupChat:(NSString *)roomname onExchange:(int)exchange
264         AIAccount               *account;
265         NSEnumerator    *enumerator;
266         
267         //Find an AIM-compatible online account which can create group chats
268         enumerator = [[[[AIObject sharedAdiumInstance] accountController] accountArray] objectEnumerator];
269         while ((account = [enumerator nextObject])) {
270                 if ([account online] &&
271                         [[account serviceClass] isEqualToString:@"AIM-compatible"] &&
272                         [[account service] canCreateGroupChats]) {
273                         break;
274                 }
275         }
276         
277         if (roomname && account) {
278                 [[[AIObject sharedAdiumInstance] chatController] chatWithName:roomname
279                                                                                                                         onAccount:account
280                                                                                                          chatCreationInfo:[NSDictionary dictionaryWithObjectsAndKeys:
281                                                                                                                                                 roomname, @"room",
282                                                                                                                                                 [NSNumber numberWithInt:exchange], @"exchange",
283                                                                                                                                                 nil]];
284         } else {
285                 NSBeep();
286         }
287 }       
289 @end