Patches to print/parse IPv6 interface ID's.
[freeradius.git] / src / lib / valuepair.c
1 /*
2  * valuepair.c  Functions to handle VALUE_PAIRs
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This 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  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  */
22
23 static const char rcsid[] = "$Id$";
24
25 #include        "autoconf.h"
26
27 #include        <sys/types.h>
28
29 #include        <stdio.h>
30 #include        <stdlib.h>
31 #include        <string.h>
32 #include        <ctype.h>
33
34 #include        "libradius.h"
35
36 #ifdef HAVE_MALLOC_H
37 #  include      <malloc.h>
38 #endif
39
40 #ifdef HAVE_REGEX_H
41 #  include      <regex.h>
42 #endif
43
44 #include        "missing.h"
45
46 static const char *months[] = {
47         "jan", "feb", "mar", "apr", "may", "jun",
48         "jul", "aug", "sep", "oct", "nov", "dec" };
49
50
51 /*
52  *      Create a new valuepair.
53  */
54 VALUE_PAIR *paircreate(int attr, int type)
55 {
56         VALUE_PAIR      *vp;
57         DICT_ATTR       *da;
58
59         if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL) {
60                 librad_log("out of memory");
61                 return NULL;
62         }
63         memset(vp, 0, sizeof(VALUE_PAIR));
64         vp->attribute = attr;
65         vp->operator = T_OP_EQ;
66         vp->type = type;
67
68         /*
69          *      Dictionary type over-rides what the caller says.
70          */
71         if ((da = dict_attrbyvalue(attr)) != NULL) {
72                 strcpy(vp->name, da->name);
73                 vp->type = da->type;
74         } else if (VENDOR(attr) == 0) {
75                 sprintf(vp->name, "Attr-%u", attr);
76         } else {
77                 sprintf(vp->name, "Vendor-%u-Attr-%u",
78                         VENDOR(attr), attr & 0xffff);
79         }
80         switch (vp->type) {
81                 case PW_TYPE_INTEGER:
82                 case PW_TYPE_IPADDR:
83                 case PW_TYPE_DATE:
84                         vp->length = 4;
85                         break;
86                 default:
87                         vp->length = 0;
88                         break;
89         }
90
91         return vp;
92 }
93
94 /*
95  *      Release the memory used by a list of attribute-value
96  *      pairs, and sets the pair pointer to NULL.
97  */
98 void pairfree(VALUE_PAIR **pair_ptr)
99 {
100         VALUE_PAIR      *next, *pair;
101
102         if (!pair_ptr) return;
103         pair = *pair_ptr;
104
105         while (pair != NULL) {
106                 next = pair->next;
107                 free(pair);
108                 pair = next;
109         }
110
111         *pair_ptr = NULL;
112 }
113
114
115 /*
116  *      Find the pair with the matching attribute
117  */
118 VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
119 {
120         while(first && first->attribute != attr)
121                 first = first->next;
122         return first;
123 }
124
125
126 /*
127  *      Delete the pair(s) with the matching attribute
128  */
129 void pairdelete(VALUE_PAIR **first, int attr)
130 {
131         VALUE_PAIR *i, *next;
132         VALUE_PAIR **last = first;
133
134         for(i = *first; i; i = next) {
135                 next = i->next;
136                 if (i->attribute == attr) {
137                         *last = next;
138                         free(i);
139                 } else {
140                         last = &i->next;
141                 }
142         }
143 }
144
145 /*
146  *      Add a pair at the end of a VALUE_PAIR list.
147  */
148 void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
149 {
150         VALUE_PAIR *i;
151
152         if (*first == NULL) {
153                 *first = add;
154                 return;
155         }
156         for(i = *first; i->next; i = i->next)
157                 ;
158         i->next = add;
159 }
160
161 /*
162  *      Copy just a certain type of pairs.
163  */
164 VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
165 {
166         VALUE_PAIR      *first, *n, **last;
167
168         first = NULL;
169         last = &first;
170
171         while (vp) {
172                 if (attr >= 0 && vp->attribute != attr) {
173                         vp = vp->next;
174                         continue;
175                 }
176                 if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
177                         librad_log("out of memory");
178                         return first;
179                 }
180                 memcpy(n, vp, sizeof(VALUE_PAIR));
181                 n->next = NULL;
182                 *last = n;
183                 last = &n->next;
184                 vp = vp->next;
185         }
186         return first;
187 }
188
189
190 /*
191  *      Copy a pairlist.
192  */
193 VALUE_PAIR *paircopy(VALUE_PAIR *vp)
194 {
195         return paircopy2(vp, -1);
196 }
197
198
199 /*
200  *      Move attributes from one list to the other
201  *      if not already present.
202  */
203 void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
204 {
205         VALUE_PAIR **tailto, *i, *j, *next;
206         VALUE_PAIR *tailfrom = NULL;
207         VALUE_PAIR *found;
208         int has_password = 0;
209
210         if (*to == NULL) {
211                 *to = *from;
212                 *from = NULL;
213                 return;
214         }
215
216         /*
217          *      First, see if there are any passwords here, and
218          *      point "tailto" to the end of the "to" list.
219          */
220         tailto = to;
221         for(i = *to; i; i = i->next) {
222                 if (i->attribute == PW_PASSWORD ||
223                     i->attribute == PW_CRYPT_PASSWORD)
224                         has_password = 1;
225                 tailto = &i->next;
226         }
227
228         /*
229          *      Loop over the "from" list.
230          */
231         for(i = *from; i; i = next) {
232                 next = i->next;
233                 /*
234                  *      If there was a password in the "to" list,
235                  *      do not move any other password from the
236                  *      "from" to the "to" list.
237                  */
238                 if (has_password &&
239                     (i->attribute == PW_PASSWORD ||
240                      i->attribute == PW_CRYPT_PASSWORD)) {
241                         tailfrom = i;
242                         continue;
243                 }
244                 /*
245                  *      If the attribute is already present in "to",
246                  *      do not move it from "from" to "to". We make
247                  *      an exception for "Hint" which can appear multiple
248                  *      times, and we never move "Fall-Through".
249                  */
250                 if (i->attribute == PW_FALL_THROUGH ||
251                     (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) {
252                   
253                         found = pairfind(*to, i->attribute);
254                         switch (i->operator) {
255
256                           /*
257                            *  If a similar attribute is found,
258                            *  delete it.
259                            */
260                         case T_OP_SUB:          /* -= */
261                                 if (found) {
262                                         if (!i->strvalue[0] ||
263                                             (strcmp((char *)found->strvalue,
264                                                     (char *)i->strvalue) == 0)){
265                                                 pairdelete(to, found->attribute);
266                                                 
267                                                 /*
268                                                  *      'tailto' may have been
269                                                  *      deleted...
270                                                  */
271                                                 tailto = to;
272                                                 for(j = *to; j; j = j->next) {
273                                                         tailto = &j->next;
274                                                 }
275                                         }
276                                 }
277                                 tailfrom = i;
278                                 continue;
279                                 break;
280                                 
281 /* really HAVE_REGEX_H */
282 #if 0 
283                                 /*
284                                  *  Attr-Name =~ "s/find/replace/"
285                                  *
286                                  *  Very bad code.  Barely working,
287                                  *  if at all.
288                                  */
289
290                         case T_OP_REG_EQ:
291                           if (found &&
292                               (i->strvalue[0] == 's')) {
293                             regex_t             reg;
294                             regmatch_t          match[1];
295
296                             char *str;
297                             char *p, *q;
298
299                             p = i->strvalue + 1;
300                             q = strchr(p + 1, *p);
301                             if (!q || (q[strlen(q) - 1] != *p)) {
302                               tailfrom = i;
303                               continue;
304                             }
305                             str = strdup(i->strvalue + 2);
306                             q = strchr(str, *p);
307                             *(q++) = '\0';
308                             q[strlen(q) - 1] = '\0';
309                             
310                             regcomp(&reg, str, 0);
311                             if (regexec(&reg, found->strvalue,
312                                         1, match, 0) == 0) {
313                               fprintf(stderr, "\"%s\" will have %d to %d replaced with %s\n",
314                                       found->strvalue, match[0].rm_so,
315                                       match[0].rm_eo, q);
316
317                             }
318                             regfree(&reg);
319                             free(str);
320                           }
321                           tailfrom = i; /* don't copy it over */
322                           continue;
323                           break;
324 #endif
325                         case T_OP_EQ:           /* = */
326                                 /*
327                                  *  FIXME: Tunnel attributes with
328                                  *  different tags are different
329                                  *  attributes.
330                                  */
331                                 if (found) {
332                                         tailfrom = i;
333                                         continue; /* with the loop */
334                                 }
335                                 break;
336
337                           /*
338                            *  If a similar attribute is found,
339                            *  replace it with the new one.  Otherwise,
340                            *  add the new one to the list.
341                            */
342                         case T_OP_SET:          /* := */
343                                 if (found) {
344                                         pairdelete(to, found->attribute);
345                                         /*
346                                          *      'tailto' may have been
347                                          *      deleted...
348                                          */
349                                         tailto = to;
350                                         for(j = *to; j; j = j->next) {
351                                                 tailto = &j->next;
352                                         }
353                                 }
354                                 break;
355
356                           /*
357                            *  Add the new element to the list, even
358                            *  if similar ones already exist.
359                            */
360                         default:
361                         case T_OP_ADD: /* += */
362                                 break;
363                         }
364                 }
365                 if (tailfrom)
366                         tailfrom->next = next;
367                 else
368                         *from = next;
369                 
370                 /*
371                  *      If ALL of the 'to' attributes have been deleted,
372                  *      then ensure that the 'tail' is updated to point
373                  *      to the head.
374                  */
375                 if (!*to) {
376                         tailto = to;
377                 }
378                 *tailto = i;
379                 if (i) {
380                         i->next = NULL;
381                         tailto = &i->next;
382                 }
383         }
384 }
385
386 /*
387  *      Move one kind of attributes from one list to the other
388  */
389 void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
390 {
391         VALUE_PAIR *to_tail, *i, *next;
392         VALUE_PAIR *iprev = NULL;
393
394         /*
395          *      Find the last pair in the "to" list and put it in "to_tail".
396          */
397         if (*to != NULL) {
398                 to_tail = *to;
399                 for(i = *to; i; i = i->next)
400                         to_tail = i;
401         } else
402                 to_tail = NULL;
403
404         for(i = *from; i; i = next) {
405                 next = i->next;
406
407
408                 /*
409                  *      If the attribute to move is NOT a VSA, then it
410                  *      ignores any attributes which do not match exactly.
411                  */
412                 if ((attr != PW_VENDOR_SPECIFIC) &&
413                     (i->attribute != attr)) {
414                         iprev = i;
415                         continue;
416                 }
417
418                 /*
419                  *      If the attribute to move IS a VSA, then it ignores
420                  *      any non-VSA attribute.
421                  */
422                 if ((attr == PW_VENDOR_SPECIFIC) &&
423                     (VENDOR(i->attribute) == 0)) {
424                         iprev = i;
425                         continue;
426                 }
427
428                 /*
429                  *      Remove the attribute from the "from" list.
430                  */
431                 if (iprev)
432                         iprev->next = next;
433                 else
434                         *from = next;
435
436                 /*
437                  *      Add the attribute to the "to" list.
438                  */
439                 if (to_tail)
440                         to_tail->next = i;
441                 else
442                         *to = i;
443                 to_tail = i;
444                 i->next = NULL;
445         }
446 }
447
448
449 /*
450  *      Sort of strtok/strsep function.
451  */
452 static char *mystrtok(char **ptr, const char *sep)
453 {
454         char    *res;
455
456         if (**ptr == 0)
457                 return NULL;
458         while (**ptr && strchr(sep, **ptr))
459                 (*ptr)++;
460         if (**ptr == 0)
461                 return NULL;
462         res = *ptr;
463         while (**ptr && strchr(sep, **ptr) == NULL)
464                 (*ptr)++;
465         if (**ptr != 0)
466                 *(*ptr)++ = 0;
467         return res;
468 }
469
470 /*
471  *      Turn printable string into time_t
472  *      Returns -1 on error, 0 on OK.
473  */
474 static int gettime(const char *valstr, time_t *lvalue)
475 {
476         int             i;
477         time_t          t;
478         struct tm       *tm, s_tm;
479         char            buf[64];
480         char            *p;
481         char            *f[3];
482
483         time(&t);
484         tm = localtime_r(&t, &s_tm);
485
486         strNcpy(buf, valstr, sizeof(buf));
487
488         p = buf;
489         f[0] = mystrtok(&p, " \t");
490         f[1] = mystrtok(&p, " \t");
491         f[2] = mystrtok(&p, " \t");
492         if (!f[0] || !f[1] || !f[2]) return -1;
493
494         /*
495          *  The month is text, which allows us to find it easily.
496          */
497         tm->tm_mon = 12;
498         for (i = 0; i < 3; i++) {
499                 if (isalpha( (int) *f[i])) {    
500                         /*
501                          *  Bubble the month to the front of the list
502                          */
503                         p = f[0];
504                         f[0] = f[i];
505                         f[i] = p;
506
507                         for (i = 0; i < 12; i++) {
508                                 if (strncasecmp(months[i], f[0], 3) == 0) {
509                                         tm->tm_mon = i;
510                                         break;
511                                 }
512                         }
513                 }
514         }
515
516         /* month not found? */
517         if (tm->tm_mon == 12) return -1;
518
519         /*
520          *  The year may be in f[1], or in f[2]
521          */
522         tm->tm_year = atoi(f[1]);
523         tm->tm_mday = atoi(f[2]);
524
525         if (tm->tm_year >= 1900) {
526                 tm->tm_year -= 1900;
527
528         } else {
529                 /*
530                  *  We can't use 2-digit years any more, they make it
531                  *  impossible to tell what's the day, and what's the year.
532                  */
533                 if (tm->tm_mday < 1900) return -1;
534
535                 /*
536                  *  Swap the year and the day.
537                  */
538                 i = tm->tm_year;
539                 tm->tm_year = tm->tm_mday - 1900;
540                 tm->tm_mday = i;
541         }
542
543         /*
544          *  If the day is out of range, die.
545          */
546         if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
547                 return -1;
548         }
549
550         /*
551          *  Returns -1 on error.
552          */
553         t = mktime(tm);
554         if (t == (time_t) -1) return -1;
555
556         *lvalue = t;
557
558         return 0;
559 }
560
561 /*
562  *  Parse a string value into a given VALUE_PAIR
563  */
564 VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
565 {
566         char            *p, *s=0;
567         const char      *cp, *cs;
568         DICT_VALUE      *dval;
569
570         /*
571          *      Even for integers, dates and ip addresses we
572          *      keep the original string in vp->strvalue.
573          */
574         strNcpy((char *)vp->strvalue, value, sizeof(vp->strvalue));
575         vp->length = strlen(vp->strvalue);
576
577         switch(vp->type) {
578                 case PW_TYPE_STRING:
579                         /*
580                          *      Already handled above.
581                          */
582                         break;
583
584                 case PW_TYPE_IPADDR:
585                         /*
586                          *      FIXME: complain if hostname
587                          *      cannot be resolved, or resolve later!
588                          */
589                         if ((p = strrchr(value, '+')) != NULL && !p[1]) {
590                                 cs = s = strdup(value);
591                                 p = strrchr(s, '+');
592                                 *p = 0;
593                                 vp->flags.addport = 1;
594                         } else {
595                                 p = NULL;
596                                 cs = value;
597                         }
598                         vp->lvalue = librad_dodns ? ip_getaddr(cs) :
599                                                     ip_addr(cs);
600                         if (s) free(s);
601                         vp->length = 4;
602                         break;
603                 case PW_TYPE_INTEGER:
604                         /*
605                          *      If it starts with a digit, it must
606                          *      be a number (or a range).
607                          *
608                          *      Note that ALL integers are unsigned!
609                          */
610                         if (isdigit((int) *value)) {
611                                 vp->lvalue = (uint32_t) strtoul(value, NULL, 10);
612                                 vp->length = 4;
613                         }
614                         /*
615                          *      Look for the named value for the given
616                          *      attribute.
617                          */
618                         else if ((dval = dict_valbyname(vp->attribute, value)) == NULL) {
619                                 librad_log("Unknown value %s for attribute %s",
620                                            value, vp->name);
621                                 return NULL;
622                         } else {
623                                 vp->lvalue = dval->value;
624                                 vp->length = 4;
625                         }
626                         break;
627
628                 case PW_TYPE_DATE:
629                         if (gettime(value, (time_t *)&vp->lvalue) < 0) {
630                                 librad_log("failed to parse time string "
631                                            "\"%s\"", value);
632                                 return NULL;
633                         }
634                         vp->length = 4;
635                         break;
636                 case PW_TYPE_ABINARY:
637 #ifdef ASCEND_BINARY
638                         /*
639                          *      Special case to convert filter to binary
640                          */
641                         if ( filterBinary( vp, value ) < 0 ) {
642                           librad_log("failed to parse Ascend binary attribute: %s",
643                                      librad_errstr);
644                           return NULL;
645                         }
646                         break;
647                         /*
648                          *      If Ascend binary is NOT defined,
649                          *      then fall through to raw octets, so that
650                          *      the user can at least make them by hand...
651                          */
652 #endif
653                         /* raw octets: 0x01020304... */
654                 case PW_TYPE_OCTETS:
655                         if (strncasecmp(value, "0x", 2) == 0) {
656                                 u_char *us;
657                                 cp = value + 2;
658                                 us = vp->strvalue;
659                                 vp->length = 0;
660
661                                 while (*cp && vp->length < MAX_STRING_LEN) {
662                                         unsigned int tmp;
663                                         
664                                         if (sscanf(cp, "%02x", &tmp) != 1) {
665                                                 librad_log("Non-hex characters at %c%c", cp[0], cp[1]);
666                                                 return NULL;
667                                         }
668
669                                         cp += 2;
670                                         *(us++) = tmp;
671                                         vp->length++;
672                                 }
673                                 *us = '\0';
674                         }
675                         break;
676
677                 case PW_TYPE_IFID:
678                         if (ifid_aton(value, vp->strvalue) == NULL) {
679                                 librad_log("failed to parse interface-id "
680                                            "string \"%s\"", value);
681                                 return NULL;
682                         }
683                         vp->length = 8;
684                         vp->strvalue[vp->length] = '\0';
685                         break;
686
687                         /*
688                          *  Anything else.
689                          */
690                 default:
691                         librad_log("unknown attribute type %d", vp->type);
692                         return NULL;
693         }
694
695         return vp;
696 }
697
698 /*
699  *      Create a VALUE_PAIR from an ASCII attribute and value.
700  */
701 VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
702 {
703         DICT_ATTR       *da;
704         VALUE_PAIR      *vp;
705         char            *tc, *ts;
706         signed char     tag;
707         int             found_tag;
708 #ifdef HAVE_REGEX_H
709         int             res;
710         regex_t         cre;
711 #endif
712
713         /*
714          *    Check for tags in 'Attribute:Tag' format.
715          */
716         found_tag = 0;
717         tag = 0;
718
719         ts = strrchr( attribute, ':' );
720         if (ts && ts[1]) {
721                  /* Colon found with something behind it */
722                  if (ts[1] == '*' && ts[2] == 0) {
723                          /* Wildcard tag for check items */
724                          tag = TAG_ANY;
725                          *ts = 0;
726                  } else if ((ts[1] >= '0') && (ts[1] <= '9')) {
727                          /* It's not a wild card tag */
728                          tag = strtol(ts + 1, &tc, 0);
729                          if (tc && !*tc && TAG_VALID_ZERO(tag))
730                                  *ts = 0;
731                          else tag = 0;
732                  } else {
733                          librad_log("Invalid tag for attribute %s", attribute);
734                          return NULL;
735                  }
736                  found_tag = 1;
737         }
738
739         if ((da = dict_attrbyname(attribute)) == NULL) {
740                 librad_log("Unknown attribute %s", attribute);
741                 return NULL;
742         }
743
744         if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
745                 librad_log("out of memory");
746                 return NULL;
747         }
748
749         memset(vp, 0, sizeof(VALUE_PAIR));
750         vp->attribute = da->attr;
751         vp->type = da->type;
752         vp->operator = (operator == 0) ? T_OP_EQ : operator;
753         strcpy(vp->name, da->name);
754         vp->flags = da->flags;
755         vp->next = NULL;
756
757         /*      Check for a tag in the 'Merit' format of:
758          *      :Tag:Value.  Print an error if we already found
759          *      a tag in the Attribute.
760          */
761
762         if (value && (*value == ':' && da->flags.has_tag)) {
763                 /* If we already found a tag, this is invalid */
764                 if(found_tag) {
765                         free(vp);
766                         librad_log("Duplicate tag %s for attribute %s",
767                                    value, vp->name);
768                         DEBUG("Duplicate tag %s for attribute %s\n",
769                                    value, vp->name);
770                         return NULL;
771
772                 }
773                 /* Colon found and attribute allows a tag */
774                 if (value[1] == '*' && value[2] == ':') {
775                        /* Wildcard tag for check items */
776                        tag = TAG_ANY;
777                        value += 3;
778                 } else {
779                        /* Real tag */
780                        tag = strtol(value + 1, &tc, 0);
781                        if (tc && *tc==':' && TAG_VALID_ZERO(tag))
782                             value = tc + 1;
783                        else tag = 0;
784                 }
785                 found_tag = 1;
786         }       
787         
788         if (found_tag) {
789           vp->flags.tag = tag;
790         }
791
792         switch (vp->operator) {
793         default:
794                 break;
795
796                 /*
797                  *      For =* and !* operators, the value is irrelevant
798                  *      so we return now.
799                  */
800         case T_OP_CMP_TRUE:
801         case T_OP_CMP_FALSE:
802                 vp->strvalue[0] = '\0';
803                 vp->length = 0;
804                 return vp;
805                 break;
806
807                 /*
808                  *      Regular expression comparison of integer attributes
809                  *      does a STRING comparison of the names of their
810                  *      integer attributes.
811                  */
812         case T_OP_REG_EQ:       /* =~ */
813         case T_OP_REG_NE:       /* !~ */
814                 if (vp->type == PW_TYPE_INTEGER) {
815                         return vp;
816                 }
817 #ifdef HAVE_REGEX_H
818                 /*
819                  *      Regular expression match with no regular
820                  *      expression is wrong.
821                  */
822                 if (!value) {
823                         pairfree(&vp);
824                         return NULL;
825                 }
826
827                 res = regcomp(&cre, value, REG_EXTENDED|REG_NOSUB);
828                 if (res != 0) {
829                         char    msg[128];
830
831                         regerror(res, &cre, msg, sizeof(msg));               
832                         librad_log("Illegal regular expression in attribute: %s: %s",
833                                 vp->name, msg);
834                         free(vp);
835                         return NULL;
836                 }
837                 regfree(&cre);
838 #else
839                 librad_log("Regelar expressions not enabled in this build, error in attribute %s",
840                                 vp->name);
841                 free(vp);
842                 return NULL;
843 #endif
844         }
845
846         if (value && (pairparsevalue(vp, value) == NULL)) {
847                 free(vp);
848                 return NULL;
849         }
850
851         return vp;
852 }
853
854 /*
855  *      Read a valuepair from a buffer, and advance pointer.
856  *      Sets *eol to T_EOL if end of line was encountered.
857  */
858 VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
859 {
860         char            buf[64];
861         char            attr[64];
862         char            value[256];
863         char            *p;
864         LRAD_TOKEN      token, t, xlat;
865         VALUE_PAIR      *vp;
866
867         *eol = 0;
868
869         /* Get attribute. */
870         token = gettoken(ptr, attr, sizeof(attr));
871
872         /*  If it's a comment, then exit, as we haven't read a pair */
873         if (token == T_HASH) {
874                 *eol = token;
875                 librad_log("Read a comment instead of a token");
876                 return NULL;
877         }
878
879         /*  It's not a comment, so it MUST be an attribute */
880         if ((token == T_EOL) ||
881             (attr[0] == 0)) {
882                 librad_log("No token read where we expected an attribute name");
883                 return NULL;
884         }
885
886         /* Now we should have an '=' here. */
887         token = gettoken(ptr, buf, sizeof(buf));
888         if (token < T_EQSTART || token > T_EQEND) {
889                 librad_log("expecting '='");
890                 return NULL;
891         }
892
893         /* Read value.  Note that empty string values are allowed */
894         xlat = gettoken(ptr, value, sizeof(value));
895         if (xlat == T_EOL) {
896                 librad_log("failed to get value");
897                 return NULL;
898         }
899
900         /*
901          *      Peek at the next token. Must be T_EOL, T_COMMA, or T_HASH
902          */
903         p = *ptr;
904         t = gettoken(&p, buf, sizeof(buf));
905         if (t != T_EOL && t != T_COMMA && t != T_HASH) {
906                 librad_log("Expected end of line or comma");
907                 return NULL;
908         }
909
910         *eol = t;
911         if (t == T_COMMA) {
912                 *ptr = p;
913         }
914
915         switch (xlat) {
916                 /*
917                  *      Make the full pair now.
918                  */
919         default:
920                 vp = pairmake(attr, value, token);
921                 break;
922
923                 /*
924                  *      Mark the pair to be allocated later.
925                  */
926         case T_BACK_QUOTED_STRING:
927                 vp = pairmake(attr, NULL, token);
928                 if (!vp) return vp;
929                 
930                 vp->flags.do_xlat = 1;
931                 strNcpy(vp->strvalue, value, sizeof(vp->strvalue));
932                 vp->length = 0;
933                 break;
934         }
935         
936         return vp;
937 }
938
939 /*
940  *      Read one line of attribute/value pairs. This might contain
941  *      multiple pairs seperated by comma's.
942  */
943 LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair)
944 {
945         VALUE_PAIR      *vp;
946         char            *p;
947         LRAD_TOKEN      last_token = T_INVALID;
948         LRAD_TOKEN      previous_token;
949
950         /*
951          *      We allow an empty line.
952          */
953         if (buffer[0] == 0)
954                 return T_EOL;
955
956         p = buffer;
957         do {
958                 previous_token = last_token;
959                 if ((vp = pairread(&p, &last_token)) == NULL) {
960                         return T_INVALID;
961                 }
962                 pairadd(first_pair, vp);
963         } while (*p && (last_token == T_COMMA));
964
965         /*
966          *      Don't tell the caller that there was a comment.
967          */
968         if (last_token == T_HASH) {
969                 return previous_token;
970         }
971
972         /*
973          *      And return the last token which we read.
974          */
975         return last_token;
976 }
977