2016-02-24 16:56:39 +01:00

933 lines
20 KiB
Objective-C

//
// XMPPvCardTemp.m
// XEP-0054 vCard-temp
//
// Created by Eric Chamberlain on 3/9/11.
// Copyright 2011 RF.com. All rights reserved.
// Copyright 2010 Martin Morrison. All rights reserved.
//
#import "XMPPvCardTemp.h"
#import "XMPPLogging.h"
#import "XMPPDateTimeProfiles.h"
#import "NSData+XMPP.h"
#import <objc/runtime.h>
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
#if DEBUG
static const int xmppLogLevel = XMPP_LOG_LEVEL_ERROR;
#endif
NSString *const kXMPPNSvCardTemp = @"vcard-temp";
NSString *const kXMPPvCardTempElement = @"vCard";
@implementation XMPPvCardTemp
#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([XMPPvCardTemp class]);
if (superSize != ourSize)
{
XMPPLogError(@"Adding instance variables to XMPPvCardTemp is not currently supported!");
[DDLog flushLog];
exit(15);
}
}
#endif
+ (XMPPvCardTemp *)vCardTempFromElement:(NSXMLElement *)elem {
object_setClass(elem, [XMPPvCardTemp class]);
return (XMPPvCardTemp *)elem;
}
+ (XMPPvCardTemp *)vCardTemp{
NSXMLElement *vCardTempElement = [NSXMLElement elementWithName:kXMPPvCardTempElement xmlns:kXMPPNSvCardTemp];
return [XMPPvCardTemp vCardTempFromElement:vCardTempElement];
}
+ (XMPPvCardTemp *)vCardTempSubElementFromIQ:(XMPPIQ *)iq
{
if ([iq isResultIQ])
{
NSXMLElement *query = [iq elementForName:kXMPPvCardTempElement xmlns:kXMPPNSvCardTemp];
if (query)
{
return [self vCardTempFromElement:query];
}
}
return nil;
}
+ (XMPPvCardTemp *)vCardTempCopyFromIQ:(XMPPIQ *)iq
{
return [[self vCardTempSubElementFromIQ:iq] copy];
}
+ (XMPPIQ *)iqvCardRequestForJID:(XMPPJID *)jid {
XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:[jid bareJID] elementID:[XMPPStream generateUUID]];
NSXMLElement *vCardElem = [NSXMLElement elementWithName:kXMPPvCardTempElement xmlns:kXMPPNSvCardTemp];
[iq addChild:vCardElem];
return iq;
}
#pragma mark -
#pragma mark Identification Types
- (NSDate *)bday {
NSDate *bday = nil;
NSXMLElement *elem = [self elementForName:@"BDAY"];
if (elem != nil) {
bday = [NSDate dateWithXmppDateString:[elem stringValue]];
}
return bday;
}
- (void)setBday:(NSDate *)bday {
NSXMLElement *elem = [self elementForName:@"BDAY"];
if (elem == nil) {
elem = [NSXMLElement elementWithName:@"BDAY"];
[self addChild:elem];
}
[elem setStringValue:[bday xmppDateString]];
}
- (NSData *)photo {
NSData *decodedData = nil;
NSXMLElement *photo = [self elementForName:@"PHOTO"];
if (photo != nil) {
// There is a PHOTO element. It should have a TYPE and a BINVAL
//NSXMLElement *fileType = [photo elementForName:@"TYPE"];
NSXMLElement *binval = [photo elementForName:@"BINVAL"];
if (binval) {
NSData *base64Data = [[binval stringValue] dataUsingEncoding:NSASCIIStringEncoding];
decodedData = [base64Data xmpp_base64Decoded];
}
}
return decodedData;
}
- (void)setPhoto:(NSData *)data {
NSXMLElement *photo = [self elementForName:@"PHOTO"];
if(photo)
{
[self removeChildAtIndex:[[self children] indexOfObject:photo]];
}
if([data length])
{
NSXMLElement *photo = [NSXMLElement elementWithName:@"PHOTO"];
[self addChild:photo];
NSString *imageType = [data xmpp_imageType];
if([imageType length])
{
NSXMLElement *type = [NSXMLElement elementWithName:@"TYPE"];
[photo addChild:type];
[type setStringValue:imageType];
}
NSXMLElement *binval = [NSXMLElement elementWithName:@"BINVAL"];
[photo addChild:binval];
[binval setStringValue:[data xmpp_base64Encoded]];
}
}
- (NSString *)nickname {
return [[self elementForName:@"NICKNAME"] stringValue];
}
- (void)setNickname:(NSString *)nick {
XMPP_VCARD_SET_STRING_CHILD(nick, @"NICKNAME");
}
- (NSString *)formattedName {
return [[self elementForName:@"FN"] stringValue];
}
- (void)setFormattedName:(NSString *)fn {
XMPP_VCARD_SET_STRING_CHILD(fn, @"FN");
}
- (NSString *)familyName {
NSString *result = nil;
NSXMLElement *name = [self elementForName:@"N"];
if (name != nil) {
NSXMLElement *part = [name elementForName:@"FAMILY"];
if (part != nil) {
result = [part stringValue];
}
}
return result;
}
- (void)setFamilyName:(NSString *)family {
XMPP_VCARD_SET_N_CHILD(family, @"FAMILY");
}
- (NSString *)givenName {
NSString *result = nil;
NSXMLElement *name = [self elementForName:@"N"];
if (name != nil) {
NSXMLElement *part = [name elementForName:@"GIVEN"];
if (part != nil) {
result = [part stringValue];
}
}
return result;
}
- (void)setGivenName:(NSString *)given {
XMPP_VCARD_SET_N_CHILD(given, @"GIVEN");
}
- (NSString *)middleName {
NSString *result = nil;
NSXMLElement *name = [self elementForName:@"N"];
if (name != nil) {
NSXMLElement *part = [name elementForName:@"MIDDLE"];
if (part != nil) {
result = [part stringValue];
}
}
return result;
}
- (void)setMiddleName:(NSString *)middle {
XMPP_VCARD_SET_N_CHILD(middle, @"MIDDLE");
}
- (NSString *)prefix {
NSString *result = nil;
NSXMLElement *name = [self elementForName:@"N"];
if (name != nil) {
NSXMLElement *part = [name elementForName:@"PREFIX"];
if (part != nil) {
result = [part stringValue];
}
}
return result;
}
- (void)setPrefix:(NSString *)prefix {
XMPP_VCARD_SET_N_CHILD(prefix, @"PREFIX");
}
- (NSString *)suffix {
NSString *result = nil;
NSXMLElement *name = [self elementForName:@"N"];
if (name != nil) {
NSXMLElement *part = [name elementForName:@"SUFFIX"];
if (part != nil) {
result = [part stringValue];
}
}
return result;
}
- (void)setSuffix:(NSString *)suffix {
XMPP_VCARD_SET_N_CHILD(suffix, @"SUFFIX");
}
#pragma mark Delivery Addressing Types
- (NSArray *)addresses { return nil; }
- (void)addAddress:(XMPPvCardTempAdr *)adr { }
- (void)removeAddress:(XMPPvCardTempAdr *)adr { }
- (void)setAddresses:(NSArray *)adrs { }
- (void)clearAddresses { }
- (NSArray *)labels { return nil; }
- (void)addLabel:(XMPPvCardTempLabel *)label { }
- (void)removeLabel:(XMPPvCardTempLabel *)label { }
- (void)setLabels:(NSArray *)labels { }
- (void)clearLabels { }
- (NSArray *)telecomsAddresses { return nil; }
- (void)addTelecomsAddress:(XMPPvCardTempTel *)tel { }
- (void)removeTelecomsAddress:(XMPPvCardTempTel *)tel { }
- (void)setTelecomsAddresses:(NSArray *)tels { }
- (void)clearTelecomsAddresses { }
- (NSArray *)emailAddresses { return nil; }
- (void)addEmailAddress:(XMPPvCardTempEmail *)email { }
- (void)removeEmailAddress:(XMPPvCardTempEmail *)email { }
- (void)setEmailAddresses:(NSArray *)emails { }
- (void)clearEmailAddresses { }
- (XMPPJID *)jid {
XMPPJID *jid = nil;
NSXMLElement *elem = [self elementForName:@"JABBERID"];
if (elem != nil) {
jid = [XMPPJID jidWithString:[elem stringValue]];
}
return jid;
}
- (void)setJid:(XMPPJID *)jid {
NSXMLElement *elem = [self elementForName:@"JABBERID"];
if (elem == nil && jid != nil) {
elem = [NSXMLElement elementWithName:@"JABBERID"];
[self addChild:elem];
}
if (jid != nil) {
[elem setStringValue:[jid full]];
} else if (elem != nil) {
[self removeChildAtIndex:[[self children] indexOfObject:elem]];
}
}
- (NSString *)mailer {
return [[self elementForName:@"MAILER"] stringValue];
}
- (void)setMailer:(NSString *)mailer {
XMPP_VCARD_SET_STRING_CHILD(mailer, @"MAILER");
}
#pragma mark Geographical Types
- (NSTimeZone *)timeZone {
// Turns out this is hard. Being lazy for now (not like anyone actually uses this, right?)
NSXMLElement *tz = [self elementForName:@"TZ"];
if (tz != nil) {
// This is unlikely to work. :-(
return [NSTimeZone timeZoneWithName:[tz stringValue]];
} else {
return nil;
}
}
- (void)setTimeZone:(NSTimeZone *)tz {
NSXMLElement *elem = [self elementForName:@"TZ"];
if (elem == nil && tz != nil) {
elem = [NSXMLElement elementWithName:@"TZ"];
[self addChild:elem];
}
if (tz != nil) {
NSInteger offset = [tz secondsFromGMT];
[elem setStringValue:[NSString stringWithFormat:@"%02ld:%02ld",
(long)(offset / 3600), (long)((offset % 3600) / 60)]];
} else if (elem != nil) {
[self removeChildAtIndex:[[self children] indexOfObject:elem]];
}
}
- (CLLocation *)location {
CLLocation *loc = nil;
NSXMLElement *geo = [self elementForName:@"GEO"];
if (geo != nil) {
NSXMLElement *lat = [geo elementForName:@"LAT"];
NSXMLElement *lon = [geo elementForName:@"LON"];
loc = [[CLLocation alloc] initWithLatitude:[[lat stringValue] doubleValue] longitude:[[lon stringValue] doubleValue]];
}
return loc;
}
- (void)setLocation:(CLLocation *)geo {
NSXMLElement *elem = [self elementForName:@"GEO"];
NSXMLElement *lat;
NSXMLElement *lon;
if (geo != nil) {
CLLocationCoordinate2D coord = [geo coordinate];
if (elem == nil) {
elem = [NSXMLElement elementWithName:@"GEO"];
[self addChild:elem];
lat = [NSXMLElement elementWithName:@"LAT"];
[elem addChild:lat];
lon = [NSXMLElement elementWithName:@"LON"];
[elem addChild:lon];
} else {
lat = [elem elementForName:@"LAT"];
lon = [elem elementForName:@"LON"];
}
[lat setStringValue:[NSString stringWithFormat:@"%.6f", coord.latitude]];
[lon setStringValue:[NSString stringWithFormat:@"%.6f", coord.longitude]];
} else if (elem != nil) {
[self removeChildAtIndex:[[self children] indexOfObject:elem]];
}
}
#pragma mark Organizational Types
- (NSString *)title {
return [[self elementForName:@"TITLE"] stringValue];
}
- (void)setTitle:(NSString *)title {
XMPP_VCARD_SET_STRING_CHILD(title, @"TITLE");
}
- (NSString *)role {
return [[self elementForName:@"ROLE"] stringValue];
}
- (void)setRole:(NSString *)role {
XMPP_VCARD_SET_STRING_CHILD(role, @"ROLE");
}
- (NSData *)logo {
NSData *decodedData = nil;
NSXMLElement *logo = [self elementForName:@"LOGO"];
if (logo != nil) {
// There is a LOGO element. It should have a TYPE and a BINVAL
//NSXMLElement *fileType = [photo elementForName:@"TYPE"];
NSXMLElement *binval = [logo elementForName:@"BINVAL"];
if (binval) {
NSData *base64Data = [[binval stringValue] dataUsingEncoding:NSASCIIStringEncoding];
decodedData = [base64Data xmpp_base64Decoded];
}
}
return decodedData;
}
- (void)setLogo:(NSData *)data {
NSXMLElement *logo = [self elementForName:@"LOGO"];
if (logo == nil) {
logo = [NSXMLElement elementWithName:@"LOGO"];
[self addChild:logo];
}
NSXMLElement *binval = [logo elementForName:@"BINVAL"];
if (binval == nil) {
binval = [NSXMLElement elementWithName:@"BINVAL"];
[logo addChild:binval];
}
[binval setStringValue:[data xmpp_base64Encoded]];
}
- (XMPPvCardTemp *)agent {
XMPPvCardTemp *agent = nil;
NSXMLElement *elem = [self elementForName:@"AGENT"];
if (elem != nil) {
agent = [XMPPvCardTemp vCardTempFromElement:elem];
}
return agent;
}
- (void)setAgent:(XMPPvCardTemp *)agent {
NSXMLElement *elem = [self elementForName:@"AGENT"];
if (elem != nil) {
[self removeChildAtIndex:[[self children] indexOfObject:elem]];
}
if (agent != nil) {
[self addChild:agent];
}
}
- (NSString *)orgName {
NSString *result = nil;
NSXMLElement *org = [self elementForName:@"ORG"];
if (org != nil) {
NSXMLElement *orgname = [org elementForName:@"ORGNAME"];
if (orgname != nil) {
result = [orgname stringValue];
}
}
return result;
}
- (void)setOrgName:(NSString *)orgname {
NSXMLElement *org = [self elementForName:@"ORG"];
NSXMLElement *elem = nil;
if (orgname != nil) {
if (org == nil) {
org = [NSXMLElement elementWithName:@"ORG"];
[self addChild:org];
} else {
elem = [org elementForName:@"ORGNAME"];
}
if (elem == nil) {
elem = [NSXMLElement elementWithName:@"ORGNAME"];
[org addChild:elem];
}
[elem setStringValue:orgname];
} else if (org != nil) {
// This implicitly removes all orgunits too, as per the spec
[self removeChildAtIndex:[[self children] indexOfObject:org]];
}
}
- (NSArray *)orgUnits {
NSArray *result = nil;
NSXMLElement *org = [self elementForName:@"ORG"];
if (org != nil) {
NSArray *elems = [org elementsForName:@"ORGUNIT"];
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:[elems count]];
for (NSXMLElement *elem in elems) {
[arr addObject:[elem stringValue]];
}
result = [NSArray arrayWithArray:arr];
}
return result;
}
- (void)setOrgUnits:(NSArray *)orgunits {
NSXMLElement *org = [self elementForName:@"ORG"];
// If there is no org, then there is nothing to do (need ORGNAME first)
if (org != nil) {
NSArray *elems = [org elementsForName:@"ORGUNIT"];
for (NSXMLElement *elem in elems) {
[org removeChildAtIndex:[[org children] indexOfObject:elem]];
}
for (NSString *unit in orgunits) {
NSXMLElement *elem = [NSXMLElement elementWithName:@"ORGUNIT"];
[elem setStringValue:unit];
[org addChild:elem];
}
}
}
#pragma mark Explanatory Types
- (NSArray *)categories {
NSArray *result = nil;
NSXMLElement *categories = [self elementForName:@"CATEGORIES"];
if (categories != nil) {
NSArray *elems = [categories elementsForName:@"KEYWORD"];
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:[elems count]];
for (NSXMLElement *elem in elems) {
[arr addObject:[elem stringValue]];
}
result = [NSArray arrayWithArray:arr];
}
return result;
}
- (void)setCategories:(NSArray *)categories {
NSXMLElement *cat = [self elementForName:@"CATEGORIES"];
if (categories != nil) {
if (cat == nil) {
cat = [NSXMLElement elementWithName:@"CATEGORIES"];
[self addChild:cat];
}
NSArray *elems = [cat elementsForName:@"KEYWORD"];
for (NSXMLElement *elem in elems) {
[cat removeChildAtIndex:[[cat children] indexOfObject:elem]];
}
for (NSString *kw in categories) {
NSXMLElement *elem = [NSXMLElement elementWithName:@"KEYWORD"];
[elem setStringValue:kw];
[cat addChild:elem];
}
} else if (cat != nil) {
[self removeChildAtIndex:[[self children] indexOfObject:cat]];
}
}
- (NSString *)note {
return [[self elementForName:@"NOTE"] stringValue];
}
- (void)setNote:(NSString *)note {
XMPP_VCARD_SET_STRING_CHILD(note, @"NOTE");
}
- (NSString *)prodid {
return [[self elementForName:@"PRODID"] stringValue];
}
- (void)setProdid:(NSString *)prodid {
XMPP_VCARD_SET_STRING_CHILD(prodid, @"PRODID");
}
- (NSDate *)revision {
NSDate *rev = nil;
NSXMLElement *elem = [self elementForName:@"REV"];
if (elem != nil) {
rev = [NSDate dateWithXmppDateTimeString:[elem stringValue]];
}
return rev;
}
- (void)setRevision:(NSDate *)rev {
NSXMLElement *elem = [self elementForName:@"REV"];
if (elem == nil) {
elem = [NSXMLElement elementWithName:@"REV"];
[self addChild:elem];
}
[elem setStringValue:[rev xmppDateTimeString]];
}
- (NSString *)sortString {
return [[self elementForName:@"SORT-STRING"] stringValue];
}
- (void)setSortString:(NSString *)sortString {
XMPP_VCARD_SET_STRING_CHILD(sortString, @"SORT-STRING");
}
- (NSString *)phoneticSound {
NSString *phon = nil;
NSXMLElement *sound = [self elementForName:@"SOUND"];
if (sound != nil) {
NSXMLElement *elem = [sound elementForName:@"PHONETIC"];
if (elem != nil) {
phon = [elem stringValue];
}
}
return phon;
}
- (void)setPhoneticSound:(NSString *)phonetic {
NSXMLElement *sound = [self elementForName:@"SOUND"];
NSXMLElement *elem = nil;
if (sound == nil && phonetic != nil) {
sound = [NSXMLElement elementWithName:@"SOUND"];
[self addChild:sound];
}
if (sound != nil) {
elem = [sound elementForName:@"PHONETIC"];
if (elem != nil && phonetic != nil) {
elem = [NSXMLElement elementWithName:@"PHONETIC"];
[sound addChild:elem];
}
}
if (phonetic != nil) {
[elem setStringValue:phonetic];
} else if (sound != nil) {
[self removeChildAtIndex:[[self children] indexOfObject:phonetic]];
}
}
- (NSData *)sound {
NSData *decodedData = nil;
NSXMLElement *sound = [self elementForName:@"SOUND"];
if (sound != nil) {
NSXMLElement *binval = [sound elementForName:@"BINVAL"];
if (binval) {
NSData *base64Data = [[binval stringValue] dataUsingEncoding:NSASCIIStringEncoding];
decodedData = [base64Data xmpp_base64Decoded];
}
}
return decodedData;
}
- (void)setSound:(NSData *)data {
NSXMLElement *sound = [self elementForName:@"SOUND"];
if (sound == nil) {
sound = [NSXMLElement elementWithName:@"SOUND"];
[self addChild:sound];
}
NSXMLElement *binval = [sound elementForName:@"BINVAL"];
if (binval == nil) {
binval = [NSXMLElement elementWithName:@"BINVAL"];
[sound addChild:binval];
}
[binval setStringValue:[data xmpp_base64Encoded]];
}
- (NSString *)uid {
return [[self elementForName:@"UID"] stringValue];
}
- (void)setUid:(NSString *)uid {
XMPP_VCARD_SET_STRING_CHILD(uid, @"UID");
}
- (NSString *)url {
return [[self elementForName:@"URL"] stringValue];
}
- (void)setUrl:(NSString *)url {
XMPP_VCARD_SET_STRING_CHILD(url, @"URL");
}
- (NSString *)version {
return [self attributeStringValueForName:@"version"];
}
- (void)setVersion:(NSString *)version {
[self addAttributeWithName:@"version" stringValue:version];
}
- (NSString *)desc {
return [[self elementForName:@"DESC"] stringValue];
}
- (void)setDesc:(NSString *)desc {
XMPP_VCARD_SET_STRING_CHILD(desc, @"DESC");
}
#pragma mark Security Types
- (XMPPvCardTempClass)privacyClass {
XMPPvCardTempClass priv = XMPPvCardTempClassNone;
NSXMLElement *elem = [self elementForName:@"CLASS"];
if (elem != nil) {
if ([elem elementForName:@"PUBLIC"] != nil) {
priv = XMPPvCardTempClassPublic;
} else if ([elem elementForName:@"PRIVATE"] != nil) {
priv = XMPPvCardTempClassPrivate;
} else if ([elem elementForName:@"CONFIDENTIAL"] != nil) {
priv = XMPPvCardTempClassConfidential;
}
}
return priv;
}
- (void)setPrivacyClass:(XMPPvCardTempClass)privacyClass {
NSXMLElement *elem = [self elementForName:@"CLASS"];
if (elem == nil && privacyClass != XMPPvCardTempClassNone) {
elem = [NSXMLElement elementWithName:@"CLASS"];
}
if (elem != nil) {
for (NSString *cls in @[@"PUBLIC", @"PRIVATE", @"CONFIDENTIAL"]) {
NSXMLElement *priv = [elem elementForName:cls];
if (priv != nil) {
[elem removeChildAtIndex:[[elem children] indexOfObject:priv]];
}
}
switch (privacyClass) {
case XMPPvCardTempClassPublic:
[elem addChild:[NSXMLElement elementWithName:@"PUBLIC"]];
break;
case XMPPvCardTempClassPrivate:
[elem addChild:[NSXMLElement elementWithName:@"PRIVATE"]];
break;
case XMPPvCardTempClassConfidential:
[elem addChild:[NSXMLElement elementWithName:@"CONFIDENTIAL"]];
break;
default:
case XMPPvCardTempClassNone:
// Remove the whole element
[self removeChildAtIndex:[[self children] indexOfObject:elem]];
break;
}
}
}
- (NSData *)key { return nil; }
- (void)setKey:(NSData *)key { }
- (NSString *)keyType {
NSString *typ = nil;
NSXMLElement *key = [self elementForName:@"KEY"];
if (key != nil) {
NSXMLElement *elem = [key elementForName:@"TYPE"];
if (elem != nil) {
typ = [elem stringValue];
}
}
return typ;
}
- (void)setKeyType:(NSString *)type {
NSXMLElement *key = [self elementForName:@"KEY"];
NSXMLElement *elem = nil;
if (key == nil && type != nil) {
key = [NSXMLElement elementWithName:@"KEY"];
[self addChild:key];
}
if (key != nil) {
elem = [key elementForName:@"TYPE"];
if (elem != nil && type != nil) {
elem = [NSXMLElement elementWithName:@"TYPE"];
[key addChild:elem];
}
}
if (type != nil) {
[elem setStringValue:type];
} else if (key != nil) {
[self removeChildAtIndex:[[self children] indexOfObject:key]];
}
}
@end