/* * Copyright (c) 2011, 2012, 2013, 2019, 2021, Jonathan Schleifer <js@nil.im> * Copyright (c) 2011, 2012, 2013, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * * 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. */ #include "config.h" #include <string.h> #include <stringprep.h> #import "XMPPJID.h" #import "XMPPExceptions.h" @implementation XMPPJID @synthesize node = _node, domain = _domain, resource = _resource; + (instancetype)JID { return [[[self alloc] init] autorelease]; } + (instancetype)JIDWithString: (OFString *)string { return [[[self alloc] initWithString: string] autorelease]; } - (instancetype)initWithString: (OFString *)string { self = [super init]; @try { size_t nodesep, resourcesep; if (string == nil) @throw [OFInvalidArgumentException exception]; nodesep = [string rangeOfString: @"@"].location; resourcesep = [string rangeOfString: @"/"].location; if (nodesep == SIZE_MAX) self.node = nil; else self.node = [string substringWithRange: OFRangeMake(0, nodesep)]; if (resourcesep == SIZE_MAX) { self.resource = nil; resourcesep = string.length; } else { OFRange range = OFRangeMake(resourcesep + 1, string.length - resourcesep - 1); self.resource = [string substringWithRange: range]; } self.domain = [string substringWithRange: OFRangeMake(nodesep + 1, resourcesep - nodesep - 1)]; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [_node release]; [_domain release]; [_resource release]; [super dealloc]; } - copy { XMPPJID *new = [[XMPPJID alloc] init]; @try { new->_node = [_node copy]; new->_domain = [_domain copy]; new->_resource = [_resource copy]; } @catch (id e) { [new release]; @throw e; } return new; } - (void)setNode: (OFString *)node { OFString *old = _node; char *nodepart; Stringprep_rc rc; if (node == nil) { [old release]; _node = nil; return; } if (((rc = stringprep_profile(node.UTF8String, &nodepart, "Nodeprep", 0)) != STRINGPREP_OK) || (nodepart[0] == '\0') || (strlen(nodepart) > 1023)) @throw [XMPPStringPrepFailedException exceptionWithConnection: nil profile: @"Nodeprep" string: node]; @try { _node = [[OFString alloc] initWithUTF8String: nodepart]; } @finally { free(nodepart); } [old release]; } - (void)setDomain: (OFString *)domain { OFString *old = _domain; char *srv; Stringprep_rc rc; if (((rc = stringprep_profile(domain.UTF8String, &srv, "Nameprep", 0)) != STRINGPREP_OK) || (srv[0] == '\0') || (strlen(srv) > 1023)) @throw [XMPPStringPrepFailedException exceptionWithConnection: nil profile: @"Nameprep" string: domain]; @try { _domain = [[OFString alloc] initWithUTF8String: srv]; } @finally { free(srv); } [old release]; } - (void)setResource: (OFString *)resource { OFString *old = _resource; char *res; Stringprep_rc rc; if (resource == nil) { [old release]; _resource = nil; return; } if (((rc = stringprep_profile(resource.UTF8String, &res, "Resourceprep", 0)) != STRINGPREP_OK) || (res[0] == '\0') || (strlen(res) > 1023)) @throw [XMPPStringPrepFailedException exceptionWithConnection: nil profile: @"Resourceprep" string: resource]; @try { _resource = [[OFString alloc] initWithUTF8String: res]; } @finally { free(res); } [old release]; } - (OFString *)bareJID { if (_node != nil) return [OFString stringWithFormat: @"%@@%@", _node, _domain]; else return [OFString stringWithFormat: @"%@", _domain]; } - (OFString *)fullJID { /* If we don't have a resource, the full JID is equal to the bare JID */ if (_resource == nil) return self.bareJID; if (_node != nil) return [OFString stringWithFormat: @"%@@%@/%@", _node, _domain, _resource]; else return [OFString stringWithFormat: @"%@/%@", _domain, _resource]; } - (OFString *)description { return [self fullJID]; } - (bool)isEqual: (id)object { XMPPJID *JID; if (object == self) return true; if (![object isKindOfClass: [XMPPJID class]]) return false; JID = object; // Node and resource may be nil if ((_node == JID->_node || [_node isEqual: JID->_node]) && [_domain isEqual: JID->_domain] && (_resource == JID->_resource || [_resource isEqual: JID->_resource])) return true; return false; } - (unsigned long)hash { unsigned long hash; OFHashInit(&hash); OFHashAddHash(&hash, _node.hash); OFHashAddHash(&hash, _domain.hash); OFHashAddHash(&hash, _resource.hash); OFHashFinalize(&hash); return hash; } @end