#import "XMPPMessage.h" #import "XMPPJID.h" #import "NSXMLElement+XMPP.h" #import #if ! __has_feature(objc_arc) #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). #endif @implementation XMPPMessage #if DEBUG + (void)initialize { // We use the object_setClass method below to dynamically change the class from a standard NSXMLElement. // The size of the two classes is expected to be the same. // // If a developer adds instance methods to this class, bad things happen at runtime that are very hard to debug. // This check is here to aid future developers who may make this mistake. // // For Fearless And Experienced Objective-C Developers: // It may be possible to support adding instance variables to this class if you seriously need it. // To do so, try realloc'ing self after altering the class, and then initialize your variables. size_t superSize = class_getInstanceSize([NSXMLElement class]); size_t ourSize = class_getInstanceSize([XMPPMessage class]); if (superSize != ourSize) { NSLog(@"Adding instance variables to XMPPMessage is not currently supported!"); exit(15); } } #endif + (XMPPMessage *)messageFromElement:(NSXMLElement *)element { object_setClass(element, [XMPPMessage class]); return (XMPPMessage *)element; } + (XMPPMessage *)message { return [[XMPPMessage alloc] init]; } + (XMPPMessage *)messageWithType:(NSString *)type { return [[XMPPMessage alloc] initWithType:type to:nil]; } + (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)to { return [[XMPPMessage alloc] initWithType:type to:to]; } + (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid { return [[XMPPMessage alloc] initWithType:type to:jid elementID:eid]; } + (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement { return [[XMPPMessage alloc] initWithType:type to:jid elementID:eid child:childElement]; } + (XMPPMessage *)messageWithType:(NSString *)type elementID:(NSString *)eid { return [[XMPPMessage alloc] initWithType:type elementID:eid]; } + (XMPPMessage *)messageWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement { return [[XMPPMessage alloc] initWithType:type elementID:eid child:childElement]; } + (XMPPMessage *)messageWithType:(NSString *)type child:(NSXMLElement *)childElement { return [[XMPPMessage alloc] initWithType:type child:childElement]; } - (id)init { return [self initWithType:nil to:nil elementID:nil child:nil]; } - (id)initWithType:(NSString *)type { return [self initWithType:type to:nil elementID:nil child:nil]; } - (id)initWithType:(NSString *)type to:(XMPPJID *)jid { return [self initWithType:type to:jid elementID:nil child:nil]; } - (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid { return [self initWithType:type to:jid elementID:eid child:nil]; } - (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement { if ((self = [super initWithName:@"message"])) { if (type) [self addAttributeWithName:@"type" stringValue:type]; if (jid) [self addAttributeWithName:@"to" stringValue:[jid full]]; if (eid) [self addAttributeWithName:@"id" stringValue:eid]; if (childElement) [self addChild:childElement]; } return self; } - (id)initWithType:(NSString *)type elementID:(NSString *)eid { return [self initWithType:type to:nil elementID:eid child:nil]; } - (id)initWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement { return [self initWithType:type to:nil elementID:eid child:childElement]; } - (id)initWithType:(NSString *)type child:(NSXMLElement *)childElement { return [self initWithType:type to:nil elementID:nil child:childElement]; } - (id)initWithXMLString:(NSString *)string error:(NSError *__autoreleasing *)error { if((self = [super initWithXMLString:string error:error])){ self = [XMPPMessage messageFromElement:self]; } return self; } - (id)copyWithZone:(NSZone *)zone { NSXMLElement *element = [super copyWithZone:zone]; return [XMPPMessage messageFromElement:element]; } - (NSString *)type { return [[self attributeForName:@"type"] stringValue]; } - (NSString *)subject { return [[self elementForName:@"subject"] stringValue]; } - (NSString *)body { return [[self elementForName:@"body"] stringValue]; } - (NSString *)bodyForLanguage:(NSString *)language { NSString *bodyForLanguage = nil; for (NSXMLElement *bodyElement in [self elementsForName:@"body"]) { NSString *lang = [[bodyElement attributeForName:@"xml:lang"] stringValue]; // Openfire strips off the xml prefix if (lang == nil) { lang = [[bodyElement attributeForName:@"lang"] stringValue]; } if ([language isEqualToString:lang] || ([language length] == 0 && [lang length] == 0)) { bodyForLanguage = [bodyElement stringValue]; break; } } return bodyForLanguage; } - (NSString *)thread { return [[self elementForName:@"thread"] stringValue]; } - (void)addSubject:(NSString *)subject { NSXMLElement *subjectElement = [NSXMLElement elementWithName:@"subject" stringValue:subject]; [self addChild:subjectElement]; } - (void)addBody:(NSString *)body { NSXMLElement *bodyElement = [NSXMLElement elementWithName:@"body" stringValue:body]; [self addChild:bodyElement]; } - (void)addBody:(NSString *)body withLanguage:(NSString *)language { NSXMLElement *bodyElement = [NSXMLElement elementWithName:@"body" stringValue:body]; if ([language length]) { [bodyElement addAttributeWithName:@"xml:lang" stringValue:language]; } [self addChild:bodyElement]; } - (void)addThread:(NSString *)thread { NSXMLElement *threadElement = [NSXMLElement elementWithName:@"thread" stringValue:thread]; [self addChild:threadElement]; } - (BOOL)isChatMessage { return [[[self attributeForName:@"type"] stringValue] isEqualToString:@"chat"]; } - (BOOL)isChatMessageWithBody { if ([self isChatMessage]) { return [self isMessageWithBody]; } return NO; } - (BOOL)isErrorMessage { return [[[self attributeForName:@"type"] stringValue] isEqualToString:@"error"]; } - (NSError *)errorMessage { if (![self isErrorMessage]) { return nil; } NSXMLElement *error = [self elementForName:@"error"]; return [NSError errorWithDomain:@"urn:ietf:params:xml:ns:xmpp-stanzas" code:[error attributeIntValueForName:@"code"] userInfo:@{NSLocalizedDescriptionKey : [error compactXMLString]}]; } - (BOOL)isMessageWithBody { return ([self elementForName:@"body"] != nil); } @end