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