]> err.no Git - sope/blob - sope-ldap/NGLdap/NGLdapURL.m
some code cleanups
[sope] / sope-ldap / NGLdap / NGLdapURL.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "NGLdapURL.h"
23 #include "NGLdapConnection.h"
24 #include "NGLdapEntry.h"
25 #include "EOQualifier+LDAP.h"
26 #include "common.h"
27 #include <string.h>
28
29 static inline BOOL isUrlAlpha(unsigned char _c) {
30   return
31     (((_c >= 'a') && (_c <= 'z')) ||
32      ((_c >= 'A') && (_c <= 'Z')))
33     ? YES : NO;
34 }
35 static inline BOOL isUrlDigit(unsigned char _c) {
36   return ((_c >= '0') && (_c <= '9')) ? YES : NO;
37 }
38 static inline BOOL isUrlSafeChar(unsigned char _c) {
39   switch (_c) {
40     case '$': case '-': case '_': case '@':
41     case '.': case '&': case '+':
42       return YES;
43
44     default:
45       return NO;
46   }
47 }
48 static inline BOOL isUrlExtraChar(unsigned char _c) {
49   switch (_c) {
50     case '!': case '*': case '"': case '\'':
51     case '|': case ',':
52       return YES;
53   }
54   return NO;
55 }
56 static inline BOOL isUrlEscapeChar(unsigned char _c) {
57   return (_c == '%') ? YES : NO;
58 }
59 static inline BOOL isUrlReservedChar(unsigned char _c) {
60   switch (_c) {
61     case '=': case ';': case '/':
62     case '#': case '?': case ':':
63     case ' ':
64       return YES;
65   }
66   return NO;
67 }
68
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;
75   return NO;
76 }
77
78 static inline BOOL isUrlHexChar(unsigned char _c) {
79   if (isUrlDigit(_c))
80     return YES;
81   if ((_c >= 'a') && (_c <= 'f'))
82     return YES;
83   if ((_c >= 'A') && (_c <= 'F'))
84     return YES;
85   return NO;
86 }
87
88 static inline BOOL isUrlAlphaNum(unsigned char _c) {
89   return (isUrlAlpha(_c) || isUrlDigit(_c)) ? YES : NO;
90 }
91
92 static inline BOOL isToBeEscaped(unsigned char _c) {
93   return (isUrlAlphaNum(_c) || (_c == '_')) ? NO : YES;
94 }
95
96 static BOOL NGContainsUrlInvalidCharacters(const unsigned char *_buffer) {
97   while (*_buffer) {
98     if (isToBeEscaped(*_buffer))
99       return YES;
100     _buffer++;
101   }
102   return NO;
103 }
104 static void NGEscapeUrlBuffer
105 (const unsigned char *_source, unsigned char *_dest) {
106   register const unsigned char *src = (void*)_source;
107   while (*src) {
108     //if (*src == ' ') { // a ' ' becomes a '+'
109     //  *_dest = '+'; _dest++;
110     //}
111     if (!isToBeEscaped(*src)) {
112       *_dest = *src;
113       _dest++;
114     } 
115     else { // any other char is escaped ..
116       *_dest = '%'; _dest++;
117       sprintf((char *)_dest, "%02X", (unsigned)*src);
118       _dest += 2;
119     }
120     src++;
121   }
122   *_dest = '\0';
123 }
124 static NSString *NGEscapeUrlString(NSString *_source) {
125   unsigned len;
126   char     *cstr;
127   NSString *s;
128
129   if ((len = [_source cStringLength]) == 0)
130     return _source;
131
132   cstr = malloc(len + 3);
133   [_source getCString:cstr];
134   cstr[len] = '\0';
135   
136   if (NGContainsUrlInvalidCharacters((unsigned char *)cstr)) {
137     // needs to be escaped ?
138     char *buffer = NULL;
139     
140     buffer = NGMallocAtomic([_source cStringLength] * 3 + 2);
141     NGEscapeUrlBuffer((unsigned char *)cstr, (unsigned char *)buffer);
142     
143     s = [[[NSString alloc]
144                     initWithCStringNoCopy:buffer
145                     length:strlen(buffer)
146                     freeWhenDone:YES] autorelease];
147   }
148   else
149     s = [[_source copy] autorelease];
150   
151   free(cstr);
152   return s;
153 }
154
155 @implementation NGLdapURL
156
157 + (id)ldapURLWithString:(NSString *)_url {
158   if (!ldap_is_ldap_url((char *)[_url UTF8String]))
159     return nil;
160
161   return [[[self alloc] initWithString:_url] autorelease];
162 }
163
164 - (id)initWithString:(NSString *)_url {
165   LDAPURLDesc *urld = NULL;
166   unsigned attrCount, i;
167   int err;
168
169   self->scope = -1;
170
171   if ((err = ldap_url_parse((char *)[_url UTF8String], &urld)) != 0) {
172     [self release];
173     return nil;
174   }
175   if (urld == NULL) {
176     [self release];
177     return nil;
178   }
179     
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];
185   
186   if (urld != NULL && urld->lud_attrs != NULL) {
187     register char *tmp, **a;
188     a = urld->lud_attrs;
189     for (i = 0; (tmp = a[i]); i++)
190       ;
191     attrCount = i;
192   }
193   else
194     attrCount = 0;
195     
196   if (attrCount > 0) {
197     id *attrs;
198
199     attrs = calloc(attrCount+1, sizeof(id));
200     
201     for (i = 0; i < attrCount; i++)
202       attrs[i] = [[NSString alloc] initWithCString:urld->lud_attrs[i]];
203       
204     self->attributes = [[NSArray alloc] initWithObjects:attrs count:attrCount];
205
206     for (i = 0; i < attrCount; i++)
207       [attrs[i] release];
208     if (attrs) free(attrs);
209   }
210   
211   if (urld) ldap_free_urldesc(urld);
212   
213   return self;
214 }
215
216 - (id)init {
217   return [self initWithString:nil];
218 }
219
220 - (void)dealloc {
221   [self->host       release];
222   [self->base       release];
223   [self->filter     release];
224   [self->attributes release];
225   [super dealloc];
226 }
227
228 /* accessors */
229
230 - (NSString *)hostName {
231   return self->host;
232 }
233 - (NSHost *)host {
234   return [NSHost hostWithName:[self hostName]];
235 }
236
237 - (int)port {
238   return self->port;
239 }
240 - (NSString *)baseDN {
241   return self->base;
242 }
243 - (int)scope {
244   return self->scope;
245 }
246
247 - (NSString *)searchFilter {
248   return self->filter;
249 }
250 - (EOQualifier *)searchFilterQualifier {
251   EOQualifier *q;
252   
253   if (self->filter == nil)
254     return nil;
255
256   q = nil;
257   q = [[EOQualifier alloc] initWithLDAPFilterString:self->filter];
258
259   return [q autorelease];
260 }
261
262 - (NSArray *)attributes {
263   return self->attributes;
264 }
265
266 /* perform fetches */
267
268 - (NGLdapConnection *)openConnection {
269   NGLdapConnection *con;
270
271   con = [[NGLdapConnection alloc] initWithHostName:[self hostName]
272                                   port:[self port] ? [self port] : 389];
273   return [con autorelease];
274 }
275
276 - (NSEnumerator *)fetchEntries {
277   NGLdapConnection *con;
278
279   if ((con = [self openConnection]) == nil)
280     return nil;
281   
282   switch (self->scope) {
283     case LDAP_SCOPE_ONELEVEL:
284       return [con flatSearchAtBaseDN:[self baseDN]
285                   qualifier:[self searchFilterQualifier]
286                   attributes:[self attributes]];
287       
288     case LDAP_SCOPE_SUBTREE:
289       return [con deepSearchAtBaseDN:[self baseDN]
290                   qualifier:[self searchFilterQualifier]
291                   attributes:[self attributes]];
292       
293     case LDAP_SCOPE_BASE:
294       return [con baseSearchAtBaseDN:[self baseDN]
295                   qualifier:[self searchFilterQualifier]
296                   attributes:[self attributes]];
297   }
298
299   return nil;
300 }
301 - (NGLdapEntry *)fetchEntry {
302   return [[self fetchEntries] nextObject];
303 }
304
305 /* url */
306
307 - (NSString *)urlString {
308   NSMutableString *s;
309   NSString *is;
310
311   s = [[NSMutableString alloc] initWithCapacity:200];
312
313   [s appendString:@"ldap://"];
314   [s appendString:self->host ? self->host : @"localhost"];
315   if (self->port > 0) [s appendFormat:@":%i", self->port];
316
317   [s appendString:@"/"];
318   is = self->base;
319   is = NGEscapeUrlString(is);
320   [s appendString:is];
321   
322   if ((self->attributes != nil) || (self->scope != -1) || (self->filter != nil)){
323     [s appendString:@"?"];
324     is = [self->attributes componentsJoinedByString:@","];
325     is = NGEscapeUrlString(is);
326     [s appendString:is];
327   }
328   if ((self->scope != -1) || (self->filter != nil)) {
329     [s appendString:@"?"];
330     switch (self->scope) {
331       case LDAP_SCOPE_ONELEVEL:
332         [s appendString:@"one"];
333         break;
334       case LDAP_SCOPE_SUBTREE:
335         [s appendString:@"sub"];
336         break;
337       case LDAP_SCOPE_BASE:
338       default:
339         [s appendString:@"base"];
340         break;
341     }
342   }
343   if (self->filter) {
344     [s appendString:@"?"];
345     is = self->filter;
346     is = NGEscapeUrlString(is);
347     [s appendString:is];
348   }
349
350   is = [s copy];
351   [s release];
352   return [is autorelease];
353 }
354
355 /* NSCopying */
356
357 - (id)copyWithZone:(NSZone *)_zone {
358   return [[[self class] allocWithZone:_zone] initWithString:[self urlString]];
359 }
360
361 /* description */
362
363 - (NSString *)description {
364   return [self urlString];
365 }
366
367 @end /* NGLdapURL */