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"
29 static inline BOOL isUrlAlpha(unsigned char _c) {
31 (((_c >= 'a') && (_c <= 'z')) ||
32 ((_c >= 'A') && (_c <= 'Z')))
35 static inline BOOL isUrlDigit(unsigned char _c) {
36 return ((_c >= '0') && (_c <= '9')) ? YES : NO;
38 static inline BOOL isUrlSafeChar(unsigned char _c) {
40 case '$': case '-': case '_': case '@':
41 case '.': case '&': case '+':
48 static inline BOOL isUrlExtraChar(unsigned char _c) {
50 case '!': case '*': case '"': case '\'':
56 static inline BOOL isUrlEscapeChar(unsigned char _c) {
57 return (_c == '%') ? YES : NO;
59 static inline BOOL isUrlReservedChar(unsigned char _c) {
61 case '=': case ';': case '/':
62 case '#': case '?': case ':':
69 static inline BOOL isUrlXalpha(unsigned char _c) {
70 if (isUrlAlpha(_c)) return YES;
71 if (isUrlDigit(_c)) return YES;
72 if (isUrlSafeChar(_c)) return YES;
73 if (isUrlExtraChar(_c)) return YES;
74 if (isUrlEscapeChar(_c)) return YES;
78 static inline BOOL isUrlHexChar(unsigned char _c) {
81 if ((_c >= 'a') && (_c <= 'f'))
83 if ((_c >= 'A') && (_c <= 'F'))
88 static inline BOOL isUrlAlphaNum(unsigned char _c) {
89 return (isUrlAlpha(_c) || isUrlDigit(_c)) ? YES : NO;
92 static inline BOOL isToBeEscaped(unsigned char _c) {
93 return (isUrlAlphaNum(_c) || (_c == '_')) ? NO : YES;
96 static BOOL NGContainsUrlInvalidCharacters(const unsigned char *_buffer) {
98 if (isToBeEscaped(*_buffer))
104 static void NGEscapeUrlBuffer
105 (const unsigned char *_source, unsigned char *_dest) {
106 register const unsigned char *src = (void*)_source;
108 //if (*src == ' ') { // a ' ' becomes a '+'
109 // *_dest = '+'; _dest++;
111 if (!isToBeEscaped(*src)) {
115 else { // any other char is escaped ..
116 *_dest = '%'; _dest++;
117 sprintf((char *)_dest, "%02X", (unsigned)*src);
124 static NSString *NGEscapeUrlString(NSString *_source) {
129 if ((len = [_source cStringLength]) == 0)
132 cstr = malloc(len + 3);
133 [_source getCString:cstr];
136 if (NGContainsUrlInvalidCharacters((unsigned char *)cstr)) {
137 // needs to be escaped ?
140 buffer = NGMallocAtomic([_source cStringLength] * 3 + 2);
141 NGEscapeUrlBuffer((unsigned char *)cstr, (unsigned char *)buffer);
143 s = [[[NSString alloc]
144 initWithCStringNoCopy:buffer
145 length:strlen(buffer)
146 freeWhenDone:YES] autorelease];
149 s = [[_source copy] autorelease];
155 @implementation NGLdapURL
157 + (id)ldapURLWithString:(NSString *)_url {
158 if (!ldap_is_ldap_url((char *)[_url UTF8String]))
161 return [[[self alloc] initWithString:_url] autorelease];
164 - (id)initWithString:(NSString *)_url {
165 LDAPURLDesc *urld = NULL;
166 unsigned attrCount, i;
171 if ((err = ldap_url_parse((char *)[_url UTF8String], &urld)) != 0) {
180 self->host = [[NSString alloc] initWithCString:urld->lud_host];
181 self->port = urld->lud_port;
182 self->base = [[NSString alloc] initWithCString:urld->lud_dn];
183 self->scope = urld->lud_scope;
184 self->filter = [[NSString alloc] initWithCString:urld->lud_filter];
186 if (urld != NULL && urld->lud_attrs != NULL) {
187 register char *tmp, **a;
189 for (i = 0; (tmp = a[i]); i++)
199 attrs = calloc(attrCount+1, sizeof(id));
201 for (i = 0; i < attrCount; i++)
202 attrs[i] = [[NSString alloc] initWithCString:urld->lud_attrs[i]];
204 self->attributes = [[NSArray alloc] initWithObjects:attrs count:attrCount];
206 for (i = 0; i < attrCount; i++)
208 if (attrs) free(attrs);
211 if (urld) ldap_free_urldesc(urld);
217 return [self initWithString:nil];
221 [self->host release];
222 [self->base release];
223 [self->filter release];
224 [self->attributes release];
230 - (NSString *)hostName {
234 return [NSHost hostWithName:[self hostName]];
240 - (NSString *)baseDN {
247 - (NSString *)searchFilter {
250 - (EOQualifier *)searchFilterQualifier {
253 if (self->filter == nil)
257 q = [[EOQualifier alloc] initWithLDAPFilterString:self->filter];
259 return [q autorelease];
262 - (NSArray *)attributes {
263 return self->attributes;
266 /* perform fetches */
268 - (NGLdapConnection *)openConnection {
269 NGLdapConnection *con;
271 con = [[NGLdapConnection alloc] initWithHostName:[self hostName]
272 port:[self port] ? [self port] : 389];
273 return [con autorelease];
276 - (NSEnumerator *)fetchEntries {
277 NGLdapConnection *con;
279 if ((con = [self openConnection]) == nil)
282 switch (self->scope) {
283 case LDAP_SCOPE_ONELEVEL:
284 return [con flatSearchAtBaseDN:[self baseDN]
285 qualifier:[self searchFilterQualifier]
286 attributes:[self attributes]];
288 case LDAP_SCOPE_SUBTREE:
289 return [con deepSearchAtBaseDN:[self baseDN]
290 qualifier:[self searchFilterQualifier]
291 attributes:[self attributes]];
293 case LDAP_SCOPE_BASE:
294 return [con baseSearchAtBaseDN:[self baseDN]
295 qualifier:[self searchFilterQualifier]
296 attributes:[self attributes]];
301 - (NGLdapEntry *)fetchEntry {
302 return [[self fetchEntries] nextObject];
307 - (NSString *)urlString {
311 s = [[NSMutableString alloc] initWithCapacity:200];
313 [s appendString:@"ldap://"];
314 [s appendString:self->host ? self->host : @"localhost"];
315 if (self->port > 0) [s appendFormat:@":%i", self->port];
317 [s appendString:@"/"];
319 is = NGEscapeUrlString(is);
322 if ((self->attributes != nil) || (self->scope != -1) || (self->filter != nil)){
323 [s appendString:@"?"];
324 is = [self->attributes componentsJoinedByString:@","];
325 is = NGEscapeUrlString(is);
328 if ((self->scope != -1) || (self->filter != nil)) {
329 [s appendString:@"?"];
330 switch (self->scope) {
331 case LDAP_SCOPE_ONELEVEL:
332 [s appendString:@"one"];
334 case LDAP_SCOPE_SUBTREE:
335 [s appendString:@"sub"];
337 case LDAP_SCOPE_BASE:
339 [s appendString:@"base"];
344 [s appendString:@"?"];
346 is = NGEscapeUrlString(is);
352 return [is autorelease];
357 - (id)copyWithZone:(NSZone *)_zone {
358 return [[[self class] allocWithZone:_zone] initWithString:[self urlString]];
363 - (NSString *)description {
364 return [self urlString];