467 lines
17 KiB
Objective-C
467 lines
17 KiB
Objective-C
//
|
|
// XMPPIQ.m
|
|
// Monal
|
|
//
|
|
// Created by Anurodh Pokharel on 6/30/13.
|
|
//
|
|
//
|
|
|
|
#import "XMPPIQ.h"
|
|
#import "XMPPDataForm.h"
|
|
#import "HelperTools.h"
|
|
#import "SignalPreKey.h"
|
|
#import "MLContact.h"
|
|
|
|
@class MLContact;
|
|
|
|
NSString* const kiqGetType = @"get";
|
|
NSString* const kiqSetType = @"set";
|
|
NSString* const kiqResultType = @"result";
|
|
NSString* const kiqErrorType = @"error";
|
|
|
|
@implementation XMPPIQ
|
|
|
|
-(id) initInternalWithId:(NSString*) iqid andType:(NSString*) iqType
|
|
{
|
|
self = [super initWithElement:@"iq"];
|
|
[self setXMLNS:@"jabber:client"];
|
|
self.id = iqid;
|
|
if(iqType)
|
|
self.attributes[@"type"] = iqType;
|
|
return self;
|
|
}
|
|
|
|
-(id) initWithType:(NSString*) iqType
|
|
{
|
|
return [self initInternalWithId:[[NSUUID UUID] UUIDString] andType:iqType];
|
|
}
|
|
|
|
-(id) initWithType:(NSString*) iqType to:(NSString*) to
|
|
{
|
|
self = [self initWithType:iqType];
|
|
if(to)
|
|
[self setiqTo:to];
|
|
return self;
|
|
}
|
|
|
|
-(id) initAsResponseTo:(XMPPIQ*) iq
|
|
{
|
|
self = [self initInternalWithId:[iq findFirst:@"/@id"] andType:kiqResultType];
|
|
if(iq.from)
|
|
[self setiqTo:iq.from];
|
|
return self;
|
|
}
|
|
|
|
-(id) initAsErrorTo:(XMPPIQ*) iq
|
|
{
|
|
self = [self initInternalWithId:[iq findFirst:@"/@id"] andType:kiqErrorType];
|
|
if(iq.from)
|
|
[self setiqTo:iq.from];
|
|
return self;
|
|
}
|
|
|
|
#pragma mark iq set
|
|
|
|
// direct push registration at xmpp server without registration at appserver
|
|
-(void) setPushEnableWithNode:(NSString*) node onAppserver:(NSString*) jid
|
|
{
|
|
NSMutableString* pushModule = [NSMutableString new];
|
|
#ifdef IS_ALPHA
|
|
[pushModule appendString:@"monalAlpha"];
|
|
#else //IS_ALPHA
|
|
#if TARGET_OS_MACCATALYST && defined(IS_QUICKSY)
|
|
[pushModule appendString:@"quicksyProdCatalyst"];
|
|
#elif TARGET_OS_MACCATALYST
|
|
[pushModule appendString:@"monalProdCatalyst"];
|
|
#elif defined(IS_QUICKSY)
|
|
[pushModule appendString:@"quicksyProdiOS"];
|
|
#else
|
|
[pushModule appendString:@"monalProdiOS"];
|
|
#endif
|
|
#endif
|
|
|
|
if([[HelperTools defaultsDB] boolForKey:@"isSandboxAPNS"])
|
|
{
|
|
[pushModule appendString:@"-sandbox"];
|
|
DDLogInfo(@"Detected APNS sandbox, using sandbox push module: %@", pushModule);
|
|
}
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"enable" andNamespace:@"urn:xmpp:push:0" withAttributes:@{
|
|
@"jid": jid,
|
|
@"node": node
|
|
} andChildren:@[
|
|
[[XMPPDataForm alloc] initWithType:@"submit" formType:@"http://jabber.org/protocol/pubsub#publish-options" andDictionary:@{
|
|
@"pushModule": pushModule
|
|
}]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setPushDisable:(NSString*) node onPushServer:(NSString*) pushServer
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"disable" andNamespace:@"urn:xmpp:push:0" withAttributes:@{
|
|
@"jid": pushServer,
|
|
@"node": node
|
|
} andChildren:@[] andData:nil]];
|
|
}
|
|
|
|
-(void) setBindWithResource:(NSString*) resource
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"bind" andNamespace:@"urn:ietf:params:xml:ns:xmpp-bind" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"resource" andData:resource]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setMucListQueryFor:(NSString*) listType
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/muc#admin" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"item" withAttributes:@{@"affiliation": listType} andChildren:@[] andData:nil]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setAdhocDiscoNode
|
|
{
|
|
MLXMLNode* queryNode = [[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/disco#items" withAttributes:@{
|
|
@"node": @"http://jabber.org/protocol/commands",
|
|
} andChildren:@[] andData:nil];
|
|
[self addChildNode:queryNode];
|
|
}
|
|
|
|
-(void) setDiscoInfoNode
|
|
{
|
|
MLXMLNode* queryNode = [[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/disco#info"];
|
|
[self addChildNode:queryNode];
|
|
}
|
|
|
|
-(void) setDiscoItemNode
|
|
{
|
|
MLXMLNode* queryNode = [[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/disco#items"];
|
|
[self addChildNode:queryNode];
|
|
}
|
|
|
|
-(void) setDiscoInfoWithFeatures:(NSSet*) features identity:(MLXMLNode*) identity andNode:(NSString*) node
|
|
{
|
|
MLXMLNode* queryNode = [[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/disco#info"];
|
|
if(node)
|
|
[queryNode.attributes setObject:node forKey:@"node"];
|
|
|
|
for(NSString* feature in features)
|
|
{
|
|
MLXMLNode* featureNode = [[MLXMLNode alloc] initWithElement:@"feature"];
|
|
featureNode.attributes[@"var"] = feature;
|
|
[queryNode addChildNode:featureNode];
|
|
}
|
|
|
|
[queryNode addChildNode:identity];
|
|
|
|
[self addChildNode:queryNode];
|
|
}
|
|
|
|
-(void) setiqTo:(NSString*) to
|
|
{
|
|
if(to)
|
|
self.to = to;
|
|
}
|
|
|
|
-(void) setPing
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"ping" andNamespace:@"urn:xmpp:ping"]];
|
|
}
|
|
|
|
-(void) setPurgeOfflineStorage
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"offline" andNamespace:@"http://jabber.org/protocol/offline" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"purge"]
|
|
] andData:nil]];
|
|
}
|
|
|
|
#pragma mark - MAM
|
|
|
|
-(void) mamArchivePref
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"prefs" andNamespace:@"urn:xmpp:mam:2"]];
|
|
}
|
|
|
|
-(void) updateMamArchivePrefDefault:(NSString *) pref
|
|
{
|
|
/**
|
|
pref is aways, never or roster
|
|
*/
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"prefs" andNamespace:@"urn:xmpp:mam:2" withAttributes:@{@"default": pref} andChildren:@[] andData:nil]];
|
|
}
|
|
|
|
-(void) setMAMQueryLatestMessagesForJid:(NSString* _Nullable) jid before:(NSString* _Nullable) uid
|
|
{
|
|
//set iq id to mam query id
|
|
self.id = [NSString stringWithFormat:@"MLhistory:%@", [[NSUUID UUID] UUIDString]];
|
|
XMPPDataForm* form = [[XMPPDataForm alloc] initWithType:@"submit" andFormType:@"urn:xmpp:mam:2"];
|
|
if(jid)
|
|
form[@"with"] = jid;
|
|
MLXMLNode* queryNode = [[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"urn:xmpp:mam:2" withAttributes:@{
|
|
@"queryid": self.id
|
|
} andChildren:@[
|
|
form,
|
|
[[MLXMLNode alloc] initWithElement:@"set" andNamespace:@"http://jabber.org/protocol/rsm" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"max" andData:@"50"],
|
|
[[MLXMLNode alloc] initWithElement:@"before" andData:uid]
|
|
] andData:nil]
|
|
] andData:nil];
|
|
[self addChildNode:queryNode];
|
|
}
|
|
|
|
-(void) setMAMQueryForLatestId
|
|
{
|
|
//set iq id to mam query id
|
|
self.id = [NSString stringWithFormat:@"MLignore:%@", [[NSUUID UUID] UUIDString]];
|
|
MLXMLNode* queryNode = [[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"urn:xmpp:mam:2" withAttributes:@{
|
|
@"queryid": self.id
|
|
} andChildren:@[
|
|
[[XMPPDataForm alloc] initWithType:@"submit" formType:@"urn:xmpp:mam:2" andDictionary:@{
|
|
@"end": [HelperTools generateDateTimeString:[NSDate date]]
|
|
}],
|
|
[[MLXMLNode alloc] initWithElement:@"set" andNamespace:@"http://jabber.org/protocol/rsm" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"max" andData:@"1"],
|
|
[[MLXMLNode alloc] initWithElement:@"before"]
|
|
] andData:nil]
|
|
] andData:nil];
|
|
[self addChildNode:queryNode];
|
|
}
|
|
|
|
-(void) setMAMQueryAfter:(NSString*) uid
|
|
{
|
|
//set iq id to mam query id
|
|
self.id = [NSString stringWithFormat:@"MLcatchup:%@", [[NSUUID UUID] UUIDString]];
|
|
MLXMLNode* queryNode = [[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"urn:xmpp:mam:2" withAttributes:@{
|
|
@"queryid": self.id
|
|
} andChildren:@[
|
|
[[XMPPDataForm alloc] initWithType:@"submit" andFormType:@"urn:xmpp:mam:2"],
|
|
[[MLXMLNode alloc] initWithElement:@"set" andNamespace:@"http://jabber.org/protocol/rsm" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"max" andData:@"50"],
|
|
[[MLXMLNode alloc] initWithElement:@"after" andData:uid]
|
|
] andData:nil]
|
|
] andData:nil];
|
|
[self addChildNode:queryNode];
|
|
}
|
|
|
|
-(void) setMAMQueryAfterTimestamp:(NSDate* _Nullable) timestamp
|
|
{
|
|
//set iq id to mam query id
|
|
self.id = [NSString stringWithFormat:@"MLcatchup:%@", [[NSUUID UUID] UUIDString]];
|
|
MLXMLNode* queryNode = [[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"urn:xmpp:mam:2" withAttributes:@{
|
|
@"queryid": self.id
|
|
} andChildren:@[
|
|
//query whole archive if the timestamp is nil (e.g. we never received any message contained in this archive
|
|
//nor did our stanzaid priming archive query succeed)
|
|
(timestamp==nil ?
|
|
[[XMPPDataForm alloc] initWithType:@"submit" andFormType:@"urn:xmpp:mam:2"]
|
|
:
|
|
[[XMPPDataForm alloc] initWithType:@"submit" formType:@"urn:xmpp:mam:2" andDictionary:@{
|
|
@"start": [HelperTools generateDateTimeString:timestamp]
|
|
}]
|
|
),
|
|
[[MLXMLNode alloc] initWithElement:@"set" andNamespace:@"http://jabber.org/protocol/rsm" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"max" andData:@"50"]
|
|
] andData:nil]
|
|
] andData:nil];
|
|
[self addChildNode:queryNode];
|
|
|
|
#ifdef IS_ALPHA
|
|
if(timestamp == nil)
|
|
showXMLErrorOnAlpha(nil, self, @"setMAMQueryAfterTimestamp: called with nil timestamp!");
|
|
#endif
|
|
}
|
|
|
|
-(void) setRemoveFromRoster:(MLContact*) contact
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"jabber:iq:roster" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"item" withAttributes:@{
|
|
@"jid": contact.contactJid,
|
|
@"subscription": @"remove"
|
|
} andChildren:@[] andData:nil]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setUpdateRosterItem:(MLContact* _Nonnull) contact withName:(NSString* _Nonnull) name
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"jabber:iq:roster" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"item" withAttributes:@{
|
|
@"jid": contact.contactJid,
|
|
@"name": name,
|
|
} andChildren:@[] andData:nil]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setRosterRequest:(NSString*) version
|
|
{
|
|
NSDictionary* attrs = @{};
|
|
if(version && ![version isEqual:@""])
|
|
attrs = @{@"ver": version};
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"jabber:iq:roster" withAttributes:attrs andChildren:@[] andData:nil]];
|
|
}
|
|
|
|
-(void) setVersion
|
|
{
|
|
NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"jabber:iq:version" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"name" andData:@"Monal"],
|
|
#if TARGET_OS_MACCATALYST
|
|
[[MLXMLNode alloc] initWithElement:@"os" andData:[NSString stringWithFormat:@"macOS %lu", osVersion.majorVersion]],
|
|
#else
|
|
[[MLXMLNode alloc] initWithElement:@"os" andData:[NSString stringWithFormat:@"iOS %lu", osVersion.majorVersion]],
|
|
#endif
|
|
[[MLXMLNode alloc] initWithElement:@"version" andData:[HelperTools appBuildVersionInfoFor:MLVersionTypeIQ]]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setBlocked:(BOOL) blocked forJid:(NSString* _Nonnull) blockedJid
|
|
{
|
|
MLXMLNode* blockNode = [[MLXMLNode alloc] initWithElement:(blocked ? @"block" : @"unblock") andNamespace:@"urn:xmpp:blocking"];
|
|
|
|
MLXMLNode* itemNode = [[MLXMLNode alloc] initWithElement:@"item"];
|
|
[itemNode.attributes setObject:blockedJid forKey:@"jid"];
|
|
[blockNode addChildNode:itemNode];
|
|
|
|
[self addChildNode:blockNode];
|
|
}
|
|
|
|
-(void) requestBlockList
|
|
{
|
|
MLXMLNode* blockNode = [[MLXMLNode alloc] initWithElement:@"blocklist" andNamespace:@"urn:xmpp:blocking"];
|
|
[self addChildNode:blockNode];
|
|
}
|
|
|
|
-(void) httpUploadforFile:(NSString *) file ofSize:(NSNumber *) filesize andContentType:(NSString *) contentType
|
|
{
|
|
MLXMLNode* requestNode = [[MLXMLNode alloc] initWithElement:@"request" andNamespace:@"urn:xmpp:http:upload:0"];
|
|
requestNode.attributes[@"filename"] = file;
|
|
requestNode.attributes[@"size"] = [NSString stringWithFormat:@"%@", filesize];
|
|
requestNode.attributes[@"content-type"] = contentType;
|
|
[self addChildNode:requestNode];
|
|
}
|
|
|
|
|
|
#pragma mark iq get
|
|
|
|
-(void) getEntitySoftwareVersionInfo
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"jabber:iq:version"]];
|
|
}
|
|
|
|
#pragma mark MUC
|
|
|
|
-(void) setGetRoomConfig
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/muc#owner"]];
|
|
}
|
|
|
|
-(void) setRoomConfig:(XMPPDataForm*) configForm
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/muc#owner" withAttributes:@{} andChildren:@[configForm] andData:nil]];
|
|
}
|
|
|
|
-(void) setInstantRoom
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/muc#owner" withAttributes:@{} andChildren:@[
|
|
[[XMPPDataForm alloc] initWithType:@"submit" andFormType:@"http://jabber.org/protocol/muc#roomconfig"]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setRemoveVcardAvatar
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"vCard" andNamespace:@"vcard-temp" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"PHOTO" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"PHOTO" andData:nil],
|
|
[[MLXMLNode alloc] initWithElement:@"BINVAL" andData:nil],
|
|
] andData:nil]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setVcardAvatarWithData:(NSData*) imageData andType:(NSString*) imageType
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"vCard" andNamespace:@"vcard-temp" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"PHOTO" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"PHOTO" andData:imageType],
|
|
[[MLXMLNode alloc] initWithElement:@"BINVAL" andData:[HelperTools encodeBase64WithData:imageData]],
|
|
] andData:nil]
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setVcardQuery
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"vCard" andNamespace:@"vcard-temp"]];
|
|
}
|
|
|
|
#pragma mark - Account Management
|
|
|
|
-(void) submitRegToken:(NSString*) token
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"preauth" andNamespace:@"urn:xmpp:pars:0" withAttributes:@{
|
|
@"token": token
|
|
} andChildren:@[] andData:nil]];
|
|
}
|
|
|
|
-(void) getRegistrationFields
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:kRegisterNameSpace]];
|
|
}
|
|
|
|
/*
|
|
This is really hardcoded for yax.im might work for others
|
|
*/
|
|
-(void) registerUser:(NSString*) user withPassword:(NSString*) newPass captcha:(NSString* _Nullable) captcha andHiddenFields:(NSDictionary* _Nullable) hiddenFields
|
|
{
|
|
//if no reg form was provided both of these are nil --> don't try to send a reg form in our response
|
|
if(captcha != nil && hiddenFields != nil)
|
|
{
|
|
NSMutableDictionary* fields = [NSMutableDictionary dictionaryWithDictionary:@{
|
|
@"username": user,
|
|
@"password": newPass,
|
|
}];
|
|
if(captcha)
|
|
fields[@"ocr"] = captcha;
|
|
[fields addEntriesFromDictionary:hiddenFields];
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:kRegisterNameSpace withAttributes:@{} andChildren:@[
|
|
[[XMPPDataForm alloc] initWithType:@"submit" formType:kRegisterNameSpace andDictionary:fields]
|
|
] andData:nil]];
|
|
}
|
|
else
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:kRegisterNameSpace withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"username" andData:user],
|
|
[[MLXMLNode alloc] initWithElement:@"password" andData:newPass],
|
|
] andData:nil]];
|
|
}
|
|
}
|
|
|
|
-(void) changePasswordForUser:(NSString*) user newPassword:(NSString*) newPass
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:kRegisterNameSpace withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"username" andData:user],
|
|
[[MLXMLNode alloc] initWithElement:@"password" andData:newPass],
|
|
] andData:nil]];
|
|
}
|
|
|
|
-(void) setMucAdminQueryWithAffiliation:(NSString*) affiliation forJid:(NSString*) jid
|
|
{
|
|
[self addChildNode:[[MLXMLNode alloc] initWithElement:@"query" andNamespace:@"http://jabber.org/protocol/muc#admin" withAttributes:@{} andChildren:@[
|
|
[[MLXMLNode alloc] initWithElement:@"item" withAttributes:@{
|
|
@"affiliation": affiliation,
|
|
@"jid": jid,
|
|
} andChildren:@[] andData:nil],
|
|
] andData:nil]];
|
|
}
|
|
|
|
#ifdef IS_QUICKSY
|
|
-(void) setQuicksyPhoneBook:(NSArray*) numbers
|
|
{
|
|
MLXMLNode* envelope = [[MLXMLNode alloc] initWithElement:@"phone-book" andNamespace:@"im.quicksy.synchronization:0"];
|
|
for(NSString* number in numbers)
|
|
{
|
|
[envelope addChildNode:[[MLXMLNode alloc] initWithElement:@"entry" withAttributes:@{
|
|
@"number": number,
|
|
} andChildren:@[] andData:nil]];
|
|
}
|
|
[self addChildNode:envelope];
|
|
}
|
|
#endif
|
|
|
|
@end
|