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 int getdtablesize (void);
35 #define isspecial(c) ( (c) == ',' || (c) == ';' || (c) == ':' || (c) == '=' )
37 static char **component = NULL;
38 static int numberOfComponents;
39 static int components = 10;
42 getToken( char *token )
44 static char *buffer = NULL;
48 struct timeval timeout;
49 int i, status, tablesize;
51 if ( buffer == NULL ) {
52 tablesize = getdtablesize();
55 if ( tablesize > FD_SETSIZE ) {
56 tablesize = FD_SETSIZE;
58 #endif /* FD_SETSIZE */
63 FD_SET( fileno( stdin ), &readfds );
65 if ( (status = select( tablesize, &readfds, 0, 0, &timeout )) <= 0 ) {
67 printFormatted( lineLength, TRUE, stdout,
68 "select: %s", strerror( errno ) );
70 printFormatted( lineLength, TRUE, stdout,
71 "Connection timed out waiting for input." );
75 * We really should determine how many characters are
76 * waiting for us and then malloc that amount rather than
79 if ( ( buffer = (char *)malloc(BUFSIZ) ) == NULL
80 || fgets( buffer, BUFSIZ, stdin ) == NULL ) {
86 while ( i-- > 0 && ( buffer[i] == '\r' || buffer[i] == '\n' ) )
89 syslog( LOG_INFO, "Whois++ Query: %s", buffer );
91 while ( buffer[idx] != '\0' && isspace( (unsigned char) buffer[idx] ) )
93 token[0] = buffer[idx++];
97 strcpy( token, "<end of line>" );
136 if ( ch == '\\' && buffer[idx] != '\0' )
137 token[i++] = buffer[idx++];
140 } while ( ch != '\0' && ch != '"' );
142 printFormatted( lineLength, TRUE, stdout,
143 "Trailing \" missing" );
153 if ( ch == '\\' && buffer[idx] != '\0' )
154 token[i++] = buffer[idx++];
157 } while ( ch != '\0' &&
158 !isspace( (unsigned char) ch ) && !isspecial( ch ) );
162 * The following is a brute force lookup, once the names
163 * have settled down this should change to a hash table,
164 * or something similar.
166 if ( EQ( token, "help" ) )
168 else if ( EQ( token, "list" ) )
170 else if ( EQ( token, "show" ) )
172 else if ( EQ( token, "constraints" ) )
174 else if ( EQ( token, "describe" ) )
176 else if ( EQ( token, "version" ) )
178 else if ( EQ( token, "template" ) )
180 else if ( EQ( token, "handle" ) )
182 else if ( EQ( token, "attribute" ) )
184 else if ( EQ( token, "value" ) )
186 else if ( EQ( token, "full" ) )
188 else if ( EQ( token, "abridged" ) )
190 else if ( EQ( token, "summary" ) )
192 else if ( EQ( token, "format" ) )
194 else if ( EQ( token, "hold" ) )
196 else if ( EQ( token, "maxhits" ) )
198 else if ( EQ( token, "match" ) )
200 else if ( EQ( token, "linelength" ) )
202 else if ( EQ( token, "command" ) )
204 else if ( EQ( token, "trace" ) )
220 char buffer[BUFSIZ], temp[BUFSIZ];
225 case ATTRIBUTE: /* . */
228 case TEMPLATE: /* ^ */
229 case SEARCH_ALL:/* * */
231 if ( strlen( value ) > 1 ) {
232 /* fullname used, so expect an equals sign */
233 if ( getToken( buffer ) != EQUALS ) {
234 printFormatted( lineLength, TRUE, stdout,
238 token = getToken( value );
240 token = getToken( value );
241 if ( token != COMMA && token != SEMICOLON && token != EQUALS
242 && token != COLON && token != EOF ) {
243 token = getToken( buffer );
252 printFormatted( lineLength, TRUE, stdout,
253 "Expected search string but got \"%s\"", buffer );
257 *specifier = SEARCH_ALL;
258 if ( ( token = getToken( buffer ) ) == EQUALS ) {
259 strcpy( attribute, value );
260 token = getToken( value );
261 if ( token == COMMA || token == SEMICOLON
262 || token == COLON || token == EOF ) {
263 printFormatted( lineLength, TRUE, stdout,
264 "Syntax error, string expected." );
267 token = getToken( buffer );
271 while ( token != COMMA && token != SEMICOLON && token != COLON
273 if ( *value != '\0' )
274 strcat( value, " " );
275 strcat( value, buffer );
276 token = getToken( buffer );
279 while ( token == COMMA ) {
280 token = getToken( buffer );
284 if ( ( token = getToken( buffer ) ) != EQUALS ) {
285 printFormatted( lineLength, TRUE, stdout,
288 token = getToken( buffer );
289 if ( EQ( buffer, "exact" ) )
291 else if ( EQ( buffer, "fuzzy" ) )
294 printFormatted( lineLength, TRUE, stdout,
295 "Unrecognised search type" );
296 token = getToken( buffer );
300 if ( iterations == 0 ) {
301 /* obviously an unrecognised constraint */
302 printFormatted( lineLength, TRUE, stdout,
303 "Constraint \"%s\" not supported",
305 while ( ( token = getToken( buffer ) ) != EOF
306 && token != COMMA && token != COLON
307 && token != SEMICOLON )
310 strcpy( temp, buffer );
311 token = getToken( buffer );
312 if ( token == EQUALS ) {
314 printFormatted( lineLength, TRUE, stdout,
315 "Constraint \"%s\" not supported",
318 while ( token != EOF && token != SEMICOLON
319 && token != COLON && token != COMMA ) {
320 if ( iterations > 0 ) {
322 strcat( temp, buffer );
324 token = getToken( buffer );
326 if ( iterations > 0 ) {
327 printFormatted( lineLength, TRUE, stdout,
328 "Assuming \"%s\" part of query and not an unrecognised constraint.", temp );
329 strcat( value, "," );
330 strcat( value, temp );
338 if ( *value == '\0' ) {
339 printFormatted( lineLength, TRUE, stdout,
340 "Value not specified" );
343 if ( *specifier == NULL )
344 *specifier = SEARCH_ALL;
359 char **reallocResult;
361 switch ( specifier ) {
363 if ( numberOfComponents+3 >= components ) {
365 reallocResult = (char **)realloc(component, sizeof(char **)*components);
366 if ( reallocResult == NULL ) {
367 printFormatted( lineLength, TRUE, stdout,
371 component = reallocResult;
373 if ( attribute != NULL && *attribute != '\0' ) {
374 /* The user obviously knows what they are doing */
375 sprintf( query, "(%s%s%s)", attribute,
376 (soundex)?"~=":"=", buffer );
378 if ( ( s = strchr( buffer, ',' ) ) != NULL ) {
380 while ( *s && isspace( (unsigned char) *s ) )
382 sprintf( query, "(sn%s%s)",
383 (soundex)?"~=":"=", buffer );
384 component[numberOfComponents++] = strdup( query );
385 /* let's just make sure there is no title */
386 if ( ( t = strrchr( s, ',' ) ) != NULL ) {
388 while ( *t && isspace( (unsigned char) *t ) )
390 sprintf( query, "(personalTitle%s%s)",
391 (soundex)?"~=":"=", t );
392 component[numberOfComponents++] = strdup( query );
394 sprintf( query, "%s %s", s, buffer );
395 strcpy( buffer, query );
396 } else if ( strncasecmp( buffer, "first ", 6 ) == 0 ) {
397 sprintf( query, "%s *", &buffer[6] );
398 strcpy( buffer, query );
400 if ( ( s = strchr( buffer, '@' ) ) != NULL ) {
402 if ( *buffer == '\0' ) /* no username */
403 sprintf( query, "(mail=*@%s)", s );
404 else if ( *s == '\0' ) /* no host */
405 sprintf( query, "(|(mail=%s@*)(userid=%s))",
408 sprintf( query, "(mail=%s@%s)",
411 printFormatted( lineLength, TRUE, stdout,
412 "Fuzzy matching not supported on e-mail address queries" );
413 } else if ( strchr( buffer, ' ' ) == NULL ) {
415 "(|(sn%s%s)(userid%s%s)(l%s%s)(ou%s%s)\
416 (&(cn%s%s)(!(objectClass=person))))",
417 (soundex)?"~=":"=", buffer,
418 (soundex)?"~=":"=", buffer,
419 (soundex)?"~=":"=", buffer,
420 (soundex)?"~=":"=", buffer,
421 (soundex)?"~=":"=", buffer );
424 sprintf( query, "(|(l%s%s)(ou%s%s)(preferredName%s%s)",
425 (soundex)?"~=":"=", buffer,
426 (soundex)?"~=":"=", buffer,
427 (soundex)?"~=":"=", buffer );
429 sprintf( query, "(|(l%s%s)(ou%s%s)",
430 (soundex)?"~=":"=", buffer,
431 (soundex)?"~=":"=", buffer );
434 * If LDAP and/or Quipu didn't strip spaces
435 * then this would be different but as it does
436 * this is easy :-) but it also means we might
440 strcat( query, "(cn~=" );
441 strcat( query, buffer );
443 strcat( query, "(cn=*" );
444 strcat( query, strtok( buffer, " " ) );
445 while ( ( s = strtok( NULL, " " ) ) != NULL ) {
446 strcat( query, " * " );
450 strcat( query, "))" );
453 component[numberOfComponents++] = strdup( query );
457 if ( numberOfComponents+1 >= components ) {
459 reallocResult = (char **)realloc(component, sizeof(char **)*components);
460 if ( reallocResult == NULL ) {
461 printFormatted( lineLength, TRUE, stdout,
465 component = reallocResult;
467 if ( *value != '\0' ) {
468 sprintf( query, "(%s%s%s)", buffer,
469 (soundex)?"~=":"=", value );
470 component[numberOfComponents++] = strdup( query );
473 if ( *attribute != '\0' ) {
474 sprintf( query, "(%s%s*)", attribute,
475 (soundex)?"~=":"=" );
476 component[numberOfComponents++] = strdup( query );
478 strcpy( attribute, buffer );
483 if ( numberOfComponents+1 >= components ) {
485 reallocResult = (char **)realloc(component, sizeof(char **)*components);
486 if ( reallocResult == NULL ) {
487 printFormatted( lineLength, TRUE, stdout,
491 component = reallocResult;
493 sprintf( query, "(objectClass%s%s)",
494 (soundex)?"~=":"=", templateToObjectClass( buffer ) );
495 component[numberOfComponents++] = strdup( query );
499 if ( *attribute != '\0' ) {
500 if ( numberOfComponents+1 >= components ) {
502 reallocResult = (char **)realloc(component, sizeof(char **)*components);
503 if ( reallocResult == NULL ) {
504 printFormatted( lineLength, TRUE, stdout,
508 component = reallocResult;
510 sprintf( query, "(%s%s%s)", attribute,
511 (soundex)?"~=":"=", buffer );
512 component[numberOfComponents++] = strdup( query );
515 if ( *value != '\0' )
516 printFormatted( lineLength, TRUE, stdout,
517 "Ignoring old value (%s)", value );
518 strcpy( value, buffer );
523 if ( numberOfComponents+1 >= components ) {
525 reallocResult = (char **)realloc(component, sizeof(char **)*components);
526 if ( reallocResult == NULL ) {
527 printFormatted( lineLength, TRUE, stdout,
531 component = reallocResult;
533 component[numberOfComponents++] = strdup( buffer );
541 parseCommand( char *query )
544 * This procedure reads the string sent by the user and breaks it
545 * down into command to execute.
547 char buffer[BUFSIZ], attribute[BUFSIZ], objectClass[BUFSIZ],
549 char **reallocResult;
550 int command, specificName, length, token, i, j, specifier, soundex;
553 switch ( command = getToken( buffer ) ) {
559 token = getToken( buffer );
564 /* <command> [ <string> ] */
565 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
566 strcpy( query, buffer );
567 token = getToken( buffer );
573 /* "show" <string> */
574 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
575 strcpy( query, buffer );
576 token = getToken( buffer );
578 printFormatted( lineLength, TRUE, stdout,
579 "Show must have a parameter" );
585 /* <term> [ ";" <term> ] */
589 numberOfComponents = 0;
590 if ( ( component = (char **)malloc(sizeof(char **)*components) ) == NULL ) {
591 printFormatted( lineLength, TRUE, stdout,
595 if ( ( token = term( command, buffer, attribute, &specifier,
596 &soundex ) ) != ERROR )
597 command = processTerm( specifier, soundex, buffer,
601 if ( token == SEMICOLON ) {
602 if ( command == READ ) {
603 printFormatted( lineLength, TRUE, stdout,
604 "Multiple components on a Handle query not supported." );
609 token = getToken( buffer );
610 token = term( token, buffer, attribute,
611 &specifier, &soundex );
612 command = processTerm( specifier, soundex,
613 buffer, attribute, value );
614 if ( command == READ ) {
615 printFormatted( lineLength, TRUE, stdout,
616 "Multiple components on a Handle query not supported." );
618 } else if ( command == ERROR )
620 } while ( token == SEMICOLON );
623 * Need to tidy up outstanding single value or attribute terms
625 if ( *attribute != '\0' ) {
626 if ( numberOfComponents+1 >= components ) {
628 reallocResult = (char **)realloc(component, sizeof(char **)*components);
629 if ( reallocResult == NULL ) {
630 printFormatted( lineLength, TRUE, stdout,
634 component = reallocResult;
636 sprintf( query, "(%s%s*)", attribute,
637 (soundex)?"~=":"=" );
638 component[numberOfComponents++] = strdup( query );
640 if ( *value != '\0' )
641 if ( processTerm( SEARCH_ALL, soundex, value, NULL, NULL ) == ERROR )
643 if ( numberOfComponents == 0 ) {
644 printFormatted( lineLength, TRUE, stdout,
647 } else if ( numberOfComponents == 1 )
648 strcpy( query, component[0] );
650 strcpy( query, "(&" );
651 for ( i = 0; i < numberOfComponents; i++ )
652 strcat( query, component[i] );
653 strcat( query, ")" );
659 if ( token == COLON ) { /* global constraints */
661 token = getToken( buffer );
664 if ( ( token = getToken( buffer ) ) != EQUALS ) {
665 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
667 token = getToken( buffer );
673 if ( outputFormat != NULL )
674 printFormatted( lineLength, TRUE, stdout, "Only one response format can be specified." );
676 outputFormat = token;
680 printFormatted( lineLength, TRUE, stdout, "Unrecognised format specifier" );
682 token = getToken( buffer );
686 holdConnection = TRUE;
687 token = getToken( buffer );
691 if ( ( token = getToken( buffer ) ) != EQUALS ) {
692 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
694 token = getToken( buffer );
695 if ( (maxHits = atoi( buffer )) < 1
696 || maxHits > maximumSize ) {
697 printFormatted( lineLength, TRUE, stdout, "Invalid maxhits value, defaulting to %s", maximumSize );
698 maxHits = maximumSize;
700 token = getToken( buffer );
704 if ( ( token = getToken( buffer ) ) != EQUALS ) {
705 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
707 token = getToken( buffer );
708 /**/ /* need to save this value and lookup locale */
709 printFormatted( lineLength, TRUE, stdout,
710 "Language not currently implemented" );
711 token = getToken( buffer );
715 if ( ( token = getToken( buffer ) ) != EQUALS ) {
716 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
718 token = getToken( buffer );
719 lineLength = atoi( buffer );
720 if ( lineLength < MIN_LINE_LENGTH
721 || lineLength > MAX_LINE_LENGTH ) {
722 printFormatted( lineLength, TRUE, stdout, "Invalid line length, using default %d", DEFAULT_LINE_LENGTH );
723 lineLength = DEFAULT_LINE_LENGTH;
725 token = getToken( buffer );
730 token = getToken( buffer );
734 printFormatted( lineLength, TRUE, stdout, "Unrecognised global constraint \"%s\"", buffer );
735 while ( ( token = getToken( buffer ) ) != EOF
741 } while ( token == COMMA );
743 if ( token != EOF ) {
744 printFormatted( lineLength, TRUE, stdout,
745 "Data following \"%s\" ignored.", buffer );
746 while ( ( token = getToken( buffer ) ) != EOF )
749 if ( trace && ( command == READ || command == SEARCH ) )
752 printFormatted( lineLength, TRUE, stdout,
753 "Attempting to read \"%s\"", query );
757 printFormatted( lineLength, TRUE, stdout,
758 "Searching using LDAP query %s", query );