1/*
2 * hashtable.c - hash tables
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "../config.h"
31
32/*
33 * On Solaris 8 there's a clash between "bool" in curses and RPC.
34 * We don't need curses here, so ensure it doesn't get included.
35 */
36#define ZSH_NO_TERM_HANDLING
37
38#include "zsh.mdh"
39#include "hashnameddir.pro"
40
41/****************************************/
42/* Named Directory Hash Table Functions */
43/****************************************/
44
45#ifdef HAVE_NIS_PLUS
46# include <rpcsvc/nis.h>
47#else
48# ifdef HAVE_NIS
49#  include	<rpc/types.h>
50#  include	<rpc/rpc.h>
51#  include	<rpcsvc/ypclnt.h>
52#  include	<rpcsvc/yp_prot.h>
53# endif
54#endif
55
56/* hash table containing named directories */
57
58/**/
59mod_export HashTable nameddirtab;
60
61/* != 0 if all the usernames have already been *
62 * added to the named directory hash table.    */
63
64static int allusersadded;
65
66/* Create new hash table for named directories */
67
68/**/
69void
70createnameddirtable(void)
71{
72    nameddirtab = newhashtable(201, "nameddirtab", NULL);
73
74    nameddirtab->hash        = hasher;
75    nameddirtab->emptytable  = emptynameddirtable;
76    nameddirtab->filltable   = fillnameddirtable;
77    nameddirtab->cmpnodes    = strcmp;
78    nameddirtab->addnode     = addnameddirnode;
79    nameddirtab->getnode     = gethashnode;
80    nameddirtab->getnode2    = gethashnode2;
81    nameddirtab->removenode  = removenameddirnode;
82    nameddirtab->disablenode = NULL;
83    nameddirtab->enablenode  = NULL;
84    nameddirtab->freenode    = freenameddirnode;
85    nameddirtab->printnode   = printnameddirnode;
86
87    allusersadded = 0;
88    finddir(NULL);		/* clear the finddir cache */
89}
90
91/* Empty the named directories table */
92
93/**/
94static void
95emptynameddirtable(HashTable ht)
96{
97    emptyhashtable(ht);
98    allusersadded = 0;
99    finddir(NULL);		/* clear the finddir cache */
100}
101
102/* Add all the usernames in the password file/database *
103 * to the named directories table.                     */
104
105#ifdef HAVE_NIS_PLUS
106static int
107add_userdir(nis_name table, nis_object *object, void *userdata)
108{
109    if (object->zo_data.objdata_u.en_data.en_cols.en_cols_len >= 6) {
110	static char name[40], dir[PATH_MAX + 1];
111	register entry_col *ec =
112	    object->zo_data.objdata_u.en_data.en_cols.en_cols_val;
113	register int nl = minimum(ec[0].ec_value.ec_value_len, 39);
114	register int dl = minimum(ec[5].ec_value.ec_value_len, PATH_MAX);
115
116	memcpy(name, ec[0].ec_value.ec_value_val, nl);
117	name[nl] = '\0';
118	memcpy(dir, ec[5].ec_value.ec_value_val, dl);
119	dir[dl] = '\0';
120
121	adduserdir(name, dir, ND_USERNAME, 1);
122    }
123    return 0;
124}
125#else
126# ifdef HAVE_NIS
127static int
128add_userdir(int status, char *key, int keylen, char *val, int vallen, char *dummy)
129{
130    char *p, *d, *de;
131
132    if (status != YP_TRUE)
133	return 1;
134
135    if (vallen > keylen && *(p = val + keylen) == ':') {
136	*p++ = '\0';
137	for (de = val + vallen - 1; *de != ':' && de > val; de--);
138	if (de > val) {
139	    *de = '\0';
140	    if ((d = strrchr(p, ':'))) {
141		if (*++d && val[0])
142		    adduserdir(val, d, ND_USERNAME, 1);
143	    }
144	}
145    }
146    return 0;
147}
148# endif /* HAVE_NIS */
149#endif  /* HAVE_NIS_PLUS */
150
151/**/
152static void
153fillnameddirtable(UNUSED(HashTable ht))
154{
155    if (!allusersadded) {
156#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS)
157	FILE *pwf;
158	char buf[BUFSIZ], *p, *d, *de;
159	int skipping, oldct = nameddirtab->ct, usepwf = 1;
160
161# ifndef HAVE_NIS_PLUS
162	char domain[YPMAXDOMAIN];
163	struct ypall_callback cb;
164
165	/* Get potential matches from NIS and cull those without local accounts */
166	if (getdomainname(domain, YPMAXDOMAIN) == 0) {
167	    cb.foreach = (int (*)()) add_userdir;
168	    cb.data = NULL;
169	    yp_all(domain, PASSWD_MAP, &cb);
170    }
171# else  /* HAVE_NIS_PLUS */
172	/* Maybe we should turn this string into a #define'd constant...? */
173
174	nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH,
175		 add_userdir, 0);
176# endif
177	if (nameddirtab->ct == oldct) {
178	    /* Using NIS or NIS+ didn't add any user directories. This seems
179	     * fishy, so we fall back to using getpwent(). If we don't have
180	     * that, we only use the passwd file. */
181#ifdef HAVE_GETPWENT
182	    struct passwd *pw;
183
184	    setpwent();
185
186	    /* loop through the password file/database *
187	     * and add all entries returned.           */
188	    while ((pw = getpwent()) && !errflag)
189		adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1);
190
191	    endpwent();
192	    usepwf = 0;
193#endif /* HAVE_GETPWENT */
194	}
195	if (usepwf) {
196	    /* Don't forget the non-NIS matches from the flat passwd file */
197	    if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) {
198		skipping = 0;
199		while (fgets(buf, BUFSIZ, pwf) != NULL) {
200		    if (strchr(buf, '\n') != NULL) {
201			if (!skipping) {
202			    if ((p = strchr(buf, ':')) != NULL) {
203				*p++ = '\0';
204				if ((de = strrchr(p, ':'))) {
205				    *de = '\0';
206				    if ((d = strrchr(p, ':'))) {
207					if (*++d && buf[0])
208					    adduserdir(buf, d, ND_USERNAME, 1);
209				    }
210				}
211			    }
212			} else
213			    skipping = 0;
214		    } else
215			skipping = 1;
216		}
217		fclose(pwf);
218	    }
219	}
220#else  /* no NIS or NIS_PLUS */
221#ifdef USE_GETPWENT
222	struct passwd *pw;
223
224	setpwent();
225
226	/* loop through the password file/database *
227	 * and add all entries returned.           */
228	while ((pw = getpwent()) && !errflag)
229	    adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1);
230
231	endpwent();
232#endif /* HAVE_GETPWENT */
233#endif
234	allusersadded = 1;
235    }
236}
237
238/* Add an entry to the named directory hash *
239 * table, clearing the finddir() cache and  *
240 * initialising the `diff' member.          */
241
242/**/
243static void
244addnameddirnode(HashTable ht, char *nam, void *nodeptr)
245{
246    Nameddir nd = (Nameddir) nodeptr;
247
248    nd->diff = strlen(nd->dir) - strlen(nam);
249    finddir(NULL);		/* clear the finddir cache */
250    addhashnode(ht, nam, nodeptr);
251}
252
253/* Remove an entry from the named directory  *
254 * hash table, clearing the finddir() cache. */
255
256/**/
257static HashNode
258removenameddirnode(HashTable ht, const char *nam)
259{
260    HashNode hn = removehashnode(ht, nam);
261
262    if(hn)
263	finddir(NULL);		/* clear the finddir cache */
264    return hn;
265}
266
267/* Free up the memory used by a named directory hash node. */
268
269/**/
270static void
271freenameddirnode(HashNode hn)
272{
273    Nameddir nd = (Nameddir) hn;
274
275    zsfree(nd->node.nam);
276    zsfree(nd->dir);
277    zfree(nd, sizeof(struct nameddir));
278}
279
280/* Print a named directory */
281
282/**/
283static void
284printnameddirnode(HashNode hn, int printflags)
285{
286    Nameddir nd = (Nameddir) hn;
287
288    if (printflags & PRINT_NAMEONLY) {
289	zputs(nd->node.nam, stdout);
290	putchar('\n');
291	return;
292    }
293
294    if (printflags & PRINT_LIST) {
295      printf("hash -d ");
296
297      if(nd->node.nam[0] == '-')
298	    printf("-- ");
299    }
300
301    quotedzputs(nd->node.nam, stdout);
302    putchar('=');
303    quotedzputs(nd->dir, stdout);
304    putchar('\n');
305}
306
307#include "../config.h"
308