2 static char copyright[] = "Copyright 1992 The University of Adelaide";
8 * Author: Mark R. Prior
9 * Communications and Systems Branch
10 * Information Technology Division
11 * The University of Adelaide
12 * E-mail: mrp@itd.adelaide.edu.au
16 * Interpret the command sent by the client
18 * Redistribution and use in source and binary forms are permitted
19 * provided that the above copyright notice and this paragraph are
20 * duplicated in all such forms and that any documentation,
21 * advertising materials, and other materials related to such
22 * distribution and use acknowledge that the software was developed
23 * by the University of Adelaide. The name of the University may not
24 * be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
28 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
33 extern char *index(), *rindex();
35 #define isspecial(c) ( (c) == ',' || (c) == ';' || (c) == ':' || (c) == '=' )
37 static char **component = NULL;
38 static int numberOfComponents;
39 static int components = 10;
41 static int getToken( token )
45 static char *buffer = NULL;
49 struct timeval timeout;
50 int i, status, tablesize;
52 if ( buffer == NULL ) {
55 * It is invalid to use a set size in excess of the type
56 * scope, as defined for the fd_set in sys/types.h. This
59 tablesize = FD_SETSIZE;
60 #else /* !FD_SETSIZE*/
61 tablesize = getdtablesize();
62 #endif /* !FD_SETSIZE*/
66 FD_SET( fileno( stdin ), &readfds );
68 if ( (status = select( tablesize, &readfds, 0, 0, &timeout )) <= 0 ) {
70 printFormatted( lineLength, TRUE, stdout,
71 "select: %s", strerror( errno ) );
73 printFormatted( lineLength, TRUE, stdout,
74 "Connection timed out waiting for input." );
78 * We really should determine how many characters are
79 * waiting for us and then malloc that amount rather than
82 if ( ( buffer = (char *)malloc(BUFSIZ) ) == NULL
83 || fgets( buffer, BUFSIZ, stdin ) == NULL ) {
89 while ( i-- > 0 && ( buffer[i] == '\r' || buffer[i] == '\n' ) )
92 syslog( LOG_INFO, "Whois++ Query: %s", buffer );
94 while ( buffer[idx] != '\0' && isspace( buffer[idx] ) )
96 token[0] = buffer[idx++];
100 strcpy( token, "<end of line>" );
139 if ( ch == '\\' && buffer[idx] != '\0' )
140 token[i++] = buffer[idx++];
143 } while ( ch != '\0' && ch != '"' );
145 printFormatted( lineLength, TRUE, stdout,
146 "Trailing \" missing" );
156 if ( ch == '\\' && buffer[idx] != '\0' )
157 token[i++] = buffer[idx++];
160 } while ( ch != '\0' && !isspace( ch ) && !isspecial( ch ) );
164 * The following is a brute force lookup, once the names
165 * have settled down this should change to a hash table,
166 * or something similar.
168 if ( EQ( token, "help" ) )
170 else if ( EQ( token, "list" ) )
172 else if ( EQ( token, "show" ) )
174 else if ( EQ( token, "constraints" ) )
176 else if ( EQ( token, "describe" ) )
178 else if ( EQ( token, "version" ) )
180 else if ( EQ( token, "template" ) )
182 else if ( EQ( token, "handle" ) )
184 else if ( EQ( token, "attribute" ) )
186 else if ( EQ( token, "value" ) )
188 else if ( EQ( token, "full" ) )
190 else if ( EQ( token, "abridged" ) )
192 else if ( EQ( token, "summary" ) )
194 else if ( EQ( token, "format" ) )
196 else if ( EQ( token, "hold" ) )
198 else if ( EQ( token, "maxhits" ) )
200 else if ( EQ( token, "match" ) )
202 else if ( EQ( token, "linelength" ) )
204 else if ( EQ( token, "command" ) )
206 else if ( EQ( token, "trace" ) )
213 static int term( token, value, attribute, specifier, soundex )
215 char *value, *attribute;
216 int *specifier, *soundex;
218 char buffer[BUFSIZ], temp[BUFSIZ];
223 case ATTRIBUTE: /* . */
226 case TEMPLATE: /* ^ */
227 case SEARCH_ALL:/* * */
229 if ( strlen( value ) > 1 ) {
230 /* fullname used, so expect an equals sign */
231 if ( getToken( buffer ) != EQUALS ) {
232 printFormatted( lineLength, TRUE, stdout,
236 token = getToken( value );
238 token = getToken( value );
239 if ( token != COMMA && token != SEMICOLON && token != EQUALS
240 && token != COLON && token != EOF ) {
241 token = getToken( buffer );
250 printFormatted( lineLength, TRUE, stdout,
251 "Expected search string but got \"%s\"", buffer );
255 *specifier = SEARCH_ALL;
256 if ( ( token = getToken( buffer ) ) == EQUALS ) {
257 strcpy( attribute, value );
258 token = getToken( value );
259 if ( token == COMMA || token == SEMICOLON
260 || token == COLON || token == EOF ) {
261 printFormatted( lineLength, TRUE, stdout,
262 "Syntax error, string expected." );
265 token = getToken( buffer );
269 while ( token != COMMA && token != SEMICOLON && token != COLON
271 if ( *value != '\0' )
272 strcat( value, " " );
273 strcat( value, buffer );
274 token = getToken( buffer );
277 while ( token == COMMA ) {
278 token = getToken( buffer );
282 if ( ( token = getToken( buffer ) ) != EQUALS ) {
283 printFormatted( lineLength, TRUE, stdout,
286 token = getToken( buffer );
287 if ( EQ( buffer, "exact" ) )
289 else if ( EQ( buffer, "fuzzy" ) )
292 printFormatted( lineLength, TRUE, stdout,
293 "Unrecognised search type" );
294 token = getToken( buffer );
298 if ( iterations == 0 ) {
299 /* obviously an unrecognised constraint */
300 printFormatted( lineLength, TRUE, stdout,
301 "Constraint \"%s\" not supported",
303 while ( ( token = getToken( buffer ) ) != EOF
304 && token != COMMA && token != COLON
305 && token != SEMICOLON )
308 strcpy( temp, buffer );
309 token = getToken( buffer );
310 if ( token == EQUALS ) {
312 printFormatted( lineLength, TRUE, stdout,
313 "Constraint \"%s\" not supported",
316 while ( token != EOF && token != SEMICOLON
317 && token != COLON && token != COMMA ) {
318 if ( iterations > 0 ) {
320 strcat( temp, buffer );
322 token = getToken( buffer );
324 if ( iterations > 0 ) {
325 printFormatted( lineLength, TRUE, stdout,
326 "Assuming \"%s\" part of query and not an unrecognised constraint.", temp );
327 strcat( value, "," );
328 strcat( value, temp );
336 if ( *value == '\0' ) {
337 printFormatted( lineLength, TRUE, stdout,
338 "Value not specified" );
341 if ( *specifier == NULL )
342 *specifier = SEARCH_ALL;
346 static int processTerm( specifier, soundex, buffer, attribute, value )
347 int specifier, soundex;
348 char *buffer, *attribute, *value;
353 char **reallocResult;
355 switch ( specifier ) {
357 if ( numberOfComponents+3 >= components ) {
359 reallocResult = (char **)realloc(component, sizeof(char **)*components);
360 if ( reallocResult == NULL ) {
361 printFormatted( lineLength, TRUE, stdout,
365 component = reallocResult;
367 if ( attribute != NULL && *attribute != '\0' ) {
368 /* The user obviously knows what they are doing */
369 sprintf( query, "(%s%s%s)", attribute,
370 (soundex)?"~=":"=", buffer );
372 if ( ( s = index( buffer, ',' ) ) != NULL ) {
374 while ( *s && isspace( *s ) )
376 sprintf( query, "(sn%s%s)",
377 (soundex)?"~=":"=", buffer );
378 component[numberOfComponents++] = strdup( query );
379 /* let's just make sure there is no title */
380 if ( ( t = rindex( s, ',' ) ) != NULL ) {
382 while ( *t && isspace( *t ) )
384 sprintf( query, "(personalTitle%s%s)",
385 (soundex)?"~=":"=", t );
386 component[numberOfComponents++] = strdup( query );
388 sprintf( query, "%s %s", s, buffer );
389 strcpy( buffer, query );
390 } else if ( strncasecmp( buffer, "first ", 6 ) == 0 ) {
391 sprintf( query, "%s *", &buffer[6] );
392 strcpy( buffer, query );
394 if ( ( s = index( buffer, '@' ) ) != NULL ) {
396 if ( *buffer == '\0' ) /* no username */
397 sprintf( query, "(mail=*@%s)", s );
398 else if ( *s == '\0' ) /* no host */
399 sprintf( query, "(|(mail=%s@*)(userid=%s))",
402 sprintf( query, "(mail=%s@%s)",
405 printFormatted( lineLength, TRUE, stdout,
406 "Fuzzy matching not supported on e-mail address queries" );
407 } else if ( index( buffer, ' ' ) == NULL ) {
409 "(|(sn%s%s)(userid%s%s)(l%s%s)(ou%s%s)\
410 (&(cn%s%s)(!(objectClass=person))))",
411 (soundex)?"~=":"=", buffer,
412 (soundex)?"~=":"=", buffer,
413 (soundex)?"~=":"=", buffer,
414 (soundex)?"~=":"=", buffer,
415 (soundex)?"~=":"=", buffer );
418 sprintf( query, "(|(l%s%s)(ou%s%s)(preferredName%s%s)",
419 (soundex)?"~=":"=", buffer,
420 (soundex)?"~=":"=", buffer,
421 (soundex)?"~=":"=", buffer );
423 sprintf( query, "(|(l%s%s)(ou%s%s)",
424 (soundex)?"~=":"=", buffer,
425 (soundex)?"~=":"=", buffer );
428 * If LDAP and/or Quipu didn't strip spaces
429 * then this would be different but as it does
430 * this is easy :-) but it also means we might
434 strcat( query, "(cn~=" );
435 strcat( query, buffer );
437 strcat( query, "(cn=*" );
438 strcat( query, strtok( buffer, " " ) );
439 while ( ( s = strtok( NULL, " " ) ) != NULL ) {
440 strcat( query, " * " );
444 strcat( query, "))" );
447 component[numberOfComponents++] = strdup( query );
451 if ( numberOfComponents+1 >= components ) {
453 reallocResult = (char **)realloc(component, sizeof(char **)*components);
454 if ( reallocResult == NULL ) {
455 printFormatted( lineLength, TRUE, stdout,
459 component = reallocResult;
461 if ( *value != '\0' ) {
462 sprintf( query, "(%s%s%s)", buffer,
463 (soundex)?"~=":"=", value );
464 component[numberOfComponents++] = strdup( query );
467 if ( *attribute != '\0' ) {
468 sprintf( query, "(%s%s*)", attribute,
469 (soundex)?"~=":"=" );
470 component[numberOfComponents++] = strdup( query );
472 strcpy( attribute, buffer );
477 if ( numberOfComponents+1 >= components ) {
479 reallocResult = (char **)realloc(component, sizeof(char **)*components);
480 if ( reallocResult == NULL ) {
481 printFormatted( lineLength, TRUE, stdout,
485 component = reallocResult;
487 sprintf( query, "(objectClass%s%s)",
488 (soundex)?"~=":"=", templateToObjectClass( buffer ) );
489 component[numberOfComponents++] = strdup( query );
493 if ( *attribute != '\0' ) {
494 if ( numberOfComponents+1 >= components ) {
496 reallocResult = (char **)realloc(component, sizeof(char **)*components);
497 if ( reallocResult == NULL ) {
498 printFormatted( lineLength, TRUE, stdout,
502 component = reallocResult;
504 sprintf( query, "(%s%s%s)", attribute,
505 (soundex)?"~=":"=", buffer );
506 component[numberOfComponents++] = strdup( query );
509 if ( *value != '\0' )
510 printFormatted( lineLength, TRUE, stdout,
511 "Ignoring old value (%s)", value );
512 strcpy( value, buffer );
517 if ( numberOfComponents+1 >= components ) {
519 reallocResult = (char **)realloc(component, sizeof(char **)*components);
520 if ( reallocResult == NULL ) {
521 printFormatted( lineLength, TRUE, stdout,
525 component = reallocResult;
527 component[numberOfComponents++] = strdup( buffer );
534 int parseCommand( query )
538 * This procedure reads the string sent by the user and breaks it
539 * down into command to execute.
541 char buffer[BUFSIZ], attribute[BUFSIZ], objectClass[BUFSIZ],
543 char **reallocResult;
544 int command, specificName, length, token, i, j, specifier, soundex;
547 switch ( command = getToken( buffer ) ) {
553 token = getToken( buffer );
558 /* <command> [ <string> ] */
559 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
560 strcpy( query, buffer );
561 token = getToken( buffer );
567 /* "show" <string> */
568 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
569 strcpy( query, buffer );
570 token = getToken( buffer );
572 printFormatted( lineLength, TRUE, stdout,
573 "Show must have a parameter" );
579 /* <term> [ ";" <term> ] */
583 numberOfComponents = 0;
584 if ( ( component = (char **)malloc(sizeof(char **)*components) ) == NULL ) {
585 printFormatted( lineLength, TRUE, stdout,
589 if ( ( token = term( command, buffer, attribute, &specifier,
590 &soundex ) ) != ERROR )
591 command = processTerm( specifier, soundex, buffer,
595 if ( token == SEMICOLON ) {
596 if ( command == READ ) {
597 printFormatted( lineLength, TRUE, stdout,
598 "Multiple components on a Handle query not supported." );
603 token = getToken( buffer );
604 token = term( token, buffer, attribute,
605 &specifier, &soundex );
606 command = processTerm( specifier, soundex,
607 buffer, attribute, value );
608 if ( command == READ ) {
609 printFormatted( lineLength, TRUE, stdout,
610 "Multiple components on a Handle query not supported." );
612 } else if ( command == ERROR )
614 } while ( token == SEMICOLON );
617 * Need to tidy up outstanding single value or attribute terms
619 if ( *attribute != '\0' ) {
620 if ( numberOfComponents+1 >= components ) {
622 reallocResult = (char **)realloc(component, sizeof(char **)*components);
623 if ( reallocResult == NULL ) {
624 printFormatted( lineLength, TRUE, stdout,
628 component = reallocResult;
630 sprintf( query, "(%s%s*)", attribute,
631 (soundex)?"~=":"=" );
632 component[numberOfComponents++] = strdup( query );
634 if ( *value != '\0' )
635 if ( processTerm( SEARCH_ALL, soundex, value, NULL, NULL ) == ERROR )
637 if ( numberOfComponents == 0 ) {
638 printFormatted( lineLength, TRUE, stdout,
641 } else if ( numberOfComponents == 1 )
642 strcpy( query, component[0] );
644 strcpy( query, "(&" );
645 for ( i = 0; i < numberOfComponents; i++ )
646 strcat( query, component[i] );
647 strcat( query, ")" );
653 if ( token == COLON ) { /* global constraints */
655 token = getToken( buffer );
658 if ( ( token = getToken( buffer ) ) != EQUALS ) {
659 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
661 token = getToken( buffer );
667 if ( outputFormat != NULL )
668 printFormatted( lineLength, TRUE, stdout, "Only one response format can be specified." );
670 outputFormat = token;
674 printFormatted( lineLength, TRUE, stdout, "Unrecognised format specifier" );
676 token = getToken( buffer );
680 holdConnection = TRUE;
681 token = getToken( buffer );
685 if ( ( token = getToken( buffer ) ) != EQUALS ) {
686 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
688 token = getToken( buffer );
689 if ( (maxHits = atoi( buffer )) < 1
690 || maxHits > maximumSize ) {
691 printFormatted( lineLength, TRUE, stdout, "Invalid maxhits value, defaulting to %s", maximumSize );
692 maxHits = maximumSize;
694 token = getToken( buffer );
698 if ( ( token = getToken( buffer ) ) != EQUALS ) {
699 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
701 token = getToken( buffer );
702 /**/ /* need to save this value and lookup locale */
703 printFormatted( lineLength, TRUE, stdout,
704 "Language not currently implemented" );
705 token = getToken( buffer );
709 if ( ( token = getToken( buffer ) ) != EQUALS ) {
710 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
712 token = getToken( buffer );
713 lineLength = atoi( buffer );
714 if ( lineLength < MIN_LINE_LENGTH
715 || lineLength > MAX_LINE_LENGTH ) {
716 printFormatted( lineLength, TRUE, stdout, "Invalid line length, using default %d", DEFAULT_LINE_LENGTH );
717 lineLength = DEFAULT_LINE_LENGTH;
719 token = getToken( buffer );
724 token = getToken( buffer );
728 printFormatted( lineLength, TRUE, stdout, "Unrecognised global constraint \"%s\"", buffer );
729 while ( ( token = getToken( buffer ) ) != EOF
735 } while ( token == COMMA );
737 if ( token != EOF ) {
738 printFormatted( lineLength, TRUE, stdout,
739 "Data following \"%s\" ignored.", buffer );
740 while ( ( token = getToken( buffer ) ) != EOF )
743 if ( trace && ( command == READ || command == SEARCH ) )
746 printFormatted( lineLength, TRUE, stdout,
747 "Attempting to read \"%s\"", query );
751 printFormatted( lineLength, TRUE, stdout,
752 "Searching using LDAP query %s", query );