Developer Area

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

Revision 382, 11.0 kB (checked in by henri, 5 years ago)

Major update: save .tel data to AddressBook?, with many other small fixes

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- (NSString *)generateServiceDescription;
35- (NSString *)generateLabelDescription;
36
37- (NSString *)stringFromStringRdf:(const ldns_rdf *)rdf;
38- (NSString *)stringFromDnameRdf:(const ldns_rdf *)rdf;
39- (NSNumber *)numberFromIntRdf:(const ldns_rdf *)rdf;
40
41@end
42
43static NSArray *locationIndicatorHints;
44
45@implementation RecordNaptr
46
47@synthesize serviceTypeArray;
48@synthesize labelArray;
49@synthesize lihArray;
50
51@synthesize order;
52@synthesize preference;
53@synthesize serviceDescription;
54@synthesize labelDescription;
55@synthesize uriContent;
56@synthesize isTerminal;
57@synthesize isPrivate;
58@synthesize isEncrypted;
59@synthesize isHidden;
60@synthesize isValid;
61@synthesize expiryDate;
62
63+ (id)recordWithRr:(ldns_rr *)rr {
64        NSDate *lookupDate = [[NSDate date] retain];
65        self = [RecordNaptr recordWithRr:rr date:lookupDate];
66        [lookupDate release];
67        return self;
68}
69
70+ (id)recordWithRr:(ldns_rr *)rr date:(NSDate *)lookupDate {
71        self = [[[RecordNaptr alloc] initWithRr:rr date:lookupDate] autorelease];
72        return self;
73}
74
75- (id)init {
76        // Never initialize it empty
77        return NULL;
78}
79
80- (id)initWithRr:(ldns_rr *)rr date:(NSDate *)lookupDate {
81        self = [super init];
82        isValid = NO;
83       
84        locationIndicatorHints = [[NSArray arrayWithObjects:
85                                                                           @"x-mobile",
86                                                                           @"x-work",
87                                                                           @"x-main",
88                                                                           @"x-home",
89                                                                           @"x-transit",
90                                                                           @"x-prs",
91                                                                           nil
92                                                                           ] retain];   
93        if (!rr)
94                return self;
95        if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_NAPTR)
96                return self;
97       
98        NSTimeInterval ttl = (NSTimeInterval)ldns_rr_ttl(rr);
99        expiryDate = [lookupDate addTimeInterval:ttl];
100       
101        serviceTypeArray = [[NSMutableArray arrayWithCapacity:2] retain];
102        labelArray = [[NSMutableArray arrayWithCapacity:2] retain];
103        lihArray = [[NSMutableArray arrayWithCapacity:2] retain];
104       
105        order = [[self numberFromIntRdf:ldns_rr_rdf(rr,0)] retain];
106        preference = [[self numberFromIntRdf:ldns_rr_rdf(rr,1)] retain];
107        flags = [[self stringFromStringRdf:ldns_rr_rdf(rr,2)] retain];
108        services = [[self stringFromStringRdf:ldns_rr_rdf(rr,3)] retain];
109        regexp = [[self stringFromStringRdf:ldns_rr_rdf(rr,4)] retain];
110       
111        if ([services isEqualToString:@"x-crypto:data:8210"]) {
112                isPrivate = YES;
113                isEncrypted = YES;
114               
115                //TODO: Try to decrypt the NAPTR record
116                // if successful, set isEncrypted to NO;
117               
118        } else { // Not encrypted
119                isPrivate = NO;
120                isEncrypted = NO;
121        }
122       
123        if ([flags isEqualToString:@"u"]) { // terminal naptr
124                isTerminal = YES;
125                replacement = @"";
126                [self parseServices:services];
127
128                if (isEncrypted) {
129                        uriContent = @"";
130                } else {
131                        // Clean Regexp (don't bother with regex, just assume it's a full overwrite)
132                        NSString *regexSeparator = [regexp substringToIndex:1];
133                        NSArray *regexParts = [regexp componentsSeparatedByString:regexSeparator];
134                        if ([regexParts count] == 4) {
135                                // looks like a regex
136                                // TODO: Should check that index 3 is empty or the flag "i"
137                                // http://www.ietf.org/rfc/rfc2915.txt
138                                uriContent = [NSString stringWithString:[regexParts objectAtIndex:2]];
139                        } else {        // Not valid
140                                return self;
141                        }
142                }
143                labelDescription = [[self generateLabelDescription] retain];
144               
145                // Hack: Check if it's a .tel URL. If so, transform it into a non-terminal naptr
146                if ([uriContent hasSuffix:@".tel"] || [uriContent hasSuffix:@".tel/"]) {
147                        NSURL *telUrl = [NSURL URLWithString:uriContent];
148                        if ([telUrl host]) {
149                                replacement = [NSString stringWithString:[telUrl host]];
150                                uriContent = replacement;
151                                isTerminal = NO;
152                                labelDescription = [[[replacement stringByReplacingOccurrencesOfString:@"-" withString:@" "] capitalizedString] retain];
153                        }
154                }
155               
156        } else if ([flags isEqualToString:@""]) { // Non-terminal naptr
157                isTerminal = NO;
158                replacement = [self stringFromDnameRdf:ldns_rr_rdf(rr,5)];
159                labelDescription = [[[replacement stringByReplacingOccurrencesOfString:@"-" withString:@" "] capitalizedString] retain];
160                uriContent = replacement;
161               
162        } else { // Not valid
163                return self;
164        }
165       
166        serviceDescription = [[self generateServiceDescription] retain];
167        [uriContent retain];   
168        isValid = YES;
169        return self;
170}
171
172- (void)parseServices:(NSString *)_services {
173        NSArray *sParts = [_services componentsSeparatedByString:@"+"];
174        NSString *aPart;
175        for (aPart in sParts) {
176                // Discard the E2U prefix
177                if ([aPart isEqualToString:@"E2U"])
178                        continue;
179                if ([aPart hasPrefix:@"x-lbl:"]) {
180                        [labelArray addObject:[aPart substringFromIndex:[@"x-lbl:" length]]];
181                        continue;
182                }
183                if ([self tokenIsLih:aPart]) {
184                        [lihArray addObject:aPart];
185                        continue;
186                }
187                // service types are anything not of the above
188                [serviceTypeArray addObject:aPart];
189        }
190}
191
192- (BOOL)tokenIsLih:(NSString *)token { 
193        return ([locationIndicatorHints containsObject:token]);
194}
195
196- (NSString *)generateServiceDescription {
197        // Create the service description string, concatenating all service types and LIH
198        NSMutableString *theDesc = [[[NSMutableString alloc] initWithCapacity:30] autorelease];
199        NSMutableString *servicePart = [NSMutableString stringWithString:@""];
200
201        if (! isTerminal) {
202                [theDesc appendFormat:@"%@:", LocTelStr(@"ntn")];
203                return theDesc;
204        }
205       
206        NSString *aPart;
207        NSInteger i = 0, j = 0; // i is # of LIH, j is # of service types
208        // Start with LIH
209        for (aPart in lihArray) {
210                if (i == 0) {
211                        [theDesc setString:LocTelStr(aPart)];
212                } else {
213                        [theDesc appendFormat:@" and %@", LocTelStr(aPart)];
214                }
215                i++;
216        }
217        // Add a separator between LIH and service if there are LIH
218        if (i > 0) {
219                [theDesc appendString:@" "];
220        }
221
222        i = 0;
223        for (aPart in serviceTypeArray) {
224                NSMutableString *strVal = [NSMutableString stringWithString:LocTelStr(aPart)];
225                if ([strVal isEqual:aPart]) {
226                        // Unknown enum service, needs cleaning: remove the leading "x-"
227                        if ([strVal rangeOfString:@"x-"].location != NSNotFound)
228                                [strVal deleteCharactersInRange:[strVal rangeOfString:@"x-"]];
229                }
230                if (i == 0) {
231                        [servicePart setString:strVal];
232                } else {
233                        // More than one service type, we have to make that look good:
234                        // Remove redundant words and append an ampersand
235                        NSArray *wordsInStr = [servicePart componentsSeparatedByString:@" "];
236                        NSString *aWord;
237                        for (aWord in wordsInStr) {
238                                if ([aWord isEqualToString:@"&"])
239                                        continue;
240                                if ([strVal rangeOfString:aWord].location != NSNotFound)
241                                        [strVal deleteCharactersInRange:[strVal rangeOfString:aWord]];
242                        }
243                        if (j > 0) {
244                                [servicePart appendFormat:@" & %@", strVal];
245                        }
246                }
247                j++;
248        }
249        [theDesc appendString:servicePart];
250        return theDesc;
251}
252
253- (NSString *)generateLabelDescription {
254        // This one is easy, just concatenate all labels and replace dashes with spaces
255        NSMutableString *theDesc = [[[NSMutableString alloc] initWithCapacity:20] autorelease];
256
257        [theDesc setString:[labelArray componentsJoinedByString:@" "]];
258        [theDesc replaceOccurrencesOfString:@"-"
259                                                         withString:@" "
260                                                                options:NSLiteralSearch
261                                                                  range:NSMakeRange(0, [theDesc length])];
262        [theDesc replaceOccurrencesOfString:@"_"
263                                                         withString:@" "
264                                                                options:NSLiteralSearch
265                                                                  range:NSMakeRange(0, [theDesc length])];
266        // cut off max label
267//      if ([theDesc length] > 20) {
268//              [theDesc deleteCharactersInRange:NSMakeRange(20, [theDesc length] - 20)];
269//      }
270        return theDesc;
271}
272
273- (NSComparisonResult)comparator:(RecordNaptr *)aNaptr {
274        // First sort by order
275        switch ([order compare:aNaptr.order]) {
276                case NSOrderedAscending:
277                        return NSOrderedAscending;
278                        break;
279                case NSOrderedDescending:
280                        return NSOrderedDescending;
281                        break;
282                default:
283                        break;
284        }
285       
286        // If order is the same, sort by preference
287        // .tel specs do not condone same order and preference, so we don't need to check equality
288        switch ([preference compare:aNaptr.preference]) {
289                case NSOrderedAscending:
290                        return NSOrderedAscending;
291                        break;
292                default:
293                        break;
294        }
295        return NSOrderedDescending;
296}       
297
298# pragma mark ------------- Utility functions for parsing record data fields ------------
299
300- (NSString *)stringFromStringRdf:(const ldns_rdf *)rdf {
301        NSMutableString *res;
302        const uint8_t *data = ldns_rdf_data(rdf);
303        uint8_t length = data[0];
304        res = [[[NSMutableString alloc] initWithBytes:(const unichar *)(data+1)
305                                                                                   length:length
306                                                                                 encoding:NSUTF8StringEncoding] autorelease];
307        return res;
308}
309
310- (NSString *)stringFromDnameRdf:(const ldns_rdf *)rdf {
311        NSMutableString *res;
312        uint8_t src_pos = 0;
313        uint8_t len;
314        uint8_t *data;
315        uint8_t i;
316       
317        data = (uint8_t*)ldns_rdf_data(rdf);
318        len = data[src_pos];
319       
320        if (ldns_rdf_size(rdf) > LDNS_MAX_DOMAINLEN) {
321                /* too large, return */
322                return nil;
323        }
324       
325        res = [NSMutableString stringWithCapacity:len];
326       
327        /* special case: root label */
328        if (1 == ldns_rdf_size(rdf)) {
329                // keep the output empty
330        } else {
331                while ((len > 0) && src_pos < ldns_rdf_size(rdf)) {
332                        src_pos++;
333                        for(i = 0; i < len; i++) {
334                                /* paranoia check for various 'strange'
335                                 characters in dnames
336                                 */
337                                if (data[src_pos] == '.' ||
338                                    data[src_pos] == '(' || data[src_pos] == ')') {
339                                        [res appendFormat:@"\\%c", data[src_pos]];
340                                } else if (!isprint((int) data[src_pos])) {
341                                        [res appendFormat:@"\\%03u", data[src_pos]];
342                                } else {
343                                        [res appendFormat:@"%c", data[src_pos]];
344                                }
345                                src_pos++;
346                        }
347                       
348                        len = data[src_pos];
349                        // Don't append the last dot
350                        if (len > 0)
351                                [res appendString:@"."];
352                }
353        }       
354        return res;
355}
356
357- (NSNumber *)numberFromIntRdf:(const ldns_rdf *)rdf {
358        NSNumber *res = [NSNumber numberWithInt:ntohs(*(uint16_t *)(ldns_rdf_data(rdf)))];
359        return res;
360}
361
362#pragma mark ---- cleanup ----
363
364- (void)dealloc {
365        [locationIndicatorHints release];
366        [super dealloc];
367}
368
369@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