66f3d3ad42f0336ce16027353acb0486b9ef0d6a
[kopensolaris-gnu/glibc.git] / nss / nss_files / files-alias.c
1 /* Mail alias file parser in nss_files module.
2    Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <aliases.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <bits/libc-lock.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "nsswitch.h"
31
32 /* Locks the static variables in this file.  */
33 __libc_lock_define_initialized (static, lock)
34 \f
35 /* Maintenance of the shared stream open on the database file.  */
36
37 static FILE *stream;
38 static fpos_t position;
39 static enum { none, getent, getby } last_use;
40
41
42 static enum nss_status
43 internal_setent (void)
44 {
45   enum nss_status status = NSS_STATUS_SUCCESS;
46
47   if (stream == NULL)
48     {
49       stream = fopen ("/etc/aliases", "r");
50
51       if (stream == NULL)
52         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
53       else
54         {
55           /* We have to make sure the file is  `closed on exec'.  */
56           int result, flags;
57
58           result = flags = fcntl (fileno (stream), F_GETFD, 0);
59           if (result >= 0)
60             {
61               flags |= FD_CLOEXEC;
62               result = fcntl (fileno (stream), F_SETFD, flags);
63             }
64           if (result < 0)
65             {
66               /* Something went wrong.  Close the stream and return a
67                  failure.  */
68               fclose (stream);
69               stream = NULL;
70               status = NSS_STATUS_UNAVAIL;
71             }
72         }
73     }
74   else
75     rewind (stream);
76
77   return status;
78 }
79
80
81 /* Thread-safe, exported version of that.  */
82 enum nss_status
83 _nss_files_setaliasent (void)
84 {
85   enum nss_status status;
86
87   __libc_lock_lock (lock);
88
89   status = internal_setent ();
90
91   if (status == NSS_STATUS_SUCCESS && fgetpos (stream, &position) < 0)
92     {
93       fclose (stream);
94       stream = NULL;
95       status = NSS_STATUS_UNAVAIL;
96     }
97
98   last_use = getent;
99
100   __libc_lock_unlock (lock);
101
102   return status;
103 }
104
105
106 /* Close the database file.  */
107 static void
108 internal_endent (void)
109 {
110   if (stream != NULL)
111     {
112       fclose (stream);
113       stream = NULL;
114     }
115 }
116
117
118 /* Thread-safe, exported version of that.  */
119 enum nss_status
120 _nss_files_endaliasent (void)
121 {
122   __libc_lock_lock (lock);
123
124   internal_endent ();
125
126   __libc_lock_unlock (lock);
127
128   return NSS_STATUS_SUCCESS;
129 }
130 \f
131 /* Parsing the database file into `struct aliasent' data structures.  */
132 static enum nss_status
133 get_next_alias (const char *match, struct aliasent *result,
134                 char *buffer, size_t buflen, int *errnop)
135 {
136   enum nss_status status = NSS_STATUS_NOTFOUND;
137   int ignore = 0;
138
139   result->alias_members_len = 0;
140
141   while (1)
142     {
143       /* Now we are ready to process the input.  We have to read a
144          line and all its continuations and construct the array of
145          string pointers.  This pointers and the names itself have to
146          be placed in BUFFER.  */
147       char *first_unused = buffer;
148       size_t room_left = buflen - (buflen % __alignof__ (char *));
149       char *line;
150
151       /* Read the first line.  It must contain the alias name and
152          possibly some alias names.  */
153       first_unused[room_left - 1] = '\xff';
154       line = fgets (first_unused, room_left, stream);
155       if (line == NULL)
156         /* Nothing to read.  */
157         break;
158       else if (first_unused[room_left - 1] != '\xff')
159         {
160           /* The line is too long for our buffer.  */
161         no_more_room:
162           *errnop = ERANGE;
163           status = NSS_STATUS_TRYAGAIN;
164           break;
165         }
166       else
167         {
168           char *cp;
169
170           /* If we are in IGNORE mode and the first character in the
171              line is a white space we ignore the line and start
172              reading the next.  */
173           if (ignore && isspace (*first_unused))
174             continue;
175
176           /* Terminate the line for any case.  */
177           cp = strpbrk (first_unused, "#\n");
178           if (cp != NULL)
179             *cp = '\0';
180
181           /* Skip leading blanks.  */
182           while (isspace (*line))
183             ++line;
184
185           result->alias_name = first_unused;
186           while (*line != '\0' && *line != ':')
187             *first_unused++ = *line++;
188           if (*line == '\0' || result->alias_name == first_unused)
189             /* No valid name.  Ignore the line.  */
190             continue;
191
192           *first_unused++ = '\0';
193           if (room_left < (size_t) (first_unused - result->alias_name))
194             goto no_more_room;
195           room_left -= first_unused - result->alias_name;
196           ++line;
197
198           /* When we search for a specific alias we can avoid all the
199              difficult parts and compare now with the name we are
200              looking for.  If it does not match we simply ignore all
201              lines until the next line containing the start of a new
202              alias is found.  */
203           ignore = (match != NULL
204                     && strcasecmp (result->alias_name, match) != 0);
205
206           while (! ignore)
207             {
208               while (isspace (*line))
209                 ++line;
210
211               cp = first_unused;
212               while (*line != '\0' && *line != ',')
213                 *first_unused++ = *line++;
214
215               if (first_unused != cp)
216                 {
217                   /* OK, we can have a regular entry or an include
218                      request.  */
219                   if (*line != '\0')
220                     ++line;
221                   *first_unused++ = '\0';
222
223                   if (strncmp (cp, ":include:", 9) != 0)
224                     {
225                       if (room_left < (first_unused - cp) + sizeof (char *))
226                         goto no_more_room;
227                       room_left -= (first_unused - cp) + sizeof (char *);
228
229                       ++result->alias_members_len;
230                     }
231                   else
232                     {
233                       /* Oh well, we have to read the addressed file.  */
234                       FILE *listfile;
235                       char *old_line = NULL;
236
237                       first_unused = cp;
238
239                       listfile = fopen (&cp[9], "r");
240                       /* If the file does not exist we simply ignore
241                          the statement.  */
242                       if (listfile != NULL
243                           && (old_line = strdup (line)) != NULL)
244                         {
245                           while (! feof (listfile))
246                             {
247                               first_unused[room_left - 1] = '\xff';
248                               line = fgets (first_unused, room_left, listfile);
249                               if (line == NULL)
250                                 break;
251                               if (first_unused[room_left - 1] != '\xff')
252                                 {
253                                   free (old_line);
254                                   goto no_more_room;
255                                 }
256
257                               /* Parse the line.  */
258                               cp = strpbrk (line, "#\n");
259                               if (cp != NULL)
260                                 *cp = '\0';
261
262                               do
263                                 {
264                                   while (isspace (*line))
265                                     ++line;
266
267                                   cp = first_unused;
268                                   while (*line != '\0' && *line != ',')
269                                     *first_unused++ = *line++;
270
271                                   if (*line != '\0')
272                                     ++line;
273
274                                   if (first_unused != cp)
275                                     {
276                                       *first_unused++ = '\0';
277                                       if (room_left < ((first_unused - cp)
278                                                        + __alignof__ (char *)))
279                                         {
280                                           free (old_line);
281                                           goto no_more_room;
282                                         }
283                                       room_left -= ((first_unused - cp)
284                                                     + __alignof__ (char *));
285                                       ++result->alias_members_len;
286                                     }
287                                 }
288                               while (*line != '\0');
289                             }
290                           fclose (listfile);
291
292                           first_unused[room_left - 1] = '\0';
293                           strncpy (first_unused, old_line, room_left);
294
295                           if (old_line != NULL)
296                             free (old_line);
297
298                           if (first_unused[room_left - 1] != '\0')
299                             goto no_more_room;
300                         }
301                     }
302                 }
303
304               if (*line == '\0')
305                 {
306                   /* Get the next line.  But we must be careful.  We
307                      must not read the whole line at once since it
308                      might belong to the current alias.  Simply read
309                      the first character.  If it is a white space we
310                      have a continuation line.  Otherwise it is the
311                      beginning of a new alias and we can push back the
312                      just read character.  */
313                   int ch;
314
315                   ch = fgetc (stream);
316                   if (ch == EOF || ch == '\n' || !isspace (ch))
317                     {
318                       size_t cnt;
319
320                       /* Now prepare the return.  Provide string
321                          pointers for the currently selected aliases.  */
322                       if (ch != EOF)
323                         ungetc (ch, stream);
324
325                       /* Adjust the pointer so it is aligned for
326                          storing pointers.  */
327                       first_unused += __alignof__ (char *) - 1;
328                       first_unused -= ((first_unused - (char *) 0)
329                                        % __alignof__ (char *));
330                       result->alias_members = (char **) first_unused;
331
332                       /* Compute addresses of alias entry strings.  */
333                       cp = result->alias_name;
334                       for (cnt = 0; cnt < result->alias_members_len; ++cnt)
335                         {
336                           cp = strchr (cp, '\0') + 1;
337                           result->alias_members[cnt] = cp;
338                         }
339
340                       status = (result->alias_members_len == 0
341                                 ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
342                       break;
343                     }
344
345                   /* The just read character is a white space and so
346                      can be ignored.  */
347                   first_unused[room_left - 1] = '\xff';
348                   line = fgets (first_unused, room_left, stream);
349                   if (first_unused[room_left - 1] != '\xff')
350                     goto no_more_room;
351                   cp = strpbrk (line, "#\n");
352                   if (cp != NULL)
353                     *cp = '\0';
354                 }
355             }
356         }
357
358       if (status != NSS_STATUS_NOTFOUND)
359         /* We read something.  In any case break here.  */
360         break;
361     }
362
363   return status;
364 }
365
366
367 enum nss_status
368 _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,
369                           int *errnop)
370 {
371   /* Return next entry in host file.  */
372   enum nss_status status = NSS_STATUS_SUCCESS;
373
374   __libc_lock_lock (lock);
375
376   /* Be prepared that the set*ent function was not called before.  */
377   if (stream == NULL)
378     status = internal_setent ();
379
380   if (status == NSS_STATUS_SUCCESS)
381     {
382       /* If the last use was not by the getent function we need the
383          position the stream.  */
384       if (last_use != getent)
385         if (fsetpos (stream, &position) < 0)
386           status = NSS_STATUS_UNAVAIL;
387         else
388           last_use = getent;
389
390       if (status == NSS_STATUS_SUCCESS)
391         {
392           result->alias_local = 1;
393
394           /* Read lines until we get a definite result.  */
395           do
396             status = get_next_alias (NULL, result, buffer, buflen, errnop);
397           while (status == NSS_STATUS_RETURN);
398
399           /* If we successfully read an entry remember this position.  */
400           if (status == NSS_STATUS_SUCCESS)
401             fgetpos (stream, &position);
402           else
403             last_use = none;
404         }
405     }
406
407   __libc_lock_unlock (lock);
408
409   return status;
410 }
411
412
413 enum nss_status
414 _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
415                              char *buffer, size_t buflen, int *errnop)
416 {
417   /* Return next entry in host file.  */
418   enum nss_status status = NSS_STATUS_SUCCESS;
419
420   if (name == NULL)
421     {
422       __set_errno (EINVAL);
423       return NSS_STATUS_UNAVAIL;
424     }
425
426   __libc_lock_lock (lock);
427
428   /* Open the stream or rest it.  */
429   status = internal_setent ();
430   last_use = getby;
431
432   if (status == NSS_STATUS_SUCCESS)
433     {
434       result->alias_local = 1;
435
436       /* Read lines until we get a definite result.  */
437       do
438         status = get_next_alias (name, result, buffer, buflen, errnop);
439       while (status == NSS_STATUS_RETURN);
440     }
441
442   internal_endent ();
443
444   __libc_lock_unlock (lock);
445
446   return status;
447 }