Libinfo-173.tar.gz
[apple/libinfo.git] / netinfo.subproj / ni_useful.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Useful stuff for programming netinfo
27 * Copyright (C) 1989 by NeXT, Inc.
28 */
29 #include <stdlib.h>
30 #include <netinfo/ni.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <netdb.h>
34 #include <stdio.h>
35 #include <arpa/inet.h>
36
37 extern void *_ni_dup(void *);
38
39 static const char *
40 eatslash(
41 const char *path
42 )
43 {
44 while (*path == '/') {
45 path++;
46 }
47 return (path);
48 }
49
50 static void
51 unescape(
52 ni_name *name
53 )
54 {
55 ni_name newname;
56 ni_name p;
57 int len;
58 int i;
59
60 p = *name;
61 len = strlen(p);
62 newname = malloc(len + 1);
63 for (i = 0; *p != 0; i++) {
64 if (*p == '\\') {
65 p++;
66 }
67 newname[i] = *p++;
68 }
69 ni_name_free(name);
70 newname[i] = 0;
71 *name = newname;
72 }
73
74 static const char *
75 escindex(
76 const char *str,
77 char ch
78 )
79 {
80 char *p;
81
82 p = index(str, ch);
83 if (p == NULL) {
84 return (NULL);
85 }
86 if (p == str) {
87 return (p);
88 }
89 if (p[-1] == '\\') {
90 return (escindex(p + 1, ch));
91 }
92 return (p);
93 }
94
95 static void
96 setstuff(
97 void *ni,
98 ni_fancyopenargs *args
99 )
100 {
101 if (args != NULL) {
102 ni_setabort(ni, args->abort);
103 if (args->rtimeout) {
104 ni_setreadtimeout(ni, args->rtimeout);
105 }
106 if (args->wtimeout) {
107 ni_setwritetimeout(ni, args->wtimeout);
108 }
109 }
110 }
111
112 static void *
113 ni_relopen(
114 void *ni,
115 const char *domain,
116 int freeold,
117 ni_fancyopenargs *args
118 )
119 {
120 void *newni;
121 void *tmpni;
122 char *start;
123 char *slash;
124 char *component;
125
126 /* look for <tag>@<address> in last component of domain */
127 start = (char *)domain;
128 while ((slash = (char *)escindex(start, '/')) != NULL) {
129 /* found a slash, keep looking for the last one */
130 start = slash + 1;
131 }
132 if (index(start, '@') != NULL) {
133 /*
134 * last component in <tag>@<address> form, skip
135 * all of the leading components.
136 */
137 component = ni_name_dup(start);
138 newni = ni_new(NULL, component);
139 free(component);
140 if (newni != NULL && args != NULL)
141 setstuff(newni, args);
142 if (ni != NULL && freeold)
143 ni_free(ni);
144 return (newni);
145 }
146
147 component = ni_name_dup(domain);
148 slash = (char *)escindex(component, '/');
149 if (slash != NULL) {
150 *slash = 0;
151 }
152 unescape(&component);
153
154 tmpni = NULL;
155 if (ni != NULL && args != NULL) {
156 tmpni = _ni_dup(ni);
157 if (freeold) {
158 ni_free(ni);
159 }
160 ni = tmpni;
161 setstuff(ni, args);
162 }
163
164 newni = ni_new(ni, component);
165 free(component);
166
167 if (tmpni != NULL) {
168 ni_free(ni);
169 ni = NULL;
170 }
171
172 if (ni != NULL && freeold) {
173 ni_free(ni);
174 }
175
176
177 if (newni == NULL) {
178 return (NULL);
179 }
180 setstuff(newni, args);
181 ni = newni;
182 if (slash != NULL) {
183 slash = (char *)escindex(domain, '/');
184 domain = eatslash(slash + 1);
185 return (ni_relopen(ni, domain, TRUE, NULL));
186 } else {
187 return (ni);
188 }
189 }
190
191 static void *
192 ni_rootopen(
193 ni_fancyopenargs *args
194 )
195 {
196 void *ni;
197 void *newni;
198
199 ni = ni_new(NULL, ".");
200 if (ni == NULL) {
201 return (NULL);
202 }
203 setstuff(ni, args);
204 for (;;) {
205 newni = ni_new(ni, "..");
206 if (newni == NULL) {
207 break;
208 }
209 ni_free(ni);
210 ni = newni;
211 }
212 return (ni);
213 }
214
215 static ni_name
216 ni_name_dupn(
217 ni_name_const start,
218 ni_name_const stop
219 )
220 {
221 int len;
222 ni_name new;
223
224 if (stop != NULL) {
225 len = stop - start;
226 } else {
227 len = strlen(start);
228 }
229 new = malloc(len + 1);
230 bcopy(start, new, len);
231 new[len] = 0;
232 return (new);
233 }
234
235
236 static ni_status
237 ni_relsearch(
238 void *ni,
239 const char *path,
240 ni_id *id
241 )
242 {
243 char *slash;
244 char *equal;
245 ni_name key;
246 ni_name val;
247 ni_idlist idl;
248 ni_status status;
249
250 slash = (char *)escindex(path, '/');
251 equal = (char *)escindex(path, '=');
252 if (equal != NULL && (slash == NULL || equal < slash)) {
253 key = ni_name_dupn(path, equal);
254 val = ni_name_dupn(equal + 1, slash);
255 } else {
256 if (equal == NULL || (slash != NULL && slash < equal)) {
257 key = ni_name_dup("name");
258 val = ni_name_dupn(path, slash);
259 } else {
260 key = ni_name_dupn(path, equal);
261 val = ni_name_dupn(equal + 1, slash);
262 }
263 }
264 unescape(&key);
265 unescape(&val);
266 NI_INIT(&idl);
267 status = ni_lookup(ni, id, key, val, &idl);
268 if (status != NI_OK) {
269 ni_name_free(&key);
270 ni_name_free(&val);
271 return (status);
272 }
273 id->nii_object = idl.niil_val[0];
274 ni_name_free(&key);
275 ni_name_free(&val);
276 ni_idlist_free(&idl);
277 if (slash == NULL) {
278 ni_self(ni, id);
279 return (NI_OK);
280 }
281 path = eatslash(slash);
282 return (ni_relsearch(ni, path, id));
283 }
284
285 ni_status
286 ni_open(
287 void *ni,
288 const char *domain,
289 void **newni
290 )
291 {
292 return (ni_fancyopen(ni, domain, newni, NULL));
293 }
294
295 ni_status
296 ni_fancyopen(
297 void *ni,
298 const char *domain,
299 void **newni,
300 ni_fancyopenargs *args
301 )
302 {
303 void *tmp = NULL;
304 int rootopen = 0;
305
306 if (*domain == '/') {
307 tmp = ni_rootopen(args);
308 if (tmp == NULL) {
309 return (NI_FAILED); /* XXX: should return real error */
310 }
311 domain = eatslash(domain);
312 ni = tmp;
313 rootopen++;
314 }
315 if (*domain != 0) {
316 tmp = ni_relopen(ni, domain, FALSE, args);
317 if (rootopen) {
318 ni_free(ni);
319 }
320 }
321 if (tmp == NULL) {
322 return (NI_FAILED);
323 }
324 *newni = tmp;
325 ni_needwrite(*newni, args == NULL ? 0 : args->needwrite);
326 return (NI_OK);
327 }
328
329 ni_status
330 ni_pathsearch(
331 void *ni,
332 ni_id *id,
333 const char *path
334 )
335 {
336 ni_status status;
337
338 if (*path == '/') {
339 status = ni_root(ni, id);
340 if (status != NI_OK) {
341 return (status);
342 }
343 }
344 path = eatslash(path);
345 if (*path != 0) {
346 status = ni_relsearch(ni, path, id);
347 if (status != NI_OK) {
348 return (status);
349 }
350 }
351 return (NI_OK);
352 }
353
354 static char **
355 _ni_append_string(char *s, char **l)
356 {
357 int i, len;
358
359 if (s == NULL) return l;
360 if (l == NULL)
361 {
362 l = (char **)malloc(2 * sizeof(char *));
363 l[0] = strdup(s);
364 l[1] = NULL;
365 return l;
366 }
367
368 for (i = 0; l[i] != NULL; i++);
369 len = i + 1; /* count the NULL on the end of the list too! */
370
371 l = (char **)realloc(l, (len + 1) * sizeof(char *));
372
373 l[len - 1] = strdup(s);
374 l[len] = NULL;
375 return l;
376 }
377
378 static char **
379 _ni_explode_string(char *s, char c)
380 {
381 char **l = NULL;
382 char *p, *t;
383 int i, n;
384
385 if (s == NULL) return NULL;
386
387 p = s;
388 while (p[0] != '\0')
389 {
390 for (i = 0; ((p[i] != '\0') && p[i] != c); i++);
391 n = i;
392 t = malloc(n + 1);
393 for (i = 0; i < n; i++) t[i] = p[i];
394 t[n] = '\0';
395 l = _ni_append_string(t, l);
396 free(t);
397 t = NULL;
398 if (p[i] == '\0') return l;
399 if (p[i + 1] == '\0') l = _ni_append_string("", l);
400 p = p + i + 1;
401 }
402 return l;
403 }
404
405 static void
406 _ni_free_list(char **l)
407 {
408 int i;
409
410 if (l == NULL) return;
411 for (i = 0; l[i] != NULL; i++)
412 {
413 if (l[i] != NULL) free(l[i]);
414 l[i] = NULL;
415 }
416 if (l != NULL) free(l);
417 }
418
419 ni_status
420 ni_host_domain(char *host, char *domspec, void **domain)
421 {
422 void *d0, *d1;
423 struct sockaddr_in server;
424 ni_name tag;
425 int i, is_tag, is_local, is_relative;
426 ni_status status;
427 char **path;
428 struct hostent *h;
429
430 is_local = 1;
431
432 /* NULL host implies localhost */
433 if (host == NULL)
434 {
435 server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
436 }
437 else
438 {
439 is_local = 0;
440 server.sin_addr.s_addr = inet_addr(host);
441 if (server.sin_addr.s_addr == -1)
442 {
443 h = gethostbyname(host);
444 if (h == NULL)
445 {
446 *domain = (void *)NULL;
447 return NI_CANTFINDADDRESS;
448 }
449 bcopy(h->h_addr_list[0], &server.sin_addr.s_addr, h->h_length);
450 }
451 }
452
453 is_relative = 1;
454 is_tag = 1;
455
456 if (domspec == NULL)
457 {
458 is_tag = 0;
459 is_relative = 0;
460 }
461 else if (domspec[0] == '/')
462 {
463 is_tag = 0;
464 is_relative = 0;
465 }
466 else if (!strcmp(domspec, ".")) is_tag = 0;
467 else if (!strcmp(domspec, "..")) is_tag = 0;
468 else if (!strncmp(domspec, "./", 2)) is_tag = 0;
469 else if (!strncmp(domspec, "../", 3)) is_tag = 0;
470
471 if (is_tag == 1)
472 {
473 d0 = ni_connect(&server, domspec);
474 status = ni_addrtag(d0, &server, &tag);
475 ni_name_free(&tag);
476 if (status != NI_OK)
477 {
478 *domain = (void *)NULL;
479 return NI_FAILED;
480 }
481
482 *domain = d0;
483 return NI_OK;
484 }
485
486 if (is_local)
487 {
488 if (domspec == NULL) status = ni_open(NULL, ".", domain);
489 else status = ni_open(NULL, domspec, domain);
490 return status;
491 }
492
493 d0 = ni_connect(&server, "local");
494 status = ni_addrtag(d0, &server, &tag);
495 ni_name_free(&tag);
496 if (status != NI_OK)
497 {
498 *domain = (void *)NULL;
499 return NI_FAILED;
500 }
501
502 if ((domspec == NULL) || (!strcmp(domspec, ".")))
503 {
504 *domain = d0;
505 return NI_OK;
506 }
507
508 if (is_relative == 1)
509 {
510 path = _ni_explode_string(domspec, '/');
511 }
512 else
513 {
514 path = _ni_explode_string(domspec + 1, '/');
515
516 status = NI_OK;
517 while (status == NI_OK)
518 {
519 status = ni_open(d0, "..", &d1);
520 if (status == NI_OK)
521 {
522 ni_free(d0);
523 d0 = d1;
524 }
525 }
526
527 if (!strcmp(domspec, "/"))
528 {
529 *domain = d0;
530 return NI_OK;
531 }
532 }
533
534 for (i = 0; path[i] != NULL; i++)
535 {
536 status = ni_open(d0, path[i], &d1);
537 if (status != NI_OK)
538 {
539 _ni_free_list(path);
540 *domain = (void *)NULL;
541 return NI_FAILED;
542 }
543 ni_free(d0);
544 d0 = d1;
545 }
546
547 _ni_free_list(path);
548 *domain = d0;
549 return NI_OK;
550 }
551
552 static void
553 _ni_parse_url_hostspec(char *s, char **u, char **p, char **h)
554 {
555
556 char *p_at, *p_colon;
557 int ulen, plen, hlen;
558
559 if (s == NULL) return;
560 if (s[0] == '\0') return;
561
562 /* Check for [[[user][:[passwd]]]@]host */
563 p_at = strchr(s, '@');
564 if (p_at == NULL)
565 {
566 hlen = strlen(s);
567 if (hlen == 0) return;
568
569 *h = malloc(hlen + 1);
570 strcpy(*h, s);
571 return;
572 }
573
574 *p_at = '\0';
575 p_at++;
576 hlen = strlen(p_at);
577 if (hlen > 0)
578 {
579 *h = malloc(hlen + 1);
580 strcpy(*h, p_at);
581 }
582
583 if (s[0] == '\0') return;
584
585 p_colon = strchr(s, ':');
586 if (p_colon == NULL)
587 {
588 ulen = strlen(s);
589 if (ulen == 0) return;
590
591 *u = malloc(ulen + 1);
592 strcpy(*u, s);
593 return;
594 }
595
596 *p_colon = '\0';
597 p_colon++;
598 plen = strlen(p_colon);
599 if (plen > 0)
600 {
601 *p = malloc(plen + 1);
602 strcpy(*p, p_colon);
603 }
604
605 ulen = strlen(s);
606 if (ulen > 0)
607 {
608 *u = malloc(ulen + 1);
609 strcpy(*u, s);
610 }
611 }
612
613 void
614 ni_parse_url(char *url, char **user, char **password, char **host,
615 char **domspec, char **dirspec)
616 {
617 int i, x, len;
618 char *str;
619
620 *host = NULL;
621 *user = NULL;
622 *password = NULL;
623 *domspec = NULL;
624 *dirspec = NULL;
625
626 /*
627 * url ::= "netinfo://" <hostspec> [/[<domainspec>][:[<dirspec>]]]
628 * hostspec ::= [[[user][:[password]]]@]hostref
629 * hostref ::= <inet_addr> | <hostname>
630 * domainspec ::= <abs_domain> | <rel_domain>
631 * dirspec ::= <path> | <unsigned_integer>
632 */
633
634 x = strlen("netinfo://");
635
636 if (strncmp(url, "netinfo://", x)) return;
637
638 /*
639 * Look for <hostspec> part
640 * Defults to NULL user, password and host
641 * NULL host implies localhost
642 */
643 len = 0;
644 for (i = x; (url[i] != '\0') && (url[i] != '/'); i++) len++;
645
646 if (len != 0)
647 {
648 str = malloc(len + 1);
649 bcopy(url + x, str, len);
650 str[len] = '\0';
651
652 _ni_parse_url_hostspec(str, user, password, host);
653
654 free(str);
655 }
656
657 /*
658 * Look for <domainspec> part
659 * NULL domainspec implies "."
660 */
661 if (url[i] != '\0') i++;
662 x = i;
663 len = 0;
664 for (; (url[i] != '\0') && (url[i] != ':'); i++) len++;
665
666 if (len > 0)
667 {
668 *domspec = malloc(len + 1);
669 bcopy(url + x, *domspec, len);
670 (*domspec)[len] = '\0';
671 }
672
673 /*
674 * Look for <dirspec> part
675 * NULL <dirspec> implies "/"
676 */
677 if (url[i] != '\0') i++;
678 x = i;
679 len = 0;
680 for (; url[i] != '\0'; i++) len++;
681 if (len > 0)
682 {
683 *dirspec = malloc(len + 1);
684 bcopy(url + x, *dirspec, len);
685 (*dirspec)[len] = '\0';
686 }
687 }
688
689 ni_status
690 ni_url(char *url, void **domain, ni_id *dir)
691 {
692 int nilen;
693 char *user, *password, *host;
694 char *domspec, *dirspec;
695 ni_status status;
696
697 nilen = strlen("netinfo://");
698
699 if (strncmp(url, "netinfo://", nilen))
700 {
701 *domain = (void *)NULL;
702 return NI_CANTFINDADDRESS;
703 }
704
705 ni_parse_url(url, &user, &password, &host, &domspec, &dirspec);
706
707 status = ni_host_domain(host, domspec, domain);
708 if (host != NULL) free(host);
709 if (domspec != NULL) free(domspec);
710 if (status != NI_OK)
711 {
712 if (user != NULL) free(user);
713 if (password != NULL) free(password);
714 if (dirspec != NULL) free(dirspec);
715 return status;
716 }
717
718 if (user != NULL)
719 {
720 ni_setuser(*domain, user);
721 free(user);
722 }
723
724 if (password != NULL)
725 {
726 ni_setpassword(*domain, password);
727 free(password);
728 }
729
730 if (dirspec == NULL)
731 {
732 status = ni_root(*domain, dir);
733 return status;
734 }
735
736 if ((dirspec[0] >= '0') && (dirspec[0] <= '9'))
737 {
738 dir->nii_object = atoi(dirspec);
739 free(dirspec);
740 status = ni_self(*domain, dir);
741 return status;
742 }
743
744 status = ni_pathsearch(*domain, dir, dirspec);
745 free(dirspec);
746 return status;
747 }