Plugins/Bonjour/libezv/Private Classes/AWEzvRendezvousData.m
author Evan Schoenberg
Sun Nov 22 09:58:41 2009 -0600 (2009-11-22)
changeset 2930 61a5bc1a37d6
parent 2794 ca3d8c8b4a3d
child 3087 30703336e5b6
permissions -rw-r--r--
* Fix a crash if `kDNSServiceErr_Invalid` is returned by `TXTRecordSetValue` which occured because the `%@` was being used for a C string
* Handle the situation which caused `kDNSServiceErr_Invalid` to be returned by `TXTRecordSetValue` if setting a long status message (≥ 250 characters) by truncating appropriately.

Fixes #11322
David@0
     1
/*
David@0
     2
 * Project:     Libezv
David@0
     3
 * File:        AWEzvRendezvousData.h
David@0
     4
 *
David@0
     5
 * Version:     1.0
David@0
     6
 * Author:      Andrew Wellington <proton[at]wiretapped.net>
David@0
     7
 *
David@0
     8
 * License:
David@0
     9
 * Copyright (C) 2004-2005 Andrew Wellington.
David@0
    10
 * All rights reserved.
David@0
    11
 * 
David@0
    12
 * Redistribution and use in source and binary forms, with or without
David@0
    13
 * modification, are permitted provided that the following conditions are met:
David@0
    14
 * 
David@0
    15
 * 1. Redistributions of source code must retain the above copyright notice,
David@0
    16
 * this list of conditions and the following disclaimer.
David@0
    17
 * 2. Redistributions in binary form must reproduce the above copyright notice,
David@0
    18
 * this list of conditions and the following disclaimer in the documentation
David@0
    19
 * and/or other materials provided with the distribution.
David@0
    20
 * 
David@0
    21
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
David@0
    22
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
David@0
    23
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
David@0
    24
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
David@0
    25
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
David@0
    26
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
David@0
    27
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
David@0
    28
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
David@0
    29
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
David@0
    30
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
David@0
    31
 */
David@0
    32
 
David@0
    33
#import "AWEzvRendezvousData.h"
David@0
    34
#import "AWEzvSupportRoutines.h"
Evan@2930
    35
#import <AIUtilities/AIStringAdditions.h>
David@0
    36
David@0
    37
@implementation AWEzvRendezvousData
David@0
    38
David@0
    39
/* subnegotiation that appears at start of rendezvous packet */
David@0
    40
/*                             Reserved version? */
David@0
    41
NSString	*subn = @"subn\x00\x00\x00\x01";
David@0
    42
David@0
    43
/* end of subnegotation. significance of value unknown */
David@0
    44
/*                        Reserved unknown       */
David@0
    45
NSString	*endn = @"\x00\x00\x00\x00";
David@0
    46
David@0
    47
/* initialization, create our dictionary */
David@0
    48
-(AWEzvRendezvousData *) init 
David@0
    49
{
David@0
    50
    if ((self = [super init])) {
David@0
    51
		keys = [[NSMutableDictionary dictionary] retain];
David@0
    52
		serial = 1;
David@0
    53
	}
David@0
    54
David@0
    55
    return self;
David@0
    56
}
David@0
    57
David@0
    58
/* intialise given an NSData containing an announcement */
David@0
    59
