Skip to content

Commit

Permalink
Merge pull request #448 from jonstaff/xep-0077
Browse files Browse the repository at this point in the history
XEP-0077 Added Functionality
  • Loading branch information
ObjColumnist committed Oct 15, 2014
2 parents bc95f7d + 53f97ce commit 9830a1a
Show file tree
Hide file tree
Showing 2 changed files with 332 additions and 0 deletions.
94 changes: 94 additions & 0 deletions Extensions/XEP-0077/XMPPRegistration.h
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
238 changes: 238 additions & 0 deletions Extensions/XEP-0077/XMPPRegistration.m
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

0 comments on commit 9830a1a

Please sign in to comment.