I am trying to implement an Instant Messaging App where users can chat as well as add other users to their roster and accept buddy requests. So, far I have been able to implement the chat and I am also able to receive and accept/reject friend requests.
For accepting/rejecting a subscription request, the code is as follows:
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
NSString *presenceType = [presence type]; // online / offline
NSString *myUsername = [[sender myJID] user];
NSString *presenceFromUser = [[presence from] user];
NSString *presencefromStr=[presence fromStr];
if ([presenceType isEqualToString:#"subscribe"]) {
if(buttonIndex==1) { // For accept button
[xmppRoster acceptPresenceSubscriptionRequestFrom:[tmpPresence from] andAddToRoster:YES];
}
else { // For reject button
[xmppRoster rejectPresenceSubscriptionRequestFrom:[tmpPresence from]];
}
}
However, now I am stuck with the problem of not being able to send a friend request. Can anyone guide me on which function of XMPPRoster to use? I tried using the subscribePresenceToUser function, but, it didn't work. Any help will be highly appreciated.
Also, can someone tell if the way I am going with this XMPPRoster subscription mechanism is right or is there a better way to handle the friend requests in XMPPFramework?
Thanks in advance.
Answer by OP in comment:
XMPPJID *jid = [XMPPJID jidWithString:self.addFriendField.text];
[xmppRoster addUser:jid withNickname:nil];
This code snippet sends the request to other users and adds them to their Roster.
You can see XMPPRoster.h to see all the functions available inside the roster extension.
For your answer you have three options:
/**
* Adds the given user to the roster with an optional nickname
* and requests permission to receive presence information from them.
**/
- (void)addUser:(XMPPJID *)jid withNickname:(nullable NSString *)optionalName;
/**
* Adds the given user to the roster with an optional nickname,
* adds the given user to groups
* and requests permission to receive presence information from them.
**/
- (void)addUser:(XMPPJID *)jid withNickname:(nullable NSString *)optionalName groups:(nullable NSArray<NSString*> *)groups;
/**
* Adds the given user to the roster with an optional nickname,
* adds the given user to groups
* and optionally requests permission to receive presence information from them.
**/
- (void)addUser:(XMPPJID *)jid withNickname:(nullable NSString *)optionalName groups:(nullable NSArray<NSString*> *)groups subscribeToPresence:(BOOL)subscribe;
And to accept the friend request : (add as friend, as Fan or Decline)
addToRoster flag = true : Friend
addToRoster flag = false : Fan
/**
* Accepts the presence subscription request the given user.
*
* If you also choose, you can add the user to your roster.
* Doing so is similar to the traditional IM model.
**/
- (void)acceptPresenceSubscriptionRequestFrom:(XMPPJID *)jid andAddToRoster:(BOOL)flag;
/**
* Rejects the presence subscription request from the given user.
*
* If you are already subscribed to the given user's presence,
* rejecting they subscription request will not affect your subscription to their presence.
**/
- (void)rejectPresenceSubscriptionRequestFrom:(XMPPJID *)jid;
Related
I am using a "Cognito User Pool authorizer" (no "AWS_IAM" option, no custom coded authorizer) to call Lambda methods via API Gateway and identify the user logged in on the iOS client.
On Lambda, I use the user id I get from the Cognito User Pool authorizer via event.requestContext.authorizer.claims.sub (to store the user id with some DynamoDB items).
I now need to compare this with the id of the logged in user in the iOS client.
I found [AWSIdentityManager defaultIdentityManager].identityId, but this (obviously) returns he IdentityID (which I can look up in the AWS console in Cognito --> Federated Identities --> Identity Browser), which is different from the "sub" id I see in Cognito --> User Pools --> Users and groups
Can I get the "sub" via the AWS iOS SDK?
If I cannot get it, what other id parameter should I use that I can retrieve both on Lambda and the client to identify the current client user/the user making the API request?
It seems that I have to specifically request the attributes via the user details like this:
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:AWSCognitoUserPoolsSignInProviderKey];
AWSCognitoIdentityUser *user = [pool currentUser];
NSString *mySub;
[[user getDetails] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserGetDetailsResponse *> * _Nonnull task) {
if(!task.error){
AWSCognitoIdentityUserGetDetailsResponse *response = task.result;
NSArray<AWSCognitoIdentityProviderAttributeType*> *userAttributes = response.userAttributes;
for (AWSCognitoIdentityProviderAttributeType *attr in self.userAttributes) {
if ([attr.name isEqualToString:#"sub"]) {
mySub = attr.value;
}
}
} else {
NSLog(#"Error fetching Cognito User Attributes: %#", task.error.localizedDescription);
}
}];
Another solution (tested with the AWS JavaScript SDK):
When we authenticate with Cognito, we can retrieve a JWT token:
user.authenticateUser(authenticationDetails, {
onSuccess: (result) => resolve(result.getIdToken().getJwtToken()),
onFailure: (err) => reject(err)
})
It happens that this JWT token is an standard object that can be decoded.
Using Auth0 JWT decode (npm install jwt-decode), we can decode this token and retrieve all user attributes (e-mail, username, etc.) and the sub.
var jwtDecode = require('jwt-decode');
var decoded = jwtDecode(token);
console.log(decoded);
// prints sub, email, username, ...
1.I had read https://www.igniterealtime.org/builds/smack/docs/latest/documentation/extensions /filetransfer.html
snippet code from this guide, it not need resource part
// Create the file transfer manager
FileTransferManager manager = new FileTransferManager(connection);
// Create the outgoing file transfer
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer("romeo#montague.net");
// Send the file
transfer.sendFile(new File("shakespeare_complete_works.txt"), "You won't believe this!");
2.so I read spark source code org.jivesoftware.spark.PresenceManager find this method , so the documentation long time no to update;
/**
* Returns the fully qualified jid of a user.
*
* #param jid the users bare jid (ex. derek#jivesoftware.com)
* #return the fully qualified jid of a user (ex. derek#jivesoftware.com --> derek#jivesoftware.com/spark)
*/
public static String getFullyQualifiedJID(String jid) {
System.out.println("getFullyQualifiedJID : " + jid);
final Roster roster = SparkManager.getConnection().getRoster();
Presence presence = roster.getPresence(jid);
System.out.println("getFullyQualifiedJID : " + presence.getFrom());
return presence.getFrom();
}
I find this method not work for asmack , so google it found this
Smack's FileTransferManager.createOutgoingFileTransfer only accepts full JIDs. How can I determine the full JID of a user in Smack?
//snippet code from my project
Roster roster = connection.getRoster();
List presenceList = roster.getPresences(jid);
Log.d(TAG, "bareJid : " + jid);
for (Presence presence : presenceList) {
Log.d(TAG, "fullJID : " + presence.getFrom());
}
why the code can not get the fullJID.
the output:
12-23 06:55:35.840: D/MChat(1805): bareJid : test#tigereye-pc
12-23 06:55:35.840: D/MChat(1805): fullJID : test#tigereye-pc
4.the result is the same, so how can I get the fullJID
Thanks & Regards
You have to supply Full user id as : user#serveripaddress/Smack
For Example :
xyz#192.168.1.1/Smack
The need the full JID and the client resource.
You can do something like that:
String fullJID = xmppConnection.getRoster().getPresence(JID).getFrom();
My JID variable is the full JID without the resource.
I use Restkit in my iOS app to call an API.
When the app first launched, a client token is retrieved from the API in order to call the service to post a new user.
After this user has successfully being created, a new access token is sent back by the API. This time is it is a user token.
All the other requests to the API made by the app will now have to use this user token.
I am using a singleton class that inherits from RKObjectManager. I then built one class per ressource to access (example : Users, Images, ...) all inheriting from that main class called AKObjectManager.
In AKObjectManager I have the following method :
+ (instancetype)sharedManager
{
NSURL *url = [NSURL URLWithString:LLY_API_BASE_URL];
AKObjectManager *sharedManager = [self managerWithBaseURL:url];
sharedManager.requestSerializationMIMEType = RKMIMETypeJSON;
...
// Access Token
NSUserDefaults* userData = [NSUserDefaults standardUserDefaults];
if ([userData objectForKey:#"accessToken"]) {
// Not too sure if this is being taken into account for the other class that inherits
[ sharedManager.HTTPClient setDefaultHeader:#"Authorization" value:[userData objectForKey:#"accessToken"]];
}
return sharedManager;
}
I thought that by checking for every access the accessToken in NSUserDefaults and setting it in the Authorization field in the header would work but no. I can see through NSLog that the new access token is set when changing it for for some reasons using Charles the header of the request still points to the old one.
I then used
[[AKObjectManager sharedManager].HTTPClient setDefaultHeader:#"Authorization" value:accessToken.accessToken];
As soon as I got the new token but faced the same issue.
Finally I went for that road (UserManager inherits from AKObjectManager)
// Force the newly refresh token to be set in the Authorization header
[[UserManager sharedManager].HTTPClient setDefaultHeader:#"Authorization" value:[[NSUserDefaults standardUserDefaults] objectForKey:#"accessToken"]];
[[UserManager sharedManager] show:nil // userId equals nil meaning it will be replaced by 'self'
success:^(User* user){
self.user = user;
...
And it worked but I am not too happy about the implementation.
Could you point me to where I got it wrong and advise on how to do it ?
I have an app in which the user uploads a video to youtube. I have the user input their password and username to sign in and then they input the "title", "description", "tags", "category", and "privacy setting" i.e. public, private, unlisted. All works well. However I am not able to verify that the password for the given username is valid or even if the username is valid. When the password and username are filled in and the "sign in" button is tapped these are saved into the documents directory as password.txt and username.txt. Then these are used to complete the process and in fact loaded from the documents directory upon subsequent uploads until the user signs out, in which case the files are removed.
My problem is I would like to check with YouTube when the user fills in the password and username and goes to save them to make sure they are valid. Can someone help me with this.
This is the code I use to input the username and password as well as developers key to YouTube to get a service to allow uploading video.
- (GDataServiceGoogleYouTube *)youTubeService {
static GDataServiceGoogleYouTube* service = nil;
if (!service) {
service = [[GDataServiceGoogleYouTube alloc] init];
[service setShouldCacheResponseData:YES];
[service setServiceShouldFollowNextLinks:YES];
[service setIsServiceRetryEnabled:YES];
/*[service setUserCredentialsWithUsername:accountView.text password:PasswordDisplayField.text];*/
}
NSString *username = [accountView.text retain];
NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
accountView.text = [username stringByTrimmingCharactersInSet:whitespace];
/*if ([accountView.text rangeOfString:#"#"].location == NSNotFound)
{ accountView.text = [kYoutubeUsername stringByAppendingString:#"#gmail.com"]; }*/
if (([accountView.text length] > 0) && ([PasswordDisplayField.text length] > 0))
{ [service setUserCredentialsWithUsername:[accountView.text retain] password:[PasswordDisplayField.text retain]]; }
else
{ [service setUserCredentialsWithUsername:nil password:nil]; }
[service setYouTubeDeveloperKey:devKey];
return service;
}
and then I use this code to get the URL for uploading
NSURL *url = [GDataServiceGoogleYouTube youTubeUploadURLForUserID:kGDataServiceDefaultUser];
but I am not sure how to use these to check to see if the username and password are matched and compatible and return an error message is they are not . Also I don't want to save them if they are not correct.
If someone can suggest a solution, a tutorial, video or something else to help me accomplish this I would greatly appreciate it.
Thanks
I would strongly suggest moving to OAuth 2 using the Objective-C client library:
http://code.google.com/p/gdata-objectivec-client/
http://code.google.com/p/gtm-oauth2/
As a user of your application, I'd feel awful knowing that you were storing my Google Account address and password in clear text like that. Please, switch to OAuth 2.
How do you change your presence to show dnd/away and etc.?
XMPPPresence *presence = [XMPPPresence presenceWithType:status];
[[[self appDelegate] xmppStream] sendElement:presence];
status is an NSString that I set to online/unavailable/away/busy/invisible.
It only works when I go online and/or unavailable.
Here's how it looks like after sending presence in my xmppStream:
<presence type="away"><x xmlns="vcard-temp:x:update"><photo/></x></presence>
To change the status of your client you will need to use this simple code:
XMPPPresence *presence = [XMPPPresence presence];
NSXMLElement *status = [NSXMLElement elementWithName:#"status"];
[status setStringValue:#"online/unavailable/away/busy/invisible"];
[presence addChild:status];
[[self xmppStream] sendElement:presence];
This simply means that the key to change the status of your client is by adding a status element to your presence. Please note that the openfire server will only show the "available/Offline" status when you hover on the user icon in the admin panel. This should not confuse you though. You can simply check the presence message sent by your client and received by the others which will show on of the status you have set ("online/unavailable/away/busy/invisible").
On top of the answer above, there is also a <show> element that should be used in conjunction with the <status> element. By using both elements, you can customize user's presence for each availability states.
Default: Available / Offline
By using <show>: Available / Busy / Away / Extended Away / Offline
By using <show> with <status>: "Free to chat" / "Hard at work" / "In a meeting" / "Out for lunch".
If you use Openfire with this method: In User Sessions > Presence column, you will see:
Different colored icons for each user (e.g. green for available, red for busy, etc.)
A descriptive text beside the icons (e.g. "In a meeting")
Presence child elements
There are 3 elements that can change types of presence in XMPP.
<show/>
<status/>
<priority/> (we'll exclude this for discussion)
Show
<show> specifies an availability status of a user.
Values of the element must be specified according to the list below.
"chat" -- user is actively interested in chatting.
"dnd" -- user is busy (dnd a.k.a 'Do Not Disturb').
"away" -- user is temporarily away.
"xa" -- user is away for an extended period (xa a.k.a. 'eXtended Away').
If this element is not provided, user is assumed to only be either online and available.
Status
<status> describes the availability status of a user. It is usually used in conjunction with the <show> element to provide a detailed description of an availability state.
Values of the element can be of any descriptive text. For instance:
"Available to chat" -- can be used for "chat"
"Busy at work" -- can be used for "dnd"
"In a meeting" -- can be used for "away"
"On a vacation" -- can be used for "xa"
Usage in Objective-C
Here's how you should apply the above concept in code.
// Initialize variables
XMPPPresence *presence = [XMPPPresence presence];
NSXMLElement *show = [NSXMLElement elementWithName:#"show"];
NSXMLElement *status = [NSXMLElement elementWithName:#"status"];
// If user is available
[show setStringValue:#"chat"];
[status setStringValue:#"Available to chat"];
// If user is busy
[show setStringValue:#"dnd"];
[status setStringValue:#"Busy at work"];
// If user is away
[show setStringValue:#"away"];
[status setStringValue:#"In a meeting"];
// If user is away for a long period of time
[show setStringValue:#"xa"];
[status setStringValue:#"On a vacation"];
// Add the XML child elements to XMPPPresence
[presence addChild:show];
[presence addChild:status];
// Update new presence to server
[[[self appDelegate] xmppStream] sendElement:presence];
There you go, your customized user's presence will now be accurately reflected in your server.
See also: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
For Swift 5 and above
You can send to status any user
let presence = XMPPPresence(show: XMPPPresence.ShowType(rawValue: XMPPPresence.ShowType.away.rawValue) , status: "I'm working")
stream.send(presence)
and You can listen to all status with above methods
class LastStatus {
var username :String
var lastStatus : String
internal init(username: String, lastStatus: String) {
self.username = username
self.lastStatus = lastStatus
}
}
var lastStatusList : [LastStatus] = []
func xmppStream(_ sender: XMPPStream, didReceive presence: XMPPPresence) {
guard let fromUser = presenceFrom.user else {return}
if presence.showType == XMPPPresence.ShowType.init(rawValue: "away") {
if let status = presence.status {
if lastStatusList.firstIndex(where: { $0.username == fromUser}) == nil {
let userStatus = LastStatus(username: fromUser , lastStatus: status)
lastStatusList.append(userStatus)
} else {
let index = lastStatusList.firstIndex(where: { $0.username == fromUser})!
let changing = lastStatusList[index]
changing.lastStatus = status
}
}
}
}