Developer Area

root/apps/iphone/superbook/trunk/DotTel_SDK/Classes/RecordNaptr.m @ 593

Revision 593, 11.9 kB (checked in by henri, 4 years ago)

Second major developement commit for my.tel 2.0. The app now compiles and runs (with bugs).

Line 
1//
2//  RecordNaptr.m
3//  DotTel SDK
4//
5//  Created by Henri Asseily on 9/12/08.
6/*
7 Copyright (c) 2008-2009, Telnic Ltd. All rights reserved.
8 
9 Redistribution and use in source and binary forms, with or without modification,
10 are permitted provided that the following conditions are met:
11 
12 Redistributions of source code must retain the above copyright notice, this list of conditions
13 and the following disclaimer. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the documentation and/or other
15 materials provided with the distribution.
16 Neither the name of the Telnic Ltd. nor the names of its contributors may be used to endorse or
17 promote products derived from this software without specific prior written permission.
18 THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
19 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
24 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27//
28
29#import "RecordNaptr.h"
30@interface RecordNaptr (PrivateMethods)
31
32- (void)parseServices:(NSString *)services;
33- (BOOL)tokenIsLih:(NSString *)token;
34- (void)appendLIHToDescription:(NSMutableString *)theDesc;
35- (void)appendServiceToDescription:(NSMutableString *)theDesc;
36- (NSString *)generateServiceDescription;
37- (NSString *)generateLabelDescription;
38
39- (NSString *)stringFromStringRdf:(const ldns_rdf *)rdf;
40- (NSString *)stringFromDnameRdf:(const ldns_rdf *)rdf;
41- (NSNumber *)numberFromIntRdf:(const ldns_rdf *)rdf;
42
43@end
44
45static NSArray *locationIndicatorHints;
46
47@implementation RecordNaptr
48
49@synthesize serviceTypeArray;
50@synthesize labelArray;
51@synthesize lihArray;
52
53@synthesize order;
54@synthesize preference;
55@synthesize serviceDescription;
56@synthesize labelDescription;
57@synthesize uriContent;
58@synthesize isTerminal;
59@synthesize isPrivate;
60@synthesize isEncrypted;
61@synthesize isHidden;
62@synthesize isValid;
63@synthesize expiryDate;
64
65+ (id)recordWithRr:(ldns_rr *)rr {
66        NSDate *lookupDate = [[NSDate date] retain];
67        self = [RecordNaptr recordWithRr:rr date:lookupDate];
68        [lookupDate release];
69        return self;
70}
71
72+ (id)recordWithRr:(ldns_rr *)rr date:(NSDate *)lookupDate {
73        self = [[[RecordNaptr alloc] initWithRr:rr date:lookupDate] autorelease];
74        return self;
75}
76
77- (id)init {
78        // Never initialize it empty
79        return NULL;
80}
81
82- (id)initWithRr:(ldns_rr *)rr date:(NSDate *)lookupDate {
83        self = [super init];
84        isValid = NO;
85       
86        locationIndicatorHints = [[NSArray arrayWithObjects:
87                                                                           @"x-mobile",
88                                                                           @"x-work",
89                                                                           @"x-main",
90                                                                           @"x-home",
91                                                                           @"x-transit",
92                                                                           @"x-prs",
93                                                                           nil
94                                                                           ] retain];   
95        if (!rr)
96                return self;
97        if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_NAPTR)
98                return self;
99       
100        NSTimeInterval ttl = (NSTimeInterval)ldns_rr_ttl(rr);
101        expiryDate = [lookupDate addTimeInterval:ttl];
102       
103        serviceTypeArray = [[NSMutableArray arrayWithCapacity:2] retain];
104        labelArray = [[NSMutableArray arrayWithCapacity:2] retain];
105        lihArray = [[NSMutableArray arrayWithCapacity:2] retain];
106       
107        order = [[self numberFromIntRdf:ldns_rr_rdf(rr,0)] retain];
108        preference = [[self numberFromIntRdf:ldns_rr_rdf(rr,1)] retain];
109        flags = [[self stringFromStringRdf:ldns_rr_rdf(rr,2)] retain];
110        services = [[self stringFromStringRdf:ldns_rr_rdf(rr,3)] retain];
111        regexp = [[self stringFromStringRdf:ldns_rr_rdf(rr,4)] retain];
112       
113        if ([services isEqualToString:@"x-crypto:data:8210"]) {
114                isPrivate = YES;
115                isEncrypted = YES;
116               
117                //TODO: Try to decrypt the NAPTR record
118                // if successful, set isEncrypted to NO;
119               
120        } else { // Not encrypted
121                isPrivate = NO;
122                isEncrypted = NO;
123        }
124       
125        if ([flags isEqualToString:@"u"]) { // terminal naptr
126                isTerminal = YES;
127                replacement = @"";
128                [self parseServices:services];
129
130                if (isEncrypted) {
131                        uriContent = @"";
132                } else {
133                        // Clean Regexp (don't bother with regex, just assume it's a full overwrite)
134                        NSString *regexSeparator = [regexp substringToIndex:1];
135                        NSArray *regexParts = [regexp componentsSeparatedByString:regexSeparator];
136                        if ([regexParts count] == 4) {
137                                // looks like a regex
138                                // TODO: Should check that index 3 is empty or the flag "i"
139                                // http://www.ietf.org/rfc/rfc2915.txt
140                                uriContent = [NSString stringWithString:[regexParts objectAtIndex:2]];
141                        } else {        // Not valid
142                                return self;
143                        }
144                }
145                labelDescription = [[self generateLabelDescription] retain];
146               
147                // Hack: Check if it's a .tel URL. If so, transform it into a non-terminal naptr
148                // Don't do that here, do it at the client application level
149//              if ([uriContent hasSuffix:@".tel"] || [uriContent hasSuffix:@".tel/"]) {
150//                      NSURL *telUrl = [NSURL URLWithString:uriContent];
151//                      if ([telUrl host]) {
152//                              replacement = [self domainFromIdnString:[telUrl host]];
153//                              uriContent = replacement;
154//                              isTerminal = NO;
155//                      }
156//              }
157               
158        } else if ([flags isEqualToString:@""]) { // Non-terminal naptr
159                NSString *idnString = [self stringFromDnameRdf:ldns_rr_rdf(rr,5)];
160                replacement = [self domainFromIdnString:idnString];
161                uriContent = replacement;
162                isTerminal = NO;
163                labelDescription = @"";         // no short labels here
164               
165        } else { // Not valid
166                return self;
167        }
168       
169        serviceDescription = [[self generateServiceDescription] retain];
170        [uriContent retain];   
171        isValid = YES;
172        return self;
173}
174
175- (void)parseServices:(NSString *)_services {
176        NSArray *sParts = [_services componentsSeparatedByString:@"+"];
177        NSString *aPart;
178        for (aPart in sParts) {
179                // Discard the E2U prefix
180                if ([aPart isEqualToString:@"E2U"])
181                        continue;
182                if ([aPart hasPrefix:@"x-lbl:"]) {
183                        [labelArray addObject:[aPart substringFromIndex:[@"x-lbl:" length]]];
184                        continue;
185                }
186                if ([self tokenIsLih:aPart]) {
187                        [lihArray addObject:aPart];
188                        continue;
189                }
190                // service types are anything not of the above
191                [serviceTypeArray addObject:aPart];
192        }
193}
194
195- (BOOL)tokenIsLih:(NSString *)token { 
196        return ([locationIndicatorHints containsObject:token]);
197}
198
199- (void)appendLIHToDescription:(NSMutableString *)theDesc {
200        NSString *aPart;
201        NSInteger i = 0;
202        for (aPart in lihArray) {
203                if (i == 0) {
204                        [theDesc appendString:LocTelStr(aPart)];
205                } else {
206                        [theDesc appendFormat:@" & %@", LocTelStr(aPart)];
207                }
208                i++;
209        }       
210}
211
212- (void)appendServiceToDescription:(NSMutableString *)theDesc {
213        NSString *aPart;
214        NSMutableString *servicePart = [NSMutableString stringWithString:@""];
215        NSInteger i = 0;
216        NSInteger j = 0;
217        for (aPart in serviceTypeArray) {
218                NSMutableString *strVal = [NSMutableString stringWithString:LocTelStr(aPart)];
219                if ([strVal isEqual:aPart]) {
220                        // Unknown enum service, needs cleaning: remove the leading "x-"
221                        if ([strVal rangeOfString:@"x-"].location != NSNotFound)
222                                [strVal deleteCharactersInRange:[strVal rangeOfString:@"x-"]];
223                }
224                if (i == 0) {
225                        [servicePart setString:strVal];
226                } else {
227                        // More than one service type, we have to make that look good:
228                        // Remove redundant words and append an ampersand
229                        NSArray *wordsInStr = [servicePart componentsSeparatedByString:@" "];
230                        NSString *aWord;
231                        for (aWord in wordsInStr) {
232                                if ([aWord isEqualToString:@"&"])
233                                        continue;
234                                if ([strVal rangeOfString:aWord].location != NSNotFound)
235                                        [strVal deleteCharactersInRange:[strVal rangeOfString:aWord]];
236                        }
237                        if (j > 0) {
238                                [servicePart appendFormat:@" & %@", strVal];
239                        }
240                }
241                j++;
242        }
243        [theDesc appendString:servicePart];
244}
245
246- (NSString *)generateServiceDescription {
247        // Create the service description string, concatenating all service types and LIH
248        NSMutableString *theDesc = [[[NSMutableString alloc] initWithCapacity:30] autorelease];
249
250        if (! isTerminal) {
251                [theDesc appendFormat:@"%@:", LocTelStr(@"ntn")];
252                return theDesc;
253        }
254       
255        BOOL startWithLIH = [LocTelStr(@"LIH Position") isEqualToString:@"1"];
256        if (startWithLIH) {
257                [self appendLIHToDescription:theDesc];
258                // Add a separator between LIH and service if there are LIH
259                if ([theDesc length] > 0) {
260                        [theDesc appendString:@" "];
261                }
262                [self appendServiceToDescription:theDesc];
263        } else {
264                [self appendServiceToDescription:theDesc];
265                [theDesc appendString:@" "];
266                NSInteger dl = [theDesc length];
267                [self appendLIHToDescription:theDesc];
268                if ([theDesc length] == dl) { // no LIH, remove the last space
269                        [theDesc deleteCharactersInRange:NSMakeRange(dl-1, 1)];
270                }
271        }
272
273        return theDesc;
274}
275
276- (NSString *)generateLabelDescription {
277        // This one is easy, just concatenate all labels and replace dashes with spaces
278        NSMutableString *theDesc = [[[NSMutableString alloc] initWithCapacity:20] autorelease];
279
280        [theDesc setString:[labelArray componentsJoinedByString:@" "]];
281        [theDesc replaceOccurrencesOfString:@"-"
282                                                         withString:@" "
283                                                                options:NSLiteralSearch
284                                                                  range:NSMakeRange(0, [theDesc length])];
285        [theDesc replaceOccurrencesOfString:@"_"
286                                                         withString:@" "
287                                                                options:NSLiteralSearch
288                                                                  range:NSMakeRange(0, [theDesc length])];
289        return theDesc;
290}
291
292- (NSComparisonResult)comparator:(RecordNaptr *)aNaptr {
293        // First sort by order
294        switch ([order compare:aNaptr.order]) {
295                case NSOrderedAscending:
296                        return NSOrderedAscending;
297                        break;
298                case NSOrderedDescending:
299                        return NSOrderedDescending;
300                        break;
301                default:
302                        break;
303        }
304       
305        // If order is the same, sort by preference
306        // .tel specs do not condone same order and preference, so we don't need to check equality
307        switch ([preference compare:aNaptr.preference]) {
308                case NSOrderedAscending:
309                        return NSOrderedAscending;
310                        break;
311                default:
312                        break;
313        }
314        return NSOrderedDescending;
315}       
316
317#pragma mark -
318#pragma mark Utility functions for parsing record data fields
319
320- (NSString *)stringFromStringRdf:(const ldns_rdf *)rdf {
321        NSMutableString *res;
322        const uint8_t *data = ldns_rdf_data(rdf);
323        uint8_t length = data[0];
324        res = [[[NSMutableString alloc] initWithBytes:(const unichar *)(data+1)
325                                                                                   length:length
326                                                                                 encoding:NSUTF8StringEncoding] autorelease];
327        return res;
328}
329
330- (NSString *)stringFromDnameRdf:(const ldns_rdf *)rdf {
331        NSMutableString *res;
332        uint8_t src_pos = 0;
333        uint8_t len;
334        uint8_t *data;
335        uint8_t i;
336       
337        data = (uint8_t*)ldns_rdf_data(rdf);
338        len = data[src_pos];
339       
340        if (ldns_rdf_size(rdf) > LDNS_MAX_DOMAINLEN) {
341                /* too large, return */
342                return nil;
343        }
344       
345        res = [NSMutableString stringWithCapacity:len];
346       
347        /* special case: root label */
348        if (1 == ldns_rdf_size(rdf)) {
349                // keep the output empty
350        } else {
351                while ((len > 0) && src_pos < ldns_rdf_size(rdf)) {
352                        src_pos++;
353                        for(i = 0; i < len; i++) {
354                                /* paranoia check for various 'strange'
355                                 characters in dnames
356                                 */
357                                if (data[src_pos] == '.' ||
358                                    data[src_pos] == '(' || data[src_pos] == ')') {
359                                        [res appendFormat:@"\\%c", data[src_pos]];
360                                } else if (!isprint((int) data[src_pos])) {
361                                        [res appendFormat:@"\\%03u", data[src_pos]];
362                                } else {
363                                        [res appendFormat:@"%c", data[src_pos]];
364                                }
365                                src_pos++;
366                        }
367                       
368                        len = data[src_pos];
369                        // Don't append the last dot
370                        if (len > 0)
371                                [res appendString:@"."];
372                }
373        }       
374        return res;
375}
376
377- (NSNumber *)numberFromIntRdf:(const ldns_rdf *)rdf {
378        NSNumber *res = [NSNumber numberWithInt:ntohs(*(uint16_t *)(ldns_rdf_data(rdf)))];
379        return res;
380}
381
382#pragma mark -
383#pragma mark IDN methods
384
385- (NSString *)domainFromIdnString:(NSString *)idnString {
386        if (idnString == nil) return nil;
387        char *p;
388        int rc;
389        rc = idna_to_unicode_8z8z([idnString UTF8String], &p, 0);
390        if (rc != IDNA_SUCCESS) {
391                NSLog(@"domainFromIdnString failed (%d): %s\n", rc, idna_strerror (rc));
392                return idnString;
393    }
394        NSString *domain = [NSString stringWithUTF8String:p];
395        free(p);
396        return domain;
397}
398
399#pragma mark -
400#pragma mark cleanup
401
402- (void)dealloc {
403        [locationIndicatorHints release];
404        [super dealloc];
405}
406
407@end
Note: See TracBrowser for help on using the browser.
Telnic
Search This Site
Partners
Neustar
ICANN
Main site | WHOIS | Sell .tel | FAQ | Archived Site | About Telnic | Contact Us