adium/adium

Updated Changes.txt for 1.5.10.4.
adium-1.5.10.4
2017-04-25, Robert Vehse
09440b7b46e0
Updated Changes.txt for 1.5.10.4.
/*
* Adium is the legal property of its developers, whose names are listed in the copyright file included
* with this source distribution.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import "AMPurpleJabberNode.h"
static NSUInteger iqCounter = 0;
@interface AMPurpleJabberNode()
@property (readwrite, copy, nonatomic) NSString *name;
@property (readwrite, copy, nonatomic) NSString *jid;
@property (readwrite, copy, nonatomic) NSString *node;
@property (readwrite, retain, nonatomic) NSSet *features;
@property (readwrite, retain, nonatomic) NSArray *identities;
@property (readwrite, retain, nonatomic) AMPurpleJabberNode *commandsNode;
@property (readwrite, assign, nonatomic) PurpleConnection *gc;
@property (readwrite, retain, nonatomic) NSMutableArray *delegates;
@property (readwrite, retain, nonatomic) NSArray *itemsArray;
@end
static CFArrayCallBacks nonretainingArrayCallbacks = {
.version = 0,
.copyDescription = (CFArrayCopyDescriptionCallBack)CFCopyDescription,
.equal = (CFArrayEqualCallBack)CFEqual,
};
@implementation AMPurpleJabberNode
static void AMPurpleJabberNode_received_data_cb(PurpleConnection *gc, xmlnode **packet, gpointer this) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AMPurpleJabberNode *self = (AMPurpleJabberNode*)this;
// we're receiving *all* packets, so let's filter out those that don't concern us
const char *from = xmlnode_get_attrib(*packet, "from");
if (!from) {
[pool release];
return;
}
if (!(*packet)->name){
[pool release];
return;
}
const char *type = xmlnode_get_attrib(*packet, "type");
if (!type || (strcmp(type, "result") && strcmp(type, "error"))){
[pool release];
return;
}
if (strcmp((*packet)->name, "iq")){
[pool release];
return;
}
if (![[NSString stringWithUTF8String:from] isEqualToString:self.jid]){
[pool release];
return;
}
xmlnode *query = xmlnode_get_child_with_namespace(*packet,"query","http://jabber.org/protocol/disco#info");
if (query) {
if (self.features || self.identities) {
[pool release];
return; // we already have that information
}
const char *queryNode = xmlnode_get_attrib(query,"node");
if ((self.node && !queryNode) || (!self.node && queryNode)){
[pool release];
return;
}
if (queryNode && ![[NSString stringWithUTF8String:queryNode] isEqualToString:self.node]){
[pool release];
return;
}
// it's us, fill in features and identities
NSMutableArray *identities = [NSMutableArray array];
NSMutableSet *features = [NSMutableSet set];
xmlnode *item;
for(item = query->child; item; item = item->next) {
if (item->type == XMLNODE_TYPE_TAG) {
if (!strcmp(item->name, "identity")) {
const char *category = xmlnode_get_attrib(item,"category");
const char *ltype = xmlnode_get_attrib(item, "type");
const char *queryName = xmlnode_get_attrib(item, "name");
[identities addObject:[NSDictionary dictionaryWithObjectsAndKeys:
category?[NSString stringWithUTF8String:category]:[NSNull null], @"category",
ltype?[NSString stringWithUTF8String:ltype]:[NSNull null], @"type",
queryName?[NSString stringWithUTF8String:queryName]:[NSNull null], @"name",
nil]];
} else if (!strcmp(item->name, "feature")) {
const char *var = xmlnode_get_attrib(item, "var");
if (var)
[features addObject:[NSString stringWithUTF8String:var]];
}
}
}
self.identities = identities;
self.features = features;
for (id delegate in self.delegates) {
if ([delegate respondsToSelector:@selector(jabberNodeGotInfo:)])
[delegate jabberNodeGotInfo:self];
}
if ([features containsObject:@"http://jabber.org/protocol/commands"]) {
// in order to avoid endless loops, check if the current node isn't a command by itself (which can't contain other commands)
BOOL isCommand = NO;
NSDictionary *identity;
for (identity in identities) {
if ([[identity objectForKey:@"type"] isEqualToString:@"command-node"]) {
isCommand = YES;
break;
}
}
if (!isCommand) {
// commands have to be prefetched to be available when the user tries to access the context menu
self.commandsNode = [[AMPurpleJabberNode alloc] initWithJID:self.jid
node:@"http://jabber.org/protocol/commands"
name:nil
connection:self.gc];
[self.commandsNode fetchItems];
}
}
[pool release];
return;
}
query = xmlnode_get_child_with_namespace(*packet,"query","http://jabber.org/protocol/disco#items");
if (query) {
if (self.itemsArray) {
[pool release];
return; // we already have that info
}
const char *checkNode = xmlnode_get_attrib(query,"node");
if ((self.node && !checkNode) || (!self.node && checkNode)) {
[pool release];
return;
}
if (checkNode && ![[NSString stringWithUTF8String:checkNode] isEqualToString:self.node]){
[pool release];
return;
}
// it's us, create the subnodes
NSMutableArray *newItems = [NSMutableArray array];
for(xmlnode *item = query->child; item; item = item->next) {
if (item->type == XMLNODE_TYPE_TAG) {
if (!strcmp(item->name, "item")) {
const char *queryJID = xmlnode_get_attrib(item,"jid");
const char *queryNode = xmlnode_get_attrib(item,"node");
const char *queryName = xmlnode_get_attrib(item,"name");
if (queryJID) {
AMPurpleJabberNode *newnode = [[AMPurpleJabberNode alloc] initWithJID:[NSString stringWithUTF8String:queryJID]
node:queryNode ? [NSString stringWithUTF8String:queryNode] : nil
name:queryName ? [NSString stringWithUTF8String:queryName] : nil
connection:self.gc];
// propagate delegates
newnode.delegates = [NSMakeCollectable(CFArrayCreateMutableCopy(kCFAllocatorDefault, /*capacity*/ 0, (CFArrayRef)self.delegates)) autorelease];
[newItems addObject:newnode];
// check if we're a conference service
if ([[self jid] rangeOfString:@"@"].location == NSNotFound) { // we can't be one when we have an @
NSDictionary *identity = nil;
for (identity in self.identities) {
if ([[identity objectForKey:@"category"] isEqualToString:@"conference"]) {
// since we're a conference service, assume that our children are conferences
newnode.identities = [NSArray arrayWithObject:identity];
break;
}
}
if (!identity)
[newnode fetchInfo];
} else
[newnode fetchInfo];
[newnode release];
}
}
}
}
self.itemsArray = newItems;
for (id delegate in self.delegates) {
if ([delegate respondsToSelector:@selector(jabberNodeGotItems:)])
[delegate jabberNodeGotItems:self];
}
}
[pool release];
}
- (id)initWithJID:(NSString*)_jid node:(NSString*)_node name:(NSString*)_name connection:(PurpleConnection*)_gc {
if ((self = [super init])) {
PurplePlugin *jabber = purple_find_prpl("prpl-jabber");
if (!jabber) {
AILog(@"Unable to locate jabber prpl");
[self release];
return nil;
}
self.jid = _jid;
self.node = _node;
self.name = _name;
self.gc = _gc;
self.delegates = [NSMakeCollectable(CFArrayCreateMutable(kCFAllocatorDefault, /*capacity*/ 0, &nonretainingArrayCallbacks)) autorelease];
purple_signal_connect(jabber, "jabber-receiving-xmlnode", self,
PURPLE_CALLBACK(AMPurpleJabberNode_received_data_cb), self);
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
PurplePlugin *jabber = purple_find_prpl("prpl-jabber");
if (!jabber) {
AILog(@"Unable to locate jabber prpl");
[self release];
return nil;
}
AMPurpleJabberNode *copy = [[AMPurpleJabberNode alloc] init];
// share the items, identities and features between copies
// copy the rest, keep delegates separate
copy.jid = self.jid;
copy.node = self.node;
copy.name = self.name;
copy.gc = self.gc;
copy.delegates = [NSMakeCollectable(CFArrayCreateMutable(kCFAllocatorDefault, /*capacity*/ 0, &nonretainingArrayCallbacks)) autorelease];
copy.features = self.features;
copy.identities = self.identities;
copy.itemsArray = self.itemsArray;
purple_signal_connect(jabber, "jabber-receiving-xmlnode", copy,
PURPLE_CALLBACK(AMPurpleJabberNode_received_data_cb), copy);
return copy;
}
- (void)dealloc {
purple_signals_disconnect_by_handle(self);
[jid release];
[node release];
[features release];
[identities release];
[items release];
[name release];
[commands release];
[delegates release];
[super dealloc];
}
- (void)fetchItems {
self.itemsArray = nil;
NSXMLElement *iq = [NSXMLNode elementWithName:@"iq"];
[iq addAttribute:[NSXMLNode attributeWithName:@"type" stringValue:@"get"]];
if (jid)
[iq addAttribute:[NSXMLNode attributeWithName:@"to" stringValue:jid]];
[iq addAttribute:[NSXMLNode attributeWithName:@"id" stringValue:[NSString stringWithFormat:@"%@%lu,",[self className], iqCounter++]]];
NSXMLElement *query = [NSXMLNode elementWithName:@"query"];
[query addNamespace:[NSXMLNode namespaceWithName:@"" stringValue:@"http://jabber.org/protocol/disco#items"]];
if (node)
[query addAttribute:[NSXMLNode attributeWithName:@"node" stringValue:node]];
[iq addChild:query];
NSData *xmlData = [[iq XMLString] dataUsingEncoding:NSUTF8StringEncoding];
NSAssert( INT_MAX >= [xmlData length],
@"More XML data than libpurple can handle. Abort." );
if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->send_raw)
(PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->send_raw)(gc, [xmlData bytes], (int)[xmlData length]);
}
- (void)fetchInfo {
self.features = nil;
self.identities = nil;
NSXMLElement *iq = [NSXMLNode elementWithName:@"iq"];
[iq addAttribute:[NSXMLNode attributeWithName:@"type" stringValue:@"get"]];
if (jid)
[iq addAttribute:[NSXMLNode attributeWithName:@"to" stringValue:jid]];
[iq addAttribute:[NSXMLNode attributeWithName:@"id" stringValue:[NSString stringWithFormat:@"%@%lu",[self className], iqCounter++]]];
NSXMLElement *query = [NSXMLNode elementWithName:@"query"];
[query addNamespace:[NSXMLNode namespaceWithName:@"" stringValue:@"http://jabber.org/protocol/disco#info"]];
if (node)
[query addAttribute:[NSXMLNode attributeWithName:@"node" stringValue:node]];
[iq addChild:query];
NSData *xmlData = [[iq XMLString] dataUsingEncoding:NSUTF8StringEncoding];
NSAssert( INT_MAX >= [xmlData length],
@"More XML data than libpurple can handle. Abort." );
if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->send_raw)
(PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->send_raw)(gc, [xmlData bytes], (gint)[xmlData length]);
}
- (NSArray*)items {
if (!items) {
BOOL isCommand = NO;
for (NSDictionary *identity in identities) {
if ([[identity objectForKey:@"type"] isEqualToString:@"command-node"]) {
isCommand = YES;
break;
}
}
// commands don't contain any other nodes
if (isCommand) {
self.itemsArray = [NSArray array];
return items;
}
}
return items;
}
- (NSArray*)commands {
return [commands items];
}
@synthesize commandsNode = commands, itemsArray = items, identities, features, node, jid, name, gc, delegates;
- (void)addDelegate:(id<AMPurpleJabberNodeDelegate>)delegate {
[delegates addObject:delegate];
}
- (void)removeDelegate:(id<AMPurpleJabberNodeDelegate>)delegate {
[delegates removeObjectIdenticalTo:delegate];
}
@end