Libinfo-78.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 = domain;
127 while ((slash = 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 status = ni_lookup(ni, id, key, val, &idl);
266 if (status != NI_OK) {
267 ni_name_free(&key);
268 ni_name_free(&val);
269 return (status);
270 }
271 id->nii_object = idl.niil_val[0];
272 ni_name_free(&key);
273 ni_name_free(&val);
274 ni_idlist_free(&idl);
275 if (slash == NULL) {
276 ni_self(ni, id);
277 return (NI_OK);
278 }
279 path = eatslash(slash);
280 return (ni_relsearch(ni, path, id));
281 }
282
283 ni_status
284 ni_open(
285 void *ni,
286 const char *domain,
287 void **newni
288 )
289 {
290 return (ni_fancyopen(ni, domain, newni, NULL));
291 }
292
293 ni_status
294 ni_fancyopen(
295 void *ni,
296 const char *domain,
297 void **newni,
298 ni_fancyopenargs *args
299 )
300 {
301 void *tmp = NULL;
302 int rootopen = 0;
303
304 if (*domain == '/') {
305 tmp = ni_rootopen(args);
306 if (tmp == NULL) {
307 return (NI_FAILED); /* XXX: should return real error */
308 }
309 domain = eatslash(domain);
310 ni = tmp;
311 rootopen++;
312 }
313 if (*domain != 0) {
314 tmp = ni_relopen(ni, domain, FALSE, args);
315 if (rootopen) {
316 ni_free(ni);
317 }
318 }
319 if (tmp == NULL) {
320 return (NI_FAILED);
321 }
322 *newni = tmp;
323 ni_needwrite(*newni, args == NULL ? 0 : args->needwrite);
324 return (NI_OK);
325 }
326
327 ni_status
328 ni_pathsearch(
329 void *ni,
330 ni_id *id,
331 const char *path
332 )
333 {
334 ni_status status;
335
336 if (*path == '/') {
337 status = ni_root(ni, id);
338 if (status != NI_OK) {
339 return (status);
340 }
341 }
342 path = eatslash(path);
343 if (*path != 0) {
344 status = ni_relsearch(ni, path, id);
345 if (status != NI_OK) {
346 return (status);
347 }
348 }
349 return (NI_OK);
350 }
351
352 static char **
353 _ni_append_string(char *s, char **l)
354 {
355 int i, len;
356
357 if (s == NULL) return l;
358 if (l == NULL)
359 {
360 l = (char **)malloc(2 * sizeof(char *));
361 l[0] = strdup(s);
362 l[1] = NULL;
363 return l;
364 }
365
366 for (i = 0; l[i] != NULL; i++);
367 len = i + 1; /* count the NULL on the end of the list too! */
368
369 l = (char **)realloc(l, (len + 1) * sizeof(char *));
370
371 l[len - 1] = strdup(s);
372 l[len] = NULL;
373 return l;
374 }
375
376 static char **
377 _ni_explode_string(char *s, char c)
378 {
379 char **l = NULL;
380 char *p, *t;
381 int i, n;
382
383 if (s == NULL) return NULL;
384
385 p = s;
386 while (p[0] != '\0')
387 {
388 for (i = 0; ((p[i] != '\0') && p[i] != c); i++);
389 n = i;
390 t = malloc(n + 1);
391 for (i = 0; i < n; i++) t[i] = p[i];
392 t[n] = '\0';
393 l = _ni_append_string(t, l);
394 free(t);
395 t = NULL;
396 if (p[i] == '\0') return l;
397 if (p[i + 1] == '\0') l = _ni_append_string("", l);
398 p = p + i + 1;
399 }
400 return l;
401 }
402
403 static void
404 _ni_free_list(char **l)
405 {
406 int i;
407
408 if (l == NULL) return;
409 for (i = 0; l[i] != NULL; i++)
410 {
411 if (l[i] != NULL) free(l[i]);
412 l[i] = NULL;
413 }
414 if (l != NULL) free(l);
415 }
416
417 ni_status
418 ni_host_domain(char *host, char *domspec, void **domain)
419 {
420 void *d0, *d1;
421 struct sockaddr_in server;
422 ni_name tag;
423 int i, is_tag, is_local, is_relative;
424 ni_status status;
425 char **path;
426 struct hostent *h;
427
428 is_local = 1;
429
430 /* NULL host implies localhost */
431 if (host == NULL)
432 {
433 server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
434 }
435 else
436 {
437 is_local = 0;
438 server.sin_addr.s_addr = inet_addr(host);
439 if (server.sin_addr.s_addr == -1)
440 {
441 h = gethostbyname(host);
442 if (h == NULL)
443 {
444 *domain = (void *)NULL;
445 return NI_CANTFINDADDRESS;
446 }
447 bcopy(h->h_addr_list[0], &server.sin_addr.s_addr, h->h_length);
448 }
449 }
450
451 is_relative = 1;
452 is_tag = 1;
453
454 if (domspec == NULL)
455 {
456 is_tag = 0;
457 is_relative = 0;
458 }
459 else if (domspec[0] == '/')
460 {
461 is_tag = 0;
462 is_relative = 0;
463 }
464 else if (!strcmp(domspec, ".")) is_tag = 0;
465 else if (!strcmp(domspec, "..")) is_tag = 0;
466 else if (!strncmp(domspec, "./", 2)) is_tag = 0;
467 else if (!strncmp(domspec, "../", 3)) is_tag = 0;
468
469 if (is_tag == 1)
470 {
471 d0 = ni_connect(&server, domspec);
472 status = ni_addrtag(d0, &server, &tag);
473 ni_name_free(&tag);
474 if (status != NI_OK)
475 {
476 *domain = (void *)NULL;
477 return NI_FAILED;
478 }
479
480 *domain = d0;
481 return NI_OK;
482 }
483
484 if (is_local)
485 {
486 if (domspec == NULL) status = ni_open(NULL, ".", domain);
487 else status = ni_open(NULL, domspec, domain);
488 return status;
489 }
490
491 d0 = ni_connect(&server, "local");
492 status = ni_addrtag(d0, &server, &tag);
493 ni_name_free(&tag);
494 if (status != NI_OK)
495 {
496 *domain = (void *)NULL;
497 return NI_FAILED;
498 }
499
500 if ((domspec == NULL) || (!strcmp(domspec, ".")))
501 {
502 *domain = d0;
503 return NI_OK;
504 }
505
506 if (is_relative == 1)
507 {
508 path = _ni_explode_string(domspec, '/');
509 }
510 else
511 {
512 path = _ni_explode_string(domspec + 1, '/');
513
514 status = NI_OK;
515 while (status == NI_OK)
516 {
517 status = ni_open(d0, "..", &d1);
518 if (status == NI_OK)
519 {
520 ni_free(d0);
521 d0 = d1;
522 }
523 }
524
525 if (!strcmp(domspec, "/"))
526 {
527 *domain = d0;
528 return NI_OK;
529 }
530 }
531
532 for (i = 0; path[i] != NULL; i++)
533 {
534 status = ni_open(d0, path[i], &d1);
535 if (status != NI_OK)
536 {
537 _ni_free_list(path);
538 *domain = (void *)NULL;
539 return NI_FAILED;
540 }
541 ni_free(d0);
542 d0 = d1;
543 }
544
545 _ni_free_list(path);
546 *domain = d0;
547 return NI_OK;
548 }
549
550 static void
551 _ni_parse_url_hostspec(char *s, char **u, char **p, char **h)
552 {
553
554 char *p_at, *p_colon;
555 int ulen, plen, hlen;
556
557 if (s == NULL) return;
558 if (s[0] == '\0') return;
559
560 /* Check for [[[user][:[passwd]]]@]host */
561 p_at = strchr(s, '@');
562 if (p_at == NULL)
563 {
564 hlen = strlen(s);
565 if (hlen == 0) return;
566
567 *h = malloc(hlen + 1);
568 strcpy(*h, s);
569 return;
570 }
571
572 *p_at = '\0';
573 p_at++;
574 hlen = strlen(p_at);
575 if (hlen > 0)
576 {
577 *h = malloc(hlen + 1);
578 strcpy(*h, p_at);
579 }
580
581 if (s[0] == '\0') return;
582
583 p_colon = strchr(s, ':');
584 if (p_colon == NULL)
585 {
586 ulen = strlen(s);
587 if (ulen == 0) return;
588
589 *u = malloc(ulen + 1);
590 strcpy(*u, s);
591 return;
592 }
593
594 *p_colon = '\0';
595 p_colon++;
596 plen = strlen(p_colon);
597 if (plen > 0)
598 {
599 *p = malloc(plen + 1);
600 strcpy(*p, p_colon);
601 }
602
603 ulen = strlen(s);
604 if (ulen > 0)
605 {
606 *u = malloc(ulen + 1);
607 strcpy(*u, s);
608 }
609 }
610
611 void
612 ni_parse_url(char *url, char **user, char **password, char **host,
613 char **domspec, char **dirspec)
614 {
615 int i, x, len;
616 char *str;
617
618 *host = NULL;
619 *user = NULL;
620 *password = NULL;
621 *domspec = NULL;
622 *dirspec = NULL;
623
624 /*
625 * url ::= "netinfo://" <hostspec> [/[<domainspec>][:[<dirspec>]]]
626 * hostspec ::= [[[user][:[password]]]@]hostref
627 * hostref ::= <inet_addr> | <hostname>
628 * domainspec ::= <abs_domain> | <rel_domain>
629 * dirspec ::= <path> | <unsigned_integer>
630 */
631
632 x = strlen("netinfo://");
633
634 if (strncmp(url, "netinfo://", x)) return;
635
636 /*
637 * Look for <hostspec> part
638 * Defults to NULL user, password and host
639 * NULL host implies localhost
640 */
641 len = 0;
642 for (i = x; (url[i] != '\0') && (url[i] != '/'); i++) len++;
643
644 if (len != 0)
645 {
646 str = malloc(len + 1);
647 bcopy(url + x, str, len);
648 str[len] = '\0';
649
650 _ni_parse_url_hostspec(str, user, password, host);
651
652 free(str);
653 }
654
655 /*
656 * Look for <domainspec> part
657 * NULL domainspec implies "."
658 */
659 if (url[i] != '\0') i++;
660 x = i;
661 len = 0;
662 for (; (url[i] != '\0') && (url[i] != ':'); i++) len++;
663
664 if (len > 0)
665 {
666 *domspec = malloc(len + 1);
667 bcopy(url + x, *domspec, len);
668 (*domspec)[len] = '\0';
669 }
670
671 /*
672 * Look for <dirspec> part
673 * NULL <dirspec> implies "/"
674 */
675 if (url[i] != '\0') i++;
676 x = i;
677 len = 0;
678 for (; url[i] != '\0'; i++) len++;
679 if (len > 0)
680 {
681 *dirspec = malloc(len + 1);
682 bcopy(url + x, *dirspec, len);
683 (*dirspec)[len] = '\0';
684 }
685 }
686
687 ni_status
688 ni_url(char *url, void **domain, ni_id *dir)
689 {
690 int nilen;
691 char *user, *password, *host;
692 char *domspec, *dirspec;
693 ni_status status;
694
695 nilen = strlen("netinfo://");
696
697 if (strncmp(url, "netinfo://", nilen))
698 {
699 *domain = (void *)NULL;
700 return NI_CANTFINDADDRESS;
701 }
702
703 ni_parse_url(url, &user, &password, &host, &domspec, &dirspec);
704
705 status = ni_host_domain(host, domspec, domain);
706 if (host != NULL) free(host);
707 if (domspec != NULL) free(domspec);
708 if (status != NI_OK)
709 {
710 if (user != NULL) free(user);
711 if (password != NULL) free(password);
712 if (dirspec != NULL) free(dirspec);
713 return status;
714 }
715
716 if (user != NULL)
717 {
718 ni_setuser(*domain, user);
719 free(user);
720 }
721
722 if (password != NULL)
723 {
724 ni_setpassword(*domain, password);
725 free(password);
726 }
727
728 if (dirspec == NULL)
729 {
730 status = ni_root(*domain, dir);
731 return status;
732 }
733
734 if ((dirspec[0] >= '0') && (dirspec[0] <= '9'))
735 {
736 dir->nii_object = atoi(dirspec);
737 free(dirspec);
738 status = ni_self(*domain, dir);
739 return status;
740 }
741
742 status = ni_pathsearch(*domain, dir, dirspec);
743 free(dirspec);
744 return status;
745 }