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 ) {
53 tablesize = getdtablesize();
57 FD_SET( fileno( stdin ), &readfds );
59 if ( (status = select( tablesize, &readfds, 0, 0, &timeout )) <= 0 ) {
61 printFormatted( lineLength, TRUE, stdout,
62 "select: %s", strerror( errno ) );
64 printFormatted( lineLength, TRUE, stdout,
65 "Connection timed out waiting for input." );
69 * We really should determine how many characters are
70 * waiting for us and then malloc that amount rather than
73 if ( ( buffer = (char *)malloc(BUFSIZ) ) == NULL
74 || fgets( buffer, BUFSIZ, stdin ) == NULL ) {
80 while ( i-- > 0 && ( buffer[i] == '\r' || buffer[i] == '\n' ) )
83 syslog( LOG_INFO, "Whois++ Query: %s", buffer );
85 while ( buffer[idx] != '\0' && isspace( buffer[idx] ) )
87 token[0] = buffer[idx++];
91 strcpy( token, "<end of line>" );
130 if ( ch == '\\' && buffer[idx] != '\0' )
131 token[i++] = buffer[idx++];
134 } while ( ch != '\0' && ch != '"' );
136 printFormatted( lineLength, TRUE, stdout,
137 "Trailing \" missing" );
147 if ( ch == '\\' && buffer[idx] != '\0' )
148 token[i++] = buffer[idx++];
151 } while ( ch != '\0' && !isspace( ch ) && !isspecial( ch ) );
155 * The following is a brute force lookup, once the names
156 * have settled down this should change to a hash table,
157 * or something similar.
159 if ( EQ( token, "help" ) )
161 else if ( EQ( token, "list" ) )
163 else if ( EQ( token, "show" ) )
165 else if ( EQ( token, "constraints" ) )
167 else if ( EQ( token, "describe" ) )
169 else if ( EQ( token, "version" ) )
171 else if ( EQ( token, "template" ) )
173 else if ( EQ( token, "handle" ) )
175 else if ( EQ( token, "attribute" ) )
177 else if ( EQ( token, "value" ) )
179 else if ( EQ( token, "full" ) )
181 else if ( EQ( token, "abridged" ) )
183 else if ( EQ( token, "summary" ) )
185 else if ( EQ( token, "format" ) )
187 else if ( EQ( token, "hold" ) )
189 else if ( EQ( token, "maxhits" ) )
191 else if ( EQ( token, "match" ) )
193 else if ( EQ( token, "linelength" ) )
195 else if ( EQ( token, "command" ) )
197 else if ( EQ( token, "trace" ) )
204 static int term( token, value, attribute, specifier, soundex )
206 char *value, *attribute;
207 int *specifier, *soundex;
209 char buffer[BUFSIZ], temp[BUFSIZ];
214 case ATTRIBUTE: /* . */
217 case TEMPLATE: /* ^ */
218 case SEARCH_ALL:/* * */
220 if ( strlen( value ) > 1 ) {
221 /* fullname used, so expect an equals sign */
222 if ( getToken( buffer ) != EQUALS ) {
223 printFormatted( lineLength, TRUE, stdout,
227 token = getToken( value );
229 token = getToken( value );
230 if ( token != COMMA && token != SEMICOLON && token != EQUALS
231 && token != COLON && token != EOF ) {
232 token = getToken( buffer );
241 printFormatted( lineLength, TRUE, stdout,
242 "Expected search string but got \"%s\"", buffer );
246 *specifier = SEARCH_ALL;
247 if ( ( token = getToken( buffer ) ) == EQUALS ) {
248 strcpy( attribute, value );
249 token = getToken( value );
250 if ( token == COMMA || token == SEMICOLON
251 || token == COLON || token == EOF ) {
252 printFormatted( lineLength, TRUE, stdout,
253 "Syntax error, string expected." );
256 token = getToken( buffer );
260 while ( token != COMMA && token != SEMICOLON && token != COLON
262 if ( *value != '\0' )
263 strcat( value, " " );
264 strcat( value, buffer );
265 token = getToken( buffer );
268 while ( token == COMMA ) {
269 token = getToken( buffer );
273 if ( ( token = getToken( buffer ) ) != EQUALS ) {
274 printFormatted( lineLength, TRUE, stdout,
277 token = getToken( buffer );
278 if ( EQ( buffer, "exact" ) )
280 else if ( EQ( buffer, "fuzzy" ) )
283 printFormatted( lineLength, TRUE, stdout,
284 "Unrecognised search type" );
285 token = getToken( buffer );
289 if ( iterations == 0 ) {
290 /* obviously an unrecognised constraint */
291 printFormatted( lineLength, TRUE, stdout,
292 "Constraint \"%s\" not supported",
294 while ( ( token = getToken( buffer ) ) != EOF
295 && token != COMMA && token != COLON
296 && token != SEMICOLON )
299 strcpy( temp, buffer );
300 token = getToken( buffer );
301 if ( token == EQUALS ) {
303 printFormatted( lineLength, TRUE, stdout,
304 "Constraint \"%s\" not supported",
307 while ( token != EOF && token != SEMICOLON
308 && token != COLON && token != COMMA ) {
309 if ( iterations > 0 ) {
311 strcat( temp, buffer );
313 token = getToken( buffer );
315 if ( iterations > 0 ) {
316 printFormatted( lineLength, TRUE, stdout,
317 "Assuming \"%s\" part of query and not an unrecognised constraint.", temp );
318 strcat( value, "," );
319 strcat( value, temp );
327 if ( *value == '\0' ) {
328 printFormatted( lineLength, TRUE, stdout,
329 "Value not specified" );
332 if ( *specifier == NULL )
333 *specifier = SEARCH_ALL;
337 static int processTerm( specifier, soundex, buffer, attribute, value )
338 int specifier, soundex;
339 char *buffer, *attribute, *value;
344 char **reallocResult;
346 switch ( specifier ) {
348 if ( numberOfComponents+3 >= components ) {
350 reallocResult = (char **)realloc(component, sizeof(char **)*components);
351 if ( reallocResult == NULL ) {
352 printFormatted( lineLength, TRUE, stdout,
356 component = reallocResult;
358 if ( attribute != NULL && *attribute != '\0' ) {
359 /* The user obviously knows what they are doing */
360 sprintf( query, "(%s%s%s)", attribute,
361 (soundex)?"~=":"=", buffer );
363 if ( ( s = index( buffer, ',' ) ) != NULL ) {
365 while ( *s && isspace( *s ) )
367 sprintf( query, "(sn%s%s)",
368 (soundex)?"~=":"=", buffer );
369 component[numberOfComponents++] = strdup( query );
370 /* let's just make sure there is no title */
371 if ( ( t = rindex( s, ',' ) ) != NULL ) {
373 while ( *t && isspace( *t ) )
375 sprintf( query, "(personalTitle%s%s)",
376 (soundex)?"~=":"=", t );
377 component[numberOfComponents++] = strdup( query );
379 sprintf( query, "%s %s", s, buffer );
380 strcpy( buffer, query );
381 } else if ( strncasecmp( buffer, "first ", 6 ) == 0 ) {
382 sprintf( query, "%s *", &buffer[6] );
383 strcpy( buffer, query );
385 if ( ( s = index( buffer, '@' ) ) != NULL ) {
387 if ( *buffer == '\0' ) /* no username */
388 sprintf( query, "(mail=*@%s)", s );
389 else if ( *s == '\0' ) /* no host */
390 sprintf( query, "(|(mail=%s@*)(userid=%s))",
393 sprintf( query, "(mail=%s@%s)",
396 printFormatted( lineLength, TRUE, stdout,
397 "Fuzzy matching not supported on e-mail address queries" );
398 } else if ( index( buffer, ' ' ) == NULL ) {
400 "(|(sn%s%s)(userid%s%s)(l%s%s)(ou%s%s)\
401 (&(cn%s%s)(!(objectClass=person))))",
402 (soundex)?"~=":"=", buffer,
403 (soundex)?"~=":"=", buffer,
404 (soundex)?"~=":"=", buffer,
405 (soundex)?"~=":"=", buffer,
406 (soundex)?"~=":"=", buffer );
409 sprintf( query, "(|(l%s%s)(ou%s%s)(preferredName%s%s)",
410 (soundex)?"~=":"=", buffer,
411 (soundex)?"~=":"=", buffer,
412 (soundex)?"~=":"=", buffer );
414 sprintf( query, "(|(l%s%s)(ou%s%s)",
415 (soundex)?"~=":"=", buffer,
416 (soundex)?"~=":"=", buffer );
419 * If LDAP and/or Quipu didn't strip spaces
420 * then this would be different but as it does
421 * this is easy :-) but it also means we might
425 strcat( query, "(cn~=" );
426 strcat( query, buffer );
428 strcat( query, "(cn=*" );
429 strcat( query, strtok( buffer, " " ) );
430 while ( ( s = strtok( NULL, " " ) ) != NULL ) {
431 strcat( query, " * " );
435 strcat( query, "))" );
438 component[numberOfComponents++] = strdup( query );
442 if ( numberOfComponents+1 >= components ) {
444 reallocResult = (char **)realloc(component, sizeof(char **)*components);
445 if ( reallocResult == NULL ) {
446 printFormatted( lineLength, TRUE, stdout,
450 component = reallocResult;
452 if ( *value != '\0' ) {
453 sprintf( query, "(%s%s%s)", buffer,
454 (soundex)?"~=":"=", value );
455 component[numberOfComponents++] = strdup( query );
458 if ( *attribute != '\0' ) {
459 sprintf( query, "(%s%s*)", attribute,
460 (soundex)?"~=":"=" );
461 component[numberOfComponents++] = strdup( query );
463 strcpy( attribute, buffer );
468 if ( numberOfComponents+1 >= components ) {
470 reallocResult = (char **)realloc(component, sizeof(char **)*components);
471 if ( reallocResult == NULL ) {
472 printFormatted( lineLength, TRUE, stdout,
476 component = reallocResult;
478 sprintf( query, "(objectClass%s%s)",
479 (soundex)?"~=":"=", templateToObjectClass( buffer ) );
480 component[numberOfComponents++] = strdup( query );
484 if ( *attribute != '\0' ) {
485 if ( numberOfComponents+1 >= components ) {
487 reallocResult = (char **)realloc(component, sizeof(char **)*components);
488 if ( reallocResult == NULL ) {
489 printFormatted( lineLength, TRUE, stdout,
493 component = reallocResult;
495 sprintf( query, "(%s%s%s)", attribute,
496 (soundex)?"~=":"=", buffer );
497 component[numberOfComponents++] = strdup( query );
500 if ( *value != '\0' )
501 printFormatted( lineLength, TRUE, stdout,
502 "Ignoring old value (%s)", value );
503 strcpy( value, buffer );
508 if ( numberOfComponents+1 >= components ) {
510 reallocResult = (char **)realloc(component, sizeof(char **)*components);
511 if ( reallocResult == NULL ) {
512 printFormatted( lineLength, TRUE, stdout,
516 component = reallocResult;
518 component[numberOfComponents++] = strdup( buffer );
525 int parseCommand( query )
529 * This procedure reads the string sent by the user and breaks it
530 * down into command to execute.
532 char buffer[BUFSIZ], attribute[BUFSIZ], objectClass[BUFSIZ],
534 char **reallocResult;
535 int command, specificName, length, token, i, j, specifier, soundex;
538 switch ( command = getToken( buffer ) ) {
544 token = getToken( buffer );
549 /* <command> [ <string> ] */
550 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
551 strcpy( query, buffer );
552 token = getToken( buffer );
558 /* "show" <string> */
559 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
560 strcpy( query, buffer );
561 token = getToken( buffer );
563 printFormatted( lineLength, TRUE, stdout,
564 "Show must have a parameter" );
570 /* <term> [ ";" <term> ] */
574 numberOfComponents = 0;
575 if ( ( component = (char **)malloc(sizeof(char **)*components) ) == NULL ) {
576 printFormatted( lineLength, TRUE, stdout,
580 if ( ( token = term( command, buffer, attribute, &specifier,
581 &soundex ) ) != ERROR )
582 command = processTerm( specifier, soundex, buffer,
586 if ( token == SEMICOLON ) {
587 if ( command == READ ) {
588 printFormatted( lineLength, TRUE, stdout,
589 "Multiple components on a Handle query not supported." );
594 token = getToken( buffer );
595 token = term( token, buffer, attribute,
596 &specifier, &soundex );
597 command = processTerm( specifier, soundex,
598 buffer, attribute, value );
599 if ( command == READ ) {
600 printFormatted( lineLength, TRUE, stdout,
601 "Multiple components on a Handle query not supported." );
603 } else if ( command == ERROR )
605 } while ( token == SEMICOLON );
608 * Need to tidy up outstanding single value or attribute terms
610 if ( *attribute != '\0' ) {
611 if ( numberOfComponents+1 >= components ) {
613 reallocResult = (char **)realloc(component, sizeof(char **)*components);
614 if ( reallocResult == NULL ) {
615 printFormatted( lineLength, TRUE, stdout,
619 component = reallocResult;
621 sprintf( query, "(%s%s*)", attribute,
622 (soundex)?"~=":"=" );
623 component[numberOfComponents++] = strdup( query );
625 if ( *value != '\0' )
626 if ( processTerm( SEARCH_ALL, soundex, value, NULL, NULL ) == ERROR )
628 if ( numberOfComponents == 0 ) {
629 printFormatted( lineLength, TRUE, stdout,
632 } else if ( numberOfComponents == 1 )
633 strcpy( query, component[0] );
635 strcpy( query, "(&" );
636 for ( i = 0; i < numberOfComponents; i++ )
637 strcat( query, component[i] );
638 strcat( query, ")" );
644 if ( token == COLON ) { /* global constraints */
646 token = getToken( buffer );
649 if ( ( token = getToken( buffer ) ) != EQUALS ) {
650 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
652 token = getToken( buffer );
658 if ( outputFormat != NULL )
659 printFormatted( lineLength, TRUE, stdout, "Only one response format can be specified." );
661 outputFormat = token;
665 printFormatted( lineLength, TRUE, stdout, "Unrecognised format specifier" );
667 token = getToken( buffer );
671 holdConnection = TRUE;
672 token = getToken( buffer );
676 if ( ( token = getToken( buffer ) ) != EQUALS ) {
677 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
679 token = getToken( buffer );
680 if ( (maxHits = atoi( buffer )) < 1
681 || maxHits > maximumSize ) {
682 printFormatted( lineLength, TRUE, stdout, "Invalid maxhits value, defaulting to %s", maximumSize );
683 maxHits = maximumSize;
685 token = getToken( buffer );
689 if ( ( token = getToken( buffer ) ) != EQUALS ) {
690 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
692 token = getToken( buffer );
693 /**/ /* need to save this value and lookup locale */
694 printFormatted( lineLength, TRUE, stdout,
695 "Language not currently implemented" );
696 token = getToken( buffer );
700 if ( ( token = getToken( buffer ) ) != EQUALS ) {
701 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
703 token = getToken( buffer );
704 lineLength = atoi( buffer );
705 if ( lineLength < MIN_LINE_LENGTH
706 || lineLength > MAX_LINE_LENGTH ) {
707 printFormatted( lineLength, TRUE, stdout, "Invalid line length, using default %d", DEFAULT_LINE_LENGTH );
708 lineLength = DEFAULT_LINE_LENGTH;
710 token = getToken( buffer );
715 token = getToken( buffer );
719 printFormatted( lineLength, TRUE, stdout, "Unrecognised global constraint \"%s\"", buffer );
720 while ( ( token = getToken( buffer ) ) != EOF
726 } while ( token == COMMA );
728 if ( token != EOF ) {
729 printFormatted( lineLength, TRUE, stdout,
730 "Data following \"%s\" ignored.", buffer );
731 while ( ( token = getToken( buffer ) ) != EOF )
734 if ( trace && ( command == READ || command == SEARCH ) )
737 printFormatted( lineLength, TRUE, stdout,
738 "Attempting to read \"%s\"", query );
742 printFormatted( lineLength, TRUE, stdout,
743 "Searching using LDAP query %s", query );