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();
56 if ( tablesize > FD_SETSIZE ) {
57 tablesize = FD_SETSIZE;
59 #endif /* FD_SETSIZE */
64 FD_SET( fileno( stdin ), &readfds );
66 if ( (status = select( tablesize, &readfds, 0, 0, &timeout )) <= 0 ) {
68 printFormatted( lineLength, TRUE, stdout,
69 "select: %s", strerror( errno ) );
71 printFormatted( lineLength, TRUE, stdout,
72 "Connection timed out waiting for input." );
76 * We really should determine how many characters are
77 * waiting for us and then malloc that amount rather than
80 if ( ( buffer = (char *)malloc(BUFSIZ) ) == NULL
81 || fgets( buffer, BUFSIZ, stdin ) == NULL ) {
87 while ( i-- > 0 && ( buffer[i] == '\r' || buffer[i] == '\n' ) )
90 syslog( LOG_INFO, "Whois++ Query: %s", buffer );
92 while ( buffer[idx] != '\0' && isspace( buffer[idx] ) )
94 token[0] = buffer[idx++];
98 strcpy( token, "<end of line>" );
137 if ( ch == '\\' && buffer[idx] != '\0' )
138 token[i++] = buffer[idx++];
141 } while ( ch != '\0' && ch != '"' );
143 printFormatted( lineLength, TRUE, stdout,
144 "Trailing \" missing" );
154 if ( ch == '\\' && buffer[idx] != '\0' )
155 token[i++] = buffer[idx++];
158 } while ( ch != '\0' && !isspace( 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" ) )
211 static int term( token, value, attribute, specifier, soundex )
213 char *value, *attribute;
214 int *specifier, *soundex;
216 char buffer[BUFSIZ], temp[BUFSIZ];
221 case ATTRIBUTE: /* . */
224 case TEMPLATE: /* ^ */
225 case SEARCH_ALL:/* * */
227 if ( strlen( value ) > 1 ) {
228 /* fullname used, so expect an equals sign */
229 if ( getToken( buffer ) != EQUALS ) {
230 printFormatted( lineLength, TRUE, stdout,
234 token = getToken( value );
236 token = getToken( value );
237 if ( token != COMMA && token != SEMICOLON && token != EQUALS
238 && token != COLON && token != EOF ) {
239 token = getToken( buffer );
248 printFormatted( lineLength, TRUE, stdout,
249 "Expected search string but got \"%s\"", buffer );
253 *specifier = SEARCH_ALL;
254 if ( ( token = getToken( buffer ) ) == EQUALS ) {
255 strcpy( attribute, value );
256 token = getToken( value );
257 if ( token == COMMA || token == SEMICOLON
258 || token == COLON || token == EOF ) {
259 printFormatted( lineLength, TRUE, stdout,
260 "Syntax error, string expected." );
263 token = getToken( buffer );
267 while ( token != COMMA && token != SEMICOLON && token != COLON
269 if ( *value != '\0' )
270 strcat( value, " " );
271 strcat( value, buffer );
272 token = getToken( buffer );
275 while ( token == COMMA ) {
276 token = getToken( buffer );
280 if ( ( token = getToken( buffer ) ) != EQUALS ) {
281 printFormatted( lineLength, TRUE, stdout,
284 token = getToken( buffer );
285 if ( EQ( buffer, "exact" ) )
287 else if ( EQ( buffer, "fuzzy" ) )
290 printFormatted( lineLength, TRUE, stdout,
291 "Unrecognised search type" );
292 token = getToken( buffer );
296 if ( iterations == 0 ) {
297 /* obviously an unrecognised constraint */
298 printFormatted( lineLength, TRUE, stdout,
299 "Constraint \"%s\" not supported",
301 while ( ( token = getToken( buffer ) ) != EOF
302 && token != COMMA && token != COLON
303 && token != SEMICOLON )
306 strcpy( temp, buffer );
307 token = getToken( buffer );
308 if ( token == EQUALS ) {
310 printFormatted( lineLength, TRUE, stdout,
311 "Constraint \"%s\" not supported",
314 while ( token != EOF && token != SEMICOLON
315 && token != COLON && token != COMMA ) {
316 if ( iterations > 0 ) {
318 strcat( temp, buffer );
320 token = getToken( buffer );
322 if ( iterations > 0 ) {
323 printFormatted( lineLength, TRUE, stdout,
324 "Assuming \"%s\" part of query and not an unrecognised constraint.", temp );
325 strcat( value, "," );
326 strcat( value, temp );
334 if ( *value == '\0' ) {
335 printFormatted( lineLength, TRUE, stdout,
336 "Value not specified" );
339 if ( *specifier == NULL )
340 *specifier = SEARCH_ALL;
344 static int processTerm( specifier, soundex, buffer, attribute, value )
345 int specifier, soundex;
346 char *buffer, *attribute, *value;
351 char **reallocResult;
353 switch ( specifier ) {
355 if ( numberOfComponents+3 >= components ) {
357 reallocResult = (char **)realloc(component, sizeof(char **)*components);
358 if ( reallocResult == NULL ) {
359 printFormatted( lineLength, TRUE, stdout,
363 component = reallocResult;
365 if ( attribute != NULL && *attribute != '\0' ) {
366 /* The user obviously knows what they are doing */
367 sprintf( query, "(%s%s%s)", attribute,
368 (soundex)?"~=":"=", buffer );
370 if ( ( s = index( buffer, ',' ) ) != NULL ) {
372 while ( *s && isspace( *s ) )
374 sprintf( query, "(sn%s%s)",
375 (soundex)?"~=":"=", buffer );
376 component[numberOfComponents++] = strdup( query );
377 /* let's just make sure there is no title */
378 if ( ( t = rindex( s, ',' ) ) != NULL ) {
380 while ( *t && isspace( *t ) )
382 sprintf( query, "(personalTitle%s%s)",
383 (soundex)?"~=":"=", t );
384 component[numberOfComponents++] = strdup( query );
386 sprintf( query, "%s %s", s, buffer );
387 strcpy( buffer, query );
388 } else if ( strncasecmp( buffer, "first ", 6 ) == 0 ) {
389 sprintf( query, "%s *", &buffer[6] );
390 strcpy( buffer, query );
392 if ( ( s = index( buffer, '@' ) ) != NULL ) {
394 if ( *buffer == '\0' ) /* no username */
395 sprintf( query, "(mail=*@%s)", s );
396 else if ( *s == '\0' ) /* no host */
397 sprintf( query, "(|(mail=%s@*)(userid=%s))",
400 sprintf( query, "(mail=%s@%s)",
403 printFormatted( lineLength, TRUE, stdout,
404 "Fuzzy matching not supported on e-mail address queries" );
405 } else if ( index( buffer, ' ' ) == NULL ) {
407 "(|(sn%s%s)(userid%s%s)(l%s%s)(ou%s%s)\
408 (&(cn%s%s)(!(objectClass=person))))",
409 (soundex)?"~=":"=", buffer,
410 (soundex)?"~=":"=", buffer,
411 (soundex)?"~=":"=", buffer,
412 (soundex)?"~=":"=", buffer,
413 (soundex)?"~=":"=", buffer );
416 sprintf( query, "(|(l%s%s)(ou%s%s)(preferredName%s%s)",
417 (soundex)?"~=":"=", buffer,
418 (soundex)?"~=":"=", buffer,
419 (soundex)?"~=":"=", buffer );
421 sprintf( query, "(|(l%s%s)(ou%s%s)",
422 (soundex)?"~=":"=", buffer,
423 (soundex)?"~=":"=", buffer );
426 * If LDAP and/or Quipu didn't strip spaces
427 * then this would be different but as it does
428 * this is easy :-) but it also means we might
432 strcat( query, "(cn~=" );
433 strcat( query, buffer );
435 strcat( query, "(cn=*" );
436 strcat( query, strtok( buffer, " " ) );
437 while ( ( s = strtok( NULL, " " ) ) != NULL ) {
438 strcat( query, " * " );
442 strcat( query, "))" );
445 component[numberOfComponents++] = strdup( query );
449 if ( numberOfComponents+1 >= components ) {
451 reallocResult = (char **)realloc(component, sizeof(char **)*components);
452 if ( reallocResult == NULL ) {
453 printFormatted( lineLength, TRUE, stdout,
457 component = reallocResult;
459 if ( *value != '\0' ) {
460 sprintf( query, "(%s%s%s)", buffer,
461 (soundex)?"~=":"=", value );
462 component[numberOfComponents++] = strdup( query );
465 if ( *attribute != '\0' ) {
466 sprintf( query, "(%s%s*)", attribute,
467 (soundex)?"~=":"=" );
468 component[numberOfComponents++] = strdup( query );
470 strcpy( attribute, buffer );
475 if ( numberOfComponents+1 >= components ) {
477 reallocResult = (char **)realloc(component, sizeof(char **)*components);
478 if ( reallocResult == NULL ) {
479 printFormatted( lineLength, TRUE, stdout,
483 component = reallocResult;
485 sprintf( query, "(objectClass%s%s)",
486 (soundex)?"~=":"=", templateToObjectClass( buffer ) );
487 component[numberOfComponents++] = strdup( query );
491 if ( *attribute != '\0' ) {
492 if ( numberOfComponents+1 >= components ) {
494 reallocResult = (char **)realloc(component, sizeof(char **)*components);
495 if ( reallocResult == NULL ) {
496 printFormatted( lineLength, TRUE, stdout,
500 component = reallocResult;
502 sprintf( query, "(%s%s%s)", attribute,
503 (soundex)?"~=":"=", buffer );
504 component[numberOfComponents++] = strdup( query );
507 if ( *value != '\0' )
508 printFormatted( lineLength, TRUE, stdout,
509 "Ignoring old value (%s)", value );
510 strcpy( value, buffer );
515 if ( numberOfComponents+1 >= components ) {
517 reallocResult = (char **)realloc(component, sizeof(char **)*components);
518 if ( reallocResult == NULL ) {
519 printFormatted( lineLength, TRUE, stdout,
523 component = reallocResult;
525 component[numberOfComponents++] = strdup( buffer );
532 int parseCommand( query )
536 * This procedure reads the string sent by the user and breaks it
537 * down into command to execute.
539 char buffer[BUFSIZ], attribute[BUFSIZ], objectClass[BUFSIZ],
541 char **reallocResult;
542 int command, specificName, length, token, i, j, specifier, soundex;
545 switch ( command = getToken( buffer ) ) {
551 token = getToken( buffer );
556 /* <command> [ <string> ] */
557 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
558 strcpy( query, buffer );
559 token = getToken( buffer );
565 /* "show" <string> */
566 if ( ( token = getToken( buffer ) ) != EOF && token != COLON ) {
567 strcpy( query, buffer );
568 token = getToken( buffer );
570 printFormatted( lineLength, TRUE, stdout,
571 "Show must have a parameter" );
577 /* <term> [ ";" <term> ] */
581 numberOfComponents = 0;
582 if ( ( component = (char **)malloc(sizeof(char **)*components) ) == NULL ) {
583 printFormatted( lineLength, TRUE, stdout,
587 if ( ( token = term( command, buffer, attribute, &specifier,
588 &soundex ) ) != ERROR )
589 command = processTerm( specifier, soundex, buffer,
593 if ( token == SEMICOLON ) {
594 if ( command == READ ) {
595 printFormatted( lineLength, TRUE, stdout,
596 "Multiple components on a Handle query not supported." );
601 token = getToken( buffer );
602 token = term( token, buffer, attribute,
603 &specifier, &soundex );
604 command = processTerm( specifier, soundex,
605 buffer, attribute, value );
606 if ( command == READ ) {
607 printFormatted( lineLength, TRUE, stdout,
608 "Multiple components on a Handle query not supported." );
610 } else if ( command == ERROR )
612 } while ( token == SEMICOLON );
615 * Need to tidy up outstanding single value or attribute terms
617 if ( *attribute != '\0' ) {
618 if ( numberOfComponents+1 >= components ) {
620 reallocResult = (char **)realloc(component, sizeof(char **)*components);
621 if ( reallocResult == NULL ) {
622 printFormatted( lineLength, TRUE, stdout,
626 component = reallocResult;
628 sprintf( query, "(%s%s*)", attribute,
629 (soundex)?"~=":"=" );
630 component[numberOfComponents++] = strdup( query );
632 if ( *value != '\0' )
633 if ( processTerm( SEARCH_ALL, soundex, value, NULL, NULL ) == ERROR )
635 if ( numberOfComponents == 0 ) {
636 printFormatted( lineLength, TRUE, stdout,
639 } else if ( numberOfComponents == 1 )
640 strcpy( query, component[0] );
642 strcpy( query, "(&" );
643 for ( i = 0; i < numberOfComponents; i++ )
644 strcat( query, component[i] );
645 strcat( query, ")" );
651 if ( token == COLON ) { /* global constraints */
653 token = getToken( buffer );
656 if ( ( token = getToken( buffer ) ) != EQUALS ) {
657 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
659 token = getToken( buffer );
665 if ( outputFormat != NULL )
666 printFormatted( lineLength, TRUE, stdout, "Only one response format can be specified." );
668 outputFormat = token;
672 printFormatted( lineLength, TRUE, stdout, "Unrecognised format specifier" );
674 token = getToken( buffer );
678 holdConnection = TRUE;
679 token = getToken( buffer );
683 if ( ( token = getToken( buffer ) ) != EQUALS ) {
684 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
686 token = getToken( buffer );
687 if ( (maxHits = atoi( buffer )) < 1
688 || maxHits > maximumSize ) {
689 printFormatted( lineLength, TRUE, stdout, "Invalid maxhits value, defaulting to %s", maximumSize );
690 maxHits = maximumSize;
692 token = getToken( buffer );
696 if ( ( token = getToken( buffer ) ) != EQUALS ) {
697 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
699 token = getToken( buffer );
700 /**/ /* need to save this value and lookup locale */
701 printFormatted( lineLength, TRUE, stdout,
702 "Language not currently implemented" );
703 token = getToken( buffer );
707 if ( ( token = getToken( buffer ) ) != EQUALS ) {
708 printFormatted( lineLength, TRUE, stdout, "\"=\" expected" );
710 token = getToken( buffer );
711 lineLength = atoi( buffer );
712 if ( lineLength < MIN_LINE_LENGTH
713 || lineLength > MAX_LINE_LENGTH ) {
714 printFormatted( lineLength, TRUE, stdout, "Invalid line length, using default %d", DEFAULT_LINE_LENGTH );
715 lineLength = DEFAULT_LINE_LENGTH;
717 token = getToken( buffer );
722 token = getToken( buffer );
726 printFormatted( lineLength, TRUE, stdout, "Unrecognised global constraint \"%s\"", buffer );
727 while ( ( token = getToken( buffer ) ) != EOF
733 } while ( token == COMMA );
735 if ( token != EOF ) {
736 printFormatted( lineLength, TRUE, stdout,
737 "Data following \"%s\" ignored.", buffer );
738 while ( ( token = getToken( buffer ) ) != EOF )
741 if ( trace && ( command == READ || command == SEARCH ) )
744 printFormatted( lineLength, TRUE, stdout,
745 "Attempting to read \"%s\"", query );
749 printFormatted( lineLength, TRUE, stdout,
750 "Searching using LDAP query %s", query );