2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "NGLdapURL.h"
23 #include "NGLdapConnection.h"
24 #include "NGLdapEntry.h"
25 #include "EOQualifier+LDAP.h"
28 static inline BOOL isUrlAlpha(unsigned char _c) {
30 (((_c >= 'a') && (_c <= 'z')) ||
31 ((_c >= 'A') && (_c <= 'Z')))
34 static inline BOOL isUrlDigit(unsigned char _c) {
35 return ((_c >= '0') && (_c <= '9')) ? YES : NO;
37 static inline BOOL isUrlSafeChar(unsigned char _c) {
39 case '$': case '-': case '_': case '@':
40 case '.': case '&': case '+':
47 static inline BOOL isUrlExtraChar(unsigned char _c) {
49 case '!': case '*': case '"': case '\'':
55 static inline BOOL isUrlEscapeChar(unsigned char _c) {
56 return (_c == '%') ? YES : NO;
58 static inline BOOL isUrlReservedChar(unsigned char _c) {
60 case '=': case ';': case '/':
61 case '#': case '?': case ':':
68 static inline BOOL isUrlXalpha(unsigned char _c) {
69 if (isUrlAlpha(_c)) return YES;
70 if (isUrlDigit(_c)) return YES;
71 if (isUrlSafeChar(_c)) return YES;
72 if (isUrlExtraChar(_c)) return YES;
73 if (isUrlEscapeChar(_c)) return YES;
77 static inline BOOL isUrlHexChar(unsigned char _c) {
80 if ((_c >= 'a') && (_c <= 'f'))
82 if ((_c >= 'A') && (_c <= 'F'))
87 static inline BOOL isUrlAlphaNum(unsigned char _c) {
88 return (isUrlAlpha(_c) || isUrlDigit(_c)) ? YES : NO;
91 static inline BOOL isToBeEscaped(unsigned char _c) {
92 return (isUrlAlphaNum(_c) || (_c == '_')) ? NO : YES;
95 static BOOL NGContainsUrlInvalidCharacters(const unsigned char *_buffer) {
97 if (isToBeEscaped(*_buffer))
103 static void NGEscapeUrlBuffer
104 (const unsigned char *_source, unsigned char *_dest) {
105 register const unsigned char *src = (void*)_source;
107 //if (*src == ' ') { // a ' ' becomes a '+'
108 // *_dest = '+'; _dest++;
110 if (!isToBeEscaped(*src)) {
114 else { // any other char is escaped ..
115 *_dest = '%'; _dest++;
116 sprintf(_dest, "%02X", (unsigned)*src);
123 static NSString *NGEscapeUrlString(NSString *_source) {
128 if ((len = [_source cStringLength]) == 0)
131 cstr = malloc(len + 1);
132 [_source getCString:cstr];
135 if (NGContainsUrlInvalidCharacters(cstr)) { // needs to be escaped ?
138 buffer = NGMallocAtomic([_source cStringLength] * 3 + 2);
139 NGEscapeUrlBuffer(cstr, buffer);
141 s = [[[NSString alloc]
142 initWithCStringNoCopy:buffer
143 length:strlen(buffer)
144 freeWhenDone:YES] autorelease];
147 s = [[_source copy] autorelease];
153 @implementation NGLdapURL
155 + (id)ldapURLWithString:(NSString *)_url {
156 if (!ldap_is_ldap_url((char *)[_url UTF8String]))
159 return [[[self alloc] initWithString:_url] autorelease];
162 - (id)initWithString:(NSString *)_url {
163 LDAPURLDesc *urld = NULL;
164 unsigned attrCount, i;
169 if ((err = ldap_url_parse((char *)[_url UTF8String], &urld)) != 0) {
178 self->host = [[NSString alloc] initWithCString:urld->lud_host];
179 self->port = urld->lud_port;
180 self->base = [[NSString alloc] initWithCString:urld->lud_dn];
181 self->scope = urld->lud_scope;
182 self->filter = [[NSString alloc] initWithCString:urld->lud_filter];
184 if (urld != NULL && urld->lud_attrs != NULL) {
185 register char *tmp, **a;
187 for (i = 0; (tmp = a[i]); i++)
197 attrs = calloc(attrCount+1, sizeof(id));
199 for (i = 0; i < attrCount; i++)
200 attrs[i] = [[NSString alloc] initWithCString:urld->lud_attrs[i]];
202 self->attributes = [[NSArray alloc] initWithObjects:attrs count:attrCount];
204 for (i = 0; i < attrCount; i++)
206 if (attrs) free(attrs);
209 if (urld) ldap_free_urldesc(urld);
215 return [self initWithString:nil];
219 [self->host release];
220 [self->base release];
221 [self->filter release];
222 [self->attributes release];
228 - (NSString *)hostName {
232 return [NSHost hostWithName:[self hostName]];
238 - (NSString *)baseDN {
245 - (NSString *)searchFilter {
248 - (EOQualifier *)searchFilterQualifier {
251 if (self->filter == nil)
255 q = [[EOQualifier alloc] initWithLDAPFilterString:self->filter];
257 return [q autorelease];
260 - (NSArray *)attributes {
261 return self->attributes;
264 /* perform fetches */
266 - (NGLdapConnection *)openConnection {
267 NGLdapConnection *con;
269 con = [[NGLdapConnection alloc] initWithHostName:[self hostName]
270 port:[self port] ? [self port] : 389];
271 return [con autorelease];
274 - (NSEnumerator *)fetchEntries {
275 NGLdapConnection *con;
277 if ((con = [self openConnection]) == nil)
280 switch (self->scope) {
281 case LDAP_SCOPE_ONELEVEL:
282 return [con flatSearchAtBaseDN:[self baseDN]
283 qualifier:[self searchFilterQualifier]
284 attributes:[self attributes]];
286 case LDAP_SCOPE_SUBTREE:
287 return [con deepSearchAtBaseDN:[self baseDN]
288 qualifier:[self searchFilterQualifier]
289 attributes:[self attributes]];
291 case LDAP_SCOPE_BASE:
292 return [con baseSearchAtBaseDN:[self baseDN]
293 qualifier:[self searchFilterQualifier]
294 attributes:[self attributes]];
299 - (NGLdapEntry *)fetchEntry {
300 return [[self fetchEntries] nextObject];
305 - (NSString *)urlString {
309 s = [[NSMutableString alloc] initWithCapacity:200];
311 [s appendString:@"ldap://"];
312 [s appendString:self->host ? self->host : @"localhost"];
313 if (self->port > 0) [s appendFormat:@":%i", self->port];
315 [s appendString:@"/"];
317 is = NGEscapeUrlString(is);
320 if ((self->attributes != nil) || (self->scope != -1) || (self->filter != nil)){
321 [s appendString:@"?"];
322 is = [self->attributes componentsJoinedByString:@","];
323 is = NGEscapeUrlString(is);
326 if ((self->scope != -1) || (self->filter != nil)) {
327 [s appendString:@"?"];
328 switch (self->scope) {
329 case LDAP_SCOPE_ONELEVEL:
330 [s appendString:@"one"];
332 case LDAP_SCOPE_SUBTREE:
333 [s appendString:@"sub"];
335 case LDAP_SCOPE_BASE:
337 [s appendString:@"base"];
342 [s appendString:@"?"];
344 is = NGEscapeUrlString(is);
350 return [is autorelease];
355 - (id)copyWithZone:(NSZone *)_zone {
356 return [[[self class] allocWithZone:_zone] initWithString:[self urlString]];
361 - (NSString *)description {
362 return [self urlString];