-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #448 from jonstaff/xep-0077
XEP-0077 Added Functionality
- Loading branch information
Showing
2 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// | ||
// Created by Jonathon Staff on 10/11/14. | ||
// Copyright (c) 2014 Jonathon Staff. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import "XMPPModule.h" | ||
|
||
@class XMPPIDTracker; | ||
|
||
#define _XMPP_REGISTRATION_H | ||
|
||
@interface XMPPRegistration : XMPPModule { | ||
XMPPIDTracker *xmppIDTracker; | ||
} | ||
|
||
/** | ||
* This method will attempt to change the current user's password to the new one provided. The | ||
* user *MUST* be authenticated for this to work successfully. | ||
* | ||
* @see passwordChangeSuccessful: | ||
* @see passwordChangeFailed:withError: | ||
* | ||
* @param newPassword The new password for the user | ||
*/ | ||
- (BOOL)changePassword:(NSString *)newPassword; | ||
|
||
/** | ||
* This method will attempt to cancel the current user's registration. Later implementations | ||
* will provide support for handling authentication challenges by the server. For now, | ||
* simply pass a value of 'nil' in for password, or preferably, use the other cancellation | ||
* method. | ||
* | ||
* @see cancelRegistration | ||
*/ | ||
- (BOOL)cancelRegistrationUsingPassword:(NSString *)password; | ||
|
||
/** | ||
* This method will attempt to cancel the current user's registration. The user *MUST* be | ||
* already authenticated for this to work successfully. | ||
* | ||
* @see cancelRegistrationSuccessful: | ||
* @see cancelRegistrationFailed:withError: | ||
*/ | ||
- (BOOL)cancelRegistration; | ||
|
||
@end | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
#pragma mark - XMPPRegistrationDelegate | ||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
@protocol XMPPRegistrationDelegate | ||
@optional | ||
|
||
/** | ||
* Implement this method when calling [regInstance changePassword:]. It will be invoked | ||
* if the request for changing the user's password is successfully executed and receives a | ||
* successful response. | ||
* | ||
* @param sender XMPPRegistration object invoking this delegate method. | ||
*/ | ||
- (void)passwordChangeSuccessful:(XMPPRegistration *)sender; | ||
|
||
/** | ||
* Implement this method when calling [regInstance changePassword:]. It will be invoked | ||
* if the request for changing the user's password is unsuccessfully executed or receives | ||
* an unsuccessful response. | ||
* | ||
* @param sender XMPPRegistration object invoking this delegate method. | ||
* @param error NSError containing more details of the failure. | ||
*/ | ||
- (void)passwordChangeFailed:(XMPPRegistration *)sender withError:(NSError *)error; | ||
|
||
/** | ||
* Implement this method when calling [regInstance cancelRegistration] or a variation. It | ||
* is invoked if the request for canceling the user's registration is successfully | ||
* executed and receives a successful response. | ||
* | ||
* @param sender XMPPRegistration object invoking this delegate method. | ||
*/ | ||
- (void)cancelRegistrationSuccessful:(XMPPRegistration *)sender; | ||
|
||
/** | ||
* Implement this method when calling [regInstance cancelRegistration] or a variation. It | ||
* is invoked if the request for canceling the user's registration is unsuccessfully | ||
* executed or receives an unsuccessful response. | ||
* | ||
* @param sender XMPPRegistration object invoking this delegate method. | ||
* @param error NSError containing more details of the failure. | ||
*/ | ||
- (void)cancelRegistrationFailed:(XMPPRegistration *)sender withError:(NSError *)error; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
// | ||
// Created by Jonathon Staff on 10/11/14. | ||
// Copyright (c) 2014 Jonathon Staff. All rights reserved. | ||
// | ||
|
||
#import "XMPPRegistration.h" | ||
#import "XMPPStream.h" | ||
#import "XMPPIDTracker.h" | ||
#import "XMPPIQ.h" | ||
#import "NSXMLElement+XMPP.h" | ||
|
||
NSString *const XMPPRegistrationErrorDomain = @"XMPPRegistrationErrorDomain"; | ||
|
||
@implementation XMPPRegistration | ||
|
||
- (void)didActivate | ||
{ | ||
xmppIDTracker = [[XMPPIDTracker alloc] initWithStream:xmppStream dispatchQueue:moduleQueue]; | ||
} | ||
|
||
- (void)willDeactivate | ||
{ | ||
[xmppIDTracker removeAllIDs]; | ||
xmppIDTracker = nil; | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
#pragma mark Public API | ||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
/** | ||
* This method provides functionality of XEP-0077 3.3 User Changes Password. | ||
* | ||
* @link {http://xmpp.org/extensions/xep-0077.html#usecases-changepw} | ||
* | ||
* Example 18. Password Change | ||
* | ||
* <iq type='set' to='shakespeare.lit' id='change1'> | ||
* <query xmlns='jabber:iq:register'> | ||
* <username>bill</username> | ||
* <password>newpass</password> | ||
* </query> | ||
* </iq> | ||
* | ||
*/ | ||
- (BOOL)changePassword:(NSString *)newPassword | ||
{ | ||
if (![xmppStream isAuthenticated]) | ||
return NO; // You must be authenticated in order to change your password | ||
|
||
dispatch_block_t block = ^{ | ||
@autoreleasepool { | ||
NSString *toStr = xmppStream.myJID.domain; | ||
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"]; | ||
|
||
NSXMLElement *username = [NSXMLElement elementWithName:@"username" | ||
stringValue:xmppStream.myJID.user]; | ||
NSXMLElement *password = [NSXMLElement elementWithName:@"password" | ||
stringValue:newPassword]; | ||
[query addChild:username]; | ||
[query addChild:password]; | ||
|
||
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" | ||
to:[XMPPJID jidWithString:toStr] | ||
elementID:[xmppStream generateUUID] | ||
child:query]; | ||
|
||
[xmppIDTracker addID:[iq elementID] | ||
target:self | ||
selector:@selector(handlePasswordChangeQueryIQ:withInfo:) | ||
timeout:60]; | ||
|
||
[xmppStream sendElement:iq]; | ||
} | ||
}; | ||
|
||
if (dispatch_get_specific(moduleQueueTag)) | ||
block(); | ||
else | ||
dispatch_async(moduleQueue, block); | ||
|
||
return YES; | ||
} | ||
|
||
/** | ||
* This method provides functionality of XEP-0077 3.2 Entity Cancels an Existing Registration. | ||
* | ||
* @link {http://xmpp.org/extensions/xep-0077.html#usecases-cancel} | ||
* | ||
* <iq type='set' from='bill@shakespeare.lit/globe' id='unreg1'> | ||
* <query xmlns='jabber:iq:register'> | ||
* <remove/> | ||
* </query> | ||
* </iq> | ||
* | ||
*/ | ||
- (BOOL)cancelRegistration | ||
{ | ||
return [self cancelRegistrationUsingPassword:nil]; | ||
} | ||
|
||
/** | ||
* Same as cancelRegistration. Handling authentication challenges is not yet implemented. | ||
*/ | ||
- (BOOL)cancelRegistrationUsingPassword:(NSString *)password | ||
{ | ||
// TODO: Handle the scenario of using password | ||
|
||
dispatch_block_t block = ^{ | ||
@autoreleasepool { | ||
|
||
NSXMLElement *remove = [NSXMLElement elementWithName:@"remove"]; | ||
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:register"]; | ||
[query addChild:remove]; | ||
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" | ||
elementID:[xmppStream generateUUID] | ||
child:query]; | ||
|
||
[xmppIDTracker addElement:iq | ||
target:self | ||
selector:@selector(handleRegistrationCancelQueryIQ:withInfo:) | ||
timeout:60]; | ||
|
||
[xmppStream sendElement:iq]; | ||
} | ||
}; | ||
|
||
if (dispatch_get_specific(moduleQueueTag)) | ||
block(); | ||
else | ||
dispatch_async(moduleQueue, block); | ||
|
||
return YES; | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
#pragma mark - XMPPIDTracker | ||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
/** | ||
* This method handles the response received (or not received) after calling changePassword. | ||
*/ | ||
- (void)handlePasswordChangeQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)info | ||
{ | ||
dispatch_block_t block = ^{ | ||
@autoreleasepool { | ||
NSXMLElement *errorElem = [iq elementForName:@"error"]; | ||
|
||
if (errorElem) { | ||
NSString *errMsg = [[errorElem children] componentsJoinedByString:@", "]; | ||
NSInteger errCode = [errorElem attributeIntegerValueForName:@"code" | ||
withDefaultValue:-1]; | ||
NSDictionary *errInfo = @{NSLocalizedDescriptionKey : errMsg}; | ||
NSError *err = [NSError errorWithDomain:XMPPRegistrationErrorDomain | ||
code:errCode | ||
userInfo:errInfo]; | ||
|
||
[multicastDelegate passwordChangeFailed:self | ||
withError:err]; | ||
return; | ||
} | ||
|
||
NSString *type = [iq type]; | ||
|
||
if ([type isEqualToString:@"result"] && iq.childCount == 0) { | ||
[multicastDelegate passwordChangeSuccessful:self]; | ||
} else { | ||
// this should be impossible to reach, but just for safety's sake... | ||
[multicastDelegate passwordChangeFailed:self | ||
withError:nil]; | ||
} | ||
} | ||
}; | ||
|
||
if (dispatch_get_specific(moduleQueueTag)) | ||
block(); | ||
else | ||
dispatch_async(moduleQueue, block); | ||
} | ||
|
||
/** | ||
* This method handles the response received (or not received) after calling cancelRegistration. | ||
*/ | ||
- (void)handleRegistrationCancelQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)info | ||
{ | ||
dispatch_block_t block = ^{ | ||
@autoreleasepool { | ||
NSXMLElement *errorElem = [iq elementForName:@"error"]; | ||
|
||
if (errorElem) { | ||
NSString *errMsg = [[errorElem children] componentsJoinedByString:@", "]; | ||
NSInteger errCode = [errorElem attributeIntegerValueForName:@"code" | ||
withDefaultValue:-1]; | ||
NSDictionary *errInfo = @{NSLocalizedDescriptionKey : errMsg}; | ||
NSError *err = [NSError errorWithDomain:XMPPRegistrationErrorDomain | ||
code:errCode | ||
userInfo:errInfo]; | ||
|
||
[multicastDelegate cancelRegistrationFailed:self | ||
withError:err]; | ||
return; | ||
} | ||
|
||
NSString *type = [iq type]; | ||
|
||
if ([type isEqualToString:@"result"] && iq.childCount == 0) { | ||
[multicastDelegate cancelRegistrationSuccessful:self]; | ||
} else { | ||
// this should be impossible to reach, but just for safety's sake... | ||
[multicastDelegate cancelRegistrationFailed:self | ||
withError:nil]; | ||
} | ||
} | ||
}; | ||
|
||
if (dispatch_get_specific(moduleQueueTag)) | ||
block(); | ||
else | ||
dispatch_async(moduleQueue, block); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
#pragma mark - XMPPStreamDelegate | ||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
- (BOOL)xmppStream:(XMPPStream *)stream didReceiveIQ:(XMPPIQ *)iq | ||
{ | ||
NSString *type = [iq type]; | ||
|
||
if ([type isEqualToString:@"result"] || [type isEqualToString:@"error"]) { | ||
NSLog(@"invoking with iq: %@", iq); | ||
return [xmppIDTracker invokeForElement:iq withObject:iq]; | ||
} | ||
|
||
return NO; | ||
} | ||
|
||
@end |