-(AWEzvRendezvousData *) initWithData:(NSData *)data {
David@0
    60
    UInt32	version;	/* the version of the iChat announcement (?) */
David@0
    61
    UInt32	fieldCount;	/* number of fields in the announcement */
David@0
    62
    UInt16	fieldLen;	/* length of field being read */
David@0
    63
    UInt32	i;		/* read index into data buffer */
David@0
    64
    NSString	*fieldName;	/* name of field */
David@0
    65
    NSString	*fieldContent;	/* contents of field */
David@0
    66
    NSData	*tmpData;	/* temporary data */
David@0
    67
    NSRange	range;		/* range for reading of data */
David@0
    68
David@0
    69
    /* call the standard initialisation */
David@0
    70
    self = [self init];
David@0
    71
    
David@0
    72
    /* check that the length is ok */
David@0
    73
    if ([data length] < ([subn length] + 4 + [endn length])) {
David@0
    74
	AWEzvLog(@"Invalid rendezvous announcement: length %u", [data length]);
David@0
    75
		[self autorelease];
David@0
    76
	return nil;
David@0
    77
    }
David@0
    78
        
David@0
    79
    /* check version (?) of iChat announcement */
David@0
    80
    range.location = 4;
David@0
    81
    range.length = 4;
David@0
    82
    [data getBytes:&version range:range];
David@0
    83
    version = ntohl(version);
David@0
    84
    if (version != 1) {
David@0
    85
	AWEzvLog(@"Invalid rendezvous announcement: incorrect version: %u", version);
David@0
    86
		[self autorelease];
David@0
    87
	return nil;
David@0
    88
    }
David@0
    89
    
David@0
    90
    /* get serial of announcement */
David@0
    91
    range.location = 8;
David@0
    92
    range.length = 4;
David@0
    93
    [data getBytes:&serial range:range];
David@0
    94
    serial = ntohl(serial);
David@0
    95
    
David@0
    96
    /* get field count of data */
David@0
    97
    range.location = 16;
David@0
    98
    range.length = 4;
David@0
    99
    [data getBytes:&fieldCount range:range];
David@0
   100
    fieldCount = ntohl(fieldCount);
David@0
   101
    
David@0
   102
    /* read fields from data */
David@0
   103
    for (i = [subn length] + 4 + [endn length] + 4; i < [data length];) {
David@0
   104
	int binFlag = 0;
David@0
   105
	
David@0
   106
	/* read length of field name */
David@0
   107
	if ([data length] < i + 2) {
David@0
   108
	    AWEzvLog(@"Invalid rendezvous announcement at field name length");
David@0
   109
		[self autorelease];
David@0
   110
	    return nil;
David@0
   111
	}
David@0
   112
	range.location = i;
David@0
   113
	range.length = 2;
David@0
   114
	[data getBytes:&fieldLen range:range];
David@0
   115
	fieldLen = ntohs(fieldLen);
David@0
   116
	fieldLen = fieldLen & 0x7FFF;
David@0
   117
        i = i + 2;
David@0
   118
        
David@0
   119
	/* read field data */
David@0
   120
	if ([data length] < i + fieldLen) {
David@0
   121
	    AWEzvLog(@"Invalid rendezvous announcement at field name");
David@0
   122
		[self autorelease];
David@0
   123
	    return nil;
David@0
   124
	}
David@0
   125
        tmpData = [NSData dataWithBytes:[data bytes] + i length:fieldLen];
David@0
   126
	fieldName = [[[NSString alloc] initWithData:tmpData encoding:NSUTF8StringEncoding] autorelease];
David@0
   127
	i = i + fieldLen;
David@0
   128
	
David@0
   129
	/* read length of field data */
David@0
   130
	if ([data length] < i + 2) {
David@0
   131
	    AWEzvLog(@"Invalid rendezvous announcement at field data length");
David@0
   132
		[self autorelease];
David@0
   133
	    return nil;
David@0
   134
	}
David@0
   135
	range.location = i;
David@0
   136
	range.length = 2;
David@0
   137
	[data getBytes:&fieldLen range:range];
David@0
   138
	fieldLen = ntohs(fieldLen);
David@0
   139
	/* most significant bit in fieldLen is a binary data flag */
David@0
   140
	if ((fieldLen & 0x7FFF) != fieldLen)
David@0
   141
	    binFlag = 1;
David@0
   142
        fieldLen = fieldLen & 0x7FFF;
David@0
   143
	i = i + 2;
David@0
   144
	
David@0
   145
	/* read field data */
David@0
   146
	if ([data length] < i + fieldLen) {
David@0
   147
	    AWEzvLog(@"Invalid rendezvous announcement at field data");
David@0
   148
		[self autorelease];
David@0
   149
	    return nil;
David@0
   150
	}
David@0
   151
        if (!binFlag) {
David@0
   152
            tmpData = [NSData dataWithBytes:[data bytes] + i length:fieldLen];
David@0
   153
            fieldContent = [[[NSString alloc] initWithData:tmpData encoding:NSUTF8StringEncoding] autorelease];
David@0
   154
        } else {
sholt@2699
   155
			fieldContent = [[[NSString alloc] initWithBytes:[data bytes] + i length:fieldLen encoding:NSUTF8StringEncoding] autorelease];
David@0
   156
        }
David@0
   157
        i = i + fieldLen;
David@0
   158
	
David@0
   159
	/* save field information */
David@0
   160
	[self setField:fieldName content:fieldContent];
David@0
   161
    }
David@0
   162
    
David@0
   163
    /* return initialised object */
David@0
   164
    return self;
David@0
   165
}
David@0
   166
