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