├── XMPPUser.m ├── KissXML ├── DDXMLNode.m ├── NSStringAdditions.h ├── DDXML.h ├── NSStringAdditions.m ├── DDXMLElement.h ├── DDXMLDocument.h ├── DDXMLPrivate.h ├── DDXMLDocument.m ├── DDXMLNode.h └── DDXMLElement.m ├── libidn ├── Mac │ └── libidn.a ├── iPhone │ └── libidn.a ├── libidn-1.11.tar.gz ├── stringprep.h └── idn-int.h ├── English.lproj ├── InfoPlist.strings ├── ChatWindow.nib │ ├── keyedobjects.nib │ ├── info.nib │ └── classes.nib └── MainMenu.nib │ ├── keyedobjects.nib │ ├── info.nib │ └── classes.nib ├── XMPPStream_Prefix.pch ├── XMPPIQ.h ├── XMPP.h ├── XMPPMessage.h ├── NSDataAdditions.h ├── XMPPElement.h ├── main.m ├── ChatWindowManager.h ├── RequestController.h ├── XMPPPresence.h ├── XMPP.exp ├── NSXMLElementAdditions.h ├── ChatController.h ├── XMPPResource.h ├── XMPPJID.h ├── XMPPTransports.h ├── XMPPPing.h ├── Info-Framework.plist ├── XMPPMessage.m ├── MulticastDelegate.h ├── LibIDN.h ├── Info.plist ├── Info-Mini.plist ├── RosterController.h ├── XMPPPresence.m ├── SCNotificationManager.h ├── LibIDN.m ├── XMPPUser.h ├── XMPPIQ.m ├── SCNotificationManager.m ├── XMPPElement.m ├── ChatWindowManager.m ├── copying.txt ├── NSXMLElementAdditions.m ├── SSCrypto.h ├── XMPPTransports.m ├── XMPPPing.m ├── XMPPClient.h ├── ChatController.m ├── XMPPResource.m ├── NSDataAdditions.m ├── MulticastDelegate.m ├── XMPPStream.h ├── RequestController.m ├── XMPPJID.m ├── RosterController.m ├── AsyncSocket.h └── XMPPClient.m /XMPPUser.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycode/xmppframework/HEAD/XMPPUser.m -------------------------------------------------------------------------------- /KissXML/DDXMLNode.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycode/xmppframework/HEAD/KissXML/DDXMLNode.m -------------------------------------------------------------------------------- /libidn/Mac/libidn.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycode/xmppframework/HEAD/libidn/Mac/libidn.a -------------------------------------------------------------------------------- /libidn/iPhone/libidn.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycode/xmppframework/HEAD/libidn/iPhone/libidn.a -------------------------------------------------------------------------------- /libidn/libidn-1.11.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycode/xmppframework/HEAD/libidn/libidn-1.11.tar.gz -------------------------------------------------------------------------------- /English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycode/xmppframework/HEAD/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /English.lproj/ChatWindow.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycode/xmppframework/HEAD/English.lproj/ChatWindow.nib/keyedobjects.nib -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycode/xmppframework/HEAD/English.lproj/MainMenu.nib/keyedobjects.nib -------------------------------------------------------------------------------- /XMPPStream_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'XMPPStream' target in the 'XMPPStream' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /XMPPIQ.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "XMPPElement.h" 3 | 4 | 5 | @interface XMPPIQ : XMPPElement 6 | 7 | + (XMPPIQ *)iqFromElement:(NSXMLElement *)element; 8 | 9 | + (BOOL)isRosterItem:(NSXMLElement *)item; 10 | 11 | - (BOOL)isRosterQuery; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /XMPP.h: -------------------------------------------------------------------------------- 1 | #import "DDXML.h" 2 | 3 | #import "NSXMLElementAdditions.h" 4 | 5 | #import "XMPPStream.h" 6 | #import "XMPPClient.h" 7 | #import "XMPPJID.h" 8 | #import "XMPPUser.h" 9 | #import "XMPPResource.h" 10 | #import "XMPPIQ.h" 11 | #import "XMPPMessage.h" 12 | #import "XMPPPresence.h" -------------------------------------------------------------------------------- /XMPPMessage.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "XMPPElement.h" 3 | 4 | 5 | @interface XMPPMessage : XMPPElement 6 | 7 | + (XMPPMessage *)messageFromElement:(NSXMLElement *)element; 8 | 9 | - (BOOL)isChatMessage; 10 | - (BOOL)isChatMessageWithBody; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /NSDataAdditions.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NSData (XMPPStreamAdditions) 4 | 5 | - (NSData *)md5Digest; 6 | 7 | - (NSData *)sha1Digest; 8 | 9 | - (NSString *)hexStringValue; 10 | 11 | - (NSString *)base64Encoded; 12 | - (NSData *)base64Decoded; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /XMPPElement.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXML.h" 3 | 4 | @class XMPPJID; 5 | 6 | 7 | @interface XMPPElement : NSXMLElement 8 | 9 | - (NSString *)elementID; 10 | 11 | - (XMPPJID *)to; 12 | - (XMPPJID *)from; 13 | 14 | - (NSString *)toStr; 15 | - (NSString *)fromStr; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // XMPPStream 4 | // 5 | // Created by Robert Hanson on 4/22/07. 6 | // Copyright __MyCompanyName__ 2007. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **) argv); 14 | } 15 | -------------------------------------------------------------------------------- /KissXML/NSStringAdditions.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | 5 | @interface NSString (NSStringAdditions) 6 | 7 | /** 8 | * xmlChar - A basic replacement for char, a byte in a UTF-8 encoded string. 9 | **/ 10 | - (const xmlChar *)xmlChar; 11 | 12 | - (NSString *)trimWhitespace; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /KissXML/DDXML.h: -------------------------------------------------------------------------------- 1 | #if TARGET_OS_IPHONE 2 | 3 | #import "DDXMLNode.h" 4 | #import "DDXMLElement.h" 5 | #import "DDXMLDocument.h" 6 | 7 | #ifndef NSXMLNode 8 | #define NSXMLNode DDXMLNode 9 | #endif 10 | #ifndef NSXMLElement 11 | #define NSXMLElement DDXMLElement 12 | #endif 13 | #ifndef NSXMLDocument 14 | #define NSXMLDocument DDXMLDocument 15 | #endif 16 | 17 | #endif -------------------------------------------------------------------------------- /ChatWindowManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | @class XMPPClient; 3 | @class XMPPUser; 4 | @class XMPPMessage; 5 | 6 | 7 | @interface ChatWindowManager : NSObject 8 | 9 | + (void)openChatWindowWithXMPPClient:(XMPPClient *)xc forXMPPUser:(XMPPUser *)user; 10 | 11 | + (void)handleChatMessage:(XMPPMessage *)message withXMPPClient:(XMPPClient *)client; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /RequestController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface RequestController : NSObject 5 | { 6 | NSMutableArray *jids; 7 | int jidIndex; 8 | 9 | IBOutlet id jidField; 10 | IBOutlet id window; 11 | IBOutlet id xmppClient; 12 | IBOutlet id xofyField; 13 | } 14 | 15 | - (IBAction)accept:(id)sender; 16 | - (IBAction)reject:(id)sender; 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /XMPPPresence.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "XMPPElement.h" 3 | 4 | 5 | @interface XMPPPresence : XMPPElement 6 | 7 | + (XMPPPresence *)presenceFromElement:(NSXMLElement *)element; 8 | 9 | - (id)initWithType:(NSString *)type to:(XMPPJID *)to; 10 | 11 | - (NSString *)type; 12 | 13 | - (NSString *)show; 14 | - (NSString *)status; 15 | 16 | - (int)priority; 17 | 18 | - (int)intShow; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /KissXML/NSStringAdditions.m: -------------------------------------------------------------------------------- 1 | #import "NSStringAdditions.h" 2 | 3 | 4 | @implementation NSString (NSStringAdditions) 5 | 6 | - (const xmlChar *)xmlChar 7 | { 8 | return (const xmlChar *)[self UTF8String]; 9 | } 10 | 11 | - (NSString *)trimWhitespace 12 | { 13 | NSMutableString *mStr = [self mutableCopy]; 14 | CFStringTrimWhitespace((CFMutableStringRef)mStr); 15 | 16 | NSString *result = [mStr copy]; 17 | 18 | [mStr release]; 19 | return [result autorelease]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /XMPP.exp: -------------------------------------------------------------------------------- 1 | # XMPP.exp -- list of exports for XMPP.framework 2 | 3 | .objc_class_name_XMPPClient 4 | .objc_class_name_XMPPDigestAuthentication 5 | .objc_class_name_XMPPElement 6 | #.objc_class_name_XMPPIQ 7 | .objc_class_name_XMPPJID 8 | .objc_class_name_XMPPMessage 9 | .objc_class_name_XMPPPresence 10 | .objc_class_name_XMPPResource 11 | .objc_class_name_XMPPStream 12 | .objc_class_name_XMPPTransports 13 | .objc_class_name_XMPPUser 14 | 15 | .objc_category_name_NSXMLElement_XMPPStreamAdditions 16 | -------------------------------------------------------------------------------- /NSXMLElementAdditions.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXML.h" 3 | 4 | @interface NSXMLElement (XMPPStreamAdditions) 5 | 6 | + (NSXMLElement *)elementWithName:(NSString *)name xmlns:(NSString *)ns; 7 | 8 | - (NSXMLElement *)elementForName:(NSString *)name; 9 | - (NSXMLElement *)elementForName:(NSString *)name xmlns:(NSString *)xmlns; 10 | 11 | - (NSString *)xmlns; 12 | - (void)setXmlns:(NSString *)ns; 13 | 14 | - (void)addAttributeWithName:(NSString *)name stringValue:(NSString *)string; 15 | - (NSDictionary *)attributesAsDictionary; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ChatController.h: -------------------------------------------------------------------------------- 1 | #import 2 | @class XMPPClient; 3 | @class XMPPMessage; 4 | @class XMPPJID; 5 | 6 | 7 | @interface ChatController : NSWindowController 8 | { 9 | XMPPClient *xmppClient; 10 | XMPPJID *jid; 11 | 12 | XMPPMessage *firstMessage; 13 | 14 | IBOutlet id messageField; 15 | IBOutlet id messageView; 16 | } 17 | 18 | - (id)initWithXMPPClient:(XMPPClient *)client jid:(XMPPJID *)fullJID; 19 | - (id)initWithXMPPClient:(XMPPClient *)client jid:(XMPPJID *)fullJID message:(XMPPMessage *)message; 20 | 21 | - (XMPPJID *)jid; 22 | 23 | - (IBAction)sendMessage:(id)sender; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /XMPPResource.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXML.h" 3 | 4 | @class XMPPJID; 5 | @class XMPPUser; 6 | @class XMPPIQ; 7 | @class XMPPPresence; 8 | 9 | 10 | @interface XMPPResource : NSObject 11 | { 12 | XMPPJID *jid; 13 | XMPPPresence *presence; 14 | 15 | NSDate *presenceReceived; 16 | } 17 | 18 | - (id)initWithPresence:(XMPPPresence *)presence; 19 | 20 | - (XMPPJID *)jid; 21 | - (XMPPPresence *)presence; 22 | 23 | - (NSDate *)presenceReceived; 24 | 25 | - (void)updateWithPresence:(XMPPPresence *)presence; 26 | 27 | - (NSComparisonResult)compare:(XMPPResource *)another; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /English.lproj/ChatWindow.nib/info.nib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBFramework Version 6 | 629 7 | IBLastKnownRelativeProjectPath 8 | ../../XMPPStream.xcodeproj 9 | IBOldestOS 10 | 5 11 | IBOpenObjects 12 | 13 | IBSystem Version 14 | 9C7010 15 | targetFramework 16 | IBCocoaFramework 17 | 18 | 19 | -------------------------------------------------------------------------------- /XMPPJID.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXML.h" 3 | 4 | 5 | @interface XMPPJID : NSObject 6 | { 7 | NSString *user; 8 | NSString *domain; 9 | NSString *resource; 10 | } 11 | 12 | + (XMPPJID *)jidWithString:(NSString *)jidStr; 13 | + (XMPPJID *)jidWithString:(NSString *)jidStr resource:(NSString *)resource; 14 | + (XMPPJID *)jidWithUser:(NSString *)user domain:(NSString *)domain resource:(NSString *)resource; 15 | 16 | - (NSString *)user; 17 | - (NSString *)domain; 18 | - (NSString *)resource; 19 | 20 | - (XMPPJID *)bareJID; 21 | 22 | - (NSString *)bare; 23 | - (NSString *)full; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /XMPPTransports.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class XMPPClient; 4 | 5 | @interface XMPPTransports : NSObject 6 | { 7 | @private 8 | XMPPClient *client; 9 | } 10 | 11 | - (id)initWithXMPPClient:(XMPPClient *)xmppClient; 12 | 13 | - (XMPPClient *)xmppClient; 14 | 15 | - (void)queryGatewayDiscoveryIdentityForLegacyService:(NSString *)service; 16 | - (void)queryGatewayAgentInfo; 17 | - (void)queryRegistrationRequirementsForLegacyService:(NSString *)service; 18 | - (void)registerLegacyService:(NSString *)service userName:(NSString *)userName password:(NSString *)password; 19 | - (void)unregisterLegacyService:(NSString *)service; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /XMPPPing.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class XMPPClient; 4 | @class XMPPResource; 5 | @class XMPPIQ; 6 | 7 | 8 | @interface XMPPPing : NSObject 9 | { 10 | id delegate; 11 | 12 | XMPPClient *client; 13 | NSMutableArray *pingIDs; 14 | } 15 | 16 | - (id)initWithXMPPClient:(XMPPClient *)xmppClient delegate:(id)delegate; 17 | 18 | - (XMPPClient *)xmppClient; 19 | 20 | - (id)delegate; 21 | - (void)setDelegate:(id)delegate; 22 | 23 | - (void)sendPingToServer; 24 | - (void)sendPingToResource:(XMPPResource *)resource; 25 | 26 | @end 27 | 28 | @interface NSObject (XMPPPingDelegate) 29 | 30 | - (void)xmppPing:(XMPPPing *)sender didReceivePong:(XMPPIQ *)pong; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/info.nib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBFramework Version 6 | 670 7 | IBLastKnownRelativeProjectPath 8 | ../XMPPStream.xcodeproj 9 | IBOldestOS 10 | 5 11 | IBOpenObjects 12 | 13 | 29 14 | 332 15 | 2 16 | 17 | IBSystem Version 18 | 9E17 19 | targetFramework 20 | IBCocoaFramework 21 | 22 | 23 | -------------------------------------------------------------------------------- /Info-Framework.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.deusty.${PRODUCT_NAME:identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | FMWK 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.0 19 | 20 | 21 | -------------------------------------------------------------------------------- /XMPPMessage.m: -------------------------------------------------------------------------------- 1 | #import "XMPPMessage.h" 2 | #import "XMPPJID.h" 3 | #import "NSXMLElementAdditions.h" 4 | 5 | 6 | @implementation XMPPMessage 7 | 8 | + (XMPPMessage *)messageFromElement:(NSXMLElement *)element 9 | { 10 | XMPPMessage *result = (XMPPMessage *)element; 11 | result->isa = [XMPPMessage class]; 12 | return result; 13 | } 14 | 15 | - (BOOL)isChatMessage 16 | { 17 | return [[[self attributeForName:@"type"] stringValue] isEqualToString:@"chat"]; 18 | } 19 | 20 | - (BOOL)isChatMessageWithBody 21 | { 22 | if([self isChatMessage]) 23 | { 24 | NSString *body = [[self elementForName:@"body"] stringValue]; 25 | 26 | return ((body != nil) && ([body length] > 0)); 27 | } 28 | 29 | return NO; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /MulticastDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | @class MulticastDelegateListNode; 3 | 4 | 5 | @interface MulticastDelegate : NSObject 6 | { 7 | NSUInteger currentInvocationIndex; 8 | MulticastDelegateListNode *delegateList; 9 | } 10 | 11 | - (void)addDelegate:(id)delegate; 12 | - (void)removeDelegate:(id)delegate; 13 | 14 | - (void)removeAllDelegates; 15 | 16 | - (NSUInteger)count; 17 | 18 | @end 19 | 20 | @interface MulticastDelegateListNode : NSObject 21 | { 22 | id delegate; 23 | 24 | MulticastDelegateListNode *prev; 25 | MulticastDelegateListNode *next; 26 | } 27 | 28 | - (id)initWithDelegate:(id)delegate; 29 | 30 | - (id)delegate; 31 | 32 | - (MulticastDelegateListNode *)prev; 33 | - (void)setPrev:(MulticastDelegateListNode *)prev; 34 | 35 | - (MulticastDelegateListNode *)next; 36 | - (void)setNext:(MulticastDelegateListNode *)next; 37 | 38 | @end -------------------------------------------------------------------------------- /LibIDN.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface LibIDN : NSObject 5 | 6 | /** 7 | * Preps a node identifier for use in a JID. 8 | * If the given node is invalid, this method returns nil. 9 | * 10 | * See the XMPP RFC (3920) for details. 11 | * 12 | * Note: The prep properly converts the string to lowercase, as per the RFC. 13 | **/ 14 | + (NSString *)prepNode:(NSString *)node; 15 | 16 | /** 17 | * Preps a domain name for use in a JID. 18 | * If the given domain is invalid, this method returns nil. 19 | * 20 | * See the XMPP RFC (3920) for details. 21 | **/ 22 | + (NSString *)prepDomain:(NSString *)domain; 23 | 24 | /** 25 | * Preps a resource identifier for use in a JID. 26 | * If the given node is invalid, this method returns nil. 27 | * 28 | * See the XMPP RFC (3920) for details. 29 | **/ 30 | + (NSString *)prepResource:(NSString *)resource; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.deusty.${PRODUCT_NAME:identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | NSMainNibFile 24 | MainMenu 25 | NSPrincipalClass 26 | NSApplication 27 | 28 | 29 | -------------------------------------------------------------------------------- /Info-Mini.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.deusty.${PRODUCT_NAME:identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | NSMainNibFile 24 | MainMenu 25 | NSPrincipalClass 26 | NSApplication 27 | 28 | 29 | -------------------------------------------------------------------------------- /RosterController.h: -------------------------------------------------------------------------------- 1 | #import 2 | @class XMPPJID; 3 | @class XMPPClient; 4 | 5 | 6 | @interface RosterController : NSObject 7 | { 8 | BOOL isRegistering; 9 | BOOL isAuthenticating; 10 | 11 | NSArray *roster; 12 | 13 | IBOutlet id buddyField; 14 | IBOutlet id jidField; 15 | IBOutlet id messageField; 16 | IBOutlet id passwordField; 17 | IBOutlet id portField; 18 | IBOutlet id registerButton; 19 | IBOutlet id resourceField; 20 | IBOutlet id rosterTable; 21 | IBOutlet id selfSignedButton; 22 | IBOutlet id serverField; 23 | IBOutlet id signInButton; 24 | IBOutlet id signInSheet; 25 | IBOutlet id sslButton; 26 | IBOutlet id window; 27 | IBOutlet id xmppClient; 28 | } 29 | 30 | - (IBAction)addBuddy:(id)sender; 31 | - (IBAction)changePresence:(id)sender; 32 | - (IBAction)chat:(id)sender; 33 | - (IBAction)createAccount:(id)sender; 34 | - (IBAction)removeBuddy:(id)sender; 35 | - (IBAction)signIn:(id)sender; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /XMPPPresence.m: -------------------------------------------------------------------------------- 1 | #import "XMPPPresence.h" 2 | #import "NSXMLElementAdditions.h" 3 | 4 | 5 | @implementation XMPPPresence 6 | 7 | + (XMPPPresence *)presenceFromElement:(NSXMLElement *)element 8 | { 9 | XMPPPresence *result = (XMPPPresence *)element; 10 | result->isa = [XMPPPresence class]; 11 | return result; 12 | } 13 | 14 | - (id)initWithType:(NSString *)type to:(XMPPJID *)to 15 | { 16 | if(self = [super initWithName:@"presence"]) 17 | { 18 | [self addAttributeWithName:@"type" stringValue:type]; 19 | [self addAttributeWithName:@"to" stringValue:[to description]]; 20 | } 21 | return self; 22 | } 23 | 24 | - (NSString *)type 25 | { 26 | NSString *type = [[self attributeForName:@"type"] stringValue]; 27 | if(type) 28 | return type; 29 | else 30 | return @"available"; 31 | } 32 | 33 | - (NSString *)show 34 | { 35 | return [[self elementForName:@"show"] stringValue]; 36 | } 37 | 38 | - (NSString *)status 39 | { 40 | return [[self elementForName:@"status"] stringValue]; 41 | } 42 | 43 | - (int)priority 44 | { 45 | return [[[self elementForName:@"priority"] stringValue] intValue]; 46 | } 47 | 48 | - (int)intShow 49 | { 50 | NSString *show = [self show]; 51 | 52 | if([show isEqualToString:@"dnd"]) 53 | return 0; 54 | if([show isEqualToString:@"xa"]) 55 | return 1; 56 | if([show isEqualToString:@"away"]) 57 | return 2; 58 | if([show isEqualToString:@"chat"]) 59 | return 4; 60 | 61 | return 3; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /SCNotificationManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Written by Theo Hultberg (theo@iconara.net) 2004-03-09 with help from Boaz Stuller. 3 | * This code is in the public domain, provided that this notice remains. 4 | */ 5 | 6 | #import 7 | #import 8 | 9 | 10 | /*! 11 | * @class SCNotificationManager 12 | * @abstract Listens for changes in the system configuration database 13 | * and posts the changes to the default notification center. 14 | * @discussion To get notifications when the key "State:/Network/Global/IPv4" 15 | * changes, register yourself as an observer for notifications 16 | * with the name "State:/Network/Global/IPv4". 17 | * If you want to recieve notifications on any change in the 18 | * system configuration databse, register for notifications 19 | * on the SCNotificationManager object. 20 | * The user info in the notification is the data in the database 21 | * for the key you listen for. 22 | */ 23 | @interface SCNotificationManager : NSObject 24 | { 25 | SCDynamicStoreRef dynStore; 26 | CFRunLoopSourceRef rlSrc; 27 | } 28 | 29 | @end 30 | 31 | 32 | /*! 33 | * @function _SCNotificationCallback 34 | * @abstract Callback for the dynamic store, just calls keysChanged: on 35 | * the notification center. 36 | */ 37 | void _SCNotificationCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info); 38 | -------------------------------------------------------------------------------- /LibIDN.m: -------------------------------------------------------------------------------- 1 | #import "LibIDN.h" 2 | #import "stringprep.h" 3 | 4 | 5 | @implementation LibIDN 6 | 7 | + (NSString *)prepNode:(NSString *)node 8 | { 9 | if(node == nil) return nil; 10 | 11 | // Each allowable portion of a JID MUST NOT be more than 1023 bytes in length. 12 | // We make the buffer just big enough to hold a null-terminated string of this length. 13 | char buf[1024]; 14 | 15 | strncpy(buf, [node UTF8String], sizeof(buf)); 16 | 17 | if(stringprep_xmpp_nodeprep(buf, sizeof(buf)) != 0) return nil; 18 | 19 | return [NSString stringWithUTF8String:buf]; 20 | } 21 | 22 | + (NSString *)prepDomain:(NSString *)domain 23 | { 24 | if(domain == nil) return nil; 25 | 26 | // Each allowable portion of a JID MUST NOT be more than 1023 bytes in length. 27 | // We make the buffer just big enough to hold a null-terminated string of this length. 28 | char buf[1024]; 29 | 30 | strncpy(buf, [domain UTF8String], sizeof(buf)); 31 | 32 | if(stringprep_nameprep(buf, sizeof(buf)) != 0) return nil; 33 | 34 | return [NSString stringWithUTF8String:buf]; 35 | } 36 | 37 | + (NSString *)prepResource:(NSString *)resource 38 | { 39 | if(resource == nil) return nil; 40 | 41 | // Each allowable portion of a JID MUST NOT be more than 1023 bytes in length. 42 | // We make the buffer just big enough to hold a null-terminated string of this length. 43 | char buf[1024]; 44 | 45 | strncpy(buf, [resource UTF8String], sizeof(buf)); 46 | 47 | if(stringprep_xmpp_resourceprep(buf, sizeof(buf)) != 0) return nil; 48 | 49 | return [NSString stringWithUTF8String:buf]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /XMPPUser.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXML.h" 3 | 4 | @class XMPPJID; 5 | @class XMPPIQ; 6 | @class XMPPPresence; 7 | @class XMPPResource; 8 | 9 | #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 10 | #define NSStringCompareOptions unsigned 11 | #endif 12 | 13 | 14 | @interface XMPPUser : NSObject 15 | { 16 | XMPPJID *jid; 17 | NSMutableDictionary *itemAttributes; 18 | 19 | NSMutableDictionary *resources; 20 | XMPPResource *primaryResource; 21 | 22 | #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 23 | int tag; 24 | #else 25 | NSInteger tag; 26 | #endif 27 | } 28 | 29 | - (id)initWithJID:(XMPPJID *)jid; 30 | - (id)initWithItem:(NSXMLElement *)item; 31 | 32 | - (XMPPJID *)jid; 33 | - (NSString *)nickname; 34 | 35 | - (NSString *)displayName; 36 | 37 | - (BOOL)isOnline; 38 | - (BOOL)isPendingApproval; 39 | 40 | - (XMPPResource *)primaryResource; 41 | - (XMPPResource *)resourceForJID:(XMPPJID *)jid; 42 | - (NSArray *)sortedResources; 43 | - (NSArray *)unsortedResources; 44 | 45 | - (void)updateWithItem:(NSXMLElement *)item; 46 | - (void)updateWithPresence:(XMPPPresence *)presence; 47 | 48 | - (NSComparisonResult)compareByName:(XMPPUser *)another; 49 | - (NSComparisonResult)compareByName:(XMPPUser *)another options:(NSStringCompareOptions)mask; 50 | 51 | - (NSComparisonResult)compareByAvailabilityName:(XMPPUser *)another; 52 | - (NSComparisonResult)compareByAvailabilityName:(XMPPUser *)another options:(NSStringCompareOptions)mask; 53 | 54 | #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 55 | - (int)tag; 56 | - (void)setTag:(int)anInt; 57 | #else 58 | - (NSInteger)tag; 59 | - (void)setTag:(NSInteger)anInt; 60 | #endif 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /XMPPIQ.m: -------------------------------------------------------------------------------- 1 | #import "XMPPIQ.h" 2 | #import "NSXMLElementAdditions.h" 3 | 4 | 5 | @implementation XMPPIQ 6 | 7 | + (XMPPIQ *)iqFromElement:(NSXMLElement *)element 8 | { 9 | XMPPIQ *result = (XMPPIQ *)element; 10 | result->isa = [XMPPIQ class]; 11 | return result; 12 | } 13 | 14 | /** 15 | * For some bizarre reason (in my opinion), when you request your roster, 16 | * the server will return JID's NOT in your roster. These are the JID's of users who have requested 17 | * to be alerted to our presence. After we sign in, we'll again be notified, via the normal presence request objects. 18 | * It's redundant, and annoying, and just plain incorrect to include these JID's when we request our personal roster. 19 | * So now, we have to go to the extra effort to filter out these JID's, which is exactly what this method does. 20 | **/ 21 | + (BOOL)isRosterItem:(NSXMLElement *)item 22 | { 23 | NSXMLNode *subscription = [item attributeForName:@"subscription"]; 24 | if([[subscription stringValue] isEqualToString:@"none"]) 25 | { 26 | NSXMLNode *ask = [item attributeForName:@"ask"]; 27 | if([[ask stringValue] isEqualToString:@"subscribe"]) 28 | { 29 | return YES; 30 | } 31 | else 32 | { 33 | return NO; 34 | } 35 | } 36 | 37 | return YES; 38 | } 39 | 40 | /** 41 | * Returns whether or not the IQ element is in the "jabber:iq:roster" namespace, 42 | * and thus represents a roster update. 43 | **/ 44 | - (BOOL)isRosterQuery 45 | { 46 | // Note: Some jabber servers send an iq element with a xmlns. 47 | // Because of the bug in Apple's NSXML (documented in our elementForName method), 48 | // it is important we specify the xmlns for the query. 49 | 50 | NSXMLElement *query = [self elementForName:@"query" xmlns:@"jabber:iq:roster"]; 51 | 52 | return (query != nil); 53 | 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /KissXML/DDXMLElement.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXMLNode.h" 3 | 4 | 5 | @interface DDXMLElement : DDXMLNode 6 | { 7 | } 8 | 9 | - (id)initWithName:(NSString *)name; 10 | - (id)initWithName:(NSString *)name URI:(NSString *)URI; 11 | - (id)initWithName:(NSString *)name stringValue:(NSString *)string; 12 | - (id)initWithXMLString:(NSString *)string error:(NSError **)error; 13 | 14 | #pragma mark --- Elements by name --- 15 | 16 | - (NSArray *)elementsForName:(NSString *)name; 17 | - (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI; 18 | 19 | #pragma mark --- Attributes --- 20 | 21 | - (void)addAttribute:(DDXMLNode *)attribute; 22 | - (void)removeAttributeForName:(NSString *)name; 23 | - (void)setAttributes:(NSArray *)attributes; 24 | //- (void)setAttributesAsDictionary:(NSDictionary *)attributes; 25 | - (NSArray *)attributes; 26 | - (DDXMLNode *)attributeForName:(NSString *)name; 27 | //- (DDXMLNode *)attributeForLocalName:(NSString *)localName URI:(NSString *)URI; 28 | 29 | #pragma mark --- Namespaces --- 30 | 31 | - (void)addNamespace:(DDXMLNode *)aNamespace; 32 | - (void)removeNamespaceForPrefix:(NSString *)name; 33 | - (void)setNamespaces:(NSArray *)namespaces; 34 | - (NSArray *)namespaces; 35 | - (DDXMLNode *)namespaceForPrefix:(NSString *)prefix; 36 | - (DDXMLNode *)resolveNamespaceForName:(NSString *)name; 37 | - (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI; 38 | 39 | #pragma mark --- Children --- 40 | 41 | - (void)insertChild:(DDXMLNode *)child atIndex:(NSUInteger)index; 42 | //- (void)insertChildren:(NSArray *)children atIndex:(NSUInteger)index; 43 | - (void)removeChildAtIndex:(NSUInteger)index; 44 | - (void)setChildren:(NSArray *)children; 45 | - (void)addChild:(DDXMLNode *)child; 46 | //- (void)replaceChildAtIndex:(NSUInteger)index withNode:(DDXMLNode *)node; 47 | //- (void)normalizeAdjacentTextNodesPreservingCDATA:(BOOL)preserve; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /SCNotificationManager.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Written by Theo Hultberg (theo@iconara.net) 2004-03-09 with help from Boaz Stuller. 3 | * This code is in the public domain, provided that this notice remains. 4 | */ 5 | 6 | #import "SCNotificationManager.h" 7 | 8 | 9 | void _SCNotificationCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) 10 | { 11 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 12 | 13 | NSEnumerator *keysE = [(NSArray *)changedKeys objectEnumerator]; 14 | NSString *key = nil; 15 | NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 16 | 17 | while(key = [keysE nextObject]) 18 | { 19 | [nc postNotificationName:key 20 | object:(id)info 21 | userInfo:[(NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef) key) autorelease]]; 22 | } 23 | 24 | [pool release]; 25 | } 26 | 27 | 28 | @implementation SCNotificationManager 29 | 30 | - (id)init 31 | { 32 | if(self = [super init]) 33 | { 34 | SCDynamicStoreContext context = { 0, (void *)self, NULL, NULL, NULL }; 35 | 36 | dynStore = SCDynamicStoreCreate( 37 | NULL, 38 | (CFStringRef) [[NSBundle mainBundle] bundleIdentifier], 39 | _SCNotificationCallback, 40 | &context 41 | ); 42 | 43 | // Add to runloop to receive automatic callbacks to our SCNotificationCallback method 44 | rlSrc = SCDynamicStoreCreateRunLoopSource(NULL, dynStore, 0); 45 | CFRunLoopAddSource([[NSRunLoop currentRunLoop] getCFRunLoop], rlSrc, kCFRunLoopCommonModes); 46 | 47 | SCDynamicStoreSetNotificationKeys( 48 | dynStore, 49 | (CFArrayRef) [(NSArray *)SCDynamicStoreCopyKeyList(dynStore, CFSTR(".*")) autorelease], 50 | NULL 51 | ); 52 | } 53 | return self; 54 | } 55 | 56 | - (void)dealloc 57 | { 58 | CFRunLoopRemoveSource([[NSRunLoop currentRunLoop] getCFRunLoop], rlSrc, kCFRunLoopCommonModes); 59 | 60 | if(rlSrc) CFRelease(rlSrc); 61 | if(dynStore) CFRelease(dynStore); 62 | 63 | [super dealloc]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /XMPPElement.m: -------------------------------------------------------------------------------- 1 | #import "XMPPElement.h" 2 | #import "XMPPJID.h" 3 | 4 | 5 | @implementation XMPPElement 6 | 7 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 8 | #pragma mark Encoding, Decoding 9 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10 | 11 | #if ! TARGET_OS_IPHONE 12 | - (id)replacementObjectForPortCoder:(NSPortCoder *)encoder 13 | { 14 | if([encoder isBycopy]) 15 | return self; 16 | else 17 | return [NSDistantObject proxyWithLocal:self connection:[encoder connection]]; 18 | } 19 | #endif 20 | 21 | - (id)initWithCoder:(NSCoder *)coder 22 | { 23 | NSString *xmlString; 24 | if([coder allowsKeyedCoding]) 25 | { 26 | xmlString = [coder decodeObjectForKey:@"xmlString"]; 27 | } 28 | else 29 | { 30 | xmlString = [coder decodeObject]; 31 | } 32 | 33 | return [super initWithXMLString:xmlString error:nil]; 34 | } 35 | 36 | - (void)encodeWithCoder:(NSCoder *)coder 37 | { 38 | NSString *xmlString = [self XMLString]; 39 | 40 | if([coder allowsKeyedCoding]) 41 | { 42 | [coder encodeObject:xmlString forKey:@"xmlString"]; 43 | } 44 | else 45 | { 46 | [coder encodeObject:xmlString]; 47 | } 48 | } 49 | 50 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 51 | #pragma mark Common Jabber Methods 52 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 53 | 54 | - (NSString *)elementID 55 | { 56 | return [[self attributeForName:@"id"] stringValue]; 57 | } 58 | 59 | - (NSString *)toStr 60 | { 61 | return [[self attributeForName:@"to"] stringValue]; 62 | } 63 | 64 | - (NSString *)fromStr 65 | { 66 | return [[self attributeForName:@"from"] stringValue];; 67 | } 68 | 69 | - (XMPPJID *)to 70 | { 71 | return [XMPPJID jidWithString:[self toStr]]; 72 | } 73 | 74 | - (XMPPJID *)from 75 | { 76 | return [XMPPJID jidWithString:[self fromStr]]; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /ChatWindowManager.m: -------------------------------------------------------------------------------- 1 | #import "ChatWindowManager.h" 2 | #import "ChatController.h" 3 | #import "XMPP.h" 4 | 5 | 6 | @implementation ChatWindowManager 7 | 8 | + (ChatController *)chatControllerForJID:(XMPPJID *)jid matchResource:(BOOL)matchResource 9 | { 10 | // Loop through all the open windows, and see if any of them are the one we want... 11 | NSArray *windows = [NSApp windows]; 12 | 13 | int i; 14 | for(i = 0; i < [windows count]; i++) 15 | { 16 | NSWindow *currentWindow = [windows objectAtIndex:i]; 17 | ChatController *currentWC = [currentWindow windowController]; 18 | 19 | if([currentWC isKindOfClass:[ChatController class]]) 20 | { 21 | if(matchResource) 22 | { 23 | XMPPJID *currentJID = [currentWC jid]; 24 | 25 | if([currentJID isEqual:jid]) 26 | { 27 | return currentWC; 28 | } 29 | } 30 | else 31 | { 32 | XMPPJID *currentJID = [[currentWC jid] bareJID]; 33 | 34 | if([currentJID isEqual:[jid bareJID]]) 35 | { 36 | return currentWC; 37 | } 38 | } 39 | } 40 | } 41 | 42 | return nil; 43 | } 44 | 45 | + (void)openChatWindowWithXMPPClient:(XMPPClient *)client forXMPPUser:(XMPPUser *)user 46 | { 47 | ChatController *cc = [[self class] chatControllerForJID:[user jid] matchResource:NO]; 48 | 49 | if(cc) 50 | { 51 | [[cc window] makeKeyAndOrderFront:self]; 52 | } 53 | else 54 | { 55 | // Create Manual Sync Window 56 | XMPPJID *jid = [[user primaryResource] jid]; 57 | 58 | ChatController *temp = [[ChatController alloc] initWithXMPPClient:client jid:jid]; 59 | [temp showWindow:self]; 60 | 61 | // Note: MSWController will automatically release itself when the user closes the window 62 | } 63 | } 64 | 65 | + (void)handleChatMessage:(XMPPMessage *)message withXMPPClient:(XMPPClient *)client 66 | { 67 | NSLog(@"ChatWindowManager: handleChatMessage"); 68 | 69 | ChatController *cc = [[self class] chatControllerForJID:[message from] matchResource:YES]; 70 | 71 | if(!cc) 72 | { 73 | // Create new chat window 74 | XMPPJID *jid = [message from]; 75 | 76 | ChatController *newCC = [[ChatController alloc] initWithXMPPClient:client jid:jid message:message]; 77 | [newCC showWindow:self]; 78 | 79 | // Note: ChatController will automatically release itself when the user closes the window. 80 | } 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /KissXML/DDXMLDocument.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXMLElement.h" 3 | #import "DDXMLNode.h" 4 | 5 | 6 | enum { 7 | DDXMLDocumentXMLKind = 0, 8 | DDXMLDocumentXHTMLKind, 9 | DDXMLDocumentHTMLKind, 10 | DDXMLDocumentTextKind 11 | }; 12 | typedef NSUInteger DDXMLDocumentContentKind; 13 | 14 | @interface DDXMLDocument : DDXMLNode 15 | { 16 | } 17 | 18 | - (id)initWithXMLString:(NSString *)string options:(NSUInteger)mask error:(NSError **)error; 19 | //- (id)initWithContentsOfURL:(NSURL *)url options:(NSUInteger)mask error:(NSError **)error; 20 | - (id)initWithData:(NSData *)data options:(NSUInteger)mask error:(NSError **)error; 21 | //- (id)initWithRootElement:(DDXMLElement *)element; 22 | 23 | //+ (Class)replacementClassForClass:(Class)cls; 24 | 25 | //- (void)setCharacterEncoding:(NSString *)encoding; //primitive 26 | //- (NSString *)characterEncoding; //primitive 27 | 28 | //- (void)setVersion:(NSString *)version; 29 | //- (NSString *)version; 30 | 31 | //- (void)setStandalone:(BOOL)standalone; 32 | //- (BOOL)isStandalone; 33 | 34 | //- (void)setDocumentContentKind:(DDXMLDocumentContentKind)kind; 35 | //- (DDXMLDocumentContentKind)documentContentKind; 36 | 37 | //- (void)setMIMEType:(NSString *)MIMEType; 38 | //- (NSString *)MIMEType; 39 | 40 | //- (void)setDTD:(DDXMLDTD *)documentTypeDeclaration; 41 | //- (DDXMLDTD *)DTD; 42 | 43 | //- (void)setRootElement:(DDXMLNode *)root; 44 | - (DDXMLElement *)rootElement; 45 | 46 | //- (void)insertChild:(DDXMLNode *)child atIndex:(NSUInteger)index; 47 | 48 | //- (void)insertChildren:(NSArray *)children atIndex:(NSUInteger)index; 49 | 50 | //- (void)removeChildAtIndex:(NSUInteger)index; 51 | 52 | //- (void)setChildren:(NSArray *)children; 53 | 54 | //- (void)addChild:(DDXMLNode *)child; 55 | 56 | //- (void)replaceChildAtIndex:(NSUInteger)index withNode:(DDXMLNode *)node; 57 | 58 | - (NSData *)XMLData; 59 | - (NSData *)XMLDataWithOptions:(NSUInteger)options; 60 | 61 | //- (id)objectByApplyingXSLT:(NSData *)xslt arguments:(NSDictionary *)arguments error:(NSError **)error; 62 | //- (id)objectByApplyingXSLTString:(NSString *)xslt arguments:(NSDictionary *)arguments error:(NSError **)error; 63 | //- (id)objectByApplyingXSLTAtURL:(NSURL *)xsltURL arguments:(NSDictionary *)argument error:(NSError **)error; 64 | 65 | //- (BOOL)validateAndReturnError:(NSError **)error; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /English.lproj/ChatWindow.nib/classes.nib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBClasses 6 | 7 | 8 | ACTIONS 9 | 10 | sendMessage 11 | id 12 | 13 | CLASS 14 | ChatController 15 | LANGUAGE 16 | ObjC 17 | OUTLETS 18 | 19 | messageField 20 | id 21 | messageView 22 | id 23 | 24 | SUPERCLASS 25 | NSWindowController 26 | 27 | 28 | CLASS 29 | XMPPClient 30 | LANGUAGE 31 | ObjC 32 | SUPERCLASS 33 | NSObject 34 | 35 | 36 | CLASS 37 | XMPPStream 38 | LANGUAGE 39 | ObjC 40 | OUTLETS 41 | 42 | delegate 43 | id 44 | 45 | SUPERCLASS 46 | NSObject 47 | 48 | 49 | ACTIONS 50 | 51 | xmppClientConnecting 52 | XMPPClient 53 | xmppClientDidAuthenticate 54 | XMPPClient 55 | xmppClientDidConnect 56 | XMPPClient 57 | xmppClientDidDisconnect 58 | XMPPClient 59 | xmppClientDidNotConnect 60 | XMPPClient 61 | xmppClientDidRegister 62 | XMPPClient 63 | xmppClientDidUpdateRoster 64 | XMPPClient 65 | xmppStreamDidAuthenticate 66 | XMPPStream 67 | xmppStreamDidClose 68 | XMPPStream 69 | xmppStreamDidOpen 70 | XMPPStream 71 | xmppStreamDidRegister 72 | XMPPStream 73 | 74 | CLASS 75 | NSObject 76 | LANGUAGE 77 | ObjC 78 | 79 | 80 | IBVersion 81 | 1 82 | 83 | 84 | -------------------------------------------------------------------------------- /KissXML/DDXMLPrivate.h: -------------------------------------------------------------------------------- 1 | #import "DDXMLNode.h" 2 | #import "DDXMLElement.h" 3 | #import "DDXMLDocument.h" 4 | 5 | // We can't rely solely on NSAssert, because many developers disable them for release builds. 6 | // Our API contract requires us to keep these assertions intact. 7 | #define DDCheck(condition, desc, ...) { if(!(condition)) { [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd object:self file:[NSString stringWithUTF8String:__FILE__] lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; } } 8 | 9 | #define DDLastErrorKey @"DDXML:LastError" 10 | 11 | struct _xmlRetain { 12 | void * retainee; 13 | struct _xmlRetain * next; 14 | }; 15 | typedef struct _xmlRetain *xmlRetainPtr; 16 | 17 | @interface DDXMLNode (PrivateAPI) 18 | 19 | + (id)nodeWithPrimitive:(xmlKindPtr)nodePtr; 20 | - (id)initWithPrimitive:(xmlKindPtr)nodePtr; 21 | 22 | + (id)nodeWithPrimitive:(xmlKindPtr)nodePtr nsParent:(xmlNodePtr)parentPtr; 23 | - (id)initWithPrimitive:(xmlKindPtr)nodePtr nsParent:(xmlNodePtr)parentPtr; 24 | 25 | + (BOOL)isXmlAttrPtr:(xmlKindPtr)kindPtr; 26 | - (BOOL)isXmlAttrPtr; 27 | 28 | + (BOOL)isXmlNodePtr:(xmlKindPtr)kindPtr; 29 | - (BOOL)isXmlNodePtr; 30 | 31 | + (BOOL)isXmlDocPtr:(xmlKindPtr)kindPtr; 32 | - (BOOL)isXmlDocPtr; 33 | 34 | + (BOOL)isXmlDtdPtr:(xmlKindPtr)kindPtr; 35 | - (BOOL)isXmlDtdPtr; 36 | 37 | + (BOOL)isXmlNsPtr:(xmlKindPtr)kindPtr; 38 | - (BOOL)isXmlNsPtr; 39 | 40 | - (BOOL)hasParent; 41 | 42 | + (void)recursiveStripDocPointersFromNode:(xmlNodePtr)node; 43 | 44 | + (void)detachAttribute:(xmlAttrPtr)attr fromNode:(xmlNodePtr)node; 45 | + (void)removeAttribute:(xmlAttrPtr)attr fromNode:(xmlNodePtr)node; 46 | + (void)removeAllAttributesFromNode:(xmlNodePtr)node; 47 | 48 | + (void)detachNamespace:(xmlNsPtr)ns fromNode:(xmlNodePtr)node; 49 | + (void)removeNamespace:(xmlNsPtr)ns fromNode:(xmlNodePtr)node; 50 | + (void)removeAllNamespacesFromNode:(xmlNodePtr)node; 51 | 52 | + (void)detachChild:(xmlNodePtr)child fromNode:(xmlNodePtr)node; 53 | + (void)removeChild:(xmlNodePtr)child fromNode:(xmlNodePtr)node; 54 | + (void)removeAllChildrenFromNode:(xmlNodePtr)node; 55 | 56 | + (void)removeAllChildrenFromDoc:(xmlDocPtr)doc; 57 | 58 | - (void)nodeRetain; 59 | - (void)nodeRelease; 60 | 61 | + (NSError *)lastError; 62 | 63 | @end 64 | 65 | @interface DDXMLElement (PrivateAPI) 66 | 67 | + (id)nodeWithPrimitive:(xmlKindPtr)nodePtr; 68 | - (id)initWithPrimitive:(xmlKindPtr)nodePtr; 69 | 70 | - (NSArray *)elementsWithName:(NSString *)name uri:(NSString *)URI; 71 | 72 | + (DDXMLNode *)resolveNamespaceForPrefix:(NSString *)prefix atNode:(xmlNodePtr)nodePtr; 73 | + (NSString *)resolvePrefixForURI:(NSString *)uri atNode:(xmlNodePtr)nodePtr; 74 | 75 | @end 76 | 77 | @interface DDXMLDocument (PrivateAPI) 78 | 79 | + (id)nodeWithPrimitive:(xmlKindPtr)nodePtr; 80 | - (id)initWithPrimitive:(xmlKindPtr)nodePtr; 81 | 82 | @end -------------------------------------------------------------------------------- /KissXML/DDXMLDocument.m: -------------------------------------------------------------------------------- 1 | #import "DDXMLDocument.h" 2 | #import "NSStringAdditions.h" 3 | #import "DDXMLPrivate.h" 4 | 5 | 6 | @implementation DDXMLDocument 7 | 8 | + (id)nodeWithPrimitive:(xmlKindPtr)nodePtr 9 | { 10 | return [[[DDXMLDocument alloc] initWithPrimitive:nodePtr] autorelease]; 11 | } 12 | 13 | - (id)initWithPrimitive:(xmlKindPtr)nodePtr 14 | { 15 | if(nodePtr == NULL || nodePtr->type != XML_DOCUMENT_NODE) 16 | { 17 | [self release]; 18 | return nil; 19 | } 20 | 21 | self = [super initWithPrimitive:nodePtr]; 22 | return self; 23 | } 24 | 25 | /** 26 | * Initializes and returns a DDXMLDocument object created from an NSData object. 27 | * 28 | * Returns an initialized DDXMLDocument object, or nil if initialization fails 29 | * because of parsing errors or other reasons. 30 | **/ 31 | - (id)initWithXMLString:(NSString *)string options:(NSUInteger)mask error:(NSError **)error 32 | { 33 | return [self initWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:mask error:error]; 34 | } 35 | 36 | /** 37 | * Initializes and returns a DDXMLDocument object created from an NSData object. 38 | * 39 | * Returns an initialized DDXMLDocument object, or nil if initialization fails 40 | * because of parsing errors or other reasons. 41 | **/ 42 | - (id)initWithData:(NSData *)data options:(NSUInteger)mask error:(NSError **)error 43 | { 44 | if(data == nil || [data length] == 0) 45 | { 46 | if(error) *error = [NSError errorWithDomain:@"DDXMLErrorDomain" code:0 userInfo:nil]; 47 | 48 | [self release]; 49 | return nil; 50 | } 51 | 52 | // Even though xmlKeepBlanksDefault(0) is called in DDXMLNode's initialize method, 53 | // it has been documented that this call seems to get reset on the iPhone: 54 | // http://code.google.com/p/kissxml/issues/detail?id=8 55 | // 56 | // Therefore, we call it again here just to be safe. 57 | xmlKeepBlanksDefault(0); 58 | 59 | xmlDocPtr doc = xmlParseMemory([data bytes], [data length]); 60 | if(doc == NULL) 61 | { 62 | if(error) *error = [NSError errorWithDomain:@"DDXMLErrorDomain" code:1 userInfo:nil]; 63 | 64 | [self release]; 65 | return nil; 66 | } 67 | 68 | return [self initWithPrimitive:(xmlKindPtr)doc]; 69 | } 70 | 71 | /** 72 | * Returns the root element of the receiver. 73 | **/ 74 | - (DDXMLElement *)rootElement 75 | { 76 | xmlDocPtr doc = (xmlDocPtr)genericPtr; 77 | 78 | // doc->children is a list containing possibly comments, DTDs, etc... 79 | 80 | xmlNodePtr rootNode = xmlDocGetRootElement(doc); 81 | 82 | if(rootNode != NULL) 83 | return [DDXMLElement nodeWithPrimitive:(xmlKindPtr)(rootNode)]; 84 | else 85 | return nil; 86 | } 87 | 88 | - (NSData *)XMLData 89 | { 90 | return [[self XMLString] dataUsingEncoding:NSUTF8StringEncoding]; 91 | } 92 | 93 | - (NSData *)XMLDataWithOptions:(NSUInteger)options 94 | { 95 | return [[self XMLStringWithOptions:options] dataUsingEncoding:NSUTF8StringEncoding]; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /copying.txt: -------------------------------------------------------------------------------- 1 | AsyncSocket: (Public Domain) 2 | 3 | This class is released under the public domain. It was originally written by Dustin Voss, and has been modified and improved by Robbie Hanson. 4 | 5 | ------------------------------------------------------------ 6 | 7 | SSCrypto Framework: (BSD Style License) 8 | 9 | Copyright (c) 2003, Septicus Software All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are 13 | met: 14 | 15 | * Redistributions of source code must retain the above 16 | copyright notice, this list of conditions and the 17 | following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above 20 | copyright notice, this list of conditions and the 21 | following disclaimer in the documentation and/or other 22 | materials provided with the distribution. 23 | 24 | * Neither the name of Septicus Software nor the names of 25 | its contributors may be used to endorse or promote 26 | products derived from this software without specific 27 | prior written permission. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | ------------------------------------------------------------ 33 | 34 | 35 | XMPPStream and all other source code: (BSD Style License) 36 | 37 | Software License Agreement (BSD License) 38 | 39 | Copyright (c) 2007, Deusty Designs, LLC 40 | All rights reserved. 41 | 42 | Redistribution and use of this software in source and binary forms, 43 | with or without modification, are permitted provided that the following conditions are met: 44 | 45 | * Redistributions of source code must retain the above 46 | copyright notice, this list of conditions and the 47 | following disclaimer. 48 | 49 | * Neither the name of Desuty Designs nor the names of its 50 | contributors may be used to endorse or promote products 51 | derived from this software without specific prior 52 | written permission of Deusty Designs, LLC. 53 | 54 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /NSXMLElementAdditions.m: -------------------------------------------------------------------------------- 1 | #import "NSXMLElementAdditions.h" 2 | 3 | @implementation NSXMLElement (XMPPStreamAdditions) 4 | 5 | /** 6 | * Quick method to create an element 7 | **/ 8 | + (NSXMLElement *)elementWithName:(NSString *)name xmlns:(NSString *)ns 9 | { 10 | NSXMLElement *element = [NSXMLElement elementWithName:name]; 11 | [element setXmlns:ns]; 12 | return element; 13 | } 14 | 15 | /** 16 | * This method returns the first child element for the given name (as an NSXMLElement). 17 | * If no child elements exist for the given name, nil is returned. 18 | **/ 19 | - (NSXMLElement *)elementForName:(NSString *)name 20 | { 21 | NSArray *elements = [self elementsForName:name]; 22 | if([elements count] > 0) 23 | { 24 | return [elements objectAtIndex:0]; 25 | } 26 | else 27 | { 28 | // There is a bug in the NSXMLElement elementsForName: method. 29 | // Consider the following XML fragment: 30 | // 31 | // 32 | // 33 | // 34 | // 35 | // Calling [query elementsForName:@"x"] results in an empty array! 36 | // 37 | // However, it will work properly if you use the following: 38 | // [query elementsForLocalName:@"x" URI:@"some:other:namespace"] 39 | // 40 | // The trouble with this is that we may not always know the xmlns in advance, 41 | // so in this particular case there is no way to access the element without looping through the children. 42 | // 43 | // This bug was submitted to apple on June 1st, 2007 and was classified as "serious". 44 | 45 | return nil; 46 | } 47 | } 48 | 49 | /** 50 | * This method returns the first child element for the given name and given xmlns (as an NSXMLElement). 51 | * If no child elements exist for the given name and given xmlns, nil is returned. 52 | **/ 53 | - (NSXMLElement *)elementForName:(NSString *)name xmlns:(NSString *)xmlns 54 | { 55 | NSArray *elements = [self elementsForLocalName:name URI:xmlns]; 56 | if([elements count] > 0) 57 | { 58 | return [elements objectAtIndex:0]; 59 | } 60 | else 61 | { 62 | return nil; 63 | } 64 | } 65 | 66 | /** 67 | * Returns the common xmlns "attribute", which is only accessible via the namespace methods. 68 | * The xmlns value is often used in jabber elements. 69 | **/ 70 | - (NSString *)xmlns 71 | { 72 | return [[self namespaceForPrefix:@""] stringValue]; 73 | } 74 | 75 | - (void)setXmlns:(NSString *)ns 76 | { 77 | // If we use setURI: then the xmlns won't be displayed in the XMLString. 78 | // Adding the namespace this way works properly. 79 | 80 | [self addNamespace:[NSXMLNode namespaceWithName:@"" stringValue:ns]]; 81 | } 82 | 83 | /** 84 | * Shortcut to avoid having to use NSXMLNode everytime 85 | **/ 86 | 87 | - (void)addAttributeWithName:(NSString *)name stringValue:(NSString *)string 88 | { 89 | [self addAttribute:[NSXMLNode attributeWithName:name stringValue:string]]; 90 | } 91 | 92 | /** 93 | * Returns all the attributes in a dictionary. 94 | **/ 95 | - (NSDictionary *)attributesAsDictionary 96 | { 97 | NSArray *attributes = [self attributes]; 98 | NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:[attributes count]]; 99 | 100 | int i; 101 | for(i = 0; i < [attributes count]; i++) 102 | { 103 | NSXMLNode *node = [attributes objectAtIndex:i]; 104 | 105 | [result setObject:[node stringValue] forKey:[node name]]; 106 | } 107 | return result; 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /SSCrypto.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2006, Septicus Software All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of Septicus Software nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 21 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | // 30 | // SSCrypto.h 31 | // 32 | // Created by Ed Silva on Sat May 31 2003. 33 | // Copyright (c) 2003-2006 Septicus Software. All rights reserved. 34 | // 35 | 36 | 37 | #import 38 | #import 39 | #import 40 | #import 41 | #import 42 | #import 43 | #import 44 | #import 45 | #import 46 | #import 47 | 48 | @interface NSData (HexDump) 49 | - (NSString *)encodeBase64; 50 | - (NSString *)encodeBase64WithNewlines:(BOOL)encodeWithNewlines; 51 | - (NSData *)decodeBase64; 52 | - (NSData *)decodeBase64WithNewLines:(BOOL)decodeWithNewLines; 53 | - (NSString *)hexval; 54 | - (NSString *)hexdump; 55 | @end 56 | 57 | @interface SSCrypto : NSObject 58 | { 59 | NSData *symmetricKey; 60 | NSData *cipherText; 61 | NSData *clearText; 62 | NSData *publicKey; 63 | NSData *privateKey; 64 | 65 | BOOL isSymmetric; 66 | } 67 | 68 | - (id)init; 69 | - (id)initWithSymmetricKey:(NSData *)k; 70 | - (id)initWithPublicKey:(NSData *)pub; 71 | - (id)initWithPrivateKey:(NSData *)priv; 72 | - (id)initWithPublicKey:(NSData *)pub privateKey:(NSData *)priv; 73 | 74 | - (BOOL)isSymmetric; 75 | - (void)setIsSymmetric:(BOOL)flag; 76 | 77 | - (NSData *)symmetricKey; 78 | - (void)setSymmetricKey:(NSData *)k; 79 | 80 | - (NSData *)publicKey; 81 | - (void)setPublicKey:(NSData *)k; 82 | 83 | - (NSData *)privateKey; 84 | - (void)setPrivateKey:(NSData *)k; 85 | 86 | - (NSData *)clearTextAsData; 87 | - (NSString *)clearTextAsString; 88 | - (void)setClearTextWithData:(NSData *)c; 89 | - (void)setClearTextWithString:(NSString *)c; 90 | 91 | - (NSData *)cipherTextAsData; 92 | - (NSString *)cipherTextAsString; 93 | - (void)setCipherText:(NSData *)c; 94 | 95 | - (NSData *)decrypt; 96 | - (NSData *)decrypt:(NSString *)cipherName; 97 | 98 | - (NSData *)verify; 99 | 100 | - (NSData *)encrypt; 101 | - (NSData *)encrypt:(NSString *)cipherName; 102 | 103 | - (NSData *)sign; 104 | 105 | - (NSData *)digest:(NSString *)digestName; 106 | 107 | + (NSData *)generateRSAPrivateKeyWithLength:(int)length; 108 | + (NSData *)generateRSAPublicKeyFromPrivateKey:(NSData *)privateKey; 109 | + (NSData *)getKeyDataWithLength:(int)length; 110 | + (NSData *)getSHA1ForData:(NSData *)d; 111 | + (NSData *)getMD5ForData:(NSData *)d; 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /XMPPTransports.m: -------------------------------------------------------------------------------- 1 | #import "XMPPTransports.h" 2 | #import "XMPPClient.h" 3 | #import "XMPPJID.h" 4 | #import "NSXMLElementAdditions.h" 5 | 6 | 7 | @implementation XMPPTransports 8 | 9 | - (id)initWithXMPPClient:(XMPPClient*)xmppClient 10 | { 11 | if(self = [super init]) 12 | { 13 | client = [xmppClient retain]; 14 | } 15 | return self; 16 | } 17 | 18 | - (void)dealloc 19 | { 20 | [client release]; 21 | [super dealloc]; 22 | } 23 | 24 | - (XMPPClient *)xmppClient 25 | { 26 | return client; 27 | } 28 | 29 | /** 30 | * Registration process 31 | * @see: http://www.xmpp.org/extensions/xep-0100.html#usecases-jabber-register-pri 32 | **/ 33 | - (void)queryGatewayDiscoveryIdentityForLegacyService:(NSString *)service 34 | { 35 | NSXMLElement *element = [NSXMLElement elementWithName:@"iq"]; 36 | [element addAttributeWithName:@"type" stringValue:@"get"]; 37 | [element addAttributeWithName:@"from" stringValue:[[client myJID] full]]; 38 | [element addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"%@.%@", service, [client domain]]]; 39 | [element addAttributeWithName:@"id" stringValue:@"disco1"]; 40 | [element addChild:[NSXMLElement elementWithName:@"query" xmlns:@"http://jabber.org/protocol/disco#info"]]; 41 | 42 | [client sendElement:element]; 43 | } 44 | 45 | - (void)queryGatewayAgentInfo 46 | { 47 | NSXMLElement *element = [NSXMLElement elementWithName:@"iq"]; 48 | [element addAttributeWithName:@"type" stringValue:@"get"]; 49 | [element addAttributeWithName:@"from" stringValue:[[client myJID] full]]; 50 | [element addAttributeWithName:@"to" stringValue:[client domain]]; 51 | [element addAttributeWithName:@"id" stringValue:@"agent1"]; 52 | [element addChild:[NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:agents"]]; 53 | 54 | [client sendElement:element]; 55 | } 56 | 57 | - (void)queryRegistrationRequirementsForLegacyService:(NSString *)service 58 | { 59 | NSXMLElement* element = [NSXMLElement elementWithName:@"iq"]; 60 | [element addAttributeWithName:@"type" stringValue:@"get"]; 61 | [element addAttributeWithName:@"from" stringValue:[[client myJID] full]]; 62 | [element addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"%@.%@", service, [client domain]]]; 63 | [element addAttributeWithName:@"id" stringValue:@"reg1"]; 64 | [element addChild:[NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"]]; 65 | 66 | [client sendElement:element]; 67 | } 68 | 69 | - (void)registerLegacyService:(NSString *)service userName:(NSString *)userName password:(NSString *)password 70 | { 71 | NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"]; 72 | [query addChild:[NSXMLElement elementWithName:@"username" stringValue:userName]]; 73 | [query addChild:[NSXMLElement elementWithName:@"password" stringValue:password]]; 74 | 75 | NSXMLElement *element = [NSXMLElement elementWithName:@"iq"]; 76 | [element addAttributeWithName:@"type" stringValue:@"set"]; 77 | [element addAttributeWithName:@"from" stringValue:[[client myJID] full]]; 78 | [element addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"%@.%@", service, [client domain]]]; 79 | [element addAttributeWithName:@"id" stringValue:@"reg2"]; 80 | [element addChild:query]; 81 | 82 | [client sendElement:element]; 83 | } 84 | 85 | /** 86 | * Unregistration process 87 | * @see: http://www.xmpp.org/extensions/xep-0100.html#usecases-jabber-unregister-pri 88 | **/ 89 | - (void)unregisterLegacyService:(NSString *)service 90 | { 91 | NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"]; 92 | [query addChild:[NSXMLElement elementWithName:@"remove"]]; 93 | 94 | NSXMLElement *element = [NSXMLElement elementWithName:@"iq"]; 95 | [element addAttributeWithName:@"type" stringValue:@"set"]; 96 | [element addAttributeWithName:@"from" stringValue:[[client myJID] full]]; 97 | [element addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"%@.%@", service, [client domain]]]; 98 | [element addAttributeWithName:@"id" stringValue:@"unreg1"]; 99 | [element addChild:query]; 100 | 101 | [client sendElement:element]; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /XMPPPing.m: -------------------------------------------------------------------------------- 1 | #import "XMPPPing.h" 2 | #import "XMPP.h" 3 | 4 | 5 | @implementation XMPPPing 6 | 7 | - (id)initWithXMPPClient:(XMPPClient *)xmppClient delegate:(id)aDelegate 8 | { 9 | if(self = [super init]) 10 | { 11 | delegate = aDelegate; 12 | 13 | client = [xmppClient retain]; 14 | [client addDelegate:self]; 15 | 16 | pingIDs = [[NSMutableArray alloc] initWithCapacity:5]; 17 | } 18 | return self; 19 | } 20 | 21 | - (void)dealloc 22 | { 23 | [client removeDelegate:self]; 24 | [client release]; 25 | [pingIDs release]; 26 | [super dealloc]; 27 | } 28 | 29 | - (XMPPClient *)xmppClient 30 | { 31 | return client; 32 | } 33 | 34 | - (id)delegate 35 | { 36 | return delegate; 37 | } 38 | 39 | - (void)setDelegate:(id)newDelegate 40 | { 41 | delegate = newDelegate; 42 | } 43 | 44 | - (NSString *)generatePingID 45 | { 46 | // Generate unique ID for Ping packet 47 | // It's important the ID be unique as the ID is the only thing that distinguishes a pong packet 48 | CFUUIDRef uuid = CFUUIDCreate(NULL); 49 | NSString *pingID = [NSMakeCollectable(CFUUIDCreateString(NULL, uuid)) autorelease]; 50 | CFRelease(uuid); 51 | 52 | // Add ping ID to list so we'll recognize it when we get a response 53 | [pingIDs addObject:pingID]; 54 | 55 | // In case we never get a response, we want to remove the ping ID eventually, 56 | // or we risk an ever increasing pingIDs array. 57 | [NSTimer scheduledTimerWithTimeInterval:30.0 58 | target:self 59 | selector:@selector(removePingID:) 60 | userInfo:pingID 61 | repeats:NO]; 62 | 63 | return pingID; 64 | } 65 | 66 | - (void)removePingID:(NSTimer *)aTimer 67 | { 68 | NSString *pingID = (NSString *)[aTimer userInfo]; 69 | 70 | [pingIDs removeObject:pingID]; 71 | } 72 | 73 | - (void)sendPingToServer 74 | { 75 | NSString *pingID = [self generatePingID]; 76 | 77 | // Send ping packet 78 | NSXMLElement *ping = [NSXMLElement elementWithName:@"ping" xmlns:@"urn:xmpp:ping"]; 79 | 80 | NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"]; 81 | [iq addAttributeWithName:@"type" stringValue:@"get"]; 82 | [iq addAttributeWithName:@"id" stringValue:pingID]; 83 | [iq addChild:ping]; 84 | 85 | [client sendElement:iq]; 86 | } 87 | 88 | - (void)sendPingToResource:(XMPPResource *)resource 89 | { 90 | NSString *pingID = [self generatePingID]; 91 | 92 | // Send ping packet 93 | NSXMLElement *ping = [NSXMLElement elementWithName:@"ping" xmlns:@"urn:xmpp:ping"]; 94 | 95 | NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"]; 96 | [iq addAttributeWithName:@"to" stringValue:[[resource jid] full]]; 97 | [iq addAttributeWithName:@"type" stringValue:@"get"]; 98 | [iq addAttributeWithName:@"id" stringValue:pingID]; 99 | [iq addChild:ping]; 100 | 101 | [client sendElement:iq]; 102 | } 103 | 104 | - (void)xmppClient:(XMPPClient *)sender didReceiveIQ:(XMPPIQ *)iq 105 | { 106 | NSString *type = [[iq attributeForName:@"type"] stringValue]; 107 | 108 | if([type isEqualToString:@"result"] || [type isEqualToString:@"error"]) 109 | { 110 | // Example: 111 | // 112 | 113 | NSString *pingID = [iq elementID]; 114 | 115 | NSUInteger index = [pingIDs indexOfObject:pingID]; 116 | if(index != NSNotFound) 117 | { 118 | [pingIDs removeObjectAtIndex:index]; 119 | 120 | if([delegate respondsToSelector:@selector(xmppPing:didReceivePong:)]) 121 | { 122 | [delegate xmppPing:self didReceivePong:iq]; 123 | } 124 | } 125 | } 126 | else if([type isEqualToString:@"get"]) 127 | { 128 | // Example: 129 | // 130 | // 131 | // 132 | 133 | NSXMLElement *ping = [iq elementForName:@"ping" xmlns:@"urn:xmpp:ping"]; 134 | if(ping) 135 | { 136 | NSXMLElement *pong = [NSXMLElement elementWithName:@"iq"]; 137 | [pong addAttributeWithName:@"to" stringValue:[iq fromStr]]; 138 | [pong addAttributeWithName:@"type" stringValue:@"result"]; 139 | [pong addAttributeWithName:@"id" stringValue:[iq elementID]]; 140 | 141 | [client sendElement:pong]; 142 | } 143 | } 144 | } 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /XMPPClient.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXML.h" 3 | 4 | @class XMPPStream; 5 | @class XMPPJID; 6 | @class XMPPUser; 7 | @class XMPPResource; 8 | @class XMPPIQ; 9 | @class XMPPMessage; 10 | @class XMPPPresence; 11 | @class MulticastDelegate; 12 | @class SCNotificationManager; 13 | 14 | 15 | @interface XMPPClient : NSObject 16 | { 17 | MulticastDelegate *multicastDelegate; 18 | 19 | NSString *domain; 20 | UInt16 port; 21 | 22 | XMPPJID *myJID; 23 | NSString *password; 24 | int priority; 25 | 26 | Byte flags; 27 | 28 | XMPPStream *xmppStream; 29 | NSError *streamError; 30 | 31 | NSMutableDictionary *roster; 32 | XMPPUser *myUser; 33 | 34 | NSMutableArray *earlyPresenceElements; 35 | 36 | SCNotificationManager *scNotificationManager; 37 | } 38 | 39 | - (id)init; 40 | 41 | - (void)addDelegate:(id)delegate; 42 | - (void)removeDelegate:(id)delegate; 43 | 44 | - (NSString *)domain; 45 | - (void)setDomain:(NSString *)domain; 46 | 47 | - (UInt16)port; 48 | - (void)setPort:(UInt16)port; 49 | 50 | - (BOOL)usesOldStyleSSL; 51 | - (void)setUsesOldStyleSSL:(BOOL)flag; 52 | 53 | - (XMPPJID *)myJID; 54 | - (void)setMyJID:(XMPPJID *)jid; 55 | 56 | - (NSString *)password; 57 | - (void)setPassword:(NSString *)password; 58 | 59 | - (int)priority; 60 | - (void)setPriority:(int)priority; 61 | 62 | - (BOOL)allowsSelfSignedCertificates; 63 | - (void)setAllowsSelfSignedCertificates:(BOOL)flag; 64 | 65 | - (BOOL)isDisconnected; 66 | - (BOOL)isConnected; 67 | - (BOOL)isSecure; 68 | 69 | - (NSError *)streamError; 70 | 71 | - (BOOL)autoLogin; 72 | - (void)setAutoLogin:(BOOL)flag; 73 | 74 | - (BOOL)autoRoster; 75 | - (void)setAutoRoster:(BOOL)flag; 76 | 77 | - (BOOL)autoPresence; 78 | - (void)setAutoPresence:(BOOL)flag; 79 | 80 | - (BOOL)autoReconnect; 81 | - (void)setAutoReconnect:(BOOL)flag; 82 | 83 | - (void)connect; 84 | - (void)disconnect; 85 | 86 | - (BOOL)supportsInBandRegistration; 87 | - (void)registerUser; 88 | 89 | - (BOOL)supportsPlainAuthentication; 90 | - (BOOL)supportsDigestMD5Authentication; 91 | 92 | - (BOOL)allowsPlaintextAuth; 93 | - (void)setAllowsPlaintextAuth:(BOOL)flag; 94 | 95 | - (void)authenticateUser; 96 | 97 | - (BOOL)isAuthenticated; 98 | 99 | - (void)goOnline; 100 | - (void)goOffline; 101 | 102 | - (void)fetchRoster; 103 | 104 | - (void)addBuddy:(XMPPJID *)jid withNickname:(NSString *)optionalName; 105 | - (void)removeBuddy:(XMPPJID *)jid; 106 | 107 | - (void)setNickname:(NSString *)nickname forBuddy:(XMPPJID *)jid; 108 | 109 | - (void)acceptBuddyRequest:(XMPPJID *)jid; 110 | - (void)rejectBuddyRequest:(XMPPJID *)jid; 111 | 112 | - (NSArray *)sortedUsersByName; 113 | - (NSArray *)sortedUsersByAvailabilityName; 114 | 115 | - (NSArray *)sortedAvailableUsersByName; 116 | - (NSArray *)sortedUnavailableUsersByName; 117 | 118 | - (NSArray *)unsortedUsers; 119 | - (NSArray *)unsortedAvailableUsers; 120 | - (NSArray *)unsortedUnavailableUsers; 121 | 122 | - (NSArray *)sortedResources:(BOOL)includeResourcesForMyUserExcludingMyself; 123 | 124 | - (XMPPUser *)userForJID:(XMPPJID *)jid; 125 | - (XMPPResource *)resourceForJID:(XMPPJID *)jid; 126 | 127 | - (XMPPUser *)myUser; 128 | 129 | - (void)sendElement:(NSXMLElement *)element; 130 | - (void)sendElement:(NSXMLElement *)element andNotifyMe:(long)tag; 131 | 132 | - (void)sendMessage:(NSString *)message toJID:(XMPPJID *)jid; 133 | 134 | @end 135 | 136 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 137 | #pragma mark - 138 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 139 | 140 | @interface NSObject (XMPPClientDelegate) 141 | 142 | - (void)xmppClientConnecting:(XMPPClient *)sender; 143 | - (void)xmppClientDidConnect:(XMPPClient *)sender; 144 | - (void)xmppClientDidNotConnect:(XMPPClient *)sender; 145 | - (void)xmppClientDidDisconnect:(XMPPClient *)sender; 146 | 147 | - (void)xmppClientDidRegister:(XMPPClient *)sender; 148 | - (void)xmppClient:(XMPPClient *)sender didNotRegister:(NSXMLElement *)error; 149 | 150 | - (void)xmppClientDidAuthenticate:(XMPPClient *)sender; 151 | - (void)xmppClient:(XMPPClient *)sender didNotAuthenticate:(NSXMLElement *)error; 152 | 153 | - (void)xmppClientDidUpdateRoster:(XMPPClient *)sender; 154 | 155 | - (void)xmppClient:(XMPPClient *)sender didReceiveBuddyRequest:(XMPPJID *)jid; 156 | 157 | - (void)xmppClient:(XMPPClient *)sender didReceiveIQ:(XMPPIQ *)iq; 158 | - (void)xmppClient:(XMPPClient *)sender didReceiveMessage:(XMPPMessage *)message; 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/classes.nib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBClasses 6 | 7 | 8 | CLASS 9 | XMPPClient 10 | LANGUAGE 11 | ObjC 12 | SUPERCLASS 13 | NSObject 14 | 15 | 16 | ACTIONS 17 | 18 | addBuddy 19 | id 20 | changePresence 21 | id 22 | chat 23 | id 24 | createAccount 25 | id 26 | removeBuddy 27 | id 28 | signIn 29 | id 30 | 31 | CLASS 32 | RosterController 33 | LANGUAGE 34 | ObjC 35 | OUTLETS 36 | 37 | buddyField 38 | id 39 | jidField 40 | id 41 | messageField 42 | id 43 | passwordField 44 | id 45 | portField 46 | id 47 | registerButton 48 | id 49 | requestController 50 | id 51 | resourceField 52 | id 53 | rosterTable 54 | id 55 | selfSignedButton 56 | id 57 | serverField 58 | id 59 | signInButton 60 | id 61 | signInSheet 62 | id 63 | sslButton 64 | id 65 | window 66 | id 67 | xmppClient 68 | id 69 | 70 | SUPERCLASS 71 | NSObject 72 | 73 | 74 | CLASS 75 | XMPPStream 76 | LANGUAGE 77 | ObjC 78 | OUTLETS 79 | 80 | delegate 81 | id 82 | 83 | SUPERCLASS 84 | NSObject 85 | 86 | 87 | CLASS 88 | FirstResponder 89 | LANGUAGE 90 | ObjC 91 | SUPERCLASS 92 | NSObject 93 | 94 | 95 | ACTIONS 96 | 97 | xmppClientConnecting 98 | XMPPClient 99 | xmppClientDidAuthenticate 100 | XMPPClient 101 | xmppClientDidConnect 102 | XMPPClient 103 | xmppClientDidDisconnect 104 | XMPPClient 105 | xmppClientDidNotConnect 106 | XMPPClient 107 | xmppClientDidRegister 108 | XMPPClient 109 | xmppClientDidUpdateRoster 110 | XMPPClient 111 | xmppStreamDidAuthenticate 112 | XMPPStream 113 | xmppStreamDidClose 114 | XMPPStream 115 | xmppStreamDidOpen 116 | XMPPStream 117 | xmppStreamDidRegister 118 | XMPPStream 119 | 120 | CLASS 121 | NSObject 122 | LANGUAGE 123 | ObjC 124 | 125 | 126 | ACTIONS 127 | 128 | accept 129 | id 130 | reject 131 | id 132 | 133 | CLASS 134 | RequestController 135 | LANGUAGE 136 | ObjC 137 | OUTLETS 138 | 139 | jidField 140 | id 141 | rosterController 142 | id 143 | window 144 | id 145 | xmppClient 146 | id 147 | xofyField 148 | id 149 | 150 | SUPERCLASS 151 | NSObject 152 | 153 | 154 | IBVersion 155 | 1 156 | 157 | 158 | -------------------------------------------------------------------------------- /ChatController.m: -------------------------------------------------------------------------------- 1 | #import "ChatController.h" 2 | #import "XMPP.h" 3 | 4 | 5 | @implementation ChatController 6 | 7 | - (id)initWithXMPPClient:(XMPPClient *)client jid:(XMPPJID *)fullJID 8 | { 9 | return [self initWithXMPPClient:client jid:fullJID message:nil]; 10 | } 11 | 12 | - (id)initWithXMPPClient:(XMPPClient *)client jid:(XMPPJID *)fullJID message:(XMPPMessage *)message 13 | { 14 | if(self = [super initWithWindowNibName:@"ChatWindow"]) 15 | { 16 | xmppClient = [client retain]; 17 | jid = [fullJID retain]; 18 | 19 | firstMessage = [message retain]; 20 | } 21 | return self; 22 | } 23 | 24 | - (void)awakeFromNib 25 | { 26 | [xmppClient addDelegate:self]; 27 | 28 | [messageView setString:@""]; 29 | 30 | [[self window] setTitle:[jid full]]; 31 | [[self window] makeFirstResponder:messageField]; 32 | 33 | if(firstMessage) 34 | { 35 | [self xmppClient:xmppClient didReceiveMessage:firstMessage]; 36 | [firstMessage release]; 37 | firstMessage = nil; 38 | } 39 | } 40 | 41 | /** 42 | * Called immediately before the window closes. 43 | * 44 | * This method's job is to release the WindowController (self) 45 | * This is so that the nib file is released from memory. 46 | **/ 47 | - (void)windowWillClose:(NSNotification *)aNotification 48 | { 49 | NSLog(@"ChatController: windowWillClose"); 50 | 51 | [xmppClient removeDelegate:self]; 52 | [self autorelease]; 53 | } 54 | 55 | - (void)dealloc 56 | { 57 | NSLog(@"Destroying self: %@", self); 58 | 59 | [xmppClient release]; 60 | [jid release]; 61 | [firstMessage release]; 62 | [super dealloc]; 63 | } 64 | 65 | - (XMPPJID *)jid 66 | { 67 | return jid; 68 | } 69 | 70 | - (void)scrollToBottom 71 | { 72 | NSScrollView *scrollView = [messageView enclosingScrollView]; 73 | NSPoint newScrollOrigin; 74 | 75 | if ([[scrollView documentView] isFlipped]) 76 | newScrollOrigin = NSMakePoint(0.0, NSMaxY([[scrollView documentView] frame])); 77 | else 78 | newScrollOrigin = NSMakePoint(0.0, 0.0); 79 | 80 | [[scrollView documentView] scrollPoint:newScrollOrigin]; 81 | } 82 | 83 | - (void)xmppClientDidAuthenticate:(XMPPClient *)sender 84 | { 85 | [messageField setEnabled:YES]; 86 | } 87 | 88 | - (void)xmppClient:(XMPPClient *)sender didReceiveMessage:(XMPPMessage *)message 89 | { 90 | if(![jid isEqual:[message from]]) return; 91 | 92 | if([message isChatMessageWithBody]) 93 | { 94 | NSString *messageStr = [[message elementForName:@"body"] stringValue]; 95 | 96 | NSString *paragraph = [NSString stringWithFormat:@"%@\n\n", messageStr]; 97 | 98 | NSMutableParagraphStyle *mps = [[[NSMutableParagraphStyle alloc] init] autorelease]; 99 | [mps setAlignment:NSLeftTextAlignment]; 100 | 101 | NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:2]; 102 | [attributes setObject:mps forKey:NSParagraphStyleAttributeName]; 103 | [attributes setObject:[NSColor colorWithCalibratedRed:250 green:250 blue:250 alpha:1] forKey:NSBackgroundColorAttributeName]; 104 | 105 | NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes]; 106 | [as autorelease]; 107 | 108 | [[messageView textStorage] appendAttributedString:as]; 109 | } 110 | } 111 | 112 | - (void)xmppClientDidDisconnect:(XMPPClient *)sender 113 | { 114 | [messageField setEnabled:NO]; 115 | } 116 | 117 | - (IBAction)sendMessage:(id)sender 118 | { 119 | NSString *messageStr = [messageField stringValue]; 120 | 121 | if([messageStr length] > 0) 122 | { 123 | NSXMLElement *body = [NSXMLElement elementWithName:@"body"]; 124 | [body setStringValue:messageStr]; 125 | 126 | NSXMLElement *message = [NSXMLElement elementWithName:@"message"]; 127 | [message addAttributeWithName:@"type" stringValue:@"chat"]; 128 | [message addAttributeWithName:@"to" stringValue:[jid full]]; 129 | [message addChild:body]; 130 | 131 | [xmppClient sendElement:message]; 132 | 133 | NSString *paragraph = [NSString stringWithFormat:@"%@\n\n", messageStr]; 134 | 135 | NSMutableParagraphStyle *mps = [[[NSMutableParagraphStyle alloc] init] autorelease]; 136 | [mps setAlignment:NSRightTextAlignment]; 137 | 138 | NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:2]; 139 | [attributes setObject:mps forKey:NSParagraphStyleAttributeName]; 140 | 141 | NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes]; 142 | [as autorelease]; 143 | 144 | [[messageView textStorage] appendAttributedString:as]; 145 | 146 | [self scrollToBottom]; 147 | 148 | [messageField setStringValue:@""]; 149 | [[self window] makeFirstResponder:messageField]; 150 | } 151 | } 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /XMPPResource.m: -------------------------------------------------------------------------------- 1 | #import "XMPPResource.h" 2 | #import "XMPPJID.h" 3 | #import "XMPPUser.h" 4 | #import "XMPPIQ.h" 5 | #import "XMPPPresence.h" 6 | 7 | 8 | @implementation XMPPResource 9 | 10 | - (id)initWithPresence:(XMPPPresence *)aPresence 11 | { 12 | if(self = [super init]) 13 | { 14 | jid = [[aPresence from] retain]; 15 | presence = [aPresence retain]; 16 | 17 | presenceReceived = [[NSDate alloc] init]; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)dealloc 23 | { 24 | [jid release]; 25 | [presence release]; 26 | [presenceReceived release]; 27 | [super dealloc]; 28 | } 29 | 30 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 31 | #pragma mark Encoding, Decoding 32 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 33 | 34 | #if ! TARGET_OS_IPHONE 35 | - (id)replacementObjectForPortCoder:(NSPortCoder *)encoder 36 | { 37 | if([encoder isBycopy]) 38 | return self; 39 | else 40 | return [NSDistantObject proxyWithLocal:self connection:[encoder connection]]; 41 | } 42 | #endif 43 | 44 | - (id)initWithCoder:(NSCoder *)coder 45 | { 46 | if(self = [super init]) 47 | { 48 | if([coder allowsKeyedCoding]) 49 | { 50 | jid = [[coder decodeObjectForKey:@"jid"] retain]; 51 | presence = [[coder decodeObjectForKey:@"presence"] retain]; 52 | presenceReceived = [[coder decodeObjectForKey:@"presenceReceived"] retain]; 53 | } 54 | else 55 | { 56 | jid = [[coder decodeObject] retain]; 57 | presence = [[coder decodeObject] retain]; 58 | presenceReceived = [[coder decodeObject] retain]; 59 | } 60 | } 61 | return self; 62 | } 63 | 64 | - (void)encodeWithCoder:(NSCoder *)coder 65 | { 66 | if([coder allowsKeyedCoding]) 67 | { 68 | [coder encodeObject:jid forKey:@"jid"]; 69 | [coder encodeObject:presence forKey:@"presence"]; 70 | [coder encodeObject:presenceReceived forKey:@"presenceReceived"]; 71 | } 72 | else 73 | { 74 | [coder encodeObject:jid]; 75 | [coder encodeObject:presence]; 76 | [coder encodeObject:presenceReceived]; 77 | } 78 | } 79 | 80 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 81 | #pragma mark Standard Methods 82 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 83 | 84 | - (XMPPJID *)jid 85 | { 86 | return jid; 87 | } 88 | 89 | - (XMPPPresence *)presence 90 | { 91 | return presence; 92 | } 93 | 94 | - (NSDate *)presenceReceived 95 | { 96 | return presenceReceived; 97 | } 98 | 99 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 100 | #pragma mark Update Methods 101 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 102 | 103 | - (void)updateWithPresence:(XMPPPresence *)aPresence 104 | { 105 | [presence release]; 106 | presence = [aPresence retain]; 107 | 108 | [presenceReceived release]; 109 | presenceReceived = [[NSDate alloc] init]; 110 | } 111 | 112 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 113 | #pragma mark Comparison Methods 114 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 115 | 116 | - (NSComparisonResult)compare:(XMPPResource *)another 117 | { 118 | int mp = [[self presence] priority]; 119 | int ap = [[another presence] priority]; 120 | 121 | if(mp < ap) 122 | return NSOrderedDescending; 123 | if(mp > ap) 124 | return NSOrderedAscending; 125 | 126 | // Priority is the same. 127 | // Determine who is more available based on their show. 128 | int ms = [[self presence] intShow]; 129 | int as = [[another presence] intShow]; 130 | 131 | if(ms < as) 132 | return NSOrderedDescending; 133 | if(ms > as) 134 | return NSOrderedAscending; 135 | 136 | // Priority and Show are the same. 137 | // Determine based on who was the last to receive a presence element. 138 | NSDate *mr = [self presenceReceived]; 139 | NSDate *ar = [another presenceReceived]; 140 | 141 | return [mr compare:ar]; 142 | } 143 | 144 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 145 | #pragma mark NSObject Methods 146 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 147 | 148 | #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 149 | - (unsigned)hash 150 | { 151 | return [jid hash]; 152 | } 153 | #else 154 | - (NSUInteger)hash 155 | { 156 | return [jid hash]; 157 | } 158 | #endif 159 | 160 | - (BOOL)isEqual:(id)anObject 161 | { 162 | if([anObject isMemberOfClass:[self class]]) 163 | { 164 | XMPPResource *another = (XMPPResource *)anObject; 165 | 166 | return [jid isEqual:[another jid]]; 167 | } 168 | 169 | return NO; 170 | } 171 | 172 | - (NSString *)description 173 | { 174 | return [NSString stringWithFormat:@"XMPPResource: %@", [jid full]]; 175 | } 176 | 177 | @end 178 | -------------------------------------------------------------------------------- /NSDataAdditions.m: -------------------------------------------------------------------------------- 1 | #import "NSDataAdditions.h" 2 | 3 | #if TARGET_OS_IPHONE 4 | #import 5 | #else 6 | #import "SSCrypto.h" 7 | #endif 8 | 9 | @implementation NSData (XMPPStreamAdditions) 10 | 11 | #if TARGET_OS_IPHONE 12 | static char encodingTable[64] = { 13 | 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 14 | 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 15 | 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 16 | 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; 17 | #endif 18 | 19 | - (NSData *)md5Digest 20 | { 21 | #if TARGET_OS_IPHONE 22 | 23 | unsigned char result[CC_MD5_DIGEST_LENGTH]; 24 | 25 | CC_MD5([self bytes], [self length], result); 26 | return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH]; 27 | 28 | #else 29 | 30 | return [SSCrypto getMD5ForData:self]; 31 | 32 | #endif 33 | } 34 | 35 | - (NSData *)sha1Digest 36 | { 37 | #if TARGET_OS_IPHONE 38 | 39 | unsigned char result[CC_SHA1_DIGEST_LENGTH]; 40 | 41 | CC_SHA1([self bytes], [self length], result); 42 | return [NSData dataWithBytes:result length:CC_SHA1_DIGEST_LENGTH]; 43 | 44 | #else 45 | 46 | return [SSCrypto getSHA1ForData:self]; 47 | 48 | #endif 49 | } 50 | 51 | - (NSString *)hexStringValue 52 | { 53 | #if TARGET_OS_IPHONE 54 | 55 | NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([self length] * 2)]; 56 | 57 | const unsigned char *dataBuffer = [self bytes]; 58 | int i; 59 | 60 | for (i = 0; i < [self length]; ++i) 61 | { 62 | [stringBuffer appendFormat:@"%02x", (unsigned long)dataBuffer[i]]; 63 | } 64 | 65 | return [[stringBuffer copy] autorelease]; 66 | 67 | #else 68 | 69 | return [self hexval]; 70 | 71 | #endif 72 | } 73 | 74 | - (NSString *)base64Encoded 75 | { 76 | #if TARGET_OS_IPHONE 77 | 78 | const unsigned char *bytes = [self bytes]; 79 | NSMutableString *result = [NSMutableString stringWithCapacity:[self length]]; 80 | unsigned long ixtext = 0; 81 | unsigned long lentext = [self length]; 82 | long ctremaining = 0; 83 | unsigned char inbuf[3], outbuf[4]; 84 | unsigned short i = 0; 85 | unsigned short charsonline = 0, ctcopy = 0; 86 | unsigned long ix = 0; 87 | 88 | while( YES ) 89 | { 90 | ctremaining = lentext - ixtext; 91 | if( ctremaining <= 0 ) break; 92 | 93 | for( i = 0; i < 3; i++ ) { 94 | ix = ixtext + i; 95 | if( ix < lentext ) inbuf[i] = bytes[ix]; 96 | else inbuf [i] = 0; 97 | } 98 | 99 | outbuf [0] = (inbuf [0] & 0xFC) >> 2; 100 | outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4); 101 | outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6); 102 | outbuf [3] = inbuf [2] & 0x3F; 103 | ctcopy = 4; 104 | 105 | switch( ctremaining ) 106 | { 107 | case 1: 108 | ctcopy = 2; 109 | break; 110 | case 2: 111 | ctcopy = 3; 112 | break; 113 | } 114 | 115 | for( i = 0; i < ctcopy; i++ ) 116 | [result appendFormat:@"%c", encodingTable[outbuf[i]]]; 117 | 118 | for( i = ctcopy; i < 4; i++ ) 119 | [result appendString:@"="]; 120 | 121 | ixtext += 3; 122 | charsonline += 4; 123 | } 124 | 125 | return [NSString stringWithString:result]; 126 | 127 | #else 128 | 129 | return [self encodeBase64WithNewlines:NO]; 130 | 131 | #endif 132 | } 133 | 134 | - (NSData *)base64Decoded 135 | { 136 | #if TARGET_OS_IPHONE 137 | 138 | const unsigned char *bytes = [self bytes]; 139 | NSMutableData *result = [NSMutableData dataWithCapacity:[self length]]; 140 | 141 | unsigned long ixtext = 0; 142 | unsigned long lentext = [self length]; 143 | unsigned char ch = 0; 144 | unsigned char inbuf[4], outbuf[3]; 145 | short i = 0, ixinbuf = 0; 146 | BOOL flignore = NO; 147 | BOOL flendtext = NO; 148 | 149 | while( YES ) 150 | { 151 | if( ixtext >= lentext ) break; 152 | ch = bytes[ixtext++]; 153 | flignore = NO; 154 | 155 | if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A'; 156 | else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26; 157 | else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52; 158 | else if( ch == '+' ) ch = 62; 159 | else if( ch == '=' ) flendtext = YES; 160 | else if( ch == '/' ) ch = 63; 161 | else flignore = YES; 162 | 163 | if( ! flignore ) 164 | { 165 | short ctcharsinbuf = 3; 166 | BOOL flbreak = NO; 167 | 168 | if( flendtext ) 169 | { 170 | if( ! ixinbuf ) break; 171 | if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1; 172 | else ctcharsinbuf = 2; 173 | ixinbuf = 3; 174 | flbreak = YES; 175 | } 176 | 177 | inbuf [ixinbuf++] = ch; 178 | 179 | if( ixinbuf == 4 ) 180 | { 181 | ixinbuf = 0; 182 | outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 ); 183 | outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 ); 184 | outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F ); 185 | 186 | for( i = 0; i < ctcharsinbuf; i++ ) 187 | [result appendBytes:&outbuf[i] length:1]; 188 | } 189 | 190 | if( flbreak ) break; 191 | } 192 | } 193 | 194 | return [NSData dataWithData:result]; 195 | 196 | #else 197 | 198 | return [self decodeBase64WithNewLines:NO]; 199 | 200 | #endif 201 | } 202 | 203 | @end 204 | -------------------------------------------------------------------------------- /MulticastDelegate.m: -------------------------------------------------------------------------------- 1 | #import "MulticastDelegate.h" 2 | 3 | @interface MulticastDelegateListNode (PrivateAPI) 4 | - (void)clearDelegate; 5 | @end 6 | 7 | @implementation MulticastDelegate 8 | 9 | - (id)init 10 | { 11 | if(self = [super init]) 12 | { 13 | currentInvocationIndex = 0; 14 | } 15 | return self; 16 | } 17 | 18 | - (void)addDelegate:(id)delegate 19 | { 20 | MulticastDelegateListNode *node = [[MulticastDelegateListNode alloc] initWithDelegate:delegate]; 21 | 22 | if(delegateList != nil) 23 | { 24 | [node setNext:delegateList]; 25 | [[node next] setPrev:node]; 26 | } 27 | 28 | delegateList = node; 29 | } 30 | 31 | - (void)removeDelegate:(id)delegate 32 | { 33 | MulticastDelegateListNode *node = delegateList; 34 | NSUInteger index = 0; 35 | 36 | while(node != nil) 37 | { 38 | if(delegate == [node delegate]) 39 | { 40 | if([node prev] == nil) 41 | delegateList = [node next]; 42 | else 43 | [[node prev] setNext:[node next]]; 44 | 45 | [[node next] setPrev:[node prev]]; 46 | 47 | // We do NOT change the prev/next pointers of the node. 48 | // If it's in use within forwardInvocation, these pointers are still needed. 49 | // However, if multiple delegates are removed in the middle of a delegate callback, 50 | // we still need to be sure not to invoke any delegates that were removed. 51 | [node clearDelegate]; 52 | [node release]; 53 | 54 | if(index < currentInvocationIndex) 55 | { 56 | currentInvocationIndex--; 57 | } 58 | break; 59 | } 60 | 61 | index++; 62 | node = [node next]; 63 | } 64 | } 65 | 66 | - (void)removeAllDelegates 67 | { 68 | MulticastDelegateListNode *node = delegateList; 69 | 70 | while(node != nil) 71 | { 72 | MulticastDelegateListNode *next = [node next]; 73 | 74 | [node setPrev:nil]; 75 | [node setNext:nil]; 76 | [node release]; 77 | 78 | node = next; 79 | } 80 | 81 | currentInvocationIndex = 0; 82 | delegateList = nil; 83 | } 84 | 85 | - (NSUInteger)count 86 | { 87 | NSUInteger count = 0; 88 | 89 | MulticastDelegateListNode *node; 90 | for(node = delegateList; node != nil; node = [node next]) 91 | { 92 | count++; 93 | } 94 | 95 | return count; 96 | } 97 | 98 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 99 | { 100 | MulticastDelegateListNode *node; 101 | for(node = delegateList; node != nil; node = [node next]) 102 | { 103 | NSMethodSignature *result = [[node delegate] methodSignatureForSelector:aSelector]; 104 | 105 | if(result != nil) 106 | { 107 | return result; 108 | } 109 | } 110 | 111 | // This causes a crash... 112 | // return [super methodSignatureForSelector:aSelector]; 113 | 114 | // This also causes a crash... 115 | // return nil; 116 | 117 | return [[self class] instanceMethodSignatureForSelector:@selector(doNothing)]; 118 | } 119 | 120 | - (void)forwardInvocation:(NSInvocation *)anInvocation 121 | { 122 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 123 | 124 | // Here are the rules: 125 | // If a delegate is added during this method, it should NOT be invoked. 126 | // If a delegate is removed during this method that has not already been invoked, it should not be invoked. 127 | // 128 | // The first rule is the reason for the currentInvocationIndex variable. 129 | 130 | MulticastDelegateListNode *node = delegateList; 131 | currentInvocationIndex = 0; 132 | 133 | while([node next] != nil) 134 | { 135 | node = [node next]; 136 | currentInvocationIndex++; 137 | } 138 | 139 | while(node != nil) 140 | { 141 | // Retain the node before we invoke the delegate. 142 | // We do this because the delegate might remove itself from the delegate list within the invoked method. 143 | // And we don't want to get the previous node now, because it may also be 144 | // removed from the list within the invoked method. 145 | [[node retain] autorelease]; 146 | 147 | if([[node delegate] respondsToSelector:[anInvocation selector]]) 148 | { 149 | [anInvocation invokeWithTarget:[node delegate]]; 150 | } 151 | 152 | node = [node prev]; 153 | if(currentInvocationIndex > 0) 154 | currentInvocationIndex--; 155 | else 156 | break; 157 | } 158 | 159 | [pool release]; 160 | } 161 | 162 | - (void)doesNotRecognizeSelector:(SEL)aSelector 163 | { 164 | // Prevent NSInvalidArgumentException 165 | } 166 | 167 | - (void)doNothing {} 168 | 169 | - (void)dealloc 170 | { 171 | [self removeAllDelegates]; 172 | [super dealloc]; 173 | } 174 | 175 | @end 176 | 177 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 178 | #pragma mark - 179 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 180 | 181 | @implementation MulticastDelegateListNode 182 | 183 | - (id)initWithDelegate:(id)aDelegate 184 | { 185 | if(self = [super init]) 186 | { 187 | delegate = aDelegate; 188 | } 189 | return self; 190 | } 191 | 192 | - (void)dealloc 193 | { 194 | [super dealloc]; 195 | } 196 | 197 | - (id)delegate 198 | { 199 | return delegate; 200 | } 201 | 202 | - (void)clearDelegate 203 | { 204 | delegate = nil; 205 | } 206 | 207 | - (MulticastDelegateListNode *)prev 208 | { 209 | return prev; 210 | } 211 | 212 | - (void)setPrev:(MulticastDelegateListNode *)newPrev 213 | { 214 | prev = newPrev; 215 | } 216 | 217 | - (MulticastDelegateListNode *)next 218 | { 219 | return next; 220 | } 221 | 222 | - (void)setNext:(MulticastDelegateListNode *)newNext 223 | { 224 | next = newNext; 225 | } 226 | 227 | @end -------------------------------------------------------------------------------- /KissXML/DDXMLNode.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @class DDXMLDocument; 5 | 6 | 7 | enum { 8 | DDXMLInvalidKind = 0, 9 | DDXMLDocumentKind = XML_DOCUMENT_NODE, 10 | DDXMLElementKind = XML_ELEMENT_NODE, 11 | DDXMLAttributeKind = XML_ATTRIBUTE_NODE, 12 | DDXMLNamespaceKind = XML_NAMESPACE_DECL, 13 | DDXMLProcessingInstructionKind = XML_PI_NODE, 14 | DDXMLCommentKind = XML_COMMENT_NODE, 15 | DDXMLTextKind = XML_TEXT_NODE, 16 | DDXMLDTDKind = XML_DTD_NODE, 17 | DDXMLEntityDeclarationKind = XML_ENTITY_DECL, 18 | DDXMLAttributeDeclarationKind = XML_ATTRIBUTE_DECL, 19 | DDXMLElementDeclarationKind = XML_ELEMENT_DECL, 20 | DDXMLNotationDeclarationKind = XML_NOTATION_NODE 21 | }; 22 | typedef NSUInteger DDXMLNodeKind; 23 | 24 | enum { 25 | DDXMLNodeOptionsNone = 0, 26 | DDXMLNodeExpandEmptyElement = 1 << 1, 27 | DDXMLNodeCompactEmptyElement = 1 << 2, 28 | DDXMLNodePrettyPrint = 1 << 17, 29 | }; 30 | 31 | /** 32 | * DDXMLNode can represent several underlying types, such as xmlNodePtr, xmlDocPtr, xmlAttrPtr, xmlNsPtr, etc. 33 | * All of these are pointers to structures, and all of those structures start with a pointer, and a type. 34 | * The xmlKind struct is used as a generic structure, and a stepping stone. 35 | * We use it to check the type of a structure, and then perform the appropriate cast. 36 | * 37 | * For example: 38 | * if(genericPtr->type == XML_ATTRIBUTE_NODE) 39 | * { 40 | * xmlAttrPtr attr = (xmlAttrPtr)genericPtr; 41 | * // Do something with attr 42 | * } 43 | **/ 44 | struct _xmlKind { 45 | void * ignore; 46 | xmlElementType type; 47 | }; 48 | typedef struct _xmlKind *xmlKindPtr; 49 | 50 | /** 51 | * Most xml types all start with this standard structure. In fact, all do except the xmlNsPtr. 52 | * We will occasionally take advantage of this to simplify code when the code wouldn't vary from type to type. 53 | * Obviously, you cannnot cast a xmlNsPtr to a xmlStdPtr. 54 | **/ 55 | struct _xmlStd { 56 | void * _private; 57 | xmlElementType type; 58 | const xmlChar *name; 59 | struct _xmlNode *children; 60 | struct _xmlNode *last; 61 | struct _xmlNode *parent; 62 | struct _xmlStd *next; 63 | struct _xmlStd *prev; 64 | struct _xmlDoc *doc; 65 | }; 66 | typedef struct _xmlStd *xmlStdPtr; 67 | 68 | @interface DDXMLNode : NSObject 69 | { 70 | // Every DDXML object is simply a wrapper around an underlying libxml node 71 | xmlKindPtr genericPtr; 72 | 73 | // The xmlNsPtr type doesn't store a reference to it's parent 74 | // This is here to fix that problem, and make this class more compatible with the NSXML classes 75 | xmlNodePtr nsParentPtr; 76 | } 77 | 78 | //- (id)initWithKind:(DDXMLNodeKind)kind; 79 | 80 | //- (id)initWithKind:(DDXMLNodeKind)kind options:(NSUInteger)options; 81 | 82 | //+ (id)document; 83 | 84 | //+ (id)documentWithRootElement:(DDXMLElement *)element; 85 | 86 | + (id)elementWithName:(NSString *)name; 87 | 88 | + (id)elementWithName:(NSString *)name URI:(NSString *)URI; 89 | 90 | + (id)elementWithName:(NSString *)name stringValue:(NSString *)string; 91 | 92 | + (id)elementWithName:(NSString *)name children:(NSArray *)children attributes:(NSArray *)attributes; 93 | 94 | + (id)attributeWithName:(NSString *)name stringValue:(NSString *)stringValue; 95 | 96 | + (id)attributeWithName:(NSString *)name URI:(NSString *)URI stringValue:(NSString *)stringValue; 97 | 98 | + (id)namespaceWithName:(NSString *)name stringValue:(NSString *)stringValue; 99 | 100 | + (id)processingInstructionWithName:(NSString *)name stringValue:(NSString *)stringValue; 101 | 102 | + (id)commentWithStringValue:(NSString *)stringValue; 103 | 104 | + (id)textWithStringValue:(NSString *)stringValue; 105 | 106 | //+ (id)DTDNodeWithXMLString:(NSString *)string; 107 | 108 | #pragma mark --- Properties --- 109 | 110 | - (DDXMLNodeKind)kind; 111 | 112 | - (void)setName:(NSString *)name; 113 | - (NSString *)name; 114 | 115 | //- (void)setObjectValue:(id)value; 116 | //- (id)objectValue; 117 | 118 | - (void)setStringValue:(NSString *)string; 119 | //- (void)setStringValue:(NSString *)string resolvingEntities:(BOOL)resolve; 120 | - (NSString *)stringValue; 121 | 122 | #pragma mark --- Tree Navigation --- 123 | 124 | - (NSUInteger)index; 125 | 126 | - (NSUInteger)level; 127 | 128 | - (DDXMLDocument *)rootDocument; 129 | 130 | - (DDXMLNode *)parent; 131 | - (NSUInteger)childCount; 132 | - (NSArray *)children; 133 | - (DDXMLNode *)childAtIndex:(NSUInteger)index; 134 | 135 | - (DDXMLNode *)previousSibling; 136 | - (DDXMLNode *)nextSibling; 137 | 138 | - (DDXMLNode *)previousNode; 139 | - (DDXMLNode *)nextNode; 140 | 141 | - (void)detach; 142 | 143 | - (NSString *)XPath; 144 | 145 | #pragma mark --- QNames --- 146 | 147 | - (NSString *)localName; 148 | - (NSString *)prefix; 149 | 150 | - (void)setURI:(NSString *)URI; 151 | - (NSString *)URI; 152 | 153 | + (NSString *)localNameForName:(NSString *)name; 154 | + (NSString *)prefixForName:(NSString *)name; 155 | //+ (DDXMLNode *)predefinedNamespaceForPrefix:(NSString *)name; 156 | 157 | #pragma mark --- Output --- 158 | 159 | - (NSString *)description; 160 | - (NSString *)XMLString; 161 | - (NSString *)XMLStringWithOptions:(NSUInteger)options; 162 | //- (NSString *)canonicalXMLStringPreservingComments:(BOOL)comments; 163 | 164 | #pragma mark --- XPath/XQuery --- 165 | 166 | - (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error; 167 | //- (NSArray *)objectsForXQuery:(NSString *)xquery constants:(NSDictionary *)constants error:(NSError **)error; 168 | //- (NSArray *)objectsForXQuery:(NSString *)xquery error:(NSError **)error; 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /XMPPStream.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDXML.h" 3 | 4 | @class AsyncSocket; 5 | @class XMPPIQ; 6 | @class XMPPMessage; 7 | @class XMPPPresence; 8 | 9 | 10 | @interface XMPPStream : NSObject 11 | { 12 | id delegate; 13 | 14 | int state; 15 | AsyncSocket *asyncSocket; 16 | 17 | BOOL isSecure; 18 | BOOL isAuthenticated; 19 | BOOL allowsSelfSignedCertificates; 20 | NSString *serverHostName; 21 | NSString *xmppHostName; 22 | 23 | NSMutableData *buffer; 24 | NSXMLElement *rootElement; 25 | 26 | NSData *terminator; 27 | 28 | NSString *authUsername; 29 | NSString *authResource; 30 | NSString *tempPassword; 31 | 32 | NSTimer *keepAliveTimer; 33 | } 34 | 35 | - (id)init; 36 | - (id)initWithDelegate:(id)delegate; 37 | 38 | - (id)delegate; 39 | - (void)setDelegate:(id)delegate; 40 | 41 | - (BOOL)allowsSelfSignedCertificates; 42 | - (void)setAllowsSelfSignedCertificates:(BOOL)flag; 43 | 44 | - (BOOL)isDisconnected; 45 | - (BOOL)isConnected; 46 | - (BOOL)isSecure; 47 | - (void)connectToHost:(NSString *)hostName onPort:(UInt16)portNumber withVirtualHost:(NSString *)vHostName; 48 | - (void)connectToSecureHost:(NSString *)hostName onPort:(UInt16)portNumber withVirtualHost:(NSString *)vHostName; 49 | 50 | - (void)disconnect; 51 | - (void)disconnectAfterSending; 52 | 53 | - (BOOL)supportsInBandRegistration; 54 | - (void)registerUser:(NSString *)username withPassword:(NSString *)password; 55 | 56 | - (BOOL)supportsPlainAuthentication; 57 | - (BOOL)supportsDigestMD5Authentication; 58 | - (void)authenticateUser:(NSString *)username withPassword:(NSString *)password resource:(NSString *)resource; 59 | 60 | - (BOOL)isAuthenticated; 61 | - (NSString *)authenticatedUsername; 62 | - (NSString *)authenticatedResource; 63 | 64 | - (NSXMLElement *)rootElement; 65 | - (float)serverXmppStreamVersionNumber; 66 | 67 | - (void)sendElement:(NSXMLElement *)element; 68 | - (void)sendElement:(NSXMLElement *)element andNotifyMe:(long)tag; 69 | 70 | @end 71 | 72 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 73 | #pragma mark - 74 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 75 | 76 | @interface NSObject (XMPPStreamDelegate) 77 | 78 | /** 79 | * This method is called after an XML stream has been opened. 80 | * More precisely, this method is called after an opening and tag have been sent and received, 81 | * and after the stream features have been received, and any required features have been fullfilled. 82 | * At this point it's safe to begin communication with the server. 83 | **/ 84 | - (void)xmppStreamDidOpen:(XMPPStream *)sender; 85 | 86 | /** 87 | * This method is called after registration of a new user has successfully finished. 88 | * If registration fails for some reason, the xmppStream:didNotRegister: method will be called instead. 89 | **/ 90 | - (void)xmppStreamDidRegister:(XMPPStream *)sender; 91 | 92 | /** 93 | * This method is called if registration fails. 94 | **/ 95 | - (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error; 96 | 97 | /** 98 | * This method is called after authentication has successfully finished. 99 | * If authentication fails for some reason, the xmppStream:didNotAuthenticate: method will be called instead. 100 | **/ 101 | - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender; 102 | 103 | /** 104 | * This method is called if authentication fails. 105 | **/ 106 | - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error; 107 | 108 | /** 109 | * These methods are called after their respective XML elements are received on the stream. 110 | **/ 111 | - (void)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq; 112 | - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message; 113 | - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence; 114 | 115 | /** 116 | * There are two types of errors: TCP errors and XMPP errors. 117 | * If a TCP error is encountered (failure to connect, broken connection, etc) a standard NSError object is passed. 118 | * If an XMPP error is encountered ( for example) an NSXMLElement object is passed. 119 | * 120 | * Note that standard errors ( for example) are delivered normally, 121 | * via the other didReceive...: methods. 122 | **/ 123 | - (void)xmppStream:(XMPPStream *)sender didReceiveError:(id)error; 124 | 125 | /** 126 | * This method is called for every sendElement:andNotifyMe: method. 127 | **/ 128 | - (void)xmppStream:(XMPPStream *)sender didSendElementWithTag:(long)tag; 129 | 130 | /** 131 | * This method is called after the stream is closed. 132 | **/ 133 | - (void)xmppStreamDidClose:(XMPPStream *)sender; 134 | 135 | @end 136 | 137 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 138 | #pragma mark - 139 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 140 | 141 | @interface XMPPDigestAuthentication : NSObject 142 | { 143 | NSString *rspauth; 144 | NSString *realm; 145 | NSString *nonce; 146 | NSString *qop; 147 | NSString *username; 148 | NSString *password; 149 | NSString *cnonce; 150 | NSString *nc; 151 | NSString *digestURI; 152 | } 153 | 154 | - (id)initWithChallenge:(NSXMLElement *)challenge; 155 | 156 | - (NSString *)rspauth; 157 | 158 | - (NSString *)realm; 159 | - (void)setRealm:(NSString *)realm; 160 | 161 | - (void)setDigestURI:(NSString *)digestURI; 162 | 163 | - (void)setUsername:(NSString *)username password:(NSString *)password; 164 | 165 | - (NSString *)response; 166 | - (NSString *)base64EncodedFullResponse; 167 | 168 | @end 169 | -------------------------------------------------------------------------------- /RequestController.m: -------------------------------------------------------------------------------- 1 | #import "RequestController.h" 2 | #import "RosterController.h" 3 | #import "XMPP.h" 4 | 5 | 6 | @implementation RequestController 7 | 8 | - (id)init 9 | { 10 | if(self = [super init]) 11 | { 12 | jids = [[NSMutableArray alloc] init]; 13 | jidIndex = -1; 14 | } 15 | return self; 16 | } 17 | 18 | - (void)awakeFromNib 19 | { 20 | [xmppClient addDelegate:self]; 21 | 22 | NSRect visibleFrame = [[window screen] visibleFrame]; 23 | NSRect windowFrame = [window frame]; 24 | 25 | NSPoint windowPosition; 26 | windowPosition.x = visibleFrame.origin.x + visibleFrame.size.width - windowFrame.size.width - 5; 27 | windowPosition.y = visibleFrame.origin.y + visibleFrame.size.height - windowFrame.size.height - 5; 28 | 29 | [window setFrameOrigin:windowPosition]; 30 | } 31 | 32 | - (void)dealloc 33 | { 34 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 35 | [jids release]; 36 | [super dealloc]; 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 40 | // NSWindow Delegate Methods 41 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 42 | 43 | - (void)windowWillClose:(NSNotification *)notification 44 | { 45 | // User chose to ignore requests by closing the window 46 | 47 | [jids removeAllObjects]; 48 | jidIndex = -1; 49 | } 50 | 51 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 52 | // Helper Methods 53 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 54 | 55 | - (void)nextRequest 56 | { 57 | NSLog(@"RequestController: nextRequest"); 58 | 59 | if(++jidIndex < [jids count]) 60 | { 61 | XMPPJID *jid = [jids objectAtIndex:jidIndex]; 62 | 63 | [jidField setStringValue:[jid bare]]; 64 | [xofyField setStringValue:[NSString stringWithFormat:@"%i of %i", (jidIndex+1), [jids count]]]; 65 | } 66 | else 67 | { 68 | [jids removeAllObjects]; 69 | jidIndex = -1; 70 | [window close]; 71 | } 72 | } 73 | 74 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 75 | // XMPPClient Delegate Methods 76 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 77 | 78 | - (void)xmppClient:(XMPPClient *)sender didReceiveBuddyRequest:(XMPPJID *)jid 79 | { 80 | if(![jids containsObject:jid]) 81 | { 82 | [jids addObject:jid]; 83 | 84 | if([jids count] == 1) 85 | { 86 | jidIndex = 0; 87 | 88 | [jidField setStringValue:[jid bare]]; 89 | [xofyField setHidden:YES]; 90 | 91 | [window setAlphaValue:0.85]; 92 | [window makeKeyAndOrderFront:self]; 93 | } 94 | else 95 | { 96 | [xofyField setStringValue:[NSString stringWithFormat:@"%i of %i", (jidIndex+1), [jids count]]]; 97 | [xofyField setHidden:NO]; 98 | } 99 | } 100 | } 101 | 102 | - (void)xmppClientDidUpdateRoster:(XMPPClient *)sender 103 | { 104 | // Often times XMPP servers send presence requests prior to sending the roster. 105 | // That is, after you authenticate, they immediately send you presence requests, 106 | // meaning that we receive them before we've had a chance to request and receive our roster. 107 | // The result is that we may not know, upon receiving a presence request, 108 | // if we've already requested this person to be our buddy. 109 | // We make up for that by fixing our mistake as soon as possible. 110 | 111 | NSArray *roster = [xmppClient sortedUsersByAvailabilityName]; 112 | 113 | // Remember: Our roster contains only those users we've added. 114 | // If the server tries to include buddies that we haven't added, but have asked to subscribe to us, 115 | // the xmpp client filters them out. 116 | 117 | int i; 118 | for(i = 0; i < [roster count]; i++) 119 | { 120 | XMPPUser *user = [roster objectAtIndex:i]; 121 | 122 | int index = [jids indexOfObject:[user jid]]; 123 | 124 | if(index != NSNotFound) 125 | { 126 | // Now we may be getting a notification of an updated roster due to an accept/reject we just sent. 127 | // The simplest way to check is if the index isn't pointing to a jid we've already processed. 128 | 129 | if(index >= jidIndex) 130 | { 131 | NSLog(@"Auto-accepting buddy request, since they already accepted us"); 132 | 133 | [sender acceptBuddyRequest:[user jid]]; 134 | 135 | [jids removeObjectAtIndex:index]; 136 | 137 | // We need to calll nextRequest, but we want jidIndex to remain at it's current jid 138 | if(index >= jidIndex) 139 | { 140 | // Subtract 1, because nextRequest will immediately add 1 141 | jidIndex = jidIndex - 1; 142 | } 143 | else 144 | { 145 | // Subtract 2, because the current jid will go down 1 146 | // and because nextRequest will immediately add 1 147 | jidIndex = jidIndex - 2; 148 | } 149 | 150 | [self nextRequest]; 151 | } 152 | } 153 | } 154 | } 155 | 156 | - (void)xmppClientDidDisconnect:(XMPPClient *)sender 157 | { 158 | // We can't accept or reject any requests when we're disconnected from the server. 159 | // We may as well close the window. 160 | 161 | [jids removeAllObjects]; 162 | jidIndex = -1; 163 | [window close]; 164 | } 165 | 166 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 167 | // Interface Builder Methods 168 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 169 | 170 | - (IBAction)accept:(id)sender 171 | { 172 | XMPPJID *jid = [jids objectAtIndex:jidIndex]; 173 | [xmppClient acceptBuddyRequest:jid]; 174 | 175 | [self nextRequest]; 176 | } 177 | 178 | - (IBAction)reject:(id)sender 179 | { 180 | XMPPJID *jid = [jids objectAtIndex:jidIndex]; 181 | [xmppClient rejectBuddyRequest:jid]; 182 | 183 | [self nextRequest]; 184 | } 185 | 186 | @end 187 | -------------------------------------------------------------------------------- /XMPPJID.m: -------------------------------------------------------------------------------- 1 | #import "XMPPJID.h" 2 | #import "LibIDN.h" 3 | 4 | @implementation XMPPJID 5 | 6 | + (BOOL)validateUser:(NSString *)user domain:(NSString *)domain resource:(NSString *)resource 7 | { 8 | // Domain is the only required part of a JID 9 | if((domain == nil) || ([domain length] == 0)) return NO; 10 | 11 | // If there's an @ symbol in the domain it probably means user put @ in their username 12 | NSRange invalidAtRange = [domain rangeOfString:@"@"]; 13 | if(invalidAtRange.location != NSNotFound) return NO; 14 | 15 | // Can't use an empty string resource name 16 | if((resource != nil) && ([resource length] == 0)) return NO; 17 | 18 | return YES; 19 | } 20 | 21 | + (BOOL)parse:(NSString *)jidStr 22 | outUser:(NSString **)user 23 | outDomain:(NSString **)domain 24 | outResource:(NSString **)resource 25 | { 26 | *user = nil; 27 | *domain = nil; 28 | *resource = nil; 29 | 30 | NSString *rawUser = nil; 31 | NSString *rawDomain = nil; 32 | NSString *rawResource = nil; 33 | 34 | NSRange atRange = [jidStr rangeOfString:@"@"]; 35 | 36 | if(atRange.location != NSNotFound) 37 | { 38 | rawUser = [jidStr substringToIndex:atRange.location]; 39 | 40 | NSString *minusUser = [jidStr substringFromIndex:atRange.location+1]; 41 | 42 | NSRange slashRange = [minusUser rangeOfString:@"/"]; 43 | 44 | if(slashRange.location != NSNotFound) 45 | { 46 | rawDomain = [minusUser substringToIndex:slashRange.location]; 47 | rawResource = [minusUser substringFromIndex:slashRange.location+1]; 48 | } 49 | else 50 | { 51 | rawDomain = minusUser; 52 | } 53 | } 54 | else 55 | { 56 | NSRange slashRange = [jidStr rangeOfString:@"/"]; 57 | 58 | if(slashRange.location != NSNotFound) 59 | { 60 | rawDomain = [jidStr substringToIndex:slashRange.location]; 61 | rawResource = [jidStr substringFromIndex:slashRange.location+1]; 62 | } 63 | else 64 | { 65 | rawDomain = jidStr; 66 | } 67 | } 68 | 69 | NSString *prepUser = [LibIDN prepNode:rawUser]; 70 | NSString *prepDomain = [LibIDN prepDomain:rawDomain]; 71 | NSString *prepResource = [LibIDN prepResource:rawResource]; 72 | 73 | if([XMPPJID validateUser:prepUser domain:prepDomain resource:prepResource]) 74 | { 75 | *user = prepUser; 76 | *domain = prepDomain; 77 | *resource = prepResource; 78 | 79 | return YES; 80 | } 81 | 82 | return NO; 83 | } 84 | 85 | + (XMPPJID *)jidWithString:(NSString *)jidStr 86 | { 87 | NSString *user; 88 | NSString *domain; 89 | NSString *resource; 90 | 91 | if([XMPPJID parse:jidStr outUser:&user outDomain:&domain outResource:&resource]) 92 | { 93 | XMPPJID *jid = [[XMPPJID alloc] init]; 94 | jid->user = [user copy]; 95 | jid->domain = [domain copy]; 96 | jid->resource = [resource copy]; 97 | 98 | return [jid autorelease]; 99 | } 100 | 101 | return nil; 102 | } 103 | 104 | + (XMPPJID *)jidWithString:(NSString *)jidStr resource:(NSString *)resource 105 | { 106 | NSString *user; 107 | NSString *domain; 108 | NSString *ignore; 109 | 110 | if([XMPPJID parse:jidStr outUser:&user outDomain:&domain outResource:&ignore]) 111 | { 112 | XMPPJID *jid = [[XMPPJID alloc] init]; 113 | jid->user = [user copy]; 114 | jid->domain = [domain copy]; 115 | jid->resource = [resource copy]; 116 | 117 | return [jid autorelease]; 118 | } 119 | 120 | return nil; 121 | } 122 | 123 | + (XMPPJID *)jidWithUser:(NSString *)user domain:(NSString *)domain resource:(NSString *)resource 124 | { 125 | NSString *prepUser = [LibIDN prepNode:user]; 126 | NSString *prepDomain = [LibIDN prepDomain:domain]; 127 | NSString *prepResource = [LibIDN prepResource:resource]; 128 | 129 | if([XMPPJID validateUser:prepUser domain:prepDomain resource:prepResource]) 130 | { 131 | XMPPJID *jid = [[XMPPJID alloc] init]; 132 | jid->user = [prepUser copy]; 133 | jid->domain = [prepDomain copy]; 134 | jid->resource = [prepResource copy]; 135 | 136 | return [jid autorelease]; 137 | } 138 | 139 | return nil; 140 | } 141 | 142 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 143 | #pragma mark Encoding, Decoding: 144 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 145 | 146 | #if ! TARGET_OS_IPHONE 147 | - (id)replacementObjectForPortCoder:(NSPortCoder *)encoder 148 | { 149 | if([encoder isBycopy]) 150 | return self; 151 | else 152 | return [NSDistantObject proxyWithLocal:self connection:[encoder connection]]; 153 | } 154 | #endif 155 | 156 | - (id)initWithCoder:(NSCoder *)coder 157 | { 158 | if(self = [super init]) 159 | { 160 | if([coder allowsKeyedCoding]) 161 | { 162 | user = [[coder decodeObjectForKey:@"user"] copy]; 163 | domain = [[coder decodeObjectForKey:@"domain"] copy]; 164 | resource = [[coder decodeObjectForKey:@"resource"] copy]; 165 | } 166 | else 167 | { 168 | user = [[coder decodeObject] copy]; 169 | domain = [[coder decodeObject] copy]; 170 | resource = [[coder decodeObject] copy]; 171 | } 172 | } 173 | return self; 174 | } 175 | 176 | - (void)encodeWithCoder:(NSCoder *)coder 177 | { 178 | if([coder allowsKeyedCoding]) 179 | { 180 | [coder encodeObject:user forKey:@"user"]; 181 | [coder encodeObject:domain forKey:@"domain"]; 182 | [coder encodeObject:resource forKey:@"resource"]; 183 | } 184 | else 185 | { 186 | [coder encodeObject:user]; 187 | [coder encodeObject:domain]; 188 | [coder encodeObject:resource]; 189 | } 190 | } 191 | 192 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 193 | #pragma mark Copying: 194 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 195 | 196 | - (id)copyWithZone:(NSZone *)zone 197 | { 198 | // This class is immutable 199 | return [self retain]; 200 | } 201 | 202 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 203 | #pragma mark Normal Methods: 204 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 205 | 206 | - (NSString *)user 207 | { 208 | return user; 209 | } 210 | 211 | - (NSString *)domain 212 | { 213 | return domain; 214 | } 215 | 216 | - (NSString *)resource 217 | { 218 | return resource; 219 | } 220 | 221 | - (XMPPJID *)bareJID 222 | { 223 | if(resource == nil) 224 | { 225 | return [[self retain] autorelease]; 226 | } 227 | else 228 | { 229 | return [XMPPJID jidWithUser:user domain:domain resource:nil]; 230 | } 231 | } 232 | 233 | - (NSString *)bare 234 | { 235 | if(user) 236 | return [NSString stringWithFormat:@"%@@%@", user, domain]; 237 | else 238 | return domain; 239 | } 240 | 241 | - (NSString *)full 242 | { 243 | if(user) 244 | { 245 | if(resource) 246 | return [NSString stringWithFormat:@"%@@%@/%@", user, domain, resource]; 247 | else 248 | return [NSString stringWithFormat:@"%@@%@", user, domain]; 249 | } 250 | else 251 | { 252 | if(resource) 253 | return [NSString stringWithFormat:@"%@/%@", domain, resource]; 254 | else 255 | return domain; 256 | } 257 | } 258 | 259 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 260 | #pragma mark NSObject Methods: 261 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 262 | 263 | #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 264 | - (unsigned)hash 265 | { 266 | return [[self full] hash]; 267 | } 268 | #else 269 | - (NSUInteger)hash 270 | { 271 | return [[self full] hash]; 272 | } 273 | #endif 274 | 275 | - (BOOL)isEqual:(id)anObject 276 | { 277 | if([anObject isMemberOfClass:[self class]]) 278 | { 279 | XMPPJID *aJID = (XMPPJID *)anObject; 280 | 281 | return [[self full] isEqualToString:[aJID full]]; 282 | } 283 | return NO; 284 | } 285 | 286 | - (NSString *)description 287 | { 288 | return [self full]; 289 | } 290 | 291 | - (void)dealloc 292 | { 293 | [user release]; 294 | [domain release]; 295 | [resource release]; 296 | [super dealloc]; 297 | } 298 | 299 | @end 300 | -------------------------------------------------------------------------------- /libidn/stringprep.h: -------------------------------------------------------------------------------- 1 | /* stringprep.h --- Header file for stringprep functions. -*- c -*- 2 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Simon Josefsson 3 | * 4 | * This file is part of GNU Libidn. 5 | * 6 | * GNU Libidn is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * GNU Libidn is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with GNU Libidn; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 19 | * 20 | */ 21 | 22 | #ifndef _STRINGPREP_H 23 | #define _STRINGPREP_H 24 | 25 | #ifdef __cplusplus 26 | extern "C" 27 | { 28 | #endif 29 | 30 | #include /* size_t */ 31 | #include /* ssize_t */ 32 | #include "idn-int.h" /* uint32_t */ 33 | 34 | /* On Windows, variables that may be in a DLL must be marked 35 | * specially. This is only active when not building libidn itself 36 | * (!LIBIDN_BUILDING). It is only used for MinGW which declare 37 | * __DECLSPEC_SUPPORTED or MSVC (_MSC_VER && _DLL). */ 38 | #if !defined (LIBIDN_BUILDING) && (defined(__DECLSPEC_SUPPORTED) || (defined(_MSC_VER) && defined(_DLL))) 39 | # define IDN_DLL_VAR __declspec (dllimport) 40 | #else 41 | # define IDN_DLL_VAR 42 | #endif 43 | 44 | #define STRINGPREP_VERSION "1.9" 45 | 46 | /* Error codes. */ 47 | typedef enum 48 | { 49 | STRINGPREP_OK = 0, 50 | /* Stringprep errors. */ 51 | STRINGPREP_CONTAINS_UNASSIGNED = 1, 52 | STRINGPREP_CONTAINS_PROHIBITED = 2, 53 | STRINGPREP_BIDI_BOTH_L_AND_RAL = 3, 54 | STRINGPREP_BIDI_LEADTRAIL_NOT_RAL = 4, 55 | STRINGPREP_BIDI_CONTAINS_PROHIBITED = 5, 56 | /* Error in calling application. */ 57 | STRINGPREP_TOO_SMALL_BUFFER = 100, 58 | STRINGPREP_PROFILE_ERROR = 101, 59 | STRINGPREP_FLAG_ERROR = 102, 60 | STRINGPREP_UNKNOWN_PROFILE = 103, 61 | /* Internal errors. */ 62 | STRINGPREP_NFKC_FAILED = 200, 63 | STRINGPREP_MALLOC_ERROR = 201 64 | } Stringprep_rc; 65 | 66 | /* Flags used when calling stringprep(). */ 67 | typedef enum 68 | { 69 | STRINGPREP_NO_NFKC = 1, 70 | STRINGPREP_NO_BIDI = 2, 71 | STRINGPREP_NO_UNASSIGNED = 4 72 | } Stringprep_profile_flags; 73 | 74 | /* Steps in a stringprep profile. */ 75 | typedef enum 76 | { 77 | STRINGPREP_NFKC = 1, 78 | STRINGPREP_BIDI = 2, 79 | STRINGPREP_MAP_TABLE = 3, 80 | STRINGPREP_UNASSIGNED_TABLE = 4, 81 | STRINGPREP_PROHIBIT_TABLE = 5, 82 | STRINGPREP_BIDI_PROHIBIT_TABLE = 6, 83 | STRINGPREP_BIDI_RAL_TABLE = 7, 84 | STRINGPREP_BIDI_L_TABLE = 8 85 | } Stringprep_profile_steps; 86 | 87 | #define STRINGPREP_MAX_MAP_CHARS 4 88 | 89 | struct Stringprep_table_element 90 | { 91 | uint32_t start; 92 | uint32_t end; /* 0 if only one character */ 93 | uint32_t map[STRINGPREP_MAX_MAP_CHARS]; /* NULL if end is not 0 */ 94 | }; 95 | typedef struct Stringprep_table_element Stringprep_table_element; 96 | 97 | struct Stringprep_table 98 | { 99 | Stringprep_profile_steps operation; 100 | Stringprep_profile_flags flags; 101 | const Stringprep_table_element *table; 102 | }; 103 | typedef struct Stringprep_table Stringprep_profile; 104 | 105 | struct Stringprep_profiles 106 | { 107 | const char *name; 108 | const Stringprep_profile *tables; 109 | }; 110 | typedef struct Stringprep_profiles Stringprep_profiles; 111 | 112 | extern IDN_DLL_VAR const Stringprep_profiles stringprep_profiles[]; 113 | 114 | /* Profiles */ 115 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_A_1[]; 116 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_B_1[]; 117 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_B_2[]; 118 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_B_3[]; 119 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_1_1[]; 120 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_1_2[]; 121 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_2_1[]; 122 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_2_2[]; 123 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_3[]; 124 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_4[]; 125 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_5[]; 126 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_6[]; 127 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_7[]; 128 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_8[]; 129 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_C_9[]; 130 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_D_1[]; 131 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_rfc3454_D_2[]; 132 | 133 | /* Nameprep */ 134 | 135 | extern IDN_DLL_VAR const Stringprep_profile stringprep_nameprep[]; 136 | 137 | #define stringprep_nameprep(in, maxlen) \ 138 | stringprep(in, maxlen, 0, stringprep_nameprep) 139 | 140 | #define stringprep_nameprep_no_unassigned(in, maxlen) \ 141 | stringprep(in, maxlen, STRINGPREP_NO_UNASSIGNED, stringprep_nameprep) 142 | 143 | /* SASL */ 144 | 145 | extern IDN_DLL_VAR const Stringprep_profile stringprep_saslprep[]; 146 | extern IDN_DLL_VAR const Stringprep_profile stringprep_plain[]; 147 | extern IDN_DLL_VAR const Stringprep_profile stringprep_trace[]; 148 | 149 | #define stringprep_plain(in, maxlen) \ 150 | stringprep(in, maxlen, 0, stringprep_plain) 151 | 152 | /* Kerberos */ 153 | 154 | extern IDN_DLL_VAR const Stringprep_profile stringprep_kerberos5[]; 155 | 156 | #define stringprep_kerberos5(in, maxlen) \ 157 | stringprep(in, maxlen, 0, stringprep_kerberos5) 158 | 159 | /* XMPP */ 160 | 161 | extern IDN_DLL_VAR const Stringprep_profile stringprep_xmpp_nodeprep[]; 162 | extern IDN_DLL_VAR const Stringprep_profile stringprep_xmpp_resourceprep[]; 163 | extern IDN_DLL_VAR const Stringprep_table_element stringprep_xmpp_nodeprep_prohibit[]; 164 | 165 | #define stringprep_xmpp_nodeprep(in, maxlen) \ 166 | stringprep(in, maxlen, 0, stringprep_xmpp_nodeprep) 167 | #define stringprep_xmpp_resourceprep(in, maxlen) \ 168 | stringprep(in, maxlen, 0, stringprep_xmpp_resourceprep) 169 | 170 | /* iSCSI */ 171 | 172 | extern IDN_DLL_VAR const Stringprep_profile stringprep_iscsi[]; 173 | 174 | #define stringprep_iscsi(in, maxlen) \ 175 | stringprep(in, maxlen, 0, stringprep_iscsi) 176 | 177 | /* API */ 178 | 179 | extern int stringprep_4i (uint32_t * ucs4, size_t * len, size_t maxucs4len, 180 | Stringprep_profile_flags flags, 181 | const Stringprep_profile * profile); 182 | extern int stringprep_4zi (uint32_t * ucs4, size_t maxucs4len, 183 | Stringprep_profile_flags flags, 184 | const Stringprep_profile * profile); 185 | extern int stringprep (char *in, size_t maxlen, 186 | Stringprep_profile_flags flags, 187 | const Stringprep_profile * profile); 188 | 189 | extern int stringprep_profile (const char *in, 190 | char **out, 191 | const char *profile, 192 | Stringprep_profile_flags flags); 193 | 194 | extern const char *stringprep_strerror (Stringprep_rc rc); 195 | 196 | extern const char *stringprep_check_version (const char *req_version); 197 | 198 | /* Utility */ 199 | 200 | extern int stringprep_unichar_to_utf8 (uint32_t c, char *outbuf); 201 | extern uint32_t stringprep_utf8_to_unichar (const char *p); 202 | 203 | extern uint32_t *stringprep_utf8_to_ucs4 (const char *str, ssize_t len, 204 | size_t * items_written); 205 | extern char *stringprep_ucs4_to_utf8 (const uint32_t * str, ssize_t len, 206 | size_t * items_read, 207 | size_t * items_written); 208 | 209 | extern char *stringprep_utf8_nfkc_normalize (const char *str, ssize_t len); 210 | extern uint32_t *stringprep_ucs4_nfkc_normalize (uint32_t * str, 211 | ssize_t len); 212 | 213 | extern const char *stringprep_locale_charset (void); 214 | extern char *stringprep_convert (const char *str, 215 | const char *to_codeset, 216 | const char *from_codeset); 217 | extern char *stringprep_locale_to_utf8 (const char *str); 218 | extern char *stringprep_utf8_to_locale (const char *str); 219 | 220 | #ifdef __cplusplus 221 | } 222 | #endif 223 | #endif /* _STRINGPREP_H */ 224 | -------------------------------------------------------------------------------- /RosterController.m: -------------------------------------------------------------------------------- 1 | #import "RosterController.h" 2 | #import "RequestController.h" 3 | #import "XMPP.h" 4 | #import "ChatWindowManager.h" 5 | #import 6 | 7 | 8 | @implementation RosterController 9 | 10 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | // Setup: 12 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 13 | 14 | - (id)init 15 | { 16 | if(self = [super init]) 17 | { 18 | // Nothing to do here 19 | } 20 | return self; 21 | } 22 | 23 | - (void)awakeFromNib 24 | { 25 | [xmppClient addDelegate:self]; 26 | [xmppClient setAutoLogin:NO]; 27 | [xmppClient setAutoRoster:YES]; 28 | [xmppClient setAutoPresence:YES]; 29 | } 30 | 31 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 32 | { 33 | // Display the sign in sheet 34 | NSUserDefaults *dflts = [NSUserDefaults standardUserDefaults]; 35 | [serverField setObjectValue:[dflts objectForKey:@"Account.Server"]]; 36 | [resourceField setObjectValue:[dflts objectForKey:@"Account.Resource"]]; 37 | [portField setObjectValue:[dflts objectForKey:@"Account.Port"]]; 38 | [jidField setObjectValue:[dflts objectForKey:@"Account.JID"]]; 39 | [sslButton setObjectValue:[dflts objectForKey:@"Account.UseSSL"]]; 40 | [selfSignedButton setObjectValue:[dflts objectForKey:@"Account.AllowSelfSignedCert"]]; 41 | 42 | [NSApp beginSheet:signInSheet 43 | modalForWindow:window 44 | modalDelegate:self 45 | didEndSelector:nil 46 | contextInfo:nil]; 47 | } 48 | 49 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 50 | // Account Management: 51 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 52 | 53 | - (void)updateAccountInfo 54 | { 55 | NSString *domain = [serverField stringValue]; 56 | if([domain length] == 0) 57 | { 58 | domain = [[serverField cell] placeholderString]; 59 | } 60 | [xmppClient setDomain:domain]; 61 | 62 | int port = [portField intValue]; 63 | [xmppClient setPort:port]; 64 | 65 | BOOL usesSSL = ([sslButton state] == NSOnState); 66 | BOOL allowsSelfSignedCertificates = ([selfSignedButton state] == NSOnState); 67 | 68 | [xmppClient setUsesOldStyleSSL:usesSSL]; 69 | [xmppClient setAllowsSelfSignedCertificates:allowsSelfSignedCertificates]; 70 | 71 | NSString *resource = [resourceField stringValue]; 72 | if([resource length] == 0) 73 | { 74 | resource = [(NSString *)SCDynamicStoreCopyComputerName(NULL, NULL) autorelease]; 75 | } 76 | 77 | XMPPJID *jid = [XMPPJID jidWithString:[jidField stringValue] resource:resource]; 78 | 79 | [xmppClient setMyJID:jid]; 80 | 81 | [xmppClient setPassword:[passwordField stringValue]]; 82 | 83 | // Update persistent defaults: 84 | NSUserDefaults *dflts = [NSUserDefaults standardUserDefaults]; 85 | [dflts setObject:domain forKey:@"Account.Server"]; 86 | [dflts setObject:[resourceField stringValue] 87 | forKey:@"Account.Resource"]; 88 | [dflts setObject:(port ? [NSNumber numberWithInt:port] : nil) 89 | forKey:@"Account.Port"]; 90 | [dflts setObject:[jidField stringValue] 91 | forKey:@"Account.JID"]; 92 | [dflts setBool:usesSSL 93 | forKey:@"Account.UseSSL"]; 94 | [dflts setBool:allowsSelfSignedCertificates 95 | forKey:@"Account.AllowSelfSignedCert"]; 96 | [dflts synchronize]; 97 | } 98 | 99 | - (IBAction)createAccount:(id)sender 100 | { 101 | [self updateAccountInfo]; 102 | 103 | isRegistering = YES; 104 | [signInButton setEnabled:NO]; 105 | [registerButton setEnabled:NO]; 106 | 107 | if(![xmppClient isConnected]) 108 | { 109 | [xmppClient connect]; 110 | } 111 | else 112 | { 113 | [xmppClient registerUser]; 114 | } 115 | } 116 | 117 | - (IBAction)signIn:(id)sender 118 | { 119 | [self updateAccountInfo]; 120 | 121 | isAuthenticating = YES; 122 | [signInButton setEnabled:NO]; 123 | [registerButton setEnabled:NO]; 124 | 125 | if(![xmppClient isConnected]) 126 | { 127 | [xmppClient connect]; 128 | } 129 | else 130 | { 131 | [xmppClient authenticateUser]; 132 | } 133 | } 134 | 135 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 136 | // Presence Management: 137 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 138 | 139 | - (IBAction)changePresence:(id)sender 140 | { 141 | if([[sender titleOfSelectedItem] isEqualToString:@"Offline"]) 142 | { 143 | [xmppClient goOffline]; 144 | } 145 | else 146 | { 147 | [xmppClient goOnline]; 148 | } 149 | } 150 | 151 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 152 | // Buddy Management: 153 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 154 | 155 | - (IBAction)addBuddy:(id)sender 156 | { 157 | XMPPJID *jid = [XMPPJID jidWithString:[buddyField stringValue]]; 158 | 159 | [xmppClient addBuddy:jid withNickname:nil]; 160 | 161 | // Clear buddy text field 162 | [buddyField setStringValue:@""]; 163 | } 164 | 165 | - (IBAction)removeBuddy:(id)sender 166 | { 167 | XMPPJID *jid = [XMPPJID jidWithString:[buddyField stringValue]]; 168 | 169 | [xmppClient removeBuddy:jid]; 170 | 171 | // Clear buddy text field 172 | [buddyField setStringValue:@""]; 173 | } 174 | 175 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 176 | // Messages: 177 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 178 | 179 | - (IBAction)chat:(id)sender 180 | { 181 | int selectedRow = [rosterTable selectedRow]; 182 | 183 | if(selectedRow >= 0) 184 | { 185 | XMPPUser *user = [roster objectAtIndex:selectedRow]; 186 | 187 | [ChatWindowManager openChatWindowWithXMPPClient:xmppClient forXMPPUser:user]; 188 | } 189 | } 190 | 191 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 192 | #pragma mark Roster Table Data Source: 193 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 194 | 195 | - (int)numberOfRowsInTableView:(NSTableView *)tableView 196 | { 197 | return [roster count]; 198 | } 199 | 200 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)rowIndex 201 | { 202 | XMPPUser *user = [roster objectAtIndex:rowIndex]; 203 | 204 | if([[tableColumn identifier] isEqualToString:@"name"]) 205 | return [user nickname]; 206 | else 207 | return [user jid]; 208 | 209 | return nil; 210 | } 211 | 212 | - (void)tableView:(NSTableView *)tableView 213 | setObjectValue:(id)anObject 214 | forTableColumn:(NSTableColumn *)tableColumn 215 | row:(int)rowIndex 216 | { 217 | XMPPUser *user = [roster objectAtIndex:rowIndex]; 218 | NSString *newName = (NSString *)anObject; 219 | 220 | [xmppClient setNickname:newName forBuddy:[user jid]]; 221 | } 222 | 223 | - (void)tableView:(NSTableView *)tableView 224 | willDisplayCell:(id)cell 225 | forTableColumn:(NSTableColumn *)tableColumn 226 | row:(int)rowIndex 227 | { 228 | XMPPUser *user = [roster objectAtIndex:rowIndex]; 229 | 230 | BOOL isRowSelected = ([tableView isRowSelected:rowIndex]); 231 | BOOL isFirstResponder = [[[tableView window] firstResponder] isEqual:tableView]; 232 | BOOL isKeyWindow = [[tableView window] isKeyWindow]; 233 | BOOL isApplicationActive = [NSApp isActive]; 234 | 235 | BOOL isRowHighlighted = (isRowSelected && isFirstResponder && isKeyWindow && isApplicationActive); 236 | 237 | if([user isOnline]) 238 | { 239 | [cell setTextColor:[NSColor blackColor]]; 240 | } 241 | else 242 | { 243 | NSColor *grayColor; 244 | if(isRowHighlighted) 245 | grayColor = [NSColor colorWithCalibratedRed:(184/255.0) green:(175/255.0) blue:(184/255.0) alpha:1.0]; 246 | else 247 | grayColor = [NSColor colorWithCalibratedRed:(134/255.0) green:(125/255.0) blue:(134/255.0) alpha:1.0]; 248 | 249 | [cell setTextColor:grayColor]; 250 | } 251 | } 252 | 253 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 254 | #pragma mark XMPPClient Delegate Methods: 255 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 256 | 257 | - (void)xmppClientDidConnect:(XMPPClient *)sender 258 | { 259 | if(isRegistering) 260 | [xmppClient registerUser]; 261 | else 262 | [xmppClient authenticateUser]; 263 | } 264 | 265 | - (void)xmppClientDidNotConnect:(XMPPClient *)sender 266 | { 267 | NSLog(@"---------- xmppClientDidNotConnect ----------"); 268 | if([sender streamError]) 269 | { 270 | NSLog(@" error: %@", [sender streamError]); 271 | } 272 | 273 | // Update tracking variables 274 | isRegistering = NO; 275 | isAuthenticating = NO; 276 | 277 | // Update GUI 278 | [signInButton setEnabled:YES]; 279 | [registerButton setEnabled:YES]; 280 | [messageField setStringValue:@"Cannot connect to server"]; 281 | } 282 | 283 | - (void)xmppClientDidDisconnect:(XMPPClient *)sender 284 | { 285 | NSLog(@"---------- xmppClientDidDisconnect ----------"); 286 | if ([sender streamError]) 287 | { 288 | NSLog(@" error: %@", [sender streamError]); 289 | } 290 | 291 | [signInButton setEnabled:YES]; 292 | [registerButton setEnabled:YES]; 293 | [messageField setStringValue:@"Unexpectedly disconnected from server"]; 294 | } 295 | 296 | - (void)xmppClientDidRegister:(XMPPClient *)sender 297 | { 298 | // Update tracking variables 299 | isRegistering = NO; 300 | 301 | // Update GUI 302 | [signInButton setEnabled:YES]; 303 | [registerButton setEnabled:YES]; 304 | [messageField setStringValue:@"Registered new user"]; 305 | } 306 | 307 | - (void)xmppClient:(XMPPClient *)sender didNotRegister:(NSXMLElement *)error 308 | { 309 | // Update tracking variables 310 | isRegistering = NO; 311 | 312 | // Update GUI 313 | [signInButton setEnabled:YES]; 314 | [registerButton setEnabled:YES]; 315 | [messageField setStringValue:@"Username is taken"]; 316 | } 317 | 318 | - (void)xmppClientDidAuthenticate:(XMPPClient *)sender 319 | { 320 | // Update tracking variables 321 | isAuthenticating = NO; 322 | 323 | // Close the sheet 324 | [signInSheet orderOut:self]; 325 | [NSApp endSheet:signInSheet]; 326 | } 327 | 328 | - (void)xmppClient:(XMPPClient *)sender didNotAuthenticate:(NSXMLElement *)error 329 | { 330 | // Update tracking variables 331 | isAuthenticating = NO; 332 | 333 | // Update GUI 334 | [signInButton setEnabled:YES]; 335 | [registerButton setEnabled:YES]; 336 | [messageField setStringValue:@"Invalid username/password"]; 337 | } 338 | 339 | - (void)xmppClientDidUpdateRoster:(XMPPClient *)sender 340 | { 341 | [roster release]; 342 | roster = [[xmppClient sortedUsersByAvailabilityName] retain]; 343 | 344 | [rosterTable abortEditing]; 345 | [rosterTable selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; 346 | [rosterTable reloadData]; 347 | } 348 | 349 | - (void)xmppClient:(XMPPClient *)sender didReceiveIQ:(XMPPIQ *)iq 350 | { 351 | NSLog(@"---------- xmppClient:didReceiveIQ: ----------"); 352 | } 353 | 354 | - (void)xmppClient:(XMPPClient *)sender didReceiveMessage:(XMPPMessage *)message 355 | { 356 | if([message isChatMessageWithBody]) 357 | { 358 | [ChatWindowManager handleChatMessage:message withXMPPClient:xmppClient]; 359 | } 360 | } 361 | 362 | @end 363 | -------------------------------------------------------------------------------- /AsyncSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncSocket.h 3 | // 4 | // This class is in the public domain. 5 | // Originally created by Dustin Voss on Wed Jan 29 2003. 6 | // Updated and maintained by Deusty Designs and the Mac development community. 7 | // 8 | // http://code.google.com/p/cocoaasyncsocket/ 9 | // 10 | 11 | #import 12 | 13 | @class AsyncSocket; 14 | @class AsyncReadPacket; 15 | @class AsyncWritePacket; 16 | 17 | extern NSString *const AsyncSocketException; 18 | extern NSString *const AsyncSocketErrorDomain; 19 | 20 | enum AsyncSocketError 21 | { 22 | AsyncSocketCFSocketError = kCFSocketError, // From CFSocketError enum. 23 | AsyncSocketNoError = 0, // Never used. 24 | AsyncSocketCanceledError, // onSocketWillConnect: returned NO. 25 | AsyncSocketReadMaxedOutError, // Reached set maxLength without completing 26 | AsyncSocketReadTimeoutError, 27 | AsyncSocketWriteTimeoutError 28 | }; 29 | typedef enum AsyncSocketError AsyncSocketError; 30 | 31 | @interface NSObject (AsyncSocketDelegate) 32 | 33 | /** 34 | * In the event of an error, the socket is closed. 35 | * You may call "unreadData" during this call-back to get the last bit of data off the socket. 36 | * When connecting, this delegate method may be called 37 | * before"onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:". 38 | **/ 39 | - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err; 40 | 41 | /** 42 | * Called when a socket disconnects with or without error. If you want to release a socket after it disconnects, 43 | * do so here. It is not safe to do that during "onSocket:willDisconnectWithError:". 44 | **/ 45 | - (void)onSocketDidDisconnect:(AsyncSocket *)sock; 46 | 47 | /** 48 | * Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have 49 | * the same delegate and will call "onSocket:didConnectToHost:port:". 50 | **/ 51 | - (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket; 52 | 53 | /** 54 | * Called when a new socket is spawned to handle a connection. This method should return the run-loop of the 55 | * thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used. 56 | **/ 57 | - (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket; 58 | 59 | /** 60 | * Called when a socket is about to connect. This method should return YES to continue, or NO to abort. 61 | * If aborted, will result in AsyncSocketCanceledError. 62 | * 63 | * If the connectToHost:onPort:error: method was called, the delegate will be able to access and configure the 64 | * CFReadStream and CFWriteStream as desired prior to connection. 65 | * 66 | * If the connectToAddress:error: method was called, the delegate will be able to access and configure the 67 | * CFSocket and CFSocketNativeHandle (BSD socket) as desired prior to connection. You will be able to access and 68 | * configure the CFReadStream and CFWriteStream in the onSocket:didConnectToHost:port: method. 69 | **/ 70 | - (BOOL)onSocketWillConnect:(AsyncSocket *)sock; 71 | 72 | /** 73 | * Called when a socket connects and is ready for reading and writing. 74 | * The host parameter will be an IP address, not a DNS name. 75 | **/ 76 | - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port; 77 | 78 | /** 79 | * Called when a socket has completed reading the requested data into memory. 80 | * Not called if there is an error. 81 | **/ 82 | - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag; 83 | 84 | /** 85 | * Called when a socket has read in data, but has not yet completed the read. 86 | * This would occur if using readToData: or readToLength: methods. 87 | * It may be used to for things such as updating progress bars. 88 | **/ 89 | - (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(CFIndex)partialLength tag:(long)tag; 90 | 91 | /** 92 | * Called when a socket has completed writing the requested data. Not called if there is an error. 93 | **/ 94 | - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag; 95 | 96 | /** 97 | * Called after the socket has completed SSL/TLS negotiation. 98 | * This method is not called unless you use the provided startTLS method. 99 | **/ 100 | - (void)onSocket:(AsyncSocket *)sock didSecure:(BOOL)flag; 101 | 102 | @end 103 | 104 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 105 | #pragma mark - 106 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 107 | 108 | @interface AsyncSocket : NSObject 109 | { 110 | CFSocketRef theSocket; // IPv4 accept or connect socket 111 | CFSocketRef theSocket6; // IPv6 accept or connect socket 112 | CFReadStreamRef theReadStream; 113 | CFWriteStreamRef theWriteStream; 114 | 115 | CFRunLoopSourceRef theSource; // For theSocket 116 | CFRunLoopSourceRef theSource6; // For theSocket6 117 | CFRunLoopRef theRunLoop; 118 | CFSocketContext theContext; 119 | NSArray *theRunLoopModes; 120 | 121 | NSMutableArray *theReadQueue; 122 | AsyncReadPacket *theCurrentRead; 123 | NSTimer *theReadTimer; 124 | NSMutableData *partialReadBuffer; 125 | 126 | NSMutableArray *theWriteQueue; 127 | AsyncWritePacket *theCurrentWrite; 128 | NSTimer *theWriteTimer; 129 | 130 | id theDelegate; 131 | Byte theFlags; 132 | 133 | long theUserData; 134 | } 135 | 136 | - (id)init; 137 | - (id)initWithDelegate:(id)delegate; 138 | - (id)initWithDelegate:(id)delegate userData:(long)userData; 139 | 140 | /* String representation is long but has no "\n". */ 141 | - (NSString *)description; 142 | 143 | /** 144 | * Use "canSafelySetDelegate" to see if there is any pending business (reads and writes) with the current delegate 145 | * before changing it. It is, of course, safe to change the delegate before connecting or accepting connections. 146 | **/ 147 | - (id)delegate; 148 | - (BOOL)canSafelySetDelegate; 149 | - (void)setDelegate:(id)delegate; 150 | 151 | /* User data can be a long, or an id or void * cast to a long. */ 152 | - (long)userData; 153 | - (void)setUserData:(long)userData; 154 | 155 | /* Don't use these to read or write. And don't close them, either! */ 156 | - (CFSocketRef)getCFSocket; 157 | - (CFReadStreamRef)getCFReadStream; 158 | - (CFWriteStreamRef)getCFWriteStream; 159 | 160 | /** 161 | * Once one of these methods is called, the AsyncSocket instance is locked in, and the rest can't be called without 162 | * disconnecting the socket first. If the attempt times out or fails, these methods either return NO or 163 | * call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:". 164 | **/ 165 | - (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr; 166 | - (BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr; 167 | - (BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr; 168 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; 169 | 170 | /** 171 | * Disconnects immediately. Any pending reads or writes are dropped. 172 | **/ 173 | - (void)disconnect; 174 | 175 | /** 176 | * Disconnects after all pending reads have completed. 177 | * After calling this, the read and write methods will do nothing. 178 | * The socket will disconnect even if there are still pending writes. 179 | **/ 180 | - (void)disconnectAfterReading; 181 | 182 | /** 183 | * Disconnects after all pending writes have completed. 184 | * After calling this, the read and write methods will do nothing. 185 | * The socket will disconnect even if there are still pending reads. 186 | **/ 187 | - (void)disconnectAfterWriting; 188 | 189 | /** 190 | * Disconnects after all pending reads and writes have completed. 191 | * After calling this, the read and write methods will do nothing. 192 | **/ 193 | - (void)disconnectAfterReadingAndWriting; 194 | 195 | /* Returns YES if the socket and streams are open, connected, and ready for reading and writing. */ 196 | - (BOOL)isConnected; 197 | 198 | /** 199 | * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected. 200 | * The host will be an IP address. 201 | **/ 202 | - (NSString *)connectedHost; 203 | - (UInt16)connectedPort; 204 | 205 | - (NSString *)localHost; 206 | - (UInt16)localPort; 207 | 208 | - (BOOL)isIPv4; 209 | - (BOOL)isIPv6; 210 | 211 | // The readData and writeData methods won't block. To not time out, use a negative time interval. 212 | // If they time out, "onSocket:disconnectWithError:" is called. The tag is for your convenience. 213 | // You can use it as an array index, step number, state id, pointer, etc., just like the socket's user data. 214 | 215 | /** 216 | * This will read a certain number of bytes into memory, and call the delegate method when those bytes have been read. 217 | * If there is an error, partially read data is lost. 218 | * If the length is 0, this method does nothing and the delegate is not called. 219 | **/ 220 | - (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; 221 | 222 | /** 223 | * This reads bytes until (and including) the passed "data" parameter, which acts as a separator. 224 | * The bytes and the separator are returned by the delegate method. 225 | * 226 | * If you pass nil or zero-length data as the "data" parameter, 227 | * the method will do nothing, and the delegate will not be called. 228 | * 229 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 230 | * Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for 231 | * a character, the read will prematurely end. 232 | **/ 233 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 234 | 235 | /** 236 | * Same as readDataToData:withTimeout:tag, with the additional restriction that the amount of data read 237 | * may not surpass the given maxLength (specified in bytes). 238 | * 239 | * If you pass a maxLength parameter that is less than the length of the data parameter, 240 | * the method will do nothing, and the delegate will not be called. 241 | * 242 | * If the max length is surpassed, it is treated the same as a timeout - the socket is closed. 243 | * 244 | * Pass -1 as maxLength if no length restriction is desired, or simply use the readDataToData:withTimeout:tag method. 245 | **/ 246 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag; 247 | 248 | /** 249 | * Reads the first available bytes that become available on the socket. 250 | **/ 251 | - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; 252 | 253 | /** 254 | * Writes data to the socket, and calls the delegate when finished. 255 | * 256 | * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called. 257 | **/ 258 | - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 259 | 260 | /** 261 | * Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check). 262 | * "tag", "done" and "total" will be filled in if they aren't NULL. 263 | **/ 264 | - (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total; 265 | - (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total; 266 | 267 | /** 268 | * Secures the connection using SSL/TLS. 269 | * 270 | * This method may be called at any time, and the TLS handshake will occur after all pending reads and writes 271 | * are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing 272 | * the upgrade to TLS at the same time, without having to wait for the write to finish. 273 | * Any reads or writes scheduled after this method is called will occur over the secured connection. 274 | * 275 | * The possible keys and values for the TLS settings are well documented. 276 | * Some possible keys are: 277 | * - kCFStreamSSLLevel 278 | * - kCFStreamSSLAllowsExpiredCertificates 279 | * - kCFStreamSSLAllowsExpiredRoots 280 | * - kCFStreamSSLAllowsAnyRoot 281 | * - kCFStreamSSLValidatesCertificateChain 282 | * - kCFStreamSSLPeerName 283 | * - kCFStreamSSLCertificates 284 | * - kCFStreamSSLIsServer 285 | * 286 | * Please refer to Apple's documentation for associated values, as well as other possible keys. 287 | * 288 | * If you pass in nil or an empty dictionary, this method does nothing and the delegate will not be called. 289 | **/ 290 | - (void)startTLS:(NSDictionary *)tlsSettings; 291 | 292 | /** 293 | * For handling readDataToData requests, data is necessarily read from the socket in small increments. 294 | * The performance can be much improved by allowing AsyncSocket to read larger chunks at a time and 295 | * store any overflow in a small internal buffer. 296 | * This is termed pre-buffering, as some data may be read for you before you ask for it. 297 | * If you use readDataToData a lot, enabling pre-buffering will result in better performance, especially on the iPhone. 298 | * 299 | * The default pre-buffering state is controlled by the DEFAULT_PREBUFFERING definition. 300 | * It is highly recommended one leave this set to YES. 301 | * 302 | * This method exists in case pre-buffering needs to be disabled by default for some reason. 303 | * In that case, this method exists to allow one to easily enable pre-buffering when ready. 304 | **/ 305 | - (void)enablePreBuffering; 306 | 307 | /** 308 | * When you create an AsyncSocket, it is added to the runloop of the current thread. 309 | * So for manually created sockets, it is easiest to simply create the socket on the thread you intend to use it. 310 | * 311 | * If a new socket is accepted, the delegate method onSocket:wantsRunLoopForNewSocket: is called to 312 | * allow you to place the socket on a separate thread. This works best in conjunction with a thread pool design. 313 | * 314 | * If, however, you need to move the socket to a separate thread at a later time, this 315 | * method may be used to accomplish the task. 316 | * 317 | * This method must be called from the thread/runloop the socket is currently running on. 318 | * 319 | * Note: After calling this method, all further method calls to this object should be done from the given runloop. 320 | * Also, all delegate calls will be sent on the given runloop. 321 | **/ 322 | - (BOOL)moveToRunLoop:(NSRunLoop *)runLoop; 323 | 324 | /** 325 | * Allows you to configure which run loop modes the socket uses. 326 | * The default set of run loop modes is NSDefaultRunLoopMode. 327 | * 328 | * If you'd like your socket to continue operation during other modes, you may want to add modes such as 329 | * NSModalPanelRunLoopMode or NSEventTrackingRunLoopMode. Or you may simply want to use NSRunLoopCommonModes. 330 | * 331 | * Accepted sockets will automatically inherit the same run loop modes as the listening socket. 332 | * 333 | * Note: NSRunLoopCommonModes is defined in 10.5. For previous versions one can use kCFRunLoopCommonModes. 334 | **/ 335 | - (BOOL)setRunLoopModes:(NSArray *)runLoopModes; 336 | 337 | /** 338 | * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read 339 | * any data that's left on the socket. 340 | **/ 341 | - (NSData *)unreadData; 342 | 343 | /* A few common line separators, for use with the readDataToData:... methods. */ 344 | + (NSData *)CRLFData; // 0x0D0A 345 | + (NSData *)CRData; // 0x0D 346 | + (NSData *)LFData; // 0x0A 347 | + (NSData *)ZeroData; // 0x00 348 | 349 | @end 350 | -------------------------------------------------------------------------------- /libidn/idn-int.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT! GENERATED AUTOMATICALLY! */ 2 | /* Copyright (C) 2001-2002, 2004-2007 Free Software Foundation, Inc. 3 | Written by Paul Eggert, Bruno Haible, Sam Steingold, Peter Burwood. 4 | This file is part of gnulib. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation; either version 2.1, or (at your option) 9 | any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with this program; if not, write to the Free Software Foundation, 18 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 19 | 20 | /* 21 | * ISO C 99 for platforms that lack it. 22 | * 23 | */ 24 | 25 | #ifndef _GL_STDINT_H 26 | 27 | /* When including a system file that in turn includes , 28 | use the system , not our substitute. This avoids 29 | problems with (for example) VMS, whose includes 30 | . */ 31 | #define _GL_JUST_INCLUDE_SYSTEM_INTTYPES_H 32 | 33 | /* Get those types that are already defined in other system include 34 | files, so that we can "#define int8_t signed char" below without 35 | worrying about a later system include file containing a "typedef 36 | signed char int8_t;" that will get messed up by our macro. Our 37 | macros should all be consistent with the system versions, except 38 | for the "fast" types and macros, which we recommend against using 39 | in public interfaces due to compiler differences. */ 40 | 41 | #if 1 42 | # if defined __sgi && ! defined __c99 43 | /* Bypass IRIX's if in C89 mode, since it merely annoys users 44 | with "This header file is to be used only for c99 mode compilations" 45 | diagnostics. */ 46 | # define __STDINT_H__ 47 | # endif 48 | /* Other systems may have an incomplete or buggy . 49 | Include it before , since any "#include " 50 | in would reinclude us, skipping our contents because 51 | _GL_STDINT_H is defined. 52 | The include requires a split double-inclusion guard. */ 53 | # include 54 | #endif 55 | 56 | #if ! defined _GL_STDINT_H && ! defined _GL_JUST_INCLUDE_SYSTEM_STDINT_H 57 | #define _GL_STDINT_H 58 | 59 | /* defines some of the stdint.h types as well, on glibc, 60 | IRIX 6.5, and OpenBSD 3.8 (via ). 61 | AIX 5.2 isn't needed and causes troubles. 62 | MacOS X 10.4.6 includes (which is us), but 63 | relies on the system definitions, so include 64 | after . */ 65 | #if 1 && ! defined _AIX 66 | # include 67 | #endif 68 | 69 | /* Get LONG_MIN, LONG_MAX, ULONG_MAX. */ 70 | #include 71 | 72 | #if 1 73 | /* In OpenBSD 3.8, includes , which defines 74 | int{8,16,32,64}_t, uint{8,16,32,64}_t and __BIT_TYPES_DEFINED__. 75 | also defines intptr_t and uintptr_t. */ 76 | # include 77 | #elif 0 78 | /* Solaris 7 has the types except the *_fast*_t types, and 79 | the macros except for *_FAST*_*, INTPTR_MIN, PTRDIFF_MIN, PTRDIFF_MAX. */ 80 | # include 81 | #endif 82 | 83 | #if 0 && ! defined __BIT_TYPES_DEFINED__ 84 | /* Linux libc4 >= 4.6.7 and libc5 have a that defines 85 | int{8,16,32,64}_t and __BIT_TYPES_DEFINED__. In libc5 >= 5.2.2 it is 86 | included by . */ 87 | # include 88 | #endif 89 | 90 | #if ! defined __cplusplus || defined __STDC_CONSTANT_MACROS 91 | 92 | /* Get WCHAR_MIN, WCHAR_MAX. */ 93 | # if ! (defined WCHAR_MIN && defined WCHAR_MAX) 94 | /* We don't need WCHAR_* in libidn, so to avoid problems with 95 | missing wchar.h, don't include wchar.h here. */ 96 | # endif 97 | 98 | #endif 99 | 100 | #undef _GL_JUST_INCLUDE_SYSTEM_INTTYPES_H 101 | 102 | /* Minimum and maximum values for a integer type under the usual assumption. 103 | Return an unspecified value if BITS == 0, adding a check to pacify 104 | picky compilers. */ 105 | 106 | #define _STDINT_MIN(signed, bits, zero) \ 107 | ((signed) ? (- ((zero) + 1) << ((bits) ? (bits) - 1 : 0)) : (zero)) 108 | 109 | #define _STDINT_MAX(signed, bits, zero) \ 110 | ((signed) \ 111 | ? ~ _STDINT_MIN (signed, bits, zero) \ 112 | : /* The expression for the unsigned case. The subtraction of (signed) \ 113 | is a nop in the unsigned case and avoids "signed integer overflow" \ 114 | warnings in the signed case. */ \ 115 | ((((zero) + 1) << ((bits) ? (bits) - 1 - (signed) : 0)) - 1) * 2 + 1) 116 | 117 | /* 7.18.1.1. Exact-width integer types */ 118 | 119 | /* Here we assume a standard architecture where the hardware integer 120 | types have 8, 16, 32, optionally 64 bits. */ 121 | 122 | #undef int8_t 123 | #undef uint8_t 124 | #define int8_t signed char 125 | #define uint8_t unsigned char 126 | 127 | #undef int16_t 128 | #undef uint16_t 129 | #define int16_t short int 130 | #define uint16_t unsigned short int 131 | 132 | #undef int32_t 133 | #undef uint32_t 134 | #define int32_t int 135 | #define uint32_t unsigned int 136 | 137 | /* Do not undefine int64_t if gnulib is not being used with 64-bit 138 | types, since otherwise it breaks platforms like Tandem/NSK. */ 139 | #if LONG_MAX >> 31 >> 31 == 1 140 | # undef int64_t 141 | # define int64_t long int 142 | # define GL_INT64_T 143 | #elif defined _MSC_VER 144 | # undef int64_t 145 | # define int64_t __int64 146 | # define GL_INT64_T 147 | #elif 1 148 | # undef int64_t 149 | # define int64_t long long int 150 | # define GL_INT64_T 151 | #endif 152 | 153 | #if ULONG_MAX >> 31 >> 31 >> 1 == 1 154 | # undef uint64_t 155 | # define uint64_t unsigned long int 156 | # define GL_UINT64_T 157 | #elif defined _MSC_VER 158 | # undef uint64_t 159 | # define uint64_t unsigned __int64 160 | # define GL_UINT64_T 161 | #elif 1 162 | # undef uint64_t 163 | # define uint64_t unsigned long long int 164 | # define GL_UINT64_T 165 | #endif 166 | 167 | /* Avoid collision with Solaris 2.5.1 etc. */ 168 | #define _UINT8_T 169 | #define _UINT32_T 170 | #define _UINT64_T 171 | 172 | 173 | /* 7.18.1.2. Minimum-width integer types */ 174 | 175 | /* Here we assume a standard architecture where the hardware integer 176 | types have 8, 16, 32, optionally 64 bits. Therefore the leastN_t types 177 | are the same as the corresponding N_t types. */ 178 | 179 | #undef int_least8_t 180 | #undef uint_least8_t 181 | #undef int_least16_t 182 | #undef uint_least16_t 183 | #undef int_least32_t 184 | #undef uint_least32_t 185 | #undef int_least64_t 186 | #undef uint_least64_t 187 | #define int_least8_t int8_t 188 | #define uint_least8_t uint8_t 189 | #define int_least16_t int16_t 190 | #define uint_least16_t uint16_t 191 | #define int_least32_t int32_t 192 | #define uint_least32_t uint32_t 193 | #ifdef GL_INT64_T 194 | # define int_least64_t int64_t 195 | #endif 196 | #ifdef GL_UINT64_T 197 | # define uint_least64_t uint64_t 198 | #endif 199 | 200 | /* 7.18.1.3. Fastest minimum-width integer types */ 201 | 202 | /* Note: Other substitutes may define these types differently. 203 | It is not recommended to use these types in public header files. */ 204 | 205 | /* Here we assume a standard architecture where the hardware integer 206 | types have 8, 16, 32, optionally 64 bits. Therefore the fastN_t types 207 | are taken from the same list of types. Assume that 'long int' 208 | is fast enough for all narrower integers. */ 209 | 210 | #undef int_fast8_t 211 | #undef uint_fast8_t 212 | #undef int_fast16_t 213 | #undef uint_fast16_t 214 | #undef int_fast32_t 215 | #undef uint_fast32_t 216 | #undef int_fast64_t 217 | #undef uint_fast64_t 218 | #define int_fast8_t long int 219 | #define uint_fast8_t unsigned int_fast8_t 220 | #define int_fast16_t long int 221 | #define uint_fast16_t unsigned int_fast16_t 222 | #define int_fast32_t long int 223 | #define uint_fast32_t unsigned int_fast32_t 224 | #ifdef GL_INT64_T 225 | # define int_fast64_t int64_t 226 | #endif 227 | #ifdef GL_UINT64_T 228 | # define uint_fast64_t uint64_t 229 | #endif 230 | 231 | /* 7.18.1.4. Integer types capable of holding object pointers */ 232 | 233 | #undef intptr_t 234 | #undef uintptr_t 235 | #define intptr_t long int 236 | #define uintptr_t unsigned long int 237 | 238 | /* 7.18.1.5. Greatest-width integer types */ 239 | 240 | /* Note: These types are compiler dependent. It may be unwise to use them in 241 | public header files. */ 242 | 243 | #undef intmax_t 244 | #if 1 && LONG_MAX >> 30 == 1 245 | # define intmax_t long long int 246 | #elif defined GL_INT64_T 247 | # define intmax_t int64_t 248 | #else 249 | # define intmax_t long int 250 | #endif 251 | 252 | #undef uintmax_t 253 | #if 1 && ULONG_MAX >> 31 == 1 254 | # define uintmax_t unsigned long long int 255 | #elif defined GL_UINT64_T 256 | # define uintmax_t uint64_t 257 | #else 258 | # define uintmax_t unsigned long int 259 | #endif 260 | 261 | /* Verify that intmax_t and uintmax_t have the same size. Too much code 262 | breaks if this is not the case. If this check fails, the reason is likely 263 | to be found in the autoconf macros. */ 264 | typedef int _verify_intmax_size[2 * (sizeof (intmax_t) == sizeof (uintmax_t)) - 1]; 265 | 266 | /* 7.18.2. Limits of specified-width integer types */ 267 | 268 | #if ! defined __cplusplus || defined __STDC_LIMIT_MACROS 269 | 270 | /* 7.18.2.1. Limits of exact-width integer types */ 271 | 272 | /* Here we assume a standard architecture where the hardware integer 273 | types have 8, 16, 32, optionally 64 bits. */ 274 | 275 | #undef INT8_MIN 276 | #undef INT8_MAX 277 | #undef UINT8_MAX 278 | #define INT8_MIN (~ INT8_MAX) 279 | #define INT8_MAX 127 280 | #define UINT8_MAX 255 281 | 282 | #undef INT16_MIN 283 | #undef INT16_MAX 284 | #undef UINT16_MAX 285 | #define INT16_MIN (~ INT16_MAX) 286 | #define INT16_MAX 32767 287 | #define UINT16_MAX 65535 288 | 289 | #undef INT32_MIN 290 | #undef INT32_MAX 291 | #undef UINT32_MAX 292 | #define INT32_MIN (~ INT32_MAX) 293 | #define INT32_MAX 2147483647 294 | #define UINT32_MAX 4294967295U 295 | 296 | #undef INT64_MIN 297 | #undef INT64_MAX 298 | #ifdef GL_INT64_T 299 | /* Prefer (- INTMAX_C (1) << 63) over (~ INT64_MAX) because SunPRO C 5.0 300 | evaluates the latter incorrectly in preprocessor expressions. */ 301 | # define INT64_MIN (- INTMAX_C (1) << 63) 302 | # define INT64_MAX INTMAX_C (9223372036854775807) 303 | #endif 304 | 305 | #undef UINT64_MAX 306 | #ifdef GL_UINT64_T 307 | # define UINT64_MAX UINTMAX_C (18446744073709551615) 308 | #endif 309 | 310 | /* 7.18.2.2. Limits of minimum-width integer types */ 311 | 312 | /* Here we assume a standard architecture where the hardware integer 313 | types have 8, 16, 32, optionally 64 bits. Therefore the leastN_t types 314 | are the same as the corresponding N_t types. */ 315 | 316 | #undef INT_LEAST8_MIN 317 | #undef INT_LEAST8_MAX 318 | #undef UINT_LEAST8_MAX 319 | #define INT_LEAST8_MIN INT8_MIN 320 | #define INT_LEAST8_MAX INT8_MAX 321 | #define UINT_LEAST8_MAX UINT8_MAX 322 | 323 | #undef INT_LEAST16_MIN 324 | #undef INT_LEAST16_MAX 325 | #undef UINT_LEAST16_MAX 326 | #define INT_LEAST16_MIN INT16_MIN 327 | #define INT_LEAST16_MAX INT16_MAX 328 | #define UINT_LEAST16_MAX UINT16_MAX 329 | 330 | #undef INT_LEAST32_MIN 331 | #undef INT_LEAST32_MAX 332 | #undef UINT_LEAST32_MAX 333 | #define INT_LEAST32_MIN INT32_MIN 334 | #define INT_LEAST32_MAX INT32_MAX 335 | #define UINT_LEAST32_MAX UINT32_MAX 336 | 337 | #undef INT_LEAST64_MIN 338 | #undef INT_LEAST64_MAX 339 | #ifdef GL_INT64_T 340 | # define INT_LEAST64_MIN INT64_MIN 341 | # define INT_LEAST64_MAX INT64_MAX 342 | #endif 343 | 344 | #undef UINT_LEAST64_MAX 345 | #ifdef GL_UINT64_T 346 | # define UINT_LEAST64_MAX UINT64_MAX 347 | #endif 348 | 349 | /* 7.18.2.3. Limits of fastest minimum-width integer types */ 350 | 351 | /* Here we assume a standard architecture where the hardware integer 352 | types have 8, 16, 32, optionally 64 bits. Therefore the fastN_t types 353 | are taken from the same list of types. */ 354 | 355 | #undef INT_FAST8_MIN 356 | #undef INT_FAST8_MAX 357 | #undef UINT_FAST8_MAX 358 | #define INT_FAST8_MIN LONG_MIN 359 | #define INT_FAST8_MAX LONG_MAX 360 | #define UINT_FAST8_MAX ULONG_MAX 361 | 362 | #undef INT_FAST16_MIN 363 | #undef INT_FAST16_MAX 364 | #undef UINT_FAST16_MAX 365 | #define INT_FAST16_MIN LONG_MIN 366 | #define INT_FAST16_MAX LONG_MAX 367 | #define UINT_FAST16_MAX ULONG_MAX 368 | 369 | #undef INT_FAST32_MIN 370 | #undef INT_FAST32_MAX 371 | #undef UINT_FAST32_MAX 372 | #define INT_FAST32_MIN LONG_MIN 373 | #define INT_FAST32_MAX LONG_MAX 374 | #define UINT_FAST32_MAX ULONG_MAX 375 | 376 | #undef INT_FAST64_MIN 377 | #undef INT_FAST64_MAX 378 | #ifdef GL_INT64_T 379 | # define INT_FAST64_MIN INT64_MIN 380 | # define INT_FAST64_MAX INT64_MAX 381 | #endif 382 | 383 | #undef UINT_FAST64_MAX 384 | #ifdef GL_UINT64_T 385 | # define UINT_FAST64_MAX UINT64_MAX 386 | #endif 387 | 388 | /* 7.18.2.4. Limits of integer types capable of holding object pointers */ 389 | 390 | #undef INTPTR_MIN 391 | #undef INTPTR_MAX 392 | #undef UINTPTR_MAX 393 | #define INTPTR_MIN LONG_MIN 394 | #define INTPTR_MAX LONG_MAX 395 | #define UINTPTR_MAX ULONG_MAX 396 | 397 | /* 7.18.2.5. Limits of greatest-width integer types */ 398 | 399 | #undef INTMAX_MIN 400 | #undef INTMAX_MAX 401 | #ifdef INT64_MAX 402 | # define INTMAX_MIN INT64_MIN 403 | # define INTMAX_MAX INT64_MAX 404 | #else 405 | # define INTMAX_MIN INT32_MIN 406 | # define INTMAX_MAX INT32_MAX 407 | #endif 408 | 409 | #undef UINTMAX_MAX 410 | #ifdef UINT64_MAX 411 | # define UINTMAX_MAX UINT64_MAX 412 | #else 413 | # define UINTMAX_MAX UINT32_MAX 414 | #endif 415 | 416 | /* 7.18.3. Limits of other integer types */ 417 | 418 | /* ptrdiff_t limits */ 419 | #undef PTRDIFF_MIN 420 | #undef PTRDIFF_MAX 421 | #define PTRDIFF_MIN \ 422 | _STDINT_MIN (1, 32, 0) 423 | #define PTRDIFF_MAX \ 424 | _STDINT_MAX (1, 32, 0) 425 | 426 | /* sig_atomic_t limits */ 427 | #undef SIG_ATOMIC_MIN 428 | #undef SIG_ATOMIC_MAX 429 | #define SIG_ATOMIC_MIN \ 430 | _STDINT_MIN (1, 32, \ 431 | 0) 432 | #define SIG_ATOMIC_MAX \ 433 | _STDINT_MAX (1, 32, \ 434 | 0) 435 | 436 | 437 | /* size_t limit */ 438 | #undef SIZE_MAX 439 | #define SIZE_MAX _STDINT_MAX (0, 32, 0ul) 440 | 441 | /* wchar_t limits */ 442 | #undef WCHAR_MIN 443 | #undef WCHAR_MAX 444 | #define WCHAR_MIN \ 445 | _STDINT_MIN (1, 32, 0) 446 | #define WCHAR_MAX \ 447 | _STDINT_MAX (1, 32, 0) 448 | 449 | /* wint_t limits */ 450 | #undef WINT_MIN 451 | #undef WINT_MAX 452 | #define WINT_MIN \ 453 | _STDINT_MIN (1, 32, 0) 454 | #define WINT_MAX \ 455 | _STDINT_MAX (1, 32, 0) 456 | 457 | #endif /* !defined __cplusplus || defined __STDC_LIMIT_MACROS */ 458 | 459 | /* 7.18.4. Macros for integer constants */ 460 | 461 | #if ! defined __cplusplus || defined __STDC_CONSTANT_MACROS 462 | 463 | /* 7.18.4.1. Macros for minimum-width integer constants */ 464 | /* According to ISO C 99 Technical Corrigendum 1 */ 465 | 466 | /* Here we assume a standard architecture where the hardware integer 467 | types have 8, 16, 32, optionally 64 bits, and int is 32 bits. */ 468 | 469 | #undef INT8_C 470 | #undef UINT8_C 471 | #define INT8_C(x) x 472 | #define UINT8_C(x) x 473 | 474 | #undef INT16_C 475 | #undef UINT16_C 476 | #define INT16_C(x) x 477 | #define UINT16_C(x) x 478 | 479 | #undef INT32_C 480 | #undef UINT32_C 481 | #define INT32_C(x) x 482 | #define UINT32_C(x) x ## U 483 | 484 | #undef INT64_C 485 | #undef UINT64_C 486 | #if LONG_MAX >> 31 >> 31 == 1 487 | # define INT64_C(x) x##L 488 | #elif defined _MSC_VER 489 | # define INT64_C(x) x##i64 490 | #elif 1 491 | # define INT64_C(x) x##LL 492 | #endif 493 | #if ULONG_MAX >> 31 >> 31 >> 1 == 1 494 | # define UINT64_C(x) x##UL 495 | #elif defined _MSC_VER 496 | # define UINT64_C(x) x##ui64 497 | #elif 1 498 | # define UINT64_C(x) x##ULL 499 | #endif 500 | 501 | /* 7.18.4.2. Macros for greatest-width integer constants */ 502 | 503 | #undef INTMAX_C 504 | #if 1 && LONG_MAX >> 30 == 1 505 | # define INTMAX_C(x) x##LL 506 | #elif defined GL_INT64_T 507 | # define INTMAX_C(x) INT64_C(x) 508 | #else 509 | # define INTMAX_C(x) x##L 510 | #endif 511 | 512 | #undef UINTMAX_C 513 | #if 1 && ULONG_MAX >> 31 == 1 514 | # define UINTMAX_C(x) x##ULL 515 | #elif defined GL_UINT64_T 516 | # define UINTMAX_C(x) UINT64_C(x) 517 | #else 518 | # define UINTMAX_C(x) x##UL 519 | #endif 520 | 521 | #endif /* !defined __cplusplus || defined __STDC_CONSTANT_MACROS */ 522 | 523 | #endif /* _GL_STDINT_H */ 524 | #endif /* !defined _GL_STDINT_H && !defined _GL_JUST_INCLUDE_SYSTEM_STDINT_H */ 525 | -------------------------------------------------------------------------------- /KissXML/DDXMLElement.m: -------------------------------------------------------------------------------- 1 | #import "DDXMLElement.h" 2 | #import "NSStringAdditions.h" 3 | #import "DDXMLPrivate.h" 4 | 5 | 6 | @implementation DDXMLElement 7 | 8 | - (id)initWithName:(NSString *)name 9 | { 10 | // Note: Make every guarantee that genericPtr is not null 11 | 12 | xmlNodePtr node = xmlNewNode(NULL, [name xmlChar]); 13 | 14 | return [self initWithPrimitive:(xmlKindPtr)node]; 15 | } 16 | 17 | - (id)initWithName:(NSString *)name URI:(NSString *)URI 18 | { 19 | // Note: Make every guarantee that genericPtr is not null 20 | 21 | xmlNodePtr node = xmlNewNode(NULL, [name xmlChar]); 22 | 23 | id result = [self initWithPrimitive:(xmlKindPtr)node]; 24 | [result setURI:URI]; 25 | 26 | return result; 27 | } 28 | 29 | - (id)initWithName:(NSString *)name stringValue:(NSString *)string 30 | { 31 | // Note: Make every guarantee that genericPtr is not null 32 | 33 | xmlNodePtr node = xmlNewNode(NULL, [name xmlChar]); 34 | 35 | id result = [self initWithPrimitive:(xmlKindPtr)node]; 36 | [result setStringValue:string]; 37 | 38 | return result; 39 | } 40 | 41 | - (id)initWithXMLString:(NSString *)string error:(NSError **)error 42 | { 43 | DDXMLDocument *doc = [[DDXMLDocument alloc] initWithXMLString:string options:0 error:error]; 44 | 45 | if(doc == nil) 46 | { 47 | return nil; 48 | } 49 | 50 | DDXMLNode *result = [doc rootElement]; 51 | [result detach]; 52 | [doc release]; 53 | 54 | return [result retain]; 55 | } 56 | 57 | + (id)nodeWithPrimitive:(xmlKindPtr)nodePtr 58 | { 59 | return [[[DDXMLElement alloc] initWithPrimitive:nodePtr] autorelease]; 60 | } 61 | 62 | - (id)initWithPrimitive:(xmlKindPtr)nodePtr 63 | { 64 | if(nodePtr == NULL || nodePtr->type != XML_ELEMENT_NODE) 65 | { 66 | [self release]; 67 | return nil; 68 | } 69 | 70 | self = [super initWithPrimitive:nodePtr]; 71 | return self; 72 | } 73 | 74 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 75 | #pragma mark Elements by name 76 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 77 | 78 | /** 79 | * Returns the child element nodes (as DDXMLElement objects) of the receiver that have a specified name. 80 | * 81 | * If name is a qualified name, then this method invokes elementsForLocalName:URI: with the URI parameter set to 82 | * the URI associated with the prefix. Otherwise comparison is based on string equality of the qualified or 83 | * non-qualified name. 84 | **/ 85 | - (NSArray *)elementsForName:(NSString *)name 86 | { 87 | if(name == nil) return [NSArray array]; 88 | 89 | // We need to check to see if name has a prefix. 90 | // If it does have a prefix, we need to figure out what the corresponding URI is for that prefix, 91 | // and then search for any elements that have the same name (including prefix) OR have the same URI. 92 | // Otherwise we loop through the children as usual and do a string compare on the name 93 | 94 | NSString *prefix = [[self class] prefixForName:name]; 95 | if([prefix length] > 0) 96 | { 97 | xmlNodePtr node = (xmlNodePtr)genericPtr; 98 | xmlNsPtr ns = xmlSearchNs(node->doc, node, [prefix xmlChar]); 99 | if(ns != NULL) 100 | { 101 | NSString *uri = [NSString stringWithUTF8String:((const char *)ns->href)]; 102 | return [self elementsWithName:name uri:uri]; 103 | } 104 | 105 | // Note: We used xmlSearchNs instead of resolveNamespaceForName: - avoid creating wrapper objects when possible 106 | } 107 | 108 | return [self elementsWithName:name uri:nil]; 109 | } 110 | 111 | - (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI 112 | { 113 | if(localName == nil) return [NSArray array]; 114 | 115 | // We need to figure out what the prefix is for this URI. 116 | // Then we search for elements that are named prefix:localName OR (named localName AND have the given URI). 117 | 118 | NSString *prefix = [self resolvePrefixForNamespaceURI:URI]; 119 | if(prefix != nil) 120 | { 121 | NSString *name = [NSString stringWithFormat:@"%@:%@", prefix, localName]; 122 | 123 | return [self elementsWithName:name uri:URI]; 124 | } 125 | else 126 | { 127 | return [self elementsWithName:localName uri:URI]; 128 | } 129 | } 130 | 131 | /** 132 | * Helper method elementsForName and elementsForLocalName:URI: so work isn't duplicated. 133 | * The name parameter is required, URI is optional. 134 | **/ 135 | - (NSArray *)elementsWithName:(NSString *)name uri:(NSString *)uri 136 | { 137 | // Supplied: name, !uri : match: name 138 | // Supplied: p:name, uri : match: p:name || (name && uri) 139 | // Supplied: name, uri : match: name && uri 140 | 141 | NSMutableArray *result = [NSMutableArray array]; 142 | 143 | xmlNodePtr node = (xmlNodePtr)genericPtr; 144 | 145 | BOOL hasPrefix = [[[self class] prefixForName:name] length] > 0; 146 | NSString *localName = [[self class] localNameForName:name]; 147 | 148 | xmlNodePtr child = node->children; 149 | while(child != NULL) 150 | { 151 | if(child->type == XML_ELEMENT_NODE) 152 | { 153 | BOOL match = NO; 154 | if(uri == nil) 155 | { 156 | match = xmlStrEqual(child->name, [name xmlChar]); 157 | } 158 | else 159 | { 160 | BOOL nameMatch = xmlStrEqual(child->name, [name xmlChar]); 161 | BOOL localNameMatch = xmlStrEqual(child->name, [localName xmlChar]); 162 | 163 | BOOL uriMatch = NO; 164 | if(child->ns != NULL) 165 | { 166 | uriMatch = xmlStrEqual(child->ns->href, [uri xmlChar]); 167 | } 168 | 169 | if(hasPrefix) 170 | match = nameMatch || (localNameMatch && uriMatch); 171 | else 172 | match = nameMatch && uriMatch; 173 | } 174 | 175 | if(match) 176 | { 177 | DDXMLElement *childElement = [[DDXMLElement alloc] initWithPrimitive:(xmlKindPtr)child]; 178 | 179 | [result addObject:childElement]; 180 | [childElement release]; 181 | } 182 | } 183 | 184 | child = child->next; 185 | } 186 | 187 | return result; 188 | } 189 | 190 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 191 | #pragma mark Attributes 192 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 193 | 194 | - (void)addAttribute:(DDXMLNode *)attribute 195 | { 196 | // NSXML version uses this same assertion 197 | DDCheck([attribute hasParent] == NO, @"Cannot add an attribute with a parent; detach or copy first"); 198 | DDCheck([attribute isXmlAttrPtr], @"Not an attribute"); 199 | 200 | // xmlNodePtr xmlAddChild(xmlNodePtr parent, xmlNodePtr cur) 201 | // Add a new node to @parent, at the end of the child (or property) list merging 202 | // adjacent TEXT nodes (in which case @cur is freed). If the new node is ATTRIBUTE, it is added 203 | // into properties instead of children. If there is an attribute with equal name, it is first destroyed. 204 | 205 | [self removeAttributeForName:[attribute name]]; 206 | 207 | xmlAddChild((xmlNodePtr)genericPtr, (xmlNodePtr)attribute->genericPtr); 208 | } 209 | 210 | - (void)removeAttribute:(xmlAttrPtr)attr 211 | { 212 | [[self class] removeAttribute:attr fromNode:(xmlNodePtr)genericPtr]; 213 | } 214 | 215 | - (void)removeAllAttributes 216 | { 217 | [[self class] removeAllAttributesFromNode:(xmlNodePtr)genericPtr]; 218 | } 219 | 220 | - (void)removeAttributeForName:(NSString *)name 221 | { 222 | // If we use xmlUnsetProp, then the attribute will be automatically freed. 223 | // We don't want this unless no other wrapper objects have a reference to the property. 224 | 225 | xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties; 226 | while(attr != NULL) 227 | { 228 | if(xmlStrEqual(attr->name, [name xmlChar])) 229 | { 230 | [self removeAttribute:attr]; 231 | return; 232 | } 233 | attr = attr->next; 234 | } 235 | } 236 | 237 | - (NSArray *)attributes 238 | { 239 | NSMutableArray *result = [NSMutableArray array]; 240 | 241 | xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties; 242 | while(attr != NULL) 243 | { 244 | DDXMLNode *attrNode = [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)attr]; 245 | 246 | [result addObject:attrNode]; 247 | [attrNode release]; 248 | 249 | attr = attr->next; 250 | } 251 | 252 | return result; 253 | } 254 | 255 | - (DDXMLNode *)attributeForName:(NSString *)name 256 | { 257 | xmlAttrPtr attr = ((xmlNodePtr)genericPtr)->properties; 258 | while(attr != NULL) 259 | { 260 | if(xmlStrEqual([name xmlChar], attr->name)) 261 | { 262 | return [DDXMLNode nodeWithPrimitive:(xmlKindPtr)attr]; 263 | } 264 | attr = attr->next; 265 | } 266 | return nil; 267 | } 268 | 269 | /** 270 | * Sets the list of attributes for the element. 271 | * Any previously set attributes are removed. 272 | **/ 273 | - (void)setAttributes:(NSArray *)attributes 274 | { 275 | [self removeAllAttributes]; 276 | 277 | NSUInteger i; 278 | for(i = 0; i < [attributes count]; i++) 279 | { 280 | DDXMLNode *attribute = [attributes objectAtIndex:i]; 281 | [self addAttribute:attribute]; 282 | } 283 | } 284 | 285 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 286 | #pragma mark Namespaces 287 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 288 | 289 | - (void)addNamespace:(DDXMLNode *)namespace 290 | { 291 | // NSXML version uses this same assertion 292 | DDCheck([namespace hasParent] == NO, @"Cannot add a namespace with a parent; detach or copy first"); 293 | DDCheck([namespace isXmlNsPtr], @"Not a namespace"); 294 | 295 | // Beware: [namespace prefix] does NOT return what you might expect. Use [namespace name] instead. 296 | 297 | [self removeNamespaceForPrefix:[namespace name]]; 298 | 299 | xmlNsPtr currentNs = ((xmlNodePtr)genericPtr)->nsDef; 300 | if(currentNs == NULL) 301 | { 302 | ((xmlNodePtr)genericPtr)->nsDef = (xmlNsPtr)namespace->genericPtr; 303 | } 304 | else 305 | { 306 | while(currentNs != NULL) 307 | { 308 | if(currentNs->next == NULL) 309 | { 310 | currentNs->next = (xmlNsPtr)namespace->genericPtr; 311 | break; // Yes this break is needed 312 | } 313 | currentNs = currentNs->next; 314 | } 315 | } 316 | 317 | // The xmlNs structure doesn't contain a reference to the parent, so we manage our own reference 318 | namespace->nsParentPtr = (xmlNodePtr)genericPtr; 319 | 320 | // Did we just add a default namespace 321 | if([[namespace name] isEqualToString:@""]) 322 | { 323 | ((xmlNodePtr)genericPtr)->ns = (xmlNsPtr)namespace->genericPtr; 324 | 325 | // Note: The removeNamespaceForPrefix method above properly handled removing any previous default namespace 326 | } 327 | } 328 | 329 | - (void)removeNamespace:(xmlNsPtr)ns 330 | { 331 | [[self class] removeNamespace:ns fromNode:(xmlNodePtr)genericPtr]; 332 | } 333 | 334 | - (void)removeAllNamespaces 335 | { 336 | [[self class] removeAllNamespacesFromNode:(xmlNodePtr)genericPtr]; 337 | } 338 | 339 | - (void)removeNamespaceForPrefix:(NSString *)name 340 | { 341 | // If name is nil or the empty string, the user is wishing to remove the default namespace 342 | const xmlChar *xmlName = [name length] > 0 ? [name xmlChar] : NULL; 343 | 344 | xmlNsPtr ns = ((xmlNodePtr)genericPtr)->nsDef; 345 | while(ns != NULL) 346 | { 347 | if(xmlStrEqual(ns->prefix, xmlName)) 348 | { 349 | [self removeNamespace:ns]; 350 | break; 351 | } 352 | ns = ns->next; 353 | } 354 | 355 | // Note: The removeNamespace method properly handles the situation where the namespace is the default namespace 356 | } 357 | 358 | - (NSArray *)namespaces 359 | { 360 | NSMutableArray *result = [NSMutableArray array]; 361 | 362 | xmlNsPtr ns = ((xmlNodePtr)genericPtr)->nsDef; 363 | while(ns != NULL) 364 | { 365 | DDXMLNode *nsNode = [[DDXMLNode alloc] initWithPrimitive:(xmlKindPtr)ns nsParent:(xmlNodePtr)genericPtr]; 366 | 367 | [result addObject:nsNode]; 368 | [nsNode release]; 369 | 370 | ns = ns->next; 371 | } 372 | 373 | return result; 374 | } 375 | 376 | - (DDXMLNode *)namespaceForPrefix:(NSString *)prefix 377 | { 378 | // If the prefix is nil or the empty string, the user is requesting the default namespace 379 | 380 | if([prefix length] == 0) 381 | { 382 | // Requesting the default namespace 383 | xmlNsPtr ns = ((xmlNodePtr)genericPtr)->ns; 384 | if(ns != NULL) 385 | { 386 | return [DDXMLNode nodeWithPrimitive:(xmlKindPtr)ns nsParent:(xmlNodePtr)genericPtr]; 387 | } 388 | } 389 | else 390 | { 391 | xmlNsPtr ns = ((xmlNodePtr)genericPtr)->nsDef; 392 | while(ns != NULL) 393 | { 394 | if(xmlStrEqual(ns->prefix, [prefix xmlChar])) 395 | { 396 | return [DDXMLNode nodeWithPrimitive:(xmlKindPtr)ns nsParent:(xmlNodePtr)genericPtr]; 397 | } 398 | ns = ns->next; 399 | } 400 | } 401 | 402 | return nil; 403 | } 404 | 405 | - (void)setNamespaces:(NSArray *)namespaces 406 | { 407 | [self removeAllNamespaces]; 408 | 409 | NSUInteger i; 410 | for(i = 0; i < [namespaces count]; i++) 411 | { 412 | DDXMLNode *namespace = [namespaces objectAtIndex:i]; 413 | [self addNamespace:namespace]; 414 | } 415 | } 416 | 417 | /** 418 | * Recursively searches the given node for the given namespace 419 | **/ 420 | + (DDXMLNode *)resolveNamespaceForPrefix:(NSString *)prefix atNode:(xmlNodePtr)nodePtr 421 | { 422 | if(nodePtr == NULL) return nil; 423 | 424 | xmlNsPtr ns = nodePtr->nsDef; 425 | while(ns != NULL) 426 | { 427 | if(xmlStrEqual(ns->prefix, [prefix xmlChar])) 428 | { 429 | return [DDXMLNode nodeWithPrimitive:(xmlKindPtr)ns nsParent:nodePtr]; 430 | } 431 | ns = ns->next; 432 | } 433 | 434 | return [self resolveNamespaceForPrefix:prefix atNode:nodePtr->parent]; 435 | } 436 | 437 | /** 438 | * Returns the namespace node with the prefix matching the given qualified name. 439 | * Eg: You pass it "a:dog", it returns the namespace (defined in this node or parent nodes) that has the "a" prefix. 440 | **/ 441 | - (DDXMLNode *)resolveNamespaceForName:(NSString *)name 442 | { 443 | // If the user passes nil or an empty string for name, they're looking for the default namespace. 444 | if([name length] == 0) 445 | { 446 | return [[self class] resolveNamespaceForPrefix:nil atNode:(xmlNodePtr)genericPtr]; 447 | } 448 | 449 | NSString *prefix = [[self class] prefixForName:name]; 450 | 451 | if([prefix length] > 0) 452 | { 453 | // Unfortunately we can't use xmlSearchNs because it returns an xmlNsPtr. 454 | // This gives us mostly what we want, except we also need to know the nsParent. 455 | // So we do the recursive search ourselves. 456 | 457 | return [[self class] resolveNamespaceForPrefix:prefix atNode:(xmlNodePtr)genericPtr]; 458 | } 459 | 460 | return nil; 461 | } 462 | 463 | /** 464 | * Recursively searches the given node for a namespace with the given URI, and a set prefix. 465 | **/ 466 | + (NSString *)resolvePrefixForURI:(NSString *)uri atNode:(xmlNodePtr)nodePtr 467 | { 468 | if(nodePtr == NULL) return nil; 469 | 470 | xmlNsPtr ns = nodePtr->nsDef; 471 | while(ns != NULL) 472 | { 473 | if(xmlStrEqual(ns->href, [uri xmlChar])) 474 | { 475 | if(ns->prefix != NULL) 476 | { 477 | return [NSString stringWithUTF8String:((const char *)ns->prefix)]; 478 | } 479 | } 480 | ns = ns->next; 481 | } 482 | 483 | return [self resolvePrefixForURI:uri atNode:nodePtr->parent]; 484 | } 485 | 486 | /** 487 | * Returns the prefix associated with the specified URI. 488 | * Returns a string that is the matching prefix or nil if it finds no matching prefix. 489 | **/ 490 | - (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI 491 | { 492 | // We can't use xmlSearchNsByHref because it will return xmlNsPtr's with NULL prefixes. 493 | // We're looking for a definitive prefix for the given URI. 494 | 495 | return [[self class] resolvePrefixForURI:namespaceURI atNode:(xmlNodePtr)genericPtr]; 496 | } 497 | 498 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 499 | #pragma mark Children 500 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 501 | 502 | - (void)removeChild:(xmlNodePtr)child 503 | { 504 | [[self class] removeChild:child fromNode:(xmlNodePtr)genericPtr]; 505 | } 506 | 507 | - (void)removeAllChildren 508 | { 509 | [[self class] removeAllChildrenFromNode:(xmlNodePtr)genericPtr]; 510 | } 511 | 512 | - (void)removeChildAtIndex:(NSUInteger)index 513 | { 514 | NSUInteger i = 0; 515 | 516 | xmlNodePtr child = ((xmlNodePtr)genericPtr)->children; 517 | while(child != NULL) 518 | { 519 | // Ignore all but element, comment, text, or processing instruction nodes 520 | if([[self class] isXmlNodePtr:(xmlKindPtr)child]) 521 | { 522 | if(i == index) 523 | { 524 | [self removeChild:child]; 525 | return; 526 | } 527 | 528 | i++; 529 | } 530 | child = child->next; 531 | } 532 | } 533 | 534 | - (void)addChild:(DDXMLNode *)child 535 | { 536 | // NSXML version uses these same assertions 537 | DDCheck([child hasParent] == NO, @"Cannot add a child that has a parent; detach or copy first"); 538 | DDCheck([child isXmlNodePtr], @"Elements can only have text, elements, processing instructions, and comments as children"); 539 | 540 | xmlAddChild((xmlNodePtr)genericPtr, (xmlNodePtr)child->genericPtr); 541 | } 542 | 543 | - (void)insertChild:(DDXMLNode *)child atIndex:(NSUInteger)index 544 | { 545 | // NSXML version uses these same assertions 546 | DDCheck([child hasParent] == NO, @"Cannot add a child that has a parent; detach or copy first"); 547 | DDCheck([child isXmlNodePtr], @"Elements can only have text, elements, processing instructions, and comments as children"); 548 | 549 | NSUInteger i = 0; 550 | 551 | xmlNodePtr childNodePtr = ((xmlNodePtr)genericPtr)->children; 552 | while(childNodePtr != NULL) 553 | { 554 | // Ignore all but element, comment, text, or processing instruction nodes 555 | if([[self class] isXmlNodePtr:(xmlKindPtr)childNodePtr]) 556 | { 557 | if(i == index) 558 | { 559 | xmlAddPrevSibling(childNodePtr, (xmlNodePtr)child->genericPtr); 560 | return; 561 | } 562 | 563 | i++; 564 | } 565 | childNodePtr = childNodePtr->next; 566 | } 567 | 568 | if(i == index) 569 | { 570 | xmlAddChild((xmlNodePtr)genericPtr, (xmlNodePtr)child->genericPtr); 571 | return; 572 | } 573 | 574 | // NSXML version uses this same assertion 575 | DDCheck(NO, @"index (%u) beyond bounds (%u)", (unsigned)index, (unsigned)++i); 576 | } 577 | 578 | - (void)setChildren:(NSArray *)children 579 | { 580 | [self removeAllChildren]; 581 | 582 | NSUInteger i; 583 | for(i = 0; i < [children count]; i++) 584 | { 585 | DDXMLNode *child = [children objectAtIndex:i]; 586 | [self addChild:child]; 587 | } 588 | } 589 | 590 | @end 591 | -------------------------------------------------------------------------------- /XMPPClient.m: -------------------------------------------------------------------------------- 1 | #import "XMPPClient.h" 2 | #import "XMPPStream.h" 3 | #import "XMPPJID.h" 4 | #import "XMPPUser.h" 5 | #import "XMPPResource.h" 6 | #import "XMPPIQ.h" 7 | #import "XMPPMessage.h" 8 | #import "XMPPPresence.h" 9 | #import "NSXMLElementAdditions.h" 10 | #import "MulticastDelegate.h" 11 | #import "SCNotificationManager.h" 12 | 13 | 14 | enum XMPPClientFlags 15 | { 16 | kUsesOldStyleSSL = 1 << 0, // If set, TLS is established prior to any communication (no StartTLS) 17 | kAutoLogin = 1 << 1, // If set, client automatically attempts login after connection is established 18 | kAllowsPlaintextAuth = 1 << 2, // If set, client allows plaintext authentication 19 | kAutoRoster = 1 << 3, // If set, client automatically request roster after authentication 20 | kAutoPresence = 1 << 4, // If set, client automatically becaomes available after authentication 21 | kAutoReconnect = 1 << 5, // If set, client automatically attempts to reconnect after a disconnection 22 | kShouldReconnect = 1 << 6, // If set, disconnection was accidental, and autoReconnect may be used 23 | kHasRoster = 1 << 7, // If set, client has received the roster 24 | }; 25 | 26 | @interface XMPPClient (PrivateAPI) 27 | 28 | - (void)onConnecting; 29 | - (void)onDidConnect; 30 | - (void)onDidDisconnect; 31 | - (void)onDidRegister; 32 | - (void)onDidNotRegister:(NSXMLElement *)error; 33 | - (void)onDidAuthenticate; 34 | - (void)onDidNotAuthenticate:(NSXMLElement *)error; 35 | - (void)onDidUpdateRoster; 36 | - (void)onDidReceiveBuddyRequest:(XMPPJID *)jid; 37 | - (void)onDidReceiveIQ:(XMPPIQ *)iq; 38 | - (void)onDidReceiveMessage:(XMPPMessage *)message; 39 | 40 | @end 41 | 42 | @implementation XMPPClient 43 | 44 | - (id)init 45 | { 46 | if(self = [super init]) 47 | { 48 | multicastDelegate = [[MulticastDelegate alloc] init]; 49 | 50 | priority = 1; 51 | flags = 0; 52 | 53 | [self setAutoLogin:YES]; 54 | [self setAllowsPlaintextAuth:YES]; 55 | [self setAutoPresence:YES]; 56 | [self setAutoRoster:YES]; 57 | [self setAutoReconnect:YES]; 58 | 59 | xmppStream = [[XMPPStream alloc] initWithDelegate:self]; 60 | 61 | roster = [[NSMutableDictionary alloc] initWithCapacity:10]; 62 | 63 | earlyPresenceElements = [[NSMutableArray alloc] initWithCapacity:2]; 64 | 65 | scNotificationManager = [[SCNotificationManager alloc] init]; 66 | 67 | // Register for network notifications from system configuration 68 | [[NSNotificationCenter defaultCenter] addObserver:self 69 | selector:@selector(networkStatusDidChange:) 70 | name:@"State:/Network/Global/IPv4" 71 | object:scNotificationManager]; 72 | } 73 | return self; 74 | } 75 | 76 | - (void)dealloc 77 | { 78 | [multicastDelegate release]; 79 | 80 | [domain release]; 81 | [myJID release]; 82 | [password release]; 83 | 84 | [xmppStream setDelegate:nil]; 85 | [xmppStream disconnect]; 86 | [xmppStream release]; 87 | [streamError release]; 88 | 89 | [roster release]; 90 | [myUser release]; 91 | 92 | [earlyPresenceElements release]; 93 | 94 | [scNotificationManager release]; 95 | 96 | [super dealloc]; 97 | } 98 | 99 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 100 | #pragma mark Configuration 101 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 102 | 103 | - (void)addDelegate:(id)delegate 104 | { 105 | [multicastDelegate addDelegate:delegate]; 106 | } 107 | 108 | - (void)removeDelegate:(id)delegate 109 | { 110 | [multicastDelegate removeDelegate:delegate]; 111 | } 112 | 113 | - (NSString *)domain 114 | { 115 | return domain; 116 | } 117 | - (void)setDomain:(NSString *)newDomain 118 | { 119 | if(![domain isEqual:newDomain]) 120 | { 121 | [domain release]; 122 | domain = [newDomain copy]; 123 | } 124 | } 125 | 126 | - (UInt16)port 127 | { 128 | return port; 129 | } 130 | - (void)setPort:(UInt16)newPort 131 | { 132 | port = newPort; 133 | } 134 | 135 | - (BOOL)usesOldStyleSSL 136 | { 137 | return (flags & kUsesOldStyleSSL); 138 | } 139 | - (void)setUsesOldStyleSSL:(BOOL)flag 140 | { 141 | if(flag) 142 | flags |= kUsesOldStyleSSL; 143 | else 144 | flags &= ~kUsesOldStyleSSL; 145 | } 146 | 147 | - (XMPPJID *)myJID 148 | { 149 | return myJID; 150 | } 151 | - (void)setMyJID:(XMPPJID *)jid 152 | { 153 | if(![myJID isEqual:jid]) 154 | { 155 | [myJID release]; 156 | myJID = [jid retain]; 157 | } 158 | } 159 | 160 | - (NSString *)password 161 | { 162 | return password; 163 | } 164 | - (void)setPassword:(NSString *)newPassword 165 | { 166 | if(![password isEqual:newPassword]) 167 | { 168 | [password release]; 169 | password = [newPassword copy]; 170 | } 171 | } 172 | 173 | - (int)priority 174 | { 175 | return priority; 176 | } 177 | - (void)setPriority:(int)newPriority 178 | { 179 | priority = newPriority; 180 | } 181 | 182 | - (BOOL)allowsSelfSignedCertificates 183 | { 184 | return [xmppStream allowsSelfSignedCertificates]; 185 | } 186 | - (void)setAllowsSelfSignedCertificates:(BOOL)flag 187 | { 188 | [xmppStream setAllowsSelfSignedCertificates:flag]; 189 | } 190 | 191 | - (BOOL)isDisconnected 192 | { 193 | return [xmppStream isDisconnected]; 194 | } 195 | 196 | - (BOOL)isConnected 197 | { 198 | return [xmppStream isConnected]; 199 | } 200 | 201 | - (BOOL)isSecure 202 | { 203 | return [xmppStream isSecure]; 204 | } 205 | 206 | - (BOOL)autoLogin 207 | { 208 | return (flags & kAutoLogin); 209 | } 210 | - (void)setAutoLogin:(BOOL)flag 211 | { 212 | if(flag) 213 | flags |= kAutoLogin; 214 | else 215 | flags &= ~kAutoLogin; 216 | } 217 | 218 | - (BOOL)autoRoster 219 | { 220 | return (flags & kAutoRoster); 221 | } 222 | - (void)setAutoRoster:(BOOL)flag 223 | { 224 | if(flag) 225 | flags |= kAutoRoster; 226 | else 227 | flags &= ~kAutoRoster; 228 | } 229 | 230 | - (BOOL)autoPresence 231 | { 232 | return (flags & kAutoPresence); 233 | } 234 | - (void)setAutoPresence:(BOOL)flag 235 | { 236 | if(flag) 237 | flags |= kAutoPresence; 238 | else 239 | flags &= ~kAutoPresence; 240 | } 241 | 242 | - (BOOL)autoReconnect 243 | { 244 | return (flags & kAutoReconnect); 245 | } 246 | - (void)setAutoReconnect:(BOOL)flag 247 | { 248 | if(flag) 249 | flags |= kAutoReconnect; 250 | else 251 | flags &= ~kAutoReconnect; 252 | } 253 | 254 | - (BOOL)shouldReconnect 255 | { 256 | return (flags & kShouldReconnect); 257 | } 258 | - (void)setShouldReconnect:(BOOL)flag 259 | { 260 | if(flag) 261 | flags |= kShouldReconnect; 262 | else 263 | flags &= ~kShouldReconnect; 264 | } 265 | 266 | - (BOOL)hasRoster 267 | { 268 | return (flags & kHasRoster); 269 | } 270 | - (void)setHasRoster:(BOOL)flag 271 | { 272 | if(flag) 273 | flags |= kHasRoster; 274 | else 275 | flags &= ~kHasRoster; 276 | } 277 | 278 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 279 | #pragma mark Connecting, Registering and Authenticating 280 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 281 | 282 | - (void)connect 283 | { 284 | [self onConnecting]; 285 | 286 | if([self usesOldStyleSSL]) 287 | [xmppStream connectToSecureHost:domain onPort:port withVirtualHost:[myJID domain]]; 288 | else 289 | [xmppStream connectToHost:domain onPort:port withVirtualHost:[myJID domain]]; 290 | } 291 | 292 | - (void)disconnect 293 | { 294 | // Turn off the shouldReconnect flag. 295 | // This flag will tell us that we should not automatically attempt to reconnect when the connection closes. 296 | [self setShouldReconnect:NO]; 297 | 298 | [xmppStream disconnect]; 299 | } 300 | 301 | - (BOOL)supportsInBandRegistration 302 | { 303 | return [xmppStream supportsInBandRegistration]; 304 | } 305 | 306 | - (void)registerUser 307 | { 308 | [xmppStream registerUser:[myJID user] withPassword:password]; 309 | } 310 | 311 | - (BOOL)supportsPlainAuthentication 312 | { 313 | return [xmppStream supportsPlainAuthentication]; 314 | } 315 | - (BOOL)supportsDigestMD5Authentication 316 | { 317 | return [xmppStream supportsDigestMD5Authentication]; 318 | } 319 | 320 | - (BOOL)allowsPlaintextAuth 321 | { 322 | return (flags & kAllowsPlaintextAuth); 323 | } 324 | - (void)setAllowsPlaintextAuth:(BOOL)flag 325 | { 326 | if(flag) 327 | flags |= kAllowsPlaintextAuth; 328 | else 329 | flags &= ~kAllowsPlaintextAuth; 330 | } 331 | 332 | - (void)authenticateUser 333 | { 334 | if(![self allowsPlaintextAuth]) 335 | { 336 | if(![xmppStream isSecure] && ![xmppStream supportsDigestMD5Authentication]) 337 | { 338 | // The only way to login is via plaintext! 339 | return; 340 | } 341 | } 342 | 343 | [xmppStream authenticateUser:[myJID user] withPassword:password resource:[myJID resource]]; 344 | } 345 | 346 | - (BOOL)isAuthenticated 347 | { 348 | return [xmppStream isAuthenticated]; 349 | } 350 | 351 | - (NSError *)streamError 352 | { 353 | return streamError; 354 | } 355 | 356 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 357 | #pragma mark Presence Managment 358 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 359 | 360 | - (void)goOnline 361 | { 362 | NSString *priorityStr = [NSString stringWithFormat:@"%i", priority]; 363 | 364 | NSXMLElement *presence = [NSXMLElement elementWithName:@"presence"]; 365 | [presence addChild:[NSXMLElement elementWithName:@"priority" stringValue:priorityStr]]; 366 | 367 | [xmppStream sendElement:presence]; 368 | } 369 | 370 | - (void)goOffline 371 | { 372 | // Send offline presence element 373 | NSXMLElement *presence = [NSXMLElement elementWithName:@"presence"]; 374 | [presence addAttributeWithName:@"type" stringValue:@"unavailable"]; 375 | 376 | [xmppStream sendElement:presence]; 377 | 378 | // Remove all users from our roster when we're offline. 379 | // We don't receive presence notifications when we're offline. 380 | 381 | BOOL didUpdateRoster = ([roster count] > 0); 382 | [roster removeAllObjects]; 383 | [self setHasRoster:NO]; 384 | 385 | if(didUpdateRoster) 386 | { 387 | [self onDidUpdateRoster]; 388 | } 389 | } 390 | 391 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 392 | #pragma mark Roster Managment 393 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 394 | 395 | - (void)fetchRoster 396 | { 397 | NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"]; 398 | 399 | NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"]; 400 | [iq addAttributeWithName:@"type" stringValue:@"get"]; 401 | [iq addChild:query]; 402 | 403 | [xmppStream sendElement:iq]; 404 | } 405 | 406 | - (void)addBuddy:(XMPPJID *)jid withNickname:(NSString *)optionalName 407 | { 408 | if(jid == nil) return; 409 | 410 | if([[myJID bare] isEqualToString:[jid bare]]) 411 | { 412 | // No, you don't need to add yourself 413 | return; 414 | } 415 | 416 | // Add the buddy to our roster 417 | NSXMLElement *item = [NSXMLElement elementWithName:@"item"]; 418 | [item addAttributeWithName:@"jid" stringValue:[jid bare]]; 419 | if(optionalName) 420 | { 421 | [item addAttributeWithName:@"name" stringValue:optionalName]; 422 | } 423 | 424 | NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"]; 425 | [query addChild:item]; 426 | 427 | NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"]; 428 | [iq addAttributeWithName:@"type" stringValue:@"set"]; 429 | [iq addChild:query]; 430 | 431 | [xmppStream sendElement:iq]; 432 | 433 | // Subscribe to the buddy's presence 434 | NSXMLElement *presence = [NSXMLElement elementWithName:@"presence"]; 435 | [presence addAttributeWithName:@"to" stringValue:[jid bare]]; 436 | [presence addAttributeWithName:@"type" stringValue:@"subscribe"]; 437 | 438 | [xmppStream sendElement:presence]; 439 | } 440 | 441 | - (void)removeBuddy:(XMPPJID *)jid 442 | { 443 | if(jid == nil) return; 444 | 445 | if([[myJID bare] isEqualToString:[jid bare]]) 446 | { 447 | // No, you shouldn't remove yourself 448 | return; 449 | } 450 | 451 | // Remove the buddy from our roster 452 | // Unsubscribe from presence 453 | // And revoke contact's subscription to our presence 454 | // ...all in one step 455 | 456 | NSXMLElement *item = [NSXMLElement elementWithName:@"item"]; 457 | [item addAttributeWithName:@"jid" stringValue:[jid bare]]; 458 | [item addAttributeWithName:@"subscription" stringValue:@"remove"]; 459 | 460 | NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"]; 461 | [query addChild:item]; 462 | 463 | NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"]; 464 | [iq addAttributeWithName:@"type" stringValue:@"set"]; 465 | [iq addChild:query]; 466 | 467 | [xmppStream sendElement:iq]; 468 | } 469 | 470 | - (void)setNickname:(NSString *)nickname forBuddy:(XMPPJID *)jid 471 | { 472 | if(jid == nil) return; 473 | 474 | NSXMLElement *item = [NSXMLElement elementWithName:@"item"]; 475 | [item addAttributeWithName:@"jid" stringValue:[jid bare]]; 476 | [item addAttributeWithName:@"name" stringValue:nickname]; 477 | 478 | NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"]; 479 | [query addChild:item]; 480 | 481 | NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"]; 482 | [iq addAttributeWithName:@"type" stringValue:@"set"]; 483 | [iq addChild:query]; 484 | 485 | [xmppStream sendElement:iq]; 486 | } 487 | 488 | - (void)acceptBuddyRequest:(XMPPJID *)jid 489 | { 490 | // Send presence response 491 | NSXMLElement *response = [NSXMLElement elementWithName:@"presence"]; 492 | [response addAttributeWithName:@"to" stringValue:[jid bare]]; 493 | [response addAttributeWithName:@"type" stringValue:@"subscribed"]; 494 | 495 | [xmppStream sendElement:response]; 496 | 497 | // Add user to our roster 498 | NSXMLElement *item = [NSXMLElement elementWithName:@"item"]; 499 | [item addAttributeWithName:@"jid" stringValue:[jid bare]]; 500 | 501 | NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"]; 502 | [query addChild:item]; 503 | 504 | NSXMLElement *iq = [NSXMLElement elementWithName:@"iq"]; 505 | [iq addAttributeWithName:@"type" stringValue:@"set"]; 506 | [iq addChild:query]; 507 | 508 | [xmppStream sendElement:iq]; 509 | 510 | // Subscribe to the user's presence 511 | NSXMLElement *presence = [NSXMLElement elementWithName:@"presence"]; 512 | [presence addAttributeWithName:@"to" stringValue:[jid bare]]; 513 | [presence addAttributeWithName:@"type" stringValue:@"subscribe"]; 514 | 515 | [xmppStream sendElement:presence]; 516 | } 517 | 518 | - (void)rejectBuddyRequest:(XMPPJID *)jid 519 | { 520 | // Send presence response 521 | NSXMLElement *response = [NSXMLElement elementWithName:@"presence"]; 522 | [response addAttributeWithName:@"to" stringValue:[jid bare]]; 523 | [response addAttributeWithName:@"type" stringValue:@"unsubscribed"]; 524 | 525 | [xmppStream sendElement:response]; 526 | } 527 | 528 | - (NSArray *)sortedUsersByName 529 | { 530 | return [[roster allValues] sortedArrayUsingSelector:@selector(compareByName:)]; 531 | } 532 | 533 | - (NSArray *)sortedUsersByAvailabilityName 534 | { 535 | return [[roster allValues] sortedArrayUsingSelector:@selector(compareByAvailabilityName:)]; 536 | } 537 | 538 | - (NSArray *)sortedAvailableUsersByName 539 | { 540 | return [[self unsortedAvailableUsers] sortedArrayUsingSelector:@selector(compareByName:)]; 541 | } 542 | 543 | - (NSArray *)sortedUnavailableUsersByName 544 | { 545 | return [[self unsortedUnavailableUsers] sortedArrayUsingSelector:@selector(compareByName:)]; 546 | } 547 | 548 | - (NSArray *)unsortedUsers 549 | { 550 | return [roster allValues]; 551 | } 552 | 553 | - (NSArray *)unsortedAvailableUsers 554 | { 555 | NSArray *allUsers = [roster allValues]; 556 | 557 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:[allUsers count]]; 558 | 559 | int i; 560 | for(i = 0; i < [allUsers count]; i++) 561 | { 562 | XMPPUser *currentUser = [allUsers objectAtIndex:i]; 563 | if([currentUser isOnline]) 564 | { 565 | [result addObject:currentUser]; 566 | } 567 | } 568 | 569 | return result; 570 | } 571 | 572 | - (NSArray *)unsortedUnavailableUsers 573 | { 574 | NSArray *allUsers = [roster allValues]; 575 | 576 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:[allUsers count]]; 577 | 578 | int i; 579 | for(i = 0; i < [allUsers count]; i++) 580 | { 581 | XMPPUser *currentUser = [allUsers objectAtIndex:i]; 582 | if(![currentUser isOnline]) 583 | { 584 | [result addObject:currentUser]; 585 | } 586 | } 587 | 588 | return result; 589 | } 590 | 591 | - (NSArray *)sortedResources:(BOOL)includeResourcesForMyUserExcludingMyself 592 | { 593 | // Add all the resouces from all the available users in the roster 594 | NSArray *availableUsers = [self unsortedAvailableUsers]; 595 | 596 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:[availableUsers count]]; 597 | 598 | NSUInteger i; 599 | for(i = 0; i < [availableUsers count]; i++) 600 | { 601 | XMPPUser *user = [availableUsers objectAtIndex:i]; 602 | 603 | [result addObjectsFromArray:[user unsortedResources]]; 604 | } 605 | 606 | if(includeResourcesForMyUserExcludingMyself) 607 | { 608 | // Now add all the available resources from our own user account (excluding ourselves) 609 | 610 | NSArray *myResources = [myUser unsortedResources]; 611 | 612 | for(i = 0; i < [myResources count]; i++) 613 | { 614 | XMPPResource *resource = [myResources objectAtIndex:i]; 615 | 616 | if(![myJID isEqual:[resource jid]]) 617 | { 618 | [result addObject:resource]; 619 | } 620 | } 621 | } 622 | 623 | return [result sortedArrayUsingSelector:@selector(compare:)]; 624 | } 625 | 626 | - (XMPPUser *)userForJID:(XMPPJID *)jid 627 | { 628 | XMPPUser *result = [roster objectForKey:[jid bareJID]]; 629 | 630 | if(result) 631 | { 632 | return result; 633 | } 634 | 635 | if([[jid bareJID] isEqual:[myJID bareJID]]) 636 | { 637 | return myUser; 638 | } 639 | 640 | return nil; 641 | } 642 | 643 | - (XMPPResource *)resourceForJID:(XMPPJID *)jid 644 | { 645 | XMPPUser *user = [self userForJID:jid]; 646 | 647 | return [user resourceForJID:jid]; 648 | } 649 | 650 | - (XMPPUser *)myUser 651 | { 652 | return [[myUser retain] autorelease]; 653 | } 654 | 655 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 656 | #pragma mark Sending Elements: 657 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 658 | 659 | - (void)sendElement:(NSXMLElement *)element 660 | { 661 | [xmppStream sendElement:element]; 662 | } 663 | 664 | - (void)sendElement:(NSXMLElement *)element andNotifyMe:(long)tag 665 | { 666 | [xmppStream sendElement:element andNotifyMe:tag]; 667 | } 668 | 669 | - (void)sendMessage:(NSString *)message toJID:(XMPPJID *)jid 670 | { 671 | NSXMLElement *body = [NSXMLElement elementWithName:@"body"]; 672 | [body setStringValue:message]; 673 | 674 | NSXMLElement *element = [NSXMLElement elementWithName:@"message"]; 675 | [element addAttributeWithName:@"type" stringValue:@"chat"]; 676 | [element addAttributeWithName:@"to" stringValue:[jid full]]; 677 | [element addChild:body]; 678 | 679 | [self sendElement:element]; 680 | } 681 | 682 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 683 | #pragma mark Delegate Helper Methods: 684 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 685 | 686 | - (void)onConnecting 687 | { 688 | [multicastDelegate xmppClientConnecting:self]; 689 | } 690 | 691 | - (void)onDidConnect 692 | { 693 | [multicastDelegate xmppClientDidConnect:self]; 694 | } 695 | 696 | - (void)onDidDisconnect 697 | { 698 | [multicastDelegate xmppClientDidDisconnect:self]; 699 | } 700 | 701 | - (void)onDidRegister 702 | { 703 | [multicastDelegate xmppClientDidRegister:self]; 704 | } 705 | 706 | - (void)onDidNotRegister:(NSXMLElement *)error 707 | { 708 | [multicastDelegate xmppClient:self didNotRegister:error]; 709 | } 710 | 711 | - (void)onDidAuthenticate 712 | { 713 | [multicastDelegate xmppClientDidAuthenticate:self]; 714 | } 715 | 716 | - (void)onDidNotAuthenticate:(NSXMLElement *)error 717 | { 718 | [multicastDelegate xmppClient:self didNotAuthenticate:error]; 719 | } 720 | 721 | - (void)onDidUpdateRoster 722 | { 723 | [multicastDelegate xmppClientDidUpdateRoster:self]; 724 | } 725 | 726 | - (void)onDidReceiveBuddyRequest:(XMPPJID *)jid 727 | { 728 | [multicastDelegate xmppClient:self didReceiveBuddyRequest:jid]; 729 | } 730 | 731 | - (void)onDidReceiveIQ:(XMPPIQ *)iq 732 | { 733 | [multicastDelegate xmppClient:self didReceiveIQ:iq]; 734 | } 735 | 736 | - (void)onDidReceiveMessage:(XMPPMessage *)message 737 | { 738 | [multicastDelegate xmppClient:self didReceiveMessage:message]; 739 | } 740 | 741 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 742 | #pragma mark XMPPStream Delegate Methods: 743 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 744 | 745 | - (void)xmppStreamDidOpen:(XMPPStream *)sender 746 | { 747 | [self onDidConnect]; 748 | 749 | if([self autoLogin]) 750 | { 751 | [self authenticateUser]; 752 | } 753 | } 754 | 755 | - (void)xmppStreamDidRegister:(XMPPStream *)sender 756 | { 757 | [self onDidRegister]; 758 | } 759 | 760 | - (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error 761 | { 762 | [self onDidNotRegister:error]; 763 | } 764 | 765 | - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender 766 | { 767 | // We're now connected and properly authenticated 768 | // Should we get accidentally disconnected we should automatically reconnect (if kAutoReconnect is set) 769 | [self setShouldReconnect:YES]; 770 | 771 | // Update myUser 772 | [myUser release]; 773 | myUser = [[XMPPUser alloc] initWithJID:myJID]; 774 | 775 | // Note: Order matters in the calls below. 776 | // We request the roster FIRST, because we need the roster before we can process any presence notifications. 777 | // We shouldn't receive any presence notification until we've set our presence to available. 778 | // 779 | // We notify the delegate(s) LAST because delegates may be sending their own custom 780 | // presence packets (and have set autoPresence to NO). The logical place for them to do so is in the 781 | // onDidAuthenticate method, so we try to request the roster before they start 782 | // sending any presence packets. 783 | // 784 | // In the event that we do receive any presence elements prior to receiving our roster, 785 | // we'll be forced to store them in the earlyPresenceElements array, and process them after we finally 786 | // get our roster list. 787 | 788 | if([self autoRoster]) 789 | { 790 | [self fetchRoster]; 791 | } 792 | if([self autoPresence]) 793 | { 794 | [self goOnline]; 795 | } 796 | 797 | [self onDidAuthenticate]; 798 | } 799 | 800 | - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error 801 | { 802 | [self onDidNotAuthenticate:error]; 803 | } 804 | 805 | - (void)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq 806 | { 807 | if([iq isRosterQuery]) 808 | { 809 | // Note: Some jabber servers send an iq element with a xmlns. 810 | // Because of the bug in Apple's NSXML (documented in our elementForName method), 811 | // it is important we specify the xmlns for the query. 812 | 813 | NSXMLElement *query = [iq elementForName:@"query" xmlns:@"jabber:iq:roster"]; 814 | NSArray *items = [query elementsForName:@"item"]; 815 | 816 | int i; 817 | for(i = 0; i < [items count]; i++) 818 | { 819 | NSXMLElement *item = (NSXMLElement *)[items objectAtIndex:i]; 820 | 821 | // Filter out items for users who aren't actually in our roster. 822 | // That is, those users who have requested to be our buddy, but we haven't approved yet. 823 | if([XMPPIQ isRosterItem:item]) 824 | { 825 | NSString *jidStr = [[item attributeForName:@"jid"] stringValue]; 826 | XMPPJID *jid = [XMPPJID jidWithString:jidStr]; 827 | 828 | NSString *subscription = [[item attributeForName:@"subscription"] stringValue]; 829 | 830 | if([subscription isEqualToString:@"remove"]) 831 | { 832 | [roster removeObjectForKey:jid]; 833 | } 834 | else 835 | { 836 | XMPPUser *user = [roster objectForKey:jid]; 837 | if(user) 838 | { 839 | [user updateWithItem:item]; 840 | } 841 | else 842 | { 843 | XMPPUser *newUser = [[XMPPUser alloc] initWithItem:item]; 844 | [roster setObject:newUser forKey:jid]; 845 | [newUser release]; 846 | } 847 | } 848 | } 849 | } 850 | 851 | [self onDidUpdateRoster]; 852 | 853 | if(![self hasRoster]) 854 | { 855 | // We should have our roster now 856 | [self setHasRoster:YES]; 857 | 858 | // Which means we can process any premature presence elements we received 859 | for(i = 0; i < [earlyPresenceElements count]; i++) 860 | { 861 | [self xmppStream:xmppStream didReceivePresence:[earlyPresenceElements objectAtIndex:i]]; 862 | } 863 | [earlyPresenceElements removeAllObjects]; 864 | } 865 | } 866 | else 867 | { 868 | [self onDidReceiveIQ:iq]; 869 | } 870 | } 871 | 872 | - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message 873 | { 874 | [self onDidReceiveMessage:message]; 875 | } 876 | 877 | - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence 878 | { 879 | if(![self hasRoster]) 880 | { 881 | // We received a presence notification, but we don't have a roster to apply it to yet. 882 | // We store the presence element until we get our roster. 883 | [earlyPresenceElements addObject:presence]; 884 | 885 | return; 886 | } 887 | 888 | if([[presence type] isEqualToString:@"subscribe"]) 889 | { 890 | XMPPUser *user = [roster objectForKey:[[presence from] bareJID]]; 891 | 892 | if(user && [self autoRoster]) 893 | { 894 | // Presence subscription request from someone who's already in our roster 895 | // Automatically approve 896 | 897 | NSXMLElement *response = [NSXMLElement elementWithName:@"presence"]; 898 | [response addAttributeWithName:@"to" stringValue:[[presence from] bare]]; 899 | [response addAttributeWithName:@"type" stringValue:@"subscribed"]; 900 | 901 | [xmppStream sendElement:response]; 902 | } 903 | else 904 | { 905 | // Presence subscription request from someone who's NOT in our roster 906 | 907 | [self onDidReceiveBuddyRequest:[presence from]]; 908 | } 909 | } 910 | else 911 | { 912 | XMPPUser *rosterUser = [roster objectForKey:[[presence from] bareJID]]; 913 | 914 | if(rosterUser) 915 | { 916 | [rosterUser updateWithPresence:presence]; 917 | } 918 | else if([[myJID bareJID] isEqual:[[presence from] bareJID]]) 919 | { 920 | [myUser updateWithPresence:presence]; 921 | } 922 | 923 | [self onDidUpdateRoster]; 924 | } 925 | } 926 | 927 | /** 928 | * There are two types of errors: TCP errors and XMPP errors. 929 | * If a TCP error is encountered (failure to connect, broken connection, etc) a standard NSError object is passed. 930 | * If an XMPP error is encountered ( for example) an NSXMLElement object is passed. 931 | * 932 | * Note that standard errors ( for example) are delivered normally, 933 | * via the other didReceive...: methods. 934 | **/ 935 | - (void)xmppStream:(XMPPStream *)xs didReceiveError:(id)error 936 | { 937 | if([error isKindOfClass:[NSError class]]) 938 | { 939 | [streamError autorelease]; 940 | streamError = [(NSError *)error copy]; 941 | 942 | if([xmppStream isAuthenticated]) 943 | { 944 | // We were fully connected to the XMPP server, but we've been disconnected for some reason. 945 | // We will wait for a few seconds or so, and then attempt to reconnect if possible 946 | [self performSelector:@selector(attemptReconnect:) withObject:nil afterDelay:4.0]; 947 | } 948 | } 949 | } 950 | 951 | - (void)xmppStreamDidClose:(XMPPStream *)sender 952 | { 953 | [roster removeAllObjects]; 954 | [self setHasRoster:NO]; 955 | 956 | [myUser release]; 957 | myUser = nil; 958 | 959 | [self onDidDisconnect]; 960 | } 961 | 962 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 963 | #pragma mark Reconnecting 964 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 965 | 966 | /** 967 | * This method is invoked a few seconds after a disconnection from the server, 968 | * or after we receive notification that we may once again have a working internet connection. 969 | * If we are still disconnected, it will attempt to reconnect if the network connection appears to be online. 970 | **/ 971 | - (void)attemptReconnect:(id)ignore 972 | { 973 | NSLog(@"XMPPClient: attempReconnect method called..."); 974 | 975 | if([xmppStream isDisconnected] && [self autoReconnect] && [self shouldReconnect]) 976 | { 977 | SCNetworkConnectionFlags reachabilityStatus; 978 | BOOL success = SCNetworkCheckReachabilityByName("www.deusty.com", &reachabilityStatus); 979 | 980 | if(success && (reachabilityStatus & kSCNetworkFlagsReachable)) 981 | { 982 | [self connect]; 983 | } 984 | } 985 | } 986 | 987 | - (void)networkStatusDidChange:(NSNotification *)notification 988 | { 989 | // The following information needs to be tested using multiple interfaces 990 | 991 | // If this is a notification of a lost internet connection, there won't be a userInfo 992 | // Otherwise, there will be...I think... 993 | 994 | if([notification userInfo]) 995 | { 996 | // We may have an internet connection now... 997 | // 998 | // If we were accidentally disconnected (user didn't tell us to disconnect) 999 | // then now would be a good time to attempt to reconnect. 1000 | if([self shouldReconnect]) 1001 | { 1002 | // We will wait for a few seconds or so, and then attempt to reconnect if possible 1003 | [self performSelector:@selector(attemptReconnect:) withObject:nil afterDelay:4.0]; 1004 | } 1005 | } 1006 | } 1007 | 1008 | @end 1009 | --------------------------------------------------------------------------------