David@0
   167
/* intialise rendezvous data with a plist of the data */
David@0
   168
-(AWEzvRendezvousData *) initWithPlist:(NSString *)plist {
David@0
   169
    id		extracted;	/* extracted data from plist */
David@0
   170
    NSData	*xmlData;	/* XML data in NSData form */
David@0
   171
    NSString	*error;		/* error from conversion of plist */
David@0
   172
    NSPropertyListFormat format;/* something we can point at for the format pointer */
David@0
   173
    
David@0
   174
    error = [NSString string];
David@0
   175
    
David@0
   176
    /* create XML data */
David@0
   177
    xmlData = [NSData dataWithBytes:[plist UTF8String] length:[plist length]];
David@0
   178
David@0
   179
    /* extract plist from XML data */
David@0
   180
    format = NSPropertyListXMLFormat_v1_0;
David@0
   181
    extracted = [NSPropertyListSerialization
David@0
   182
		    propertyListFromData:xmlData
David@0
   183
		    mutabilityOption:NSPropertyListImmutable
David@0
   184
		    format:&format
David@0
   185
		    errorDescription:&error];
David@0
   186
David@0
   187
    /* check if there was an error in extraction */
David@0
   188
    if (extracted == nil) {
David@0
   189
	AWEzvLog(@"Unable to extract XML into plist");
David@0
   190
		[self autorelease];
David@0
   191
	return nil;
David@0
   192
    }
David@0
   193
    
David@0
   194
    /* make sure it's an NSData, or reponds to getBytes:range: */
David@0
   195
    if (![extracted respondsToSelector:@selector(getBytes:range:)]) {
David@0
   196
	AWEzvLog(@"Extracted object from XML is not an NSData");
David@0
   197
		[self autorelease];
David@0
   198
	return nil;  
David@0
   199
    }
David@0
   200
David@0
   201
    /* pass it to another initialiser */
David@0
   202
    return [self initWithData:extracted];
David@0
   203
}
David@0
   204
David@0
   205
/* initialise object with a dictionary */
David@0
   206
- (AWEzvRendezvousData *)initWithDictionary:(NSDictionary *)dictionary 
David@0
   207
{
David@0
   208
    if ((self = [super init])) {
David@0
   209
		keys = [dictionary retain];
David@0
   210
		serial++;
David@0
   211
	}	
David@0
   212
    
David@0
   213
    return self;
David@0
   214
}
David@0
   215
David@0
   216
/* initialise object with an AV TXT record */
David@0
   217
- (AWEzvRendezvousData *)initWithAVTxt:(NSString *)txt {
David@0
   218
    NSArray *attribs;
David@0
   219
    NSString *key;
David@0
   220
    
David@0
   221
    self = [self init];
David@0
   222
    
David@0
   223
    attribs = [txt componentsSeparatedByString:@"\001"];
David@0
   224
David@75
   225
    for (key in attribs) {
David@0
   226
	NSRange range;
David@0
   227
	
David@0
   228
	range = [key rangeOfString:@"="];
David@0
   229
	if (range.location != NSNotFound) {
David@0
   230
	    [self setField:[key substringToIndex:range.location]
David@0
   231
		   content:[key substringFromIndex:range.location+1]];
David@0
   232
	}
David@0
   233
    }
David@0
   234
    
David@0
   235
    return self;
David@0
   236
}
David@571
   237
- (AWEzvRendezvousData *) initWithTXTRecordRef:(const unsigned char *) txtRecord length:(uint16_t)len{
David@0
   238
	
David@0
   239
	self = [self init];
David@0
   240
    
David@0
   241
	DNSServiceErrorType txtRecordError;
David@0
   242
	
David@0
   243
	int i, numKeys;
David@0
   244
	numKeys = TXTRecordGetCount(len, txtRecord);
David@0
   245
	for (i=0; i<numKeys; i++) {
David@0
   246
		char key[256];
David@0
   247
		uint8_t valLen;
David@0
   248
		const void *value;
David@0
   249
		
David@0
   250
		txtRecordError = TXTRecordGetItemAtIndex (
David@0
   251
			/* length */ len,
David@0
   252
			/* txtrecord */ txtRecord,
David@0
   253
			/* index */ i,
David@0
   254
			/* keybuffer length */ sizeof(key), 
David@0
   255
		    /* key buffer */ key,
David@0
   256
			/* valueLength */ &valLen,
David@0
   257
			/* value pointer */ &value );
David@0
   258
		if (txtRecordError == kDNSServiceErr_NoError) {
David@0
   259
			NSString *keyString = NULL;
David@0
   260
			NSString *data = NULL;
David@0
   261
			keyString = [NSString stringWithUTF8String: key];
David@0
   262
			
David@0
   263
			if (value) {
David@0
   264
				data = [[[NSString alloc] initWithBytes: value length: valLen encoding: NSUTF8StringEncoding] autorelease];
David@0
   265
			}
David@0
   266
			
David@0
   267
			if (data != NULL && keyString != NULL) {
David@0
   268
				[self setField:keyString content:data];
David@0
   269
			} else {
David@0
   270
				AWEzvLog(@"Creating TXTRecord: No data and No key");
David@0
   271
			}
David@0
   272
			
David@0
   273
			/* AWEzvLog(@"key:%@ value=%@", keyString, data); */
David@0
   274
		} else {
David@0
   275
			AWEzvLog(@"Error reading txt keys");
David@0
   276
		}
David@0
   277
		
David@0
   278
		
David@0
   279
		
David@0
   280
	}
David@0
   281
		
David@0
   282
//	// kind of a hack: munge txtRecord so it's human-readable
David@0
   283
//	if ( len > 0) {
David@0
   284
//		char	*readableText = (char*) malloc( len);
David@0
   285
//		if ( readableText != nil) {
David@0
   286
//			ByteCount   index, subStrLen;
David@0
   287
//			memcpy( readableText, txtRecord, len);
David@0
   288
//			for ( index=0; index < len - 1; index += subStrLen + 1) {
David@0
   289
//				subStrLen = readableText[ index];
David@0
   290
//				readableText[ index] = '\n';
David@0
   291
//			}
David@0
   292
//			//NSLog(@"%@\n\n",[NSString stringWithCString:&readableText[1] length:len - 1]);
David@0
   293
//			free( readableText);
David@0
   294
//		}
David@0
   295
//	}
David@0
   296
	
David@0
   297
	return self;
David@0
   298
	
David@0
   299
}
David@0
   300
David@0
   301
/* deallocate, destroy our dictionary */
David@0
   302
- (void)dealloc
David@0
   303
{
David@0
   304
	[keys release];
David@0
   305
	[super dealloc];
David@0
   306
}
David@0
   307
David@0
   308
/* sets a field in the rendezvous data structures */
David@0
   309
-(void) setField:(NSString *)fieldName content:(NSObject *)content {
David@0
   310
    if (content == nil || fieldName == nil)
David@0
   311
        return;
David@0
   312
    
David@0
   313
    [keys setObject:content forKey:fieldName];
David@0
   314
    serial++;
David@0
   315
}
David@0
   316
David@0
   317
/* delete a field in the rendezvous data structure */
David@0
   318
-(void) deleteField:(NSString *)fieldName {
David@0
   319
    if ([keys objectForKey:fieldName] != nil)
David@0
   320
        [keys removeObjectForKey:fieldName];
David@0
   321
}
David@0
   322
David@0
   323
/* get a field from the rendezvous data structure */
David@0
   324
-(NSString *) getField:(NSString *)fieldName {
David@0
   325
    return [[[keys objectForKey:fieldName] copy] autorelease];
David@0
   326
}
David@0
   327
David@0
   328
/* return if a field exists */
David@0
   329
-(BOOL) fieldExists:(NSString *)fieldName {
David@0
   330
    return [keys objectForKey:fieldName] != nil;
David@0
   331
}
David@0
   332
David@0
   333
/* return the serial number of the data */
David@0
   334
-(UInt32) serial {
David@0
   335
    return serial;
David@0
   336
}
David@0
   337
David@0
   338
/* return the dictionary */
David@0
   339
-(NSDictionary *)dictionary {
David@0
   340
    return [[keys copy] autorelease];
David@0
   341
}
David@0
   342
David@0
   343
/*
David@0
   344
 * Generate data to be placed in the protocolSpecificInformation (TXT record)
David@0
   345
 * of the rendezvous announcement. This is data shouldn't be used directly, but
David@0
   346
 * should be used indirectly via dataAsDNSTXT or dataAsPackedPString
David@0
   347
 */
David@0
   348
-(NSString *) data {
David@0
   349
    NSMutableData   *data;		/* binary representation of rendezvous data */
David@0
   350
    NSData	    *xmlData;		/* data converted to an XML plist */
David@0
   351
    NSMutableString *infoData;		/* XML plist as a string */
David@0
   352
    NSString *key;			/* strings used when manipulating data */
David@0
   353
    id value;				/* value for data field */
David@0
   354
    NSString	    *error;		/* error from creation of plist */
David@0
   355
    UInt32	    keycount;		/* a 32-bit integer, count of keys in data */
David@0
   356
    UInt16	    fieldlen;		/* a 16-bit integer, length of field being added to data */
David@0
   357
    UInt16	    fieldlenBE;		/* fieldlen as converted to network byte order */
David@0
   358
    UInt32      serialBE = htonl(serial); /* serial as converted to network byte order */
David@0
   359
David@0
   360
    /* allocate NSData to create data in */
David@0
   361
    data = [[NSMutableData alloc] init];
David@0
   362
    [data autorelease];
David@0
   363
    /* add the subnegotiation string */
David@0
   364
    [data appendBytes:[subn UTF8String] length:[subn length]];
David@0
   365
    [data appendBytes:&serialBE length:4];
David@0
   366
    [data appendBytes:[endn UTF8String] length:[endn length]];
David@0
   367
    /* add a field containing the number of fields for the rest of the data */
David@0
   368
    keycount = [keys count] + 1; /* +1 for slumming field */
David@0
   369
    keycount = htonl(keycount);
David@0
   370
    [data appendBytes:&keycount length:4];
David@0
   371
David@0
   372
    /* loop through fields to be added and add them to data */
David@1616
   373
		for (key in keys) {	    
David@0
   374
        /* add length of field name, then field name */
David@0
   375
		const char *field;
David@0
   376
		field = [key UTF8String];
David@0
   377
		fieldlen = strlen(field);
David@0
   378
		fieldlenBE = htons(fieldlen);
David@0
   379
		[data appendBytes:&fieldlenBE length:2];
David@0
   380
		[data appendBytes:field length:fieldlen];
David@0
   381
		
David@0
   382
        /* add length of field data, then field data */
David@0
   383
        value = [keys objectForKey:key];
David@0
   384
        if ([value isKindOfClass: [NSData class]]) {
David@0
   385
			field = [value bytes];
David@0
   386
			fieldlen = [(NSData *)value length];
David@0
   387
            fieldlen = fieldlen | ~0x7FFF;
David@0
   388
		} else {
David@0
   389
			field = [value UTF8String];
David@0
   390
			fieldlen = strlen(field);
David@0
   391
		}
David@0
   392
		fieldlenBE = htons(fieldlen);
David@0
   393
        [data appendBytes:&fieldlenBE length:2];
David@0
   394
        if ([value isKindOfClass: [NSData class]]) {
David@0
   395
			fieldlen = [(NSData *)value length];
David@0
   396
		}
David@0
   397
		[data appendBytes:field length:fieldlen];
David@0
   398
	}
David@0
   399
    
David@0
   400
    /* we're slumming it in iChat-land */
David@0
   401
    key = @"slumming";
David@0
   402
    fieldlen = [key length];
David@0
   403
    fieldlenBE = htons(fieldlen);
hg@2794
   404
    [data appendBytes:&fieldlenBE length:2];
David@0
   405
    [data appendBytes:[key UTF8String] length:[key length]];
David@0
   406
    value = @"1";
David@0
   407
    fieldlen = [(NSData *)value length];
David@0
   408
    fieldlenBE = htons(fieldlen);
hg@2794
   409
    [data appendBytes:&fieldlenBE length:2];
David@0
   410
    [data appendBytes:[value UTF8String] length:[(NSData *)value length]];
David@0
   411
    
David@0
   412
    /* create XML plist of data and convert to string */
David@0
   413
    xmlData = [NSPropertyListSerialization dataFromPropertyList:data
David@0
   414
    				    format:NSPropertyListXMLFormat_v1_0
David@0
   415
    				    errorDescription:&error];
David@0
   416
    infoData = [[NSMutableString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding];
David@0
   417
    [infoData autorelease];
David@0
   418
	
David@0
   419
    /* and now we have the rendezvous data to return to the caller, the copy
David@0
   420
       converts it to immutable */
David@0
   421
    return [[infoData copy] autorelease];
David@0
   422
}
David@0
   423
David@0
   424
/* 
David@0
   425
 * Converts data: to a format appropriate for passing to service registration.
David@0
   426
 * We add an ASCII 1 character every 255 characters for pascal string separation
David@0
   427
 */
David@0
   428
-(NSString *)dataAsDNSTXT {
David@0
   429
    NSMutableString	*infoData = [[[self data] mutableCopy] autorelease]; /* data to be done */
David@0
   430
    unsigned long	i;	/* loop counter */
David@0
   431
David@0
   432
    /* add the character \001 when we exceed 255 characters, required to allow announcement
David@0
   433
    to be longer than 255 characters */
David@0
   434
    for (i = 255; i < [infoData length]; i += 255)
David@0
   435
    {
David@0
   436
	[infoData insertString:@"\001" atIndex:i];
David@0
   437
    }
David@0
   438
David@0
   439
    /* return a copy so it is immutable */
David@0
   440
    return [[infoData copy] autorelease];
David@0
   441
}
David@0
   442
David@0
   443
/* ichat AV style TXT record */
David@0
   444
-(NSString *)avDataAsDNSTXT {
David@0
   445
    NSMutableString *infoData = [NSMutableString string];
David@0
   446
    id value;
David@0
   447
    NSString	    *key;
David@0
   448
    
David@0
   449
    [infoData appendString:@"\001txtvers=1"];
David@0
   450
    [infoData appendString:@"\001version=1"];
David@0
   451
    
David@1616
   452
    /* enumerate through fields for announcement */    
David@1616
   453
		for (key in keys) {	    
David@0
   454
	[infoData appendString:@"\001"];	
David@0
   455
	[infoData appendString:key];
David@0
   456
	[infoData appendString:@"="];
David@0
   457
	value = [keys objectForKey:key];
David@0
   458
	
David@0
   459
	if ([value isKindOfClass: [NSData class]]) {
David@0
   460
	    /* convert binary to hex */
David@0
   461
	    char *hexdata = (char *)malloc([(NSData *)value length] * 2 + 1);
David@0
   462
	    int i;
David@0
   463
	    
David@0
   464
	    for (i = 0; i < 20; i++) {
David@0
   465
		sprintf(hexdata + (i*2), "%.2x", ((unsigned char *)[(NSData *)value bytes])[i]);
David@0
   466
	    }
David@0
   467
	    hexdata[[(NSData *)value length] * 2] = '\0';
David@0
   468
	    
sholt@2699
   469
	    [infoData appendString:[NSString stringWithUTF8String:hexdata]];
David@0
   470
	} else {
David@0
   471
	    [infoData appendString:value];
David@0
   472
	}
David@0
   473
    }
David@0
   474
    
David@0
   475
    return infoData;
David@0
   476
}
David@0
   477
David@0
   478
-(TXTRecordRef)dataAsTXTRecordRef {
David@0
   479
	//AWEzvLog(@"dataAsTXTRecordRef called");
David@0
   480
	TXTRecordRef txtRecord;
David@0
   481
	DNSServiceErrorType txtRecordError;
David@0
   482
	id value;
David@565
   483
	const char *valueToSet;
David@0
   484
	uint8_t valueSize;
David@0
   485
	TXTRecordCreate(/* TXTRecordRef */ &txtRecord, /* buffer length */ 0, /* buffer */ NULL);
David@0
   486
	
David@0
   487
	/* Enumerate through keys setting the txtrecordvalue */
David@559
   488
	for (NSString *key in [keys keyEnumerator]) {		
David@0
   489
		value = [keys objectForKey:key];
David@0
   490
		 //AWEzvLog(@"key=%@ value=%@", key, value);
David@0
   491
		
David@0
   492
		if ([value isKindOfClass: [NSData class]]) {
David@0
   493
		    /* convert binary to hex */
David@0
   494
		    char *hexdata = (char *)malloc([(NSData *)value length] * 2 + 1);
David@0
   495
		    int i;
David@0
   496
            
David@0
   497
		    for (i = 0; i < 20; i++) {
David@0
   498
			sprintf(hexdata + (i*2), "%.2x", ((unsigned char *)[(NSData *)value bytes])[i]);
David@0
   499
		    }
David@0
   500
		    hexdata[[(NSData *)value length] * 2] = '\0';
sholt@2699
   501
			valueToSet = [[NSString stringWithUTF8String:hexdata] UTF8String];
David@0
   502
			valueSize = strlen(valueToSet);
David@0
   503
			free(hexdata);
David@0
   504
		} else {
David@0
   505
		    valueToSet = [value UTF8String];
David@0
   506
			valueSize = strlen(valueToSet);
David@0
   507
		}
David@0
   508
		
David@0
   509
		txtRecordError = TXTRecordSetValue (
David@0
   510
			/* TXTRecord */ &txtRecord,
David@0
   511
			/* key */ [key UTF8String],
David@0
   512
			/* size, may be zero */ valueSize,
Evan@2930
   513
			/* value, may be null */ valueToSet);
Evan@2930
   514
		
Evan@2930
   515
		if ((txtRecordError == kDNSServiceErr_Invalid) &&
Evan@2930
   516
			[value isKindOfClass:[NSString class]]) {
Evan@2930
   517
			/* kDNSServiceErr_Invalid may be returned if:
Evan@2930
   518
			 *	1. Invalid characters were included (per documentation)
Evan@2930
   519
			 *	2. The length of the value is >= 250 characters (at least for the msg key)
Evan@2930
   520
			 *
Evan@2930
   521
			 * So: First, try stripping out any non-ASCII characters, as I'm not sure what might consitute an
Evan@2930
   522
			 * "illegal character" in a field which requests UTF8.
Evan@2930
   523
			 *
Evan@2930
   524
			 * Then, if that still fails, truncate the string to 248 characters (248,249,250 are the ellipsis).
Evan@2930
   525
			 */
Evan@2930
   526
			valueToSet = [[value dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES] bytes];
Evan@2930
   527
			valueSize = strlen(valueToSet);
Evan@2930
   528
Evan@2930
   529
			txtRecordError = TXTRecordSetValue (
Evan@2930
   530
												/* TXTRecord */ &txtRecord,
Evan@2930
   531
												/* key */ [key UTF8String],
Evan@2930
   532
												/* size, may be zero */ valueSize,
Evan@2930
   533
												/* value, may be null */ valueToSet);
Evan@2930
   534
Evan@2930
   535
			if (txtRecordError == kDNSServiceErr_Invalid) {
Evan@2930
   536
				valueToSet = [[value stringWithEllipsisByTruncatingToLength:248] UTF8String];
Evan@2930
   537
				valueSize = strlen(valueToSet);
Evan@2930
   538
				txtRecordError = TXTRecordSetValue (
Evan@2930
   539
													/* TXTRecord */ &txtRecord,
Evan@2930
   540
													/* key */ [key UTF8String],
Evan@2930
   541
													/* size, may be zero */ valueSize,
Evan@2930
   542
													/* value, may be null */ valueToSet);				
Evan@2930
   543
			}
Evan@2930
   544
		}
Evan@2930
   545
David@0
   546
		if (txtRecordError != kDNSServiceErr_NoError) {
Evan@2930
   547
			if (txtRecordError == kDNSServiceErr_Invalid) {
Evan@2930
   548
				AWEzvLog(@"Error setting TXTRecord of key=%@ and value=%s: Value contains illegal characters", key, valueToSet);
Evan@2930
   549
				
Evan@2930
   550
			} else if (txtRecordError == kDNSServiceErr_NoMemory) {
Evan@2930
   551
				AWEzvLog(@"Error setting TXTRecord of key=%@ and value=%s: Exceeded available storage", key, valueToSet);
Evan@2930
   552
			} else {
Evan@2930
   553
				AWEzvLog(@"Error setting TXTRecord of key=%@ and value=%s: Error is %i", key, valueToSet, txtRecordError);
Evan@2930
   554
			}
David@0
   555
			
David@0
   556
		}
David@0
   557
		
David@0
   558
	}
David@0
   559
	
David@0
   560
	return txtRecord;
David@0
   561
}
David@0
   562
/*
David@0
   563
 * Converts data: to packed PString format as required by the low level rendezvous
David@0
   564
 * functions when passing an opaque RData structure
David@0
   565
 */
David@0
   566
-(NSData *) dataAsPackedPString {
David@0
   567
    NSString		*origdata = [self data];	/* original data */
David@0
   568
    NSMutableData	*data = [NSMutableData data];	/* modified data to return */
David@0
   569
    unsigned char	pstring[256];			/* pascal string under construction */
David@0
   570
    unsigned long	i;				/* loop counter */
David@0
   571
    
David@0
   572
    /* initialise pstring */
David@0
   573
    pstring[0] = 0;
David@0
   574
    
David@0
   575
    /* create strings */
David@0
   576
    for (i = 0; i < [origdata length]; i++) {
David@0
   577
	pstring[0]++;
David@0
   578
	pstring[pstring[0]] = [origdata characterAtIndex:i];
David@0
   579
	if (pstring[0] == 254 || i == [origdata length] - 1) {
David@0
   580
	    [data appendBytes:(char *)&pstring length:pstring[0] + 1];
David@0
   581
	    pstring[0] = 0;
David@0
   582
	}
David@0
   583
    }
David@0
   584
    
David@0
   585
    /* return copy so it is immutable */
David@0
   586
    return [[data copy] autorelease];
David@0
   587
}
David@0
   588
David@0
   589
/* ichat AV style TXT record */
David@0
   590
-(NSData *)avDataAsPackedPString {
David@0
   591
    NSMutableString *infoData = [NSMutableString string];
David@0
   592
    id value;
David@0
   593
    NSString	    *key;
David@0
   594
    const char *data;
David@0
   595
    
David@0
   596
    [infoData appendString:@"\x09txtvers=1"];
David@0
   597
    [infoData appendString:@"\x09version=1"];
David@0
   598
    
David@1616
   599
    /* enumerate through fields for announcement */    
David@1616
   600
		for (key in keys) {
David@0
   601
		/* convert binary to hex */
David@0
   602
		char *hexdata;
David@0
   603
		int i;
David@0
   604
		
David@0
   605
		value = [keys objectForKey:key];
David@0
   606
		
David@0
   607
		if ([value isKindOfClass:[NSData class]]) {
David@0
   608
			hexdata = (char *)malloc([(NSData *)value length] * 2 + 1);
David@0
   609
			
David@0
   610
			for (i = 0; i < 20; i++) {
David@0
   611
				sprintf(hexdata + (i*2), "%.2x", ((unsigned char *)[(NSData *)value bytes])[i]);
David@0
   612
			}
David@0
   613
			hexdata[[(NSData *)value length] * 2] = '\0';
David@0
   614
			
David@0
   615
			[infoData appendFormat:@"%c", ([(NSData *)value length] * 2 + [key length] + 1)];
David@0
   616
			[infoData appendString:key];
David@0
   617
			[infoData appendString:@"="];
sholt@2699
   618
			[infoData appendString:[NSString stringWithUTF8String:hexdata]];
David@0
   619
			free(hexdata);
David@0
   620
		} else {
David@0
   621
			const char *val = [(NSString *)value UTF8String];
David@0
   622
			int len = strlen(val);
David@0
   623
			[infoData appendFormat:@"%c", len + [key length] + 1];
David@0
   624
			[infoData appendString:key];
David@0
   625
			[infoData appendString:@"="];
David@0
   626
			[infoData appendString:value];
David@0
   627
		}
David@0
   628
    }
David@0
   629
    
David@0
   630
	data = [infoData UTF8String];
David@0
   631
	
David@0
   632
	return [NSData dataWithBytes:data length:strlen(data)];
David@0
   633
}
David@0
   634
David@0
   635
David@0
   636
@end