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( 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' && !isspace( ch ) && !isspecial( ch ) );
161 * The following is a brute force lookup, once the names
162 * have settled down this should change to a hash table,
163 * or something similar.
165 if ( EQ( token, "help" ) )
167 else if ( EQ( token, "list" ) )
169 else if ( EQ( token, "show" ) )
171 else if ( EQ( token, "constraints" ) )
173 else if ( EQ( token, "describe" ) )
175 else if ( EQ( token, "version" ) )
177 else if ( EQ( token, "template" ) )
179 else if ( EQ( token, "handle" ) )
181 else if ( EQ( token, "attribute" ) )
183 else if ( EQ( token, "value" ) )
185 else if ( EQ( token, "full" ) )
187 else if ( EQ( token, "abridged" ) )
189 else if ( EQ( token, "summary" ) )
191 else if ( EQ( token, "format" ) )
193 else if ( EQ( token, "hold" ) )
195 else if ( EQ( token, "maxhits" ) )
197 else if ( EQ( token, "match" ) )
199 else if ( EQ( token, "linelength" ) )
201 else if ( EQ( token, "command" ) )
203 else if ( EQ( token, "trace" ) )
219 char buffer[BUFSIZ], temp[BUFSIZ];
224 case ATTRIBUTE: /* . */
227 case TEMPLATE: /* ^ */
228 case SEARCH_ALL:/* * */
230 if ( strlen( value ) > 1 ) {
231 /* fullname used, so expect an equals sign */
232 if ( getToken( buffer ) != EQUALS ) {
233 printFormatted( lineLength, TRUE, stdout,
237 token = getToken( value );
239 token = getToken( value );
240 if ( token != COMMA && token != SEMICOLON && token != EQUALS
241 && token != COLON && token != EOF ) {
242 token = getToken( buffer );
251 printFormatted( lineLength, TRUE, stdout,
252 "Expected search string but got \"%s\"", buffer );
256 *specifier = SEARCH_ALL;
257 if ( ( token = getToken( buffer ) ) == EQUALS ) {
258 strcpy( attribute, value );
259 token = getToken( value );
260 if ( token == COMMA || token == SEMICOLON
261 || token == COLON || token == EOF ) {
262 printFormatted( lineLength, TRUE, stdout,
263 "Syntax error, string expected." );
266 token = getToken( buffer );
270 while ( token != COMMA && token != SEMICOLON && token != COLON
272 if ( *value != '\0' )
273 strcat( value, " " );
274 strcat( value, buffer );
275 token = getToken( buffer );
278 while ( token == COMMA ) {
279 token = getToken( buffer );
283 if ( ( token = getToken( buffer ) ) != EQUALS ) {
284 printFormatted( lineLength, TRUE, stdout,
287 token = getToken( buffer );
288 if ( EQ( buffer, "exact" ) )
290 else if ( EQ( buffer, "fuzzy" ) )
293 printFormatted( lineLength, TRUE, stdout,
294 "Unrecognised search type" );
295 token = getToken( buffer );
299 if ( iterations == 0 ) {
300 /* obviously an unrecognised constraint */
301 printFormatted( lineLength, TRUE, stdout,
302 "Constraint \"%s\" not supported",
304 while ( ( token = getToken( buffer ) ) != EOF
305 && token != COMMA && token != COLON
306 && token != SEMICOLON )
309 strcpy( temp, buffer );
310 token = getToken( buffer );
311 if ( token == EQUALS ) {
313 printFormatted( lineLength, TRUE, stdout,
314 "Constraint \"%s\" not supported",
317 while ( token != EOF && token != SEMICOLON
318 && token != COLON && token != COMMA ) {
319 if ( iterations > 0 ) {
321 strcat( temp, buffer );
323 token = getToken( buffer );
325 if ( iterations > 0 ) {
326 printFormatted( lineLength, TRUE, stdout,
327 "Assuming \"%s\" part of query and not an unrecognised constraint.", temp );
328 strcat( value, "," );
329 strcat( value, temp );
337 if ( *value == '\0' ) {
338 printFormatted( lineLength, TRUE, stdout,
339 "Value not specified" );
342 if ( *specifier == NULL )
343 *specifier = SEARCH_ALL;
358 char **reallocResult;
360 switch ( specifier ) {
362 if ( numberOfComponents+3 >= components ) {
364 reallocResult = (char **)realloc(component, sizeof(char **)*components);
365 if ( reallocResult == NULL ) {
366 printFormatted( lineLength, TRUE, stdout,
370 component = reallocResult;
372 if ( attribute != NULL && *attribute != '\0' ) {
373 /* The user obviously knows what they are doing */
374 sprintf( query, "(%s%s%s)", attribute,
375 (soundex)?"~=":"=", buffer );
377 if ( ( s = strchr( buffer, ',' ) ) != NULL ) {
379 while ( *s && isspace( *s ) )
381 sprintf( query, "(sn%s%s)",
382 (soundex)?"~=":"=", buffer );
383 component[numberOfComponents++] = strdup( query );
384 /* let's just make sure there is no title */
385 if ( ( t = strrchr( s, ',' ) ) != NULL ) {
387 while ( *t && isspace( *t ) )
389 sprintf( query, "(personalTitle%s%s)",
390 (soundex)?"~=":"=", t );
391 component[numberOfComponents++] = strdup( query );
393 sprintf( query, "%s %s", s, buffer );
394 strcpy( buffer, query );
395 } else if ( strncasecmp( buffer, "first ", 6 ) == 0 ) {
396 sprintf( query, "%s *", &buffer[6] );
397 strcpy( buffer, query );
399 if ( ( s = strchr( buffer, '@' ) ) != NULL ) {
401 if ( *buffer == '\0' ) /* no username */
402 sprintf( query, "(mail=*@%s)", s );
403 else if ( *s == '\0' ) /* no host */
404 sprintf( query, "(|(mail=%s@*)(userid=%s))",
407 sprintf( query, "(mail=%s@%s)",
410 printFormatted( lineLength, TRUE, stdout,
411 "Fuzzy matching not supported on e-mail address queries" );
412 } else if ( strchr( buffer, ' ' ) == NULL ) {
414 "(|(sn%s%s)(userid%s%s)(l%s%s)(ou%s%s)\
415 (&(cn%s%s)(!(objectClass=person))))",
416 (soundex)?"~=":"=", buffer,
417 (soundex)?"~=":"=", buffer,
418 (soundex)?"~=":"=", buffer,
419 (soundex)?"~=":"=", buffer,
420 (soundex)?"~=":"=", buffer );
423 sprintf( query, "(|(l%s%s)(ou%s%s)(preferredName%s%s)",
424 (soundex)?"~=":"=", buffer,
425 (soundex)?"~=":"=", buffer,
426 (soundex)?"~=":"=", buffer );
428 sprintf( query, "(|(l%s%s)(ou%s%s)",
429 (soundex)?"~=":"=", buffer,
430 (soundex)?"~=":"=", buffer );
433 * If LDAP and/or Quipu didn't strip spaces
434 * then this would be different but as it does
435 * this is easy :-) but it also means we might
439 strcat( query, "(cn~=" );
440 strcat( query, buffer );
442 strcat( query, "(cn=*" );
443 strcat( query, strtok( buffer, " " ) );
444 while ( ( s = strtok( NULL, " " ) ) != NULL ) {
445 strcat( query, " * " );
449 strcat( query, "))" );
452 component[numberOfComponents++] = strdup( query );
456 if ( numberOfComponents+1 >= components ) {
458 reallocResult = (char **)realloc(component, sizeof(char **)*components);
459 if ( reallocResult == NULL ) {
460 printFormatted( lineLength, TRUE, stdout,
464 component = reallocResult;
466 if ( *value != '\0' ) {
467 sprintf( query, "(%s%s%s)", buffer,
468 (soundex)?"~=":"=", value );
469 component[numberOfComponents++] = strdup( query );
472 if ( *attribute != '\0' ) {
473 sprintf( query, "(%s%s*)", attribute,
474 (soundex)?"~=":"=" );
475 component[numberOfComponents++] = strdup( query );
477 strcpy( attribute, buffer );
482 if ( numberOfComponents+1 >= components ) {
484 reallocResult = (char **)realloc(component, sizeof(char **)*components);
485 if ( reallocResult == NULL ) {
486 printFormatted( lineLength, TRUE, stdout,
490 component = reallocResult;
492 sprintf( query, "(objectClass%s%s)",
493 (soundex)?"~=":"=", templateToObjectClass( buffer ) );
494 component[numberOfComponents++] = strdup( query );
498 if ( *attribute != '\0' ) {
499 if ( numberOfComponents+1 >= components ) {
501 reallocResult = (char **)realloc(component, sizeof(char **)*components);
502 if ( reallocResult == NULL ) {
503 printFormatted( lineLength, TRUE, stdout,
507 component = reallocResult;
509 sprintf( query, "(%s%s%s)", attribute,
510 (soundex)?"~=":"=", buffer );
511 component[numberOfComponents++] = strdup( query );
514 if ( *value != '\0' )
515 printFormatted( lineLength, TRUE, stdout,
516 "Ignoring old value (%s)", value );
517 strcpy( value, buffer );
522 if ( numberOfComponents+1 >= components ) {
524 reallocResult = (char **)realloc(component, sizeof(char **)*components);
525 if ( reallocResult == NULL ) {
526 printFormatted( lineLength, TRUE, stdout,
530 component = reallocResult;
532 component[numberOfComponents++] = strdup( buffer );
540 parseCommand( char *query )
543 * This procedure reads the string sent by the user and breaks it
544 * down into command to execute.
546 char buffer[BUFSIZ], attribute[BUFSIZ], objectClass[BUFSIZ],
548 char **reallocResult;
549 int command, specificName, length, token, i, j, specifier, soundex;
552 switch ( command = getToken( buffer ) ) {
558 token = getToken( buffer );
563 /* <command> [ <string> ] */
564 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
565 strcpy( query, buffer );
566 token = getToken( buffer );
572 /* "show" <string> */
573 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
574 strcpy( query, buffer );
575 token = getToken( buffer );
577 printFormatted( lineLength, TRUE, stdout,
578 "Show must have a parameter" );
584 /* <term> [ ";" <term> ] */
588 numberOfComponents = 0;
589 if ( ( component = (char **)malloc(sizeof(char **)*components) ) == NULL ) {
590 printFormatted( lineLength, TRUE, stdout,
594 if ( ( token = term( command, buffer, attribute, &specifier,
595 &soundex ) ) != ERROR )
596 command = processTerm( specifier, soundex, buffer,
600 if ( token == SEMICOLON ) {
601 if ( command == READ ) {
602 printFormatted( lineLength, TRUE, stdout,
603 "Multiple components on a Handle query not supported." );
608 token = getToken( buffer );
609 token = term( token, buffer, attribute,
610 &specifier, &soundex );
611 command = processTerm( specifier, soundex,
612 buffer, attribute, value );
613 if ( command == READ ) {
614 printFormatted( lineLength, TRUE, stdout,
615 "Multiple components on a Handle query not supported." );
617 } else if ( command == ERROR )
619 } while ( token == SEMICOLON );
622 * Need to tidy up outstanding single value or attribute terms
624 if ( *attribute != '\0' ) {
625 if ( numberOfComponents+1 >= components ) {
627 reallocResult = (char **)realloc(component, sizeof(char **)*components);
628 if ( reallocResult == NULL ) {
629 printFormatted( lineLength, TRUE, stdout,
633 component = reallocResult;
635 sprintf( query, "(%s%s*)", attribute,
636 (soundex)?"~=":"=" );
637 component[numberOfComponents++] = strdup( query );
639 if ( *value != '\0' )
640 if ( processTerm( SEARCH_ALL, soundex, value, NULL, NULL ) == ERROR )
642 if ( numberOfComponents == 0 ) {
643 printFormatted( lineLength, TRUE, stdout,
646 } else if ( numberOfComponents == 1 )
647 strcpy( query, component[0] );
649 strcpy( query, "(&" );
650 for ( i = 0; i < numberOfComponents; i++ )
651 strcat( query, component[i] );
652 strcat( query, ")" );
658 if ( token == COLON ) { /* global constraints */
660 token = getToken( buffer );
663 if ( ( token = getToken( buffer ) ) != EQUALS ) {
664 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
666 token = getToken( buffer );
672 if ( outputFormat != NULL )
673 printFormatted( lineLength, TRUE, stdout, "Only one response format can be specified." );
675 outputFormat = token;
679 printFormatted( lineLength, TRUE, stdout, "Unrecognised format specifier" );
681 token = getToken( buffer );
685 holdConnection = TRUE;
686 token = getToken( buffer );
690 if ( ( token = getToken( buffer ) ) != EQUALS ) {
691 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
693 token = getToken( buffer );
694 if ( (maxHits = atoi( buffer )) < 1
695 || maxHits > maximumSize ) {
696 printFormatted( lineLength, TRUE, stdout, "Invalid maxhits value, defaulting to %s", maximumSize );
697 maxHits = maximumSize;
699 token = getToken( buffer );
703 if ( ( token = getToken( buffer ) ) != EQUALS ) {
704 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
706 token = getToken( buffer );
707 /**/ /* need to save this value and lookup locale */
708 printFormatted( lineLength, TRUE, stdout,
709 "Language not currently implemented" );
710 token = getToken( buffer );
714 if ( ( token = getToken( buffer ) ) != EQUALS ) {
715 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
717 token = getToken( buffer );
718 lineLength = atoi( buffer );
719 if ( lineLength < MIN_LINE_LENGTH
720 || lineLength > MAX_LINE_LENGTH ) {
721 printFormatted( lineLength, TRUE, stdout, "Invalid line length, using default %d", DEFAULT_LINE_LENGTH );
722 lineLength = DEFAULT_LINE_LENGTH;
724 token = getToken( buffer );
729 token = getToken( buffer );
733 printFormatted( lineLength, TRUE, stdout, "Unrecognised global constraint \"%s\"", buffer );
734 while ( ( token = getToken( buffer ) ) != EOF
740 } while ( token == COMMA );
742 if ( token != EOF ) {
743 printFormatted( lineLength, TRUE, stdout,
744 "Data following \"%s\" ignored.", buffer );
745 while ( ( token = getToken( buffer ) ) != EOF )
748 if ( trace && ( command == READ || command == SEARCH ) )
751 printFormatted( lineLength, TRUE, stdout,
752 "Attempting to read \"%s\"", query );
756 printFormatted( lineLength, TRUE, stdout,
757 "Searching using LDAP query %s", query );