829 lines
43 KiB
Mathematica
829 lines
43 KiB
Mathematica
|
//
|
||
|
// MLPubSubProcessor.m
|
||
|
// monalxmpp
|
||
|
//
|
||
|
// Created by Thilo Molitor on 31.10.20.
|
||
|
// Copyright © 2020 Monal.im. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#import <Foundation/Foundation.h>
|
||
|
|
||
|
#import "MLConstants.h"
|
||
|
#import "MLPubSubProcessor.h"
|
||
|
#import "MLPubSub.h"
|
||
|
#import "MLHandler.h"
|
||
|
#import "xmpp.h"
|
||
|
#import "DataLayer.h"
|
||
|
#import "MLImageManager.h"
|
||
|
#import "MLNotificationQueue.h"
|
||
|
#import "MLMucProcessor.h"
|
||
|
#import "XMPPIQ.h"
|
||
|
#import "HelperTools.h"
|
||
|
|
||
|
@interface MLPubSubProcessor()
|
||
|
|
||
|
@end
|
||
|
|
||
|
@interface MLMucProcessor ()
|
||
|
-(void) sendDiscoQueryFor:(NSString*) roomJid withJoin:(BOOL) join andBookmarksUpdate:(BOOL) updateBookmarks;
|
||
|
-(void) sendJoinPresenceFor:(NSString*) room;
|
||
|
-(NSString*) calculateNickForMuc:(NSString*) room;
|
||
|
@end
|
||
|
|
||
|
@implementation MLPubSubProcessor
|
||
|
|
||
|
$$class_handler(mdsHandler, $$ID(xmpp*, account), $$ID(NSString*, jid), $$ID(NSString*, type), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data))
|
||
|
DDLogDebug(@"Got new mds displayed status from '%@' (should be own jid)...", jid);
|
||
|
if(![jid isEqualToString:account.connectionProperties.identity.jid])
|
||
|
{
|
||
|
DDLogWarn(@"Ignoring mds update not coming from our own jid");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if([type isEqualToString:@"publish"])
|
||
|
[account updateMdsData:data];
|
||
|
$$
|
||
|
|
||
|
$$class_handler(handleMdsFetchResult, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data))
|
||
|
if(!success)
|
||
|
{
|
||
|
//item-not-found means: no mds items in storage --> use an empty data dict
|
||
|
if([errorIq check:@"error/{urn:ietf:params:xml:ns:xmpp-stanzas}item-not-found"])
|
||
|
data = @{};
|
||
|
else
|
||
|
{
|
||
|
DDLogWarn(@"Could not fetch mds from pep, doing nothing!");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//call +notify handler to process our data dictionary containing all mds items
|
||
|
[account updateMdsData:data];
|
||
|
$$
|
||
|
|
||
|
$$class_handler(avatarHandler, $$ID(xmpp*, account), $$ID(NSString*, jid), $$ID(NSString*, type), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data))
|
||
|
DDLogDebug(@"Got new avatar metadata from '%@'", jid);
|
||
|
if([type isEqualToString:@"publish"])
|
||
|
{
|
||
|
for(NSString* entry in data)
|
||
|
{
|
||
|
MLXMLNode* metadata = [data[entry] findFirst:@"{urn:xmpp:avatar:metadata}metadata/info"];
|
||
|
NSString* avatarHash = [metadata findFirst:@"/@id"];
|
||
|
if(!avatarHash) //the user disabled his avatar
|
||
|
{
|
||
|
DDLogInfo(@"User '%@' disabled his avatar", jid);
|
||
|
[[MLImageManager sharedInstance] setIconForContact:[MLContact createContactFromJid:jid andAccountID:account.accountID] WithData:nil];
|
||
|
[[DataLayer sharedInstance] setAvatarHash:@"" forContact:jid andAccount:account.accountID];
|
||
|
//delete cache to make sure the image will be regenerated
|
||
|
[[MLImageManager sharedInstance] purgeCacheForContact:jid andAccount:account.accountID];
|
||
|
[[MLNotificationQueue currentQueue] postNotificationName:kMonalContactRefresh object:account userInfo:@{
|
||
|
@"contact": [MLContact createContactFromJid:jid andAccountID:account.accountID]
|
||
|
}];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NSString* currentHash = [[DataLayer sharedInstance] getAvatarHashForContact:jid andAccount:account.accountID];
|
||
|
if(currentHash && [avatarHash isEqualToString:currentHash])
|
||
|
{
|
||
|
DDLogInfo(@"Avatar hash of '%@' is the same, we don't need to update our avatar image data", jid);
|
||
|
break;
|
||
|
}
|
||
|
//only allow a maximum of 72KiB of image data when in appex due to appex memory limits
|
||
|
//--> ignore metadata elements bigger than this size and only hande them once not in appex anymore
|
||
|
NSUInteger avatarByteSize = [[metadata findFirst:@"/@bytes|int"] unsignedIntegerValue];
|
||
|
if(![HelperTools isAppExtension] || avatarByteSize < 128 * 1024)
|
||
|
[account.pubsub fetchNode:@"urn:xmpp:avatar:data" from:jid withItemsList:@[avatarHash] andHandler:$newHandler(self, handleAvatarFetchResult, $ID(metadata))];
|
||
|
else
|
||
|
{
|
||
|
DDLogWarn(@"Not loading avatar image of '%@' because it is too big to be handled in appex (%lu bytes), rescheduling it to be fetched in mainapp", jid, (unsigned long)avatarByteSize);
|
||
|
[account addReconnectionHandler:$newHandler(self, fetchAvatarAgain, $ID(jid), $ID(avatarHash), $ID(metadata))];
|
||
|
}
|
||
|
}
|
||
|
break; //we only want to process the first item (this should also be the only item)
|
||
|
}
|
||
|
if([data count] > 1)
|
||
|
DDLogWarn(@"Got more than one avatar metadata item!");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DDLogInfo(@"User %@ disabled his avatar", jid);
|
||
|
[[MLImageManager sharedInstance] setIconForContact:[MLContact createContactFromJid:jid andAccountID:account.accountID] WithData:nil];
|
||
|
[[DataLayer sharedInstance] setAvatarHash:@"" forContact:jid andAccount:account.accountID];
|
||
|
//delete cache to make sure the image will be regenerated
|
||
|
[[MLImageManager sharedInstance] purgeCacheForContact:jid andAccount:account.accountID];
|
||
|
[[MLNotificationQueue currentQueue] postNotificationName:kMonalContactRefresh object:account userInfo:@{
|
||
|
@"contact": [MLContact createContactFromJid:jid andAccountID:account.accountID]
|
||
|
}];
|
||
|
}
|
||
|
$$
|
||
|
|
||
|
//this handler will simply retry the fetchNode for urn:xmpp:avatar:data if in mainapp
|
||
|
$$class_handler(fetchAvatarAgain, $$ID(xmpp*, account), $$ID(NSString*, jid), $$ID(NSString*, avatarHash), $$ID(MLXMLNode*, metadata))
|
||
|
if([HelperTools isAppExtension])
|
||
|
{
|
||
|
DDLogWarn(@"Not loading avatar image of '%@' because we are still in appex, rescheduling it again!", jid);
|
||
|
[account addReconnectionHandler:$newHandler(self, fetchAvatarAgain, $ID(jid), $ID(avatarHash), $ID(metadata))];
|
||
|
}
|
||
|
else
|
||
|
[account.pubsub fetchNode:@"urn:xmpp:avatar:data" from:jid withItemsList:@[avatarHash] andHandler:$newHandler(self, handleAvatarFetchResult, $ID(metadata))];
|
||
|
$$
|
||
|
|
||
|
$$class_handler(handleAvatarFetchResult, $$ID(xmpp*, account), $$ID(NSString*, jid), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(XMPPIQ*, errorReason), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data), $$ID(MLXMLNode*, metadata))
|
||
|
//ignore errors here (e.g. simply don't update the avatar image)
|
||
|
//(this should never happen if other clients and servers behave properly)
|
||
|
if(!success)
|
||
|
{
|
||
|
DDLogWarn(@"Got avatar image fetch error from jid %@: errorIq=%@, errorReason=%@", jid, errorIq, errorReason);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for(NSString* avatarHash in data)
|
||
|
{
|
||
|
//this should be small enough to not crash the appex when loading the image from file later on but large enough to have excellent quality
|
||
|
NSData* avatarData = [data[avatarHash] findFirst:@"{urn:xmpp:avatar:data}data#|base64"];
|
||
|
UIImage* image = nil;
|
||
|
if([[metadata findFirst:@"/@type"] hasPrefix:@"image/svg"])
|
||
|
image = (UIImage*)nilExtractor(PMKHang([HelperTools renderUIImageFromSVGData:avatarData]));
|
||
|
else
|
||
|
image = [UIImage imageWithData:avatarData];
|
||
|
if(image == nil)
|
||
|
{
|
||
|
DDLogWarn(@"Failed to load avatar of %@", jid);
|
||
|
return;
|
||
|
}
|
||
|
//this upper limit is roughly 1.4MiB memory (600x600 with 4 byte per pixel)
|
||
|
if(![HelperTools isAppExtension] || image.size.width * image.size.height < 600 * 600)
|
||
|
{
|
||
|
NSData* imageData = [HelperTools resizeAvatarImage:image withCircularMask:YES toMaxBase64Size:256000];
|
||
|
[[MLImageManager sharedInstance] setIconForContact:[MLContact createContactFromJid:jid andAccountID:account.accountID] WithData:imageData];
|
||
|
[[DataLayer sharedInstance] setAvatarHash:avatarHash forContact:jid andAccount:account.accountID];
|
||
|
//delete cache to make sure the image will be regenerated
|
||
|
[[MLImageManager sharedInstance] purgeCacheForContact:jid andAccount:account.accountID];
|
||
|
[[MLNotificationQueue currentQueue] postNotificationName:kMonalContactRefresh object:account userInfo:@{
|
||
|
@"contact": [MLContact createContactFromJid:jid andAccountID:account.accountID]
|
||
|
}];
|
||
|
DDLogInfo(@"Avatar of '%@' fetched and updated successfully", jid);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DDLogWarn(@"Not loading avatar image of '%@' because it is too big to be processed in appex (%lux%lu pixels), rescheduling it to be fetched in mainapp", jid, (unsigned long)image.size.width, (unsigned long)image.size.height);
|
||
|
[account addReconnectionHandler:$newHandler(self, fetchAvatarAgain, $ID(jid), $ID(avatarHash), $ID(metadata))];
|
||
|
}
|
||
|
}
|
||
|
$$
|
||
|
|
||
|
$$class_handler(rosterNameHandler, $$ID(xmpp*, account), $$ID(NSString*, jid), $$ID(NSString*, type), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data))
|
||
|
//new/updated nickname
|
||
|
if([type isEqualToString:@"publish"])
|
||
|
{
|
||
|
for(NSString* itemId in data)
|
||
|
{
|
||
|
if([jid isEqualToString:account.connectionProperties.identity.jid]) //own roster name
|
||
|
{
|
||
|
DDLogInfo(@"Got own nickname: %@", [data[itemId] findFirst:@"{http://jabber.org/protocol/nick}nick#"]);
|
||
|
NSMutableDictionary* accountDic = [[NSMutableDictionary alloc] initWithDictionary:[[DataLayer sharedInstance] detailsForAccount:account.accountID] copyItems:YES];
|
||
|
accountDic[kRosterName] = [data[itemId] findFirst:@"{http://jabber.org/protocol/nick}nick#"];
|
||
|
[[DataLayer sharedInstance] updateAccounWithDictionary:accountDic];
|
||
|
}
|
||
|
else //roster name of contact
|
||
|
{
|
||
|
DDLogInfo(@"Got nickname of %@: %@", jid, [data[itemId] findFirst:@"{http://jabber.org/protocol/nick}nick#"]);
|
||
|
[[DataLayer sharedInstance] setFullName:[data[itemId] findFirst:@"{http://jabber.org/protocol/nick}nick#"] forContact:jid andAccount:account.accountID];
|
||
|
MLContact* contact = [MLContact createContactFromJid:jid andAccountID:account.accountID];
|
||
|
if(contact) //ignore updates for jids not in our roster
|
||
|
{
|
||
|
//delete cache to make sure the image will be regenerated
|
||
|
[[MLImageManager sharedInstance] purgeCacheForContact:jid andAccount:account.accountID];
|
||
|
[[MLNotificationQueue currentQueue] postNotificationName:kMonalContactRefresh object:account userInfo:@{
|
||
|
@"contact": contact
|
||
|
}];
|
||
|
}
|
||
|
}
|
||
|
break; //we only need the first item (there should be only one item in the first place)
|
||
|
}
|
||
|
}
|
||
|
//deleted/purged node or retracted item
|
||
|
else
|
||
|
{
|
||
|
if([jid isEqualToString:account.connectionProperties.identity.jid]) //own roster name
|
||
|
{
|
||
|
DDLogInfo(@"Own nickname got retracted");
|
||
|
NSMutableDictionary* accountDic = [[NSMutableDictionary alloc] initWithDictionary:[[DataLayer sharedInstance] detailsForAccount:account.accountID] copyItems:NO];
|
||
|
accountDic[kRosterName] = @"";
|
||
|
[[DataLayer sharedInstance] updateAccounWithDictionary:accountDic];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DDLogInfo(@"Nickname of %@ got retracted", jid);
|
||
|
[[DataLayer sharedInstance] setFullName:@"" forContact:jid andAccount:account.accountID];
|
||
|
MLContact* contact = [MLContact createContactFromJid:jid andAccountID:account.accountID];
|
||
|
if(contact) //ignore updates for jids not in our roster
|
||
|
{
|
||
|
//delete cache to make sure the image will be regenerated
|
||
|
[[MLImageManager sharedInstance] purgeCacheForContact:jid andAccount:account.accountID];
|
||
|
[[MLNotificationQueue currentQueue] postNotificationName:kMonalContactRefresh object:account userInfo:@{
|
||
|
@"contact": contact
|
||
|
}];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$$
|
||
|
|
||
|
$$class_handler(bookmarks2Handler, $$ID(xmpp*, account), $$ID(NSString*, jid), $$ID(NSString*, type), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data))
|
||
|
if(!account.connectionProperties.supportsBookmarksCompat)
|
||
|
{
|
||
|
DDLogWarn(@"Ignoring new XEP-0402 bookmarks, server does not support syncing between XEP-0048 and XEP-0402!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//type will be "publish", "retract", "purge" or "delete". "publish" and "retract" will have the data dictionary filled with id --> data pairs
|
||
|
//the data for "publish" is the item node with the given id, the data for "retract" is always @YES
|
||
|
if(![jid isEqualToString:account.connectionProperties.identity.jid])
|
||
|
{
|
||
|
DDLogWarn(@"Ignoring bookmarks update not coming from our own jid");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
NSSet* ownFavorites = [[DataLayer sharedInstance] listMucsForAccount:account.accountID];
|
||
|
|
||
|
//new/updated bookmarks
|
||
|
if([type isEqualToString:@"publish"])
|
||
|
{
|
||
|
//iterate through all conference elements provided
|
||
|
for(NSString* itemId in data)
|
||
|
{
|
||
|
//we ignore the conference name (the name will be taken from the muc itself)
|
||
|
//NSString* name = [data[itemId] findFirst:@"{urn:xmpp:bookmarks:1}conference@name"];
|
||
|
NSString* room = [itemId lowercaseString];
|
||
|
NSString* nick = [data[itemId] findFirst:@"{urn:xmpp:bookmarks:1}conference/nick#"];
|
||
|
//ignore password protected mucs
|
||
|
if([data[itemId] check:@"{urn:xmpp:bookmarks:1}conference/password"])
|
||
|
continue;
|
||
|
NSNumber* autojoin = [data[itemId] findFirst:@"{urn:xmpp:bookmarks:1}conference@autojoin|bool"];
|
||
|
if(autojoin == nil)
|
||
|
autojoin = @NO; //default value specified in xep
|
||
|
|
||
|
//check if this is a new entry with autojoin=true
|
||
|
if(![ownFavorites containsObject:room] && [autojoin boolValue])
|
||
|
{
|
||
|
DDLogInfo(@"Entering muc '%@' on account %@ because it got added to bookmarks...", room, account.accountID);
|
||
|
//make sure we update our favorites table right away, to counter any race conditions when joining multiple mucs with one bookmarks update
|
||
|
if(nick == nil)
|
||
|
nick = [account.mucProcessor calculateNickForMuc:room];
|
||
|
//this will record the desired nickname: the mucProcessor will pick that up and use it to join the muc
|
||
|
[[DataLayer sharedInstance] addMucFavorite:room forAccountID:account.accountID andMucNick:nick];
|
||
|
//try to join muc, but don't perform a bookmarks update (this muc came in through a bookmark already)
|
||
|
[account.mucProcessor sendDiscoQueryFor:room withJoin:YES andBookmarksUpdate:NO];
|
||
|
}
|
||
|
//check if it is a known entry that changed autojoin to false
|
||
|
else if([ownFavorites containsObject:room] && ![autojoin boolValue])
|
||
|
{
|
||
|
DDLogInfo(@"Leaving muc '%@' on account %@ because not listed as autojoin=true in bookmarks...", room, account.accountID);
|
||
|
//delete local favorites entry and leave room afterwards, but keep buddylist entry because only the autojoin flag changed
|
||
|
[account.mucProcessor leave:room withBookmarksUpdate:NO keepBuddylistEntry:YES];
|
||
|
}
|
||
|
//check for nickname changes
|
||
|
else if([ownFavorites containsObject:room] && nick != nil)
|
||
|
{
|
||
|
NSString* oldNick = [[DataLayer sharedInstance] ownNickNameforMuc:room forAccount:account.accountID];
|
||
|
if(![nick isEqualToString:oldNick])
|
||
|
{
|
||
|
DDLogInfo(@"Updating muc '%@' nick on account %@ in database to nick provided by bookmarks: '%@'...", room, account.accountID, nick);
|
||
|
|
||
|
//update muc nickname in database
|
||
|
[[DataLayer sharedInstance] updateOwnNickName:nick forMuc:room forAccount:account.accountID];
|
||
|
[[DataLayer sharedInstance] addMucFavorite:room forAccountID:account.accountID andMucNick:nick]; //this will upate the already existing favorites entry
|
||
|
|
||
|
//rejoin the muc (e.g. change nick)
|
||
|
//we don't have to do a full disco because we are sure this is a real muc and we are joined already
|
||
|
//(only real mucs are part of our local favorites list and this list is joined automatically)
|
||
|
[account.mucProcessor sendJoinPresenceFor:room];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if([type isEqualToString:@"retract"])
|
||
|
{
|
||
|
for(NSString* itemId in data)
|
||
|
{
|
||
|
NSString* room = [itemId lowercaseString];
|
||
|
if([ownFavorites containsObject:room])
|
||
|
{
|
||
|
DDLogInfo(@"Leaving muc '%@' on account %@ because not listed in bookmarks anymore...", room, account.accountID);
|
||
|
//delete local favorites entry and leave room afterwards
|
||
|
[account.mucProcessor leave:room withBookmarksUpdate:NO keepBuddylistEntry:NO];
|
||
|
}
|
||
|
else
|
||
|
DDLogVerbose(@"Ignoring retracted bookmark because not listed in muc_favorites already...");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//deleted/purged node (e.g. all bookmarks deleted)
|
||
|
//--> remove and leave all mucs
|
||
|
for(NSString* room in ownFavorites)
|
||
|
{
|
||
|
DDLogInfo(@"Leaving muc '%@' on account %@ because all bookmarks got deleted...", room, account.accountID);
|
||
|
//delete local favorites entry and leave room afterwards
|
||
|
[account.mucProcessor leave:room withBookmarksUpdate:NO keepBuddylistEntry:NO];
|
||
|
}
|
||
|
}
|
||
|
$$
|
||
|
|
||
|
$$class_handler(handleBookmarks2FetchResult, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data))
|
||
|
if(!account.connectionProperties.supportsBookmarksCompat)
|
||
|
{
|
||
|
DDLogWarn(@"Ignoring new XEP-0402 bookmarks, server does not support syncing between XEP-0048 and XEP-0402!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!success)
|
||
|
{
|
||
|
//item-not-found means: no bookmarks in storage --> use an empty data dict
|
||
|
if([errorIq check:@"error/{urn:ietf:params:xml:ns:xmpp-stanzas}item-not-found"])
|
||
|
data = @{};
|
||
|
else
|
||
|
{
|
||
|
DDLogWarn(@"Could not fetch bookmarks from pep prior to publishing!");
|
||
|
[self handleErrorWithDescription:NSLocalizedString(@"Failed to save groupchat bookmarks", @"") andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:YES];
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NSString* max_items = @"255"; //fallback for servers not supporting "max"
|
||
|
if(account.connectionProperties.supportsPubSubMax)
|
||
|
max_items = @"max";
|
||
|
NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
|
||
|
|
||
|
NSSet* ownFavorites = [[DataLayer sharedInstance] listMucsForAccount:account.accountID];
|
||
|
DDLogVerbose(@"Own favorites: %@", ownFavorites);
|
||
|
|
||
|
//filter passwort protected mucs and make sure jids (the item ids) are always lowercase
|
||
|
NSMutableDictionary* _data = [NSMutableDictionary new];
|
||
|
for(NSString* itemId in data)
|
||
|
{
|
||
|
if([data[itemId] check:@"{urn:xmpp:bookmarks:1}conference/password"])
|
||
|
{
|
||
|
DDLogVerbose(@"Not copying muc %@ to bookmark data: password protected", itemId);
|
||
|
continue;
|
||
|
}
|
||
|
_data[[itemId lowercaseString]] = data[itemId];
|
||
|
}
|
||
|
DDLogVerbose(@"Mucs listed in bookmarks2: %@", [_data allKeys]);
|
||
|
|
||
|
//handle all changes of existing bookmarks
|
||
|
for(NSString* room in _data)
|
||
|
{
|
||
|
MLXMLNode* item = _data[room];
|
||
|
|
||
|
//we ignore the conference name (the name will be taken from the muc itself)
|
||
|
//NSString* name = [_data[room] findFirst:@"{urn:xmpp:bookmarks:1}conference@name"];
|
||
|
//NSString* nick = [_data[room] findFirst:@"{urn:xmpp:bookmarks:1}conference/nick#"];
|
||
|
NSNumber* autojoin = [item findFirst:@"{urn:xmpp:bookmarks:1}conference@autojoin|bool"];
|
||
|
if(autojoin == nil)
|
||
|
autojoin = @NO; //default value specified in xep
|
||
|
|
||
|
//check if the bookmark exists with autojoin==false and only update the autojoin and nick values, if true
|
||
|
if([ownFavorites containsObject:room] && ![autojoin boolValue])
|
||
|
{
|
||
|
DDLogInfo(@"Updating autojoin of bookmarked muc '%@' on account %@ to 'true'...", room, account.accountID);
|
||
|
|
||
|
//add or update nickname
|
||
|
NSString* nick = [[DataLayer sharedInstance] ownNickNameforMuc:room forAccount:account.accountID];
|
||
|
if(nick != nil)
|
||
|
{
|
||
|
if(![item check:@"{urn:xmpp:bookmarks:1}conference/nick"])
|
||
|
[[item findFirst:@"{urn:xmpp:bookmarks:1}conference"] addChildNode:[[MLXMLNode alloc] initWithElement:@"nick"]];
|
||
|
((MLXMLNode*)[item findFirst:@"{urn:xmpp:bookmarks:1}conference/nick"]).data = nick;
|
||
|
}
|
||
|
|
||
|
//update autojoin value to true
|
||
|
((MLXMLNode*)[item findFirst:@"{urn:xmpp:bookmarks:1}conference"]).attributes[@"autojoin"] = @"true";
|
||
|
|
||
|
//publish this bookmark item again
|
||
|
[account.pubsub publishItem:item onNode:@"urn:xmpp:bookmarks:1" withConfigOptions:@{
|
||
|
@"pubsub#persist_items": @"true",
|
||
|
@"pubsub#access_model": @"whitelist",
|
||
|
@"pubsub#max_items": max_items,
|
||
|
} andHandler:$newHandler(self, bookmarks2Published, $ID(room))];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//add all mucs not yet listed in bookmarks
|
||
|
NSMutableSet* toAdd = [ownFavorites mutableCopy];
|
||
|
[toAdd minusSet:[NSSet setWithArray:[_data allKeys]]];
|
||
|
for(NSString* room in toAdd)
|
||
|
{
|
||
|
DDLogInfo(@"Adding muc '%@' on account %@ to bookmarks...", room, account.accountID);
|
||
|
NSString* nick = [[DataLayer sharedInstance] ownNickNameforMuc:room forAccount:account.accountID];
|
||
|
[account.pubsub publishItem:
|
||
|
[[MLXMLNode alloc] initWithElement:@"item" withAttributes:@{@"id": room} andChildren:@[
|
||
|
[[MLXMLNode alloc] initWithElement:@"conference" andNamespace:@"urn:xmpp:bookmarks:1" withAttributes:@{
|
||
|
@"autojoin": @"true",
|
||
|
} andChildren:@[
|
||
|
nilWrapper(nick != nil ? [[MLXMLNode alloc] initWithElement:@"nick" withAttributes:@{} andChildren:@[] andData:nick] : nil),
|
||
|
[[MLXMLNode alloc] initWithElement:@"extensions" withAttributes:@{} andChildren:@[
|
||
|
[[MLXMLNode alloc] initWithElement:@"added-by" andNamespace:@"urn:monal.im:bookmarks:info" withAttributes:@{
|
||
|
@"name": @"Monal",
|
||
|
@"version": infoDict[@"CFBundleShortVersionString"],
|
||
|
@"build": infoDict[@"CFBundleVersion"],
|
||
|
} andChildren:@[] andData:nil]
|
||
|
] andData:nil]
|
||
|
]andData:nil]
|
||
|
] andData:nil]
|
||
|
onNode:@"urn:xmpp:bookmarks:1" withConfigOptions:@{
|
||
|
@"pubsub#persist_items": @"true",
|
||
|
@"pubsub#access_model": @"whitelist",
|
||
|
@"pubsub#max_items": max_items,
|
||
|
} andHandler:$newHandler(self, bookmarks2Published, $ID(room))];
|
||
|
}
|
||
|
|
||
|
//remove all mucs not listed in local favorites table
|
||
|
NSMutableSet* toRemove = [NSMutableSet setWithArray:[_data allKeys]];
|
||
|
[toRemove minusSet:ownFavorites];
|
||
|
for(NSString* room in toRemove)
|
||
|
{
|
||
|
DDLogInfo(@"Removing muc '%@' on account %@ from bookmarks...", room, account.accountID);
|
||
|
[account.pubsub retractItemWithId:room onNode:@"urn:xmpp:bookmarks:1" andHandler:$newHandler(self, bookmarks2Retracted, $ID(room))];
|
||
|
}
|
||
|
$$
|
||
|
|
||
|
$$class_handler(bookmarks2Published, $$ID(xmpp*, account), $$ID(NSString*, room), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason))
|
||
|
if(!account.connectionProperties.supportsBookmarksCompat)
|
||
|
{
|
||
|
DDLogWarn(@"Ignoring new XEP-0402 bookmarks, server does not support syncing between XEP-0048 and XEP-0402!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!success)
|
||
|
{
|
||
|
DDLogWarn(@"Could not publish bookmark for muc '%@' to pep!", room);
|
||
|
[self handleErrorWithDescription:[NSString stringWithFormat:NSLocalizedString(@"Failed to save bookmark for Group/Channel: %@", @""), room] andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:YES];
|
||
|
return;
|
||
|
}
|
||
|
DDLogDebug(@"Published bookmark for muc '%@' to pep", room);
|
||
|
$$
|
||
|
|
||
|
$$class_handler(bookmarks2Retracted, $$ID(xmpp*, account), $$ID(NSString*, room), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason))
|
||
|
if(!account.connectionProperties.supportsBookmarksCompat)
|
||
|
{
|
||
|
DDLogWarn(@"Ignoring new XEP-0402 bookmarks, server does not support syncing between XEP-0048 and XEP-0402!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!success)
|
||
|
{
|
||
|
DDLogWarn(@"Could not retract bookmark for muc '%@' from pep!", room);
|
||
|
[self handleErrorWithDescription:[NSString stringWithFormat:NSLocalizedString(@"Failed to remove bookmark for Group/Channel: %@", @""), room] andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:YES];
|
||
|
return;
|
||
|
}
|
||
|
DDLogDebug(@"Retracted bookmark for muc '%@' from pep", room);
|
||
|
$$
|
||
|
|
||
|
$$class_handler(bookmarksHandler, $$ID(xmpp*, account), $$ID(NSString*, jid), $$ID(NSString*, type), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data))
|
||
|
if(account.connectionProperties.supportsBookmarksCompat)
|
||
|
{
|
||
|
DDLogInfo(@"Ignoring old XEP-0048 bookmarks, server supports syncing between XEP-0048 and XEP-0402...");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(![jid isEqualToString:account.connectionProperties.identity.jid])
|
||
|
{
|
||
|
DDLogWarn(@"Ignoring bookmarks update not coming from our own jid");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
NSSet* ownFavorites = [[DataLayer sharedInstance] listMucsForAccount:account.accountID];
|
||
|
|
||
|
//new/updated bookmarks
|
||
|
if([type isEqualToString:@"publish"])
|
||
|
{
|
||
|
for(NSString* itemId in data)
|
||
|
{
|
||
|
//iterate through all conference elements provided
|
||
|
NSMutableSet* bookmarkedMucs = [NSMutableSet new];
|
||
|
for(MLXMLNode* conference in [data[itemId] find:@"{storage:bookmarks}storage/conference"])
|
||
|
{
|
||
|
//we ignore the conference name (the name will be taken from the muc itself)
|
||
|
//NSString* name = [conference findFirst:@"/@name"];
|
||
|
NSString* room = [[conference findFirst:@"/@jid"] lowercaseString];
|
||
|
//ignore non-xep-compliant entries
|
||
|
if(!room)
|
||
|
{
|
||
|
DDLogError(@"Received non-xep-compliant bookmarks entry, ignoring: %@", conference);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//ignore password protected mucs
|
||
|
if([conference check:@"password"])
|
||
|
continue;
|
||
|
|
||
|
[bookmarkedMucs addObject:room];
|
||
|
NSString* nick = [conference findFirst:@"nick#"];
|
||
|
NSNumber* autojoin = [conference findFirst:@"/@autojoin|bool"];
|
||
|
if(autojoin == nil)
|
||
|
autojoin = @NO; //default value specified in xep
|
||
|
|
||
|
//check if this is a new entry with autojoin=true
|
||
|
if(![ownFavorites containsObject:room] && [autojoin boolValue])
|
||
|
{
|
||
|
DDLogInfo(@"Entering muc '%@' on account %@ because it got added to bookmarks...", room, account.accountID);
|
||
|
//make sure we update our favorites table right away, to counter any race conditions when joining multiple mucs with one bookmarks update
|
||
|
if(nick == nil)
|
||
|
nick = [account.mucProcessor calculateNickForMuc:room];
|
||
|
//this will record the desired nickname: the mucProcessor will pick that up and use it to join the muc
|
||
|
[[DataLayer sharedInstance] addMucFavorite:room forAccountID:account.accountID andMucNick:nick];
|
||
|
//try to join muc, but don't perform a bookmarks update (this muc came in through a bookmark already)
|
||
|
[account.mucProcessor sendDiscoQueryFor:room withJoin:YES andBookmarksUpdate:NO];
|
||
|
}
|
||
|
//check if it is a known entry that changed autojoin to false
|
||
|
else if([ownFavorites containsObject:room] && ![autojoin boolValue])
|
||
|
{
|
||
|
DDLogInfo(@"Leaving muc '%@' on account %@ because not listed as autojoin=true in bookmarks...", room, account.accountID);
|
||
|
//delete local favorites entry and leave room afterwards, but keep buddylist entry because only the autojoin flag changed
|
||
|
[account.mucProcessor leave:room withBookmarksUpdate:NO keepBuddylistEntry:YES];
|
||
|
}
|
||
|
//check for nickname changes
|
||
|
else if([ownFavorites containsObject:room] && nick != nil)
|
||
|
{
|
||
|
NSString* oldNick = [[DataLayer sharedInstance] ownNickNameforMuc:room forAccount:account.accountID];
|
||
|
if(![nick isEqualToString:oldNick])
|
||
|
{
|
||
|
DDLogInfo(@"Updating muc '%@' nick on account %@ in database to nick provided by bookmarks: '%@'...", room, account.accountID, nick);
|
||
|
|
||
|
//update muc nickname in database
|
||
|
[[DataLayer sharedInstance] updateOwnNickName:nick forMuc:room forAccount:account.accountID];
|
||
|
[[DataLayer sharedInstance] addMucFavorite:room forAccountID:account.accountID andMucNick:nick]; //this will upate the already existing favorites entry
|
||
|
|
||
|
//rejoin the muc (e.g. change nick)
|
||
|
//we don't have to do a full disco because we are sure this is a real muc and we are joined already
|
||
|
//(only real mucs are part of our local favorites list and this list is joined automatically)
|
||
|
[account.mucProcessor sendJoinPresenceFor:room];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//remove and leave all mucs removed from bookmarks
|
||
|
NSMutableSet* toLeave = [ownFavorites mutableCopy];
|
||
|
[toLeave minusSet:bookmarkedMucs];
|
||
|
for(NSString* room in toLeave)
|
||
|
{
|
||
|
DDLogInfo(@"Leaving muc '%@' on account %@ because not listed in bookmarks anymore...", room, account.accountID);
|
||
|
//delete local favorites entry and leave room afterwards
|
||
|
[account.mucProcessor leave:room withBookmarksUpdate:NO keepBuddylistEntry:NO];
|
||
|
}
|
||
|
|
||
|
return; //we only need the first pep item (there should be only one item in the first place)
|
||
|
}
|
||
|
//FALLTHROUGH to "delete all" if no item was found
|
||
|
}
|
||
|
//deleted/purged node or retracted item (e.g. all bookmarks deleted)
|
||
|
//--> remove and leave all mucs
|
||
|
for(NSString* room in ownFavorites)
|
||
|
{
|
||
|
DDLogInfo(@"Leaving muc '%@' on account %@ because all bookmarks got deleted...", room, account.accountID);
|
||
|
//delete local favorites entry and leave room afterwards
|
||
|
[account.mucProcessor leave:room withBookmarksUpdate:NO keepBuddylistEntry:NO];
|
||
|
}
|
||
|
$$
|
||
|
|
||
|
$$class_handler(handleBookarksFetchResult, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason), $_ID((NSDictionary<NSString*, MLXMLNode*>*), data))
|
||
|
if(account.connectionProperties.supportsBookmarksCompat)
|
||
|
{
|
||
|
DDLogInfo(@"Ignoring old XEP-0048 bookmarks, server supports syncing between XEP-0048 and XEP-0402...");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!success)
|
||
|
{
|
||
|
//item-not-found means: no bookmarks in storage --> use an empty data dict
|
||
|
if([errorIq check:@"error/{urn:ietf:params:xml:ns:xmpp-stanzas}item-not-found"])
|
||
|
data = @{};
|
||
|
else
|
||
|
{
|
||
|
DDLogWarn(@"Could not fetch bookmarks from pep prior to publishing!");
|
||
|
[self handleErrorWithDescription:NSLocalizedString(@"Failed to save groupchat bookmarks", @"") andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:YES];
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL changed = NO;
|
||
|
NSSet* ownFavorites = [[DataLayer sharedInstance] listMucsForAccount:account.accountID];
|
||
|
|
||
|
for(NSString* itemId in data)
|
||
|
{
|
||
|
//ignore non-xep-compliant data and continue as if no data was received at all
|
||
|
if(![data[itemId] check:@"{storage:bookmarks}storage"])
|
||
|
{
|
||
|
DDLogError(@"Received non-xep-compliant bookmarks data: %@", data);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
NSMutableSet* bookmarkedMucs = [NSMutableSet new];
|
||
|
for(MLXMLNode* conference in [data[itemId] find:@"{storage:bookmarks}storage/conference"])
|
||
|
{
|
||
|
//we ignore the conference name (the name will be taken from the muc itself)
|
||
|
//NSString* name = [conference findFirst:@"/@name"];
|
||
|
NSString* room = [[conference findFirst:@"/@jid"] lowercaseString];
|
||
|
//ignore non-xep-compliant entries
|
||
|
if(!room)
|
||
|
{
|
||
|
DDLogError(@"Received non-xep-compliant bookmarks entry, ignoring: %@", conference);
|
||
|
continue;
|
||
|
}
|
||
|
[bookmarkedMucs addObject:room];
|
||
|
NSNumber* autojoin = [conference findFirst:@"/@autojoin|bool"];
|
||
|
if(autojoin == nil)
|
||
|
autojoin = @NO; //default value specified in xep
|
||
|
|
||
|
//check if the bookmark exists with autojoin==false and only update the autojoin and nick values, if true
|
||
|
if([ownFavorites containsObject:room] && ![autojoin boolValue])
|
||
|
{
|
||
|
DDLogInfo(@"Updating autojoin of bookmarked muc '%@' on account %@ to 'true'...", room, account.accountID);
|
||
|
|
||
|
//add or update nickname
|
||
|
NSString* nick = [[DataLayer sharedInstance] ownNickNameforMuc:room forAccount:account.accountID];
|
||
|
if(nick != nil)
|
||
|
{
|
||
|
if(![conference check:@"nick"])
|
||
|
[conference addChildNode:[[MLXMLNode alloc] initWithElement:@"nick"]];
|
||
|
((MLXMLNode*)[conference findFirst:@"nick"]).data = [[DataLayer sharedInstance] ownNickNameforMuc:room forAccount:account.accountID];
|
||
|
}
|
||
|
|
||
|
//update autojoin value to true
|
||
|
conference.attributes[@"autojoin"] = @"true";
|
||
|
changed = YES;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//add all mucs not yet listed in bookmarks
|
||
|
NSMutableSet* toAdd = [ownFavorites mutableCopy];
|
||
|
[toAdd minusSet:bookmarkedMucs];
|
||
|
for(NSString* room in toAdd)
|
||
|
{
|
||
|
DDLogInfo(@"Adding muc '%@' on account %@ to bookmarks...", room, account.accountID);
|
||
|
NSString* nick = [[DataLayer sharedInstance] ownNickNameforMuc:room forAccount:account.accountID];
|
||
|
[[data[itemId] findFirst:@"{storage:bookmarks}storage"] addChildNode:[[MLXMLNode alloc] initWithElement:@"conference" withAttributes:@{
|
||
|
@"jid": room,
|
||
|
@"name": [[MLContact createContactFromJid:room andAccountID:account.accountID] contactDisplayName],
|
||
|
@"autojoin": @"true",
|
||
|
} andChildren:(nick != nil ? @[[[MLXMLNode alloc] initWithElement:@"nick" withAttributes:@{} andChildren:@[] andData:nick]] : @[]) andData:nil]];
|
||
|
changed = YES;
|
||
|
}
|
||
|
|
||
|
//remove all mucs not listed in local favorites table
|
||
|
NSMutableSet* toRemove = [bookmarkedMucs mutableCopy];
|
||
|
[toRemove minusSet:ownFavorites];
|
||
|
for(NSString* room in toRemove)
|
||
|
{
|
||
|
DDLogInfo(@"Removing muc '%@' on account %@ from bookmarks...", room, account.accountID);
|
||
|
[[data[itemId] findFirst:@"{storage:bookmarks}storage"] removeChildNode:[data[itemId] findFirst:@"{storage:bookmarks}storage/conference<jid=%@>", room]];
|
||
|
changed = YES;
|
||
|
}
|
||
|
|
||
|
//publish new bookmarks if something was changed
|
||
|
if(changed)
|
||
|
[account.pubsub publishItem:data[itemId] onNode:@"storage:bookmarks" withConfigOptions:@{
|
||
|
@"pubsub#persist_items": @"true",
|
||
|
@"pubsub#access_model": @"whitelist"
|
||
|
} andHandler:$newHandler(self, bookmarksPublished)];
|
||
|
|
||
|
//we only need the first pep item (there should be only one item in the first place)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//don't publish an empty bookmarks node if there is nothing to publish at all
|
||
|
if([ownFavorites count] == 0)
|
||
|
{
|
||
|
DDLogInfo(@"neither a pep item was found, nor do we have any local muc favorites: don't publish anything");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
DDLogInfo(@"no pep item was found: publish our bookmarks the first time");
|
||
|
NSMutableArray* conferences = [NSMutableArray new];
|
||
|
for(NSString* room in ownFavorites)
|
||
|
{
|
||
|
DDLogInfo(@"Adding muc '%@' on account %@ to bookmarks...", room, account.accountID);
|
||
|
NSString* nick = [[DataLayer sharedInstance] ownNickNameforMuc:room forAccount:account.accountID];
|
||
|
[conferences addObject:[[MLXMLNode alloc] initWithElement:@"conference" withAttributes:@{
|
||
|
@"jid": room,
|
||
|
@"name": [[MLContact createContactFromJid:room andAccountID:account.accountID] contactDisplayName],
|
||
|
@"autojoin": @"true",
|
||
|
} andChildren:(nick != nil ? @[[[MLXMLNode alloc] initWithElement:@"nick" withAttributes:@{} andChildren:@[] andData:nick]] : @[]) andData:nil]];
|
||
|
}
|
||
|
[account.pubsub publishItem:
|
||
|
[[MLXMLNode alloc] initWithElement:@"item" withAttributes:@{@"id": @"current"} andChildren:@[
|
||
|
[[MLXMLNode alloc] initWithElement:@"storage" andNamespace:@"storage:bookmarks" withAttributes:@{} andChildren:conferences andData:nil]
|
||
|
] andData:nil]
|
||
|
onNode:@"storage:bookmarks" withConfigOptions:@{
|
||
|
@"pubsub#persist_items": @"true",
|
||
|
@"pubsub#access_model": @"whitelist"
|
||
|
} andHandler:$newHandler(self, bookmarksPublished)];
|
||
|
$$
|
||
|
|
||
|
$$class_handler(bookmarksPublished, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason))
|
||
|
if(account.connectionProperties.supportsBookmarksCompat)
|
||
|
{
|
||
|
DDLogInfo(@"Ignoring old XEP-0048 bookmarks, server supports syncing between XEP-0048 and XEP-0402...");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!success)
|
||
|
{
|
||
|
DDLogWarn(@"Could not publish bookmarks to pep!");
|
||
|
[self handleErrorWithDescription:NSLocalizedString(@"Failed to save groupchat bookmarks", @"") andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:YES];
|
||
|
return;
|
||
|
}
|
||
|
DDLogDebug(@"Published bookmarks to pep");
|
||
|
$$
|
||
|
|
||
|
$$class_handler(rosterNamePublished, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason))
|
||
|
if(!success)
|
||
|
{
|
||
|
DDLogWarn(@"Could not publish roster name to pep!");
|
||
|
[self handleErrorWithDescription:NSLocalizedString(@"Failed to publish own nickname", @"") andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:NO];
|
||
|
return;
|
||
|
}
|
||
|
DDLogDebug(@"Published roster name to pep");
|
||
|
$$
|
||
|
|
||
|
$$class_handler(rosterNameDeleted, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason))
|
||
|
if(!success)
|
||
|
{
|
||
|
//item-not-found means: nick already deleted --> ignore this error
|
||
|
if([errorIq check:@"error/{urn:ietf:params:xml:ns:xmpp-stanzas}item-not-found"])
|
||
|
{
|
||
|
DDLogWarn(@"Roster name was already deleted from pep, ignoring error!");
|
||
|
return;
|
||
|
}
|
||
|
DDLogWarn(@"Could not remove roster name from pep!");
|
||
|
[self handleErrorWithDescription:NSLocalizedString(@"Failed to delete own nickname", @"") andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:NO];
|
||
|
return;
|
||
|
}
|
||
|
DDLogDebug(@"Removed roster name from pep");
|
||
|
$$
|
||
|
|
||
|
$$class_handler(avatarDeleted, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason))
|
||
|
if(!success)
|
||
|
{
|
||
|
//item-not-found means: avatar already deleted --> ignore this error
|
||
|
if([errorIq check:@"error/{urn:ietf:params:xml:ns:xmpp-stanzas}item-not-found"])
|
||
|
{
|
||
|
DDLogWarn(@"Avatar image was already deleted from pep, ignoring error!");
|
||
|
return;
|
||
|
}
|
||
|
DDLogWarn(@"Could not delete avatar image from pep!");
|
||
|
[self handleErrorWithDescription:NSLocalizedString(@"Failed to delete own avatar", @"") andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:NO];
|
||
|
return;
|
||
|
}
|
||
|
DDLogDebug(@"Removed avatar from pep");
|
||
|
$$
|
||
|
|
||
|
$$class_handler(avatarMetadataPublished, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason))
|
||
|
if(!success)
|
||
|
{
|
||
|
DDLogWarn(@"Could not publish avatar metadata to pep!");
|
||
|
[self handleErrorWithDescription:NSLocalizedString(@"Failed to publish own avatar", @"") andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:NO];
|
||
|
return;
|
||
|
}
|
||
|
DDLogDebug(@"Published avatar metadata to pep");
|
||
|
$$
|
||
|
|
||
|
$$class_handler(avatarDataPublished, $$ID(xmpp*, account), $$BOOL(success), $_ID(XMPPIQ*, errorIq), $_ID(NSString*, errorReason), $$ID(NSString*, imageHash), $$UINTEGER(imageBytesLen))
|
||
|
if(!success)
|
||
|
{
|
||
|
DDLogWarn(@"Could not publish avatar image data for hash %@!", imageHash);
|
||
|
[self handleErrorWithDescription:NSLocalizedString(@"Failed to publish own avatar", @"") andAccount:account andErrorIq:errorIq andErrorReason:errorReason andIsSevere:NO];
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
DDLogInfo(@"Avatar image data for hash %@ published successfully, now publishing metadata", imageHash);
|
||
|
|
||
|
//publish metadata node (must be done *after* publishing the new data node)
|
||
|
[account.pubsub publishItem:
|
||
|
[[MLXMLNode alloc] initWithElement:@"item" withAttributes:@{@"id": imageHash} andChildren:@[
|
||
|
[[MLXMLNode alloc] initWithElement:@"metadata" andNamespace:@"urn:xmpp:avatar:metadata" withAttributes:@{} andChildren:@[
|
||
|
[[MLXMLNode alloc] initWithElement:@"info" withAttributes:@{
|
||
|
@"id": imageHash,
|
||
|
@"type": @"image/jpeg",
|
||
|
@"bytes": [NSString stringWithFormat:@"%lu", (unsigned long)imageBytesLen]
|
||
|
} andChildren:@[] andData:nil]
|
||
|
] andData:nil]
|
||
|
] andData:nil]
|
||
|
onNode:@"urn:xmpp:avatar:metadata" withConfigOptions:@{
|
||
|
@"pubsub#persist_items": @"true",
|
||
|
@"pubsub#access_model": @"presence"
|
||
|
} andHandler:$newHandler(self, avatarMetadataPublished)];
|
||
|
$$
|
||
|
|
||
|
+(void) handleErrorWithDescription:(NSString*) description andAccount:(xmpp*) account andErrorIq:(XMPPIQ*) errorIq andErrorReason:(NSString*) errorReason andIsSevere:(BOOL) isSevere
|
||
|
{
|
||
|
MLAssert(errorIq || errorReason, @"at least one of errorIq or errorReason must be set when calling error handler!");
|
||
|
if(errorIq)
|
||
|
[HelperTools postError:description withNode:errorIq andAccount:account andIsSevere:isSevere];
|
||
|
else if(errorReason)
|
||
|
[HelperTools postError:[NSString stringWithFormat:@"%@: %@", description, errorReason] withNode:nil andAccount:account andIsSevere:isSevere];
|
||
|
}
|
||
|
|
||
|
@end
|