Skip to content

Commit 9830a1a

Browse files
committed
Merge pull request robbiehanson#448 from jonstaff/xep-0077
XEP-0077 Added Functionality
2 parents bc95f7d + 53f97ce commit 9830a1a

File tree

2 files changed

+332
-0
lines changed

2 files changed

+332
-0
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//
2+
// Created by Jonathon Staff on 10/11/14.
3+
// Copyright (c) 2014 Jonathon Staff. All rights reserved.
4+
//
5+
6+
#import <Foundation/Foundation.h>
7+
#import "XMPPModule.h"
8+
9+
@class XMPPIDTracker;
10+
11+
#define _XMPP_REGISTRATION_H
12+
13+
@interface XMPPRegistration : XMPPModule {
14+
XMPPIDTracker *xmppIDTracker;
15+
}
16+
17+
/**
18+
* This method will attempt to change the current user's password to the new one provided. The
19+
* user *MUST* be authenticated for this to work successfully.
20+
*
21+
* @see passwordChangeSuccessful:
22+
* @see passwordChangeFailed:withError:
23+
*
24+
* @param newPassword The new password for the user
25+
*/
26+
- (BOOL)changePassword:(NSString *)newPassword;
27+
28+
/**
29+
* This method will attempt to cancel the current user's registration. Later implementations
30+
* will provide support for handling authentication challenges by the server. For now,
31+
* simply pass a value of 'nil' in for password, or preferably, use the other cancellation
32+
* method.
33+
*
34+
* @see cancelRegistration
35+
*/
36+
- (BOOL)cancelRegistrationUsingPassword:(NSString *)password;
37+
38+
/**
39+
* This method will attempt to cancel the current user's registration. The user *MUST* be
40+
* already authenticated for this to work successfully.
41+
*
42+
* @see cancelRegistrationSuccessful:
43+
* @see cancelRegistrationFailed:withError:
44+
*/
45+
- (BOOL)cancelRegistration;
46+
47+
@end
48+
49+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
50+
#pragma mark - XMPPRegistrationDelegate
51+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
52+
53+
@protocol XMPPRegistrationDelegate
54+
@optional
55+
56+
/**
57+
* Implement this method when calling [regInstance changePassword:]. It will be invoked
58+
* if the request for changing the user's password is successfully executed and receives a
59+
* successful response.
60+
*
61+
* @param sender XMPPRegistration object invoking this delegate method.
62+
*/
63+
- (void)passwordChangeSuccessful:(XMPPRegistration *)sender;
64+
65+
/**
66+
* Implement this method when calling [regInstance changePassword:]. It will be invoked
67+
* if the request for changing the user's password is unsuccessfully executed or receives
68+
* an unsuccessful response.
69+
*
70+
* @param sender XMPPRegistration object invoking this delegate method.
71+
* @param error NSError containing more details of the failure.
72+
*/
73+
- (void)passwordChangeFailed:(XMPPRegistration *)sender withError:(NSError *)error;
74+
75+
/**
76+
* Implement this method when calling [regInstance cancelRegistration] or a variation. It
77+
* is invoked if the request for canceling the user's registration is successfully
78+
* executed and receives a successful response.
79+
*
80+
* @param sender XMPPRegistration object invoking this delegate method.
81+
*/
82+
- (void)cancelRegistrationSuccessful:(XMPPRegistration *)sender;
83+
84+
/**
85+
* Implement this method when calling [regInstance cancelRegistration] or a variation. It
86+
* is invoked if the request for canceling the user's registration is unsuccessfully
87+
* executed or receives an unsuccessful response.
88+
*
89+
* @param sender XMPPRegistration object invoking this delegate method.
90+
* @param error NSError containing more details of the failure.
91+
*/
92+
- (void)cancelRegistrationFailed:(XMPPRegistration *)sender withError:(NSError *)error;
93+
94+
@end
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
//
2+
// Created by Jonathon Staff on 10/11/14.
3+
// Copyright (c) 2014 Jonathon Staff. All rights reserved.
4+
//
5+
6+
#import "XMPPRegistration.h"
7+
#import "XMPPStream.h"
8+
#import "XMPPIDTracker.h"
9+
#import "XMPPIQ.h"
10+
#import "NSXMLElement+XMPP.h"
11+
12+
NSString *const XMPPRegistrationErrorDomain = @"XMPPRegistrationErrorDomain";
13+
14+
@implementation XMPPRegistration
15+
16+
- (void)didActivate
17+
{
18+
xmppIDTracker = [[XMPPIDTracker alloc] initWithStream:xmppStream dispatchQueue:moduleQueue];
19+
}
20+
21+
- (void)willDeactivate
22+
{
23+
[xmppIDTracker removeAllIDs];
24+
xmppIDTracker = nil;
25+
}
26+
27+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
28+
#pragma mark Public API
29+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
30+
31+
/**
32+
* This method provides functionality of XEP-0077 3.3 User Changes Password.
33+
*
34+
* @link {http://xmpp.org/extensions/xep-0077.html#usecases-changepw}
35+
*
36+
* Example 18. Password Change
37+
*
38+
* <iq type='set' to='shakespeare.lit' id='change1'>
39+
* <query xmlns='jabber:iq:register'>
40+
* <username>bill</username>
41+
* <password>newpass</password>
42+
* </query>
43+
* </iq>
44+
*
45+
*/
46+
- (BOOL)changePassword:(NSString *)newPassword
47+
{
48+
if (![xmppStream isAuthenticated])
49+
return NO; // You must be authenticated in order to change your password
50+
51+
dispatch_block_t block = ^{
52+
@autoreleasepool {
53+
NSString *toStr = xmppStream.myJID.domain;
54+
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"];
55+
56+
NSXMLElement *username = [NSXMLElement elementWithName:@"username"
57+
stringValue:xmppStream.myJID.user];
58+
NSXMLElement *password = [NSXMLElement elementWithName:@"password"
59+
stringValue:newPassword];
60+
[query addChild:username];
61+
[query addChild:password];
62+
63+
XMPPIQ *iq = [XMPPIQ iqWithType:@"set"
64+
to:[XMPPJID jidWithString:toStr]
65+
elementID:[xmppStream generateUUID]
66+
child:query];
67+
68+
[xmppIDTracker addID:[iq elementID]
69+
target:self
70+
selector:@selector(handlePasswordChangeQueryIQ:withInfo:)
71+
timeout:60];
72+
73+
[xmppStream sendElement:iq];
74+
}
75+
};
76+
77+
if (dispatch_get_specific(moduleQueueTag))
78+
block();
79+
else
80+
dispatch_async(moduleQueue, block);
81+
82+
return YES;
83+
}
84+
85+
/**
86+
* This method provides functionality of XEP-0077 3.2 Entity Cancels an Existing Registration.
87+
*
88+
* @link {http://xmpp.org/extensions/xep-0077.html#usecases-cancel}
89+
*
90+
* <iq type='set' from='bill@shakespeare.lit/globe' id='unreg1'>
91+
* <query xmlns='jabber:iq:register'>
92+
* <remove/>
93+
* </query>
94+
* </iq>
95+
*
96+
*/
97+
- (BOOL)cancelRegistration
98+
{
99+
return [self cancelRegistrationUsingPassword:nil];
100+
}
101+
102+
/**
103+
* Same as cancelRegistration. Handling authentication challenges is not yet implemented.
104+
*/
105+
- (BOOL)cancelRegistrationUsingPassword:(NSString *)password
106+
{
107+
// TODO: Handle the scenario of using password
108+
109+
dispatch_block_t block = ^{
110+
@autoreleasepool {
111+
112+
NSXMLElement *remove = [NSXMLElement elementWithName:@"remove"];
113+
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"];
114+
[query addChild:remove];
115+
XMPPIQ *iq = [XMPPIQ iqWithType:@"set"
116+
elementID:[xmppStream generateUUID]
117+
child:query];
118+
119+
[xmppIDTracker addElement:iq
120+
target:self
121+
selector:@selector(handleRegistrationCancelQueryIQ:withInfo:)
122+
timeout:60];
123+
124+
[xmppStream sendElement:iq];
125+
}
126+
};
127+
128+
if (dispatch_get_specific(moduleQueueTag))
129+
block();
130+
else
131+
dispatch_async(moduleQueue, block);
132+
133+
return YES;
134+
}
135+
136+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
137+
#pragma mark - XMPPIDTracker
138+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
139+
140+
/**
141+
* This method handles the response received (or not received) after calling changePassword.
142+
*/
143+
- (void)handlePasswordChangeQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)info
144+
{
145+
dispatch_block_t block = ^{
146+
@autoreleasepool {
147+
NSXMLElement *errorElem = [iq elementForName:@"error"];
148+
149+
if (errorElem) {
150+
NSString *errMsg = [[errorElem children] componentsJoinedByString:@", "];
151+
NSInteger errCode = [errorElem attributeIntegerValueForName:@"code"
152+
withDefaultValue:-1];
153+
NSDictionary *errInfo = @{NSLocalizedDescriptionKey : errMsg};
154+
NSError *err = [NSError errorWithDomain:XMPPRegistrationErrorDomain
155+
code:errCode
156+
userInfo:errInfo];
157+
158+
[multicastDelegate passwordChangeFailed:self
159+
withError:err];
160+
return;
161+
}
162+
163+
NSString *type = [iq type];
164+
165+
if ([type isEqualToString:@"result"] && iq.childCount == 0) {
166+
[multicastDelegate passwordChangeSuccessful:self];
167+
} else {
168+
// this should be impossible to reach, but just for safety's sake...
169+
[multicastDelegate passwordChangeFailed:self
170+
withError:nil];
171+
}
172+
}
173+
};
174+
175+
if (dispatch_get_specific(moduleQueueTag))
176+
block();
177+
else
178+
dispatch_async(moduleQueue, block);
179+
}
180+
181+
/**
182+
* This method handles the response received (or not received) after calling cancelRegistration.
183+
*/
184+
- (void)handleRegistrationCancelQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)info
185+
{
186+
dispatch_block_t block = ^{
187+
@autoreleasepool {
188+
NSXMLElement *errorElem = [iq elementForName:@"error"];
189+
190+
if (errorElem) {
191+
NSString *errMsg = [[errorElem children] componentsJoinedByString:@", "];
192+
NSInteger errCode = [errorElem attributeIntegerValueForName:@"code"
193+
withDefaultValue:-1];
194+
NSDictionary *errInfo = @{NSLocalizedDescriptionKey : errMsg};
195+
NSError *err = [NSError errorWithDomain:XMPPRegistrationErrorDomain
196+
code:errCode
197+
userInfo:errInfo];
198+
199+
[multicastDelegate cancelRegistrationFailed:self
200+
withError:err];
201+
return;
202+
}
203+
204+
NSString *type = [iq type];
205+
206+
if ([type isEqualToString:@"result"] && iq.childCount == 0) {
207+
[multicastDelegate cancelRegistrationSuccessful:self];
208+
} else {
209+
// this should be impossible to reach, but just for safety's sake...
210+
[multicastDelegate cancelRegistrationFailed:self
211+
withError:nil];
212+
}
213+
}
214+
};
215+
216+
if (dispatch_get_specific(moduleQueueTag))
217+
block();
218+
else
219+
dispatch_async(moduleQueue, block);
220+
}
221+
222+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
223+
#pragma mark - XMPPStreamDelegate
224+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
225+
226+
- (BOOL)xmppStream:(XMPPStream *)stream didReceiveIQ:(XMPPIQ *)iq
227+
{
228+
NSString *type = [iq type];
229+
230+
if ([type isEqualToString:@"result"] || [type isEqualToString:@"error"]) {
231+
NSLog(@"invoking with iq: %@", iq);
232+
return [xmppIDTracker invokeForElement:iq withObject:iq];
233+
}
234+
235+
return NO;
236+
}
237+
238+
@end

0 commit comments

Comments
 (0)