1 /* 2 * Copyright (c) 2001 by Sun Microsystems, Inc. 3 * All rights reserved. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * The contents of this file are subject to the Netscape Public 10 * License Version 1.1 (the "License"); you may not use this file 11 * except in compliance with the License. You may obtain a copy of 12 * the License at http://www.mozilla.org/NPL/ 13 * 14 * Software distributed under the License is distributed on an "AS 15 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 16 * implied. See the License for the specific language governing 17 * rights and limitations under the License. 18 * 19 * The Original Code is Mozilla Communicator client code, released 20 * March 31, 1998. 21 * 22 * The Initial Developer of the Original Code is Netscape 23 * Communications Corporation. Portions created by Netscape are 24 * Copyright (C) 1998-1999 Netscape Communications Corporation. All 25 * Rights Reserved. 26 * 27 * Contributor(s): 28 */ 29 30 /* 31 * tmplout.c: display template library output routines for LDAP clients 32 * 33 */ 34 35 #include "ldap-int.h" 36 #include "disptmpl.h" 37 38 #if defined(_WINDOWS) || defined(aix) || defined(SCOOS) || defined(OSF1) || defined(SOLARIS) 39 #include <time.h> /* for struct tm and ctime */ 40 #endif 41 42 43 /* This is totally lame, since it should be coming from time.h, but isn't. */ 44 #if defined(SOLARIS) 45 char *ctime_r(const time_t *, char *, int); 46 #endif 47 48 static int do_entry2text( LDAP *ld, char *buf, char *base, LDAPMessage *entry, 49 struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals, 50 writeptype writeproc, void *writeparm, char *eol, int rdncount, 51 unsigned long opts, char *urlprefix ); 52 static int do_entry2text_search( LDAP *ld, char *dn, char *base, 53 LDAPMessage *entry, struct ldap_disptmpl *tmpllist, char **defattrs, 54 char ***defvals, writeptype writeproc, void *writeparm, char *eol, 55 int rdncount, unsigned long opts, char *urlprefix ); 56 static int do_vals2text( LDAP *ld, char *buf, char **vals, char *label, 57 int labelwidth, unsigned long syntaxid, writeptype writeproc, 58 void *writeparm, char *eol, int rdncount, char *urlprefix ); 59 static int max_label_len( struct ldap_disptmpl *tmpl ); 60 static int output_label( char *buf, char *label, int width, 61 writeptype writeproc, void *writeparm, char *eol, int html ); 62 static int output_dn( char *buf, char *dn, int width, int rdncount, 63 writeptype writeproc, void *writeparm, char *eol, char *urlprefix ); 64 static void strcat_escaped( char *s1, char *s2 ); 65 static char *time2text( char *ldtimestr, int dateonly ); 66 static long gtime( struct tm *tm ); 67 static int searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, 68 char *dn, struct ldap_tmplitem *tip, int labelwidth, int rdncount, 69 writeptype writeproc, void *writeparm, char *eol, char *urlprefix ); 70 71 #define DEF_LABEL_WIDTH 15 72 #define SEARCH_TIMEOUT_SECS 120 73 #define OCATTRNAME "objectClass" 74 75 76 #define NONFATAL_LDAP_ERR( err ) ( err == LDAP_SUCCESS || \ 77 err == LDAP_TIMELIMIT_EXCEEDED || err == LDAP_SIZELIMIT_EXCEEDED ) 78 79 #define DEF_LDAP_URL_PREFIX "ldap:///" 80 81 82 int 83 LDAP_CALL 84 ldap_entry2text( 85 LDAP *ld, 86 char *buf, /* NULL for "use internal" */ 87 LDAPMessage *entry, 88 struct ldap_disptmpl *tmpl, 89 char **defattrs, 90 char ***defvals, 91 writeptype writeproc, 92 void *writeparm, 93 char *eol, 94 int rdncount, 95 unsigned long opts 96 ) 97 { 98 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_entry2text\n", 0, 0, 0 ); 99 100 return( do_entry2text( ld, buf, NULL, entry, tmpl, defattrs, defvals, 101 writeproc, writeparm, eol, rdncount, opts, NULL )); 102 103 } 104 105 106 107 int 108 LDAP_CALL 109 ldap_entry2html( 110 LDAP *ld, 111 char *buf, /* NULL for "use internal" */ 112 LDAPMessage *entry, 113 struct ldap_disptmpl *tmpl, 114 char **defattrs, 115 char ***defvals, 116 writeptype writeproc, 117 void *writeparm, 118 char *eol, 119 int rdncount, 120 unsigned long opts, 121 char *base, 122 char *urlprefix 123 ) 124 { 125 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_entry2html\n", 0, 0, 0 ); 126 127 if ( urlprefix == NULL ) { 128 urlprefix = DEF_LDAP_URL_PREFIX; 129 } 130 131 return( do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals, 132 writeproc, writeparm, eol, rdncount, opts, urlprefix )); 133 } 134 135 136 static int 137 do_entry2text( 138 LDAP *ld, 139 char *buf, /* NULL for use-internal */ 140 char *base, /* used for search actions */ 141 LDAPMessage *entry, 142 struct ldap_disptmpl *tmpl, 143 char **defattrs, 144 char ***defvals, 145 writeptype writeproc, 146 void *writeparm, 147 char *eol, 148 int rdncount, 149 unsigned long opts, 150 char *urlprefix /* if non-NULL, do HTML */ 151 ) 152 { 153 int i, err, html, show, labelwidth; 154 int freebuf, freevals; 155 char *dn, **vals; 156 struct ldap_tmplitem *rowp, *colp; 157 158 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { 159 return( LDAP_PARAM_ERROR ); 160 } 161 162 if ( writeproc == NULL || 163 !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { 164 err = LDAP_PARAM_ERROR; 165 LDAP_SET_LDERRNO( ld, err, NULL, NULL ); 166 return( err ); 167 } 168 169 if (( dn = ldap_get_dn( ld, entry )) == NULL ) { 170 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); 171 } 172 173 if ( buf == NULL ) { 174 if (( buf = NSLDAPI_MALLOC( LDAP_DTMPL_BUFSIZ )) == NULL ) { 175 err = LDAP_NO_MEMORY; 176 LDAP_SET_LDERRNO( ld, err, NULL, NULL ); 177 NSLDAPI_FREE( dn ); 178 return( err ); 179 } 180 freebuf = 1; 181 } else { 182 freebuf = 0; 183 } 184 185 html = ( urlprefix != NULL ); 186 187 if ( html ) { 188 /* 189 * add HTML intro. and title 190 */ 191 if (!(( opts & LDAP_DISP_OPT_HTMLBODYONLY ) != 0 )) { 192 sprintf( buf, "<HTML>%s<HEAD>%s<TITLE>%s%s - ", eol, eol, eol, 193 ( tmpl == NULL ) ? "Entry" : tmpl->dt_name ); 194 (*writeproc)( writeparm, buf, strlen( buf )); 195 output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL ); 196 sprintf( buf, "%s</TITLE>%s</HEAD>%s<BODY>%s<H3>%s - ", eol, eol, 197 eol, eol, ( tmpl == NULL ) ? "Entry" : tmpl->dt_name ); 198 (*writeproc)( writeparm, buf, strlen( buf )); 199 output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL ); 200 sprintf( buf, "</H3>%s", eol ); 201 (*writeproc)( writeparm, buf, strlen( buf )); 202 } 203 204 if (( opts & LDAP_DISP_OPT_NONLEAF ) != 0 && 205 ( vals = ldap_explode_dn( dn, 0 )) != NULL ) { 206 char *untagged; 207 208 /* 209 * add "Move Up" link 210 */ 211 sprintf( buf, "<A HREF=\"%s", urlprefix ); 212 for ( i = 1; vals[ i ] != NULL; ++i ) { 213 if ( i > 1 ) { 214 strcat_escaped( buf, ", " ); 215 } 216 strcat_escaped( buf, vals[ i ] ); 217 } 218 if ( vals[ 1 ] != NULL ) { 219 untagged = strchr( vals[ 1 ], '=' ); 220 } else { 221 untagged = "=The World"; 222 } 223 sprintf( buf + strlen( buf ), 224 "%s\">Move Up To <EM>%s</EM></A>%s<BR>", 225 ( vals[ 1 ] == NULL ) ? "??one" : "", 226 ( untagged != NULL ) ? untagged + 1 : vals[ 1 ], eol ); 227 (*writeproc)( writeparm, buf, strlen( buf )); 228 229 /* 230 * add "Browse" link 231 */ 232 untagged = strchr( vals[ 0 ], '=' ); 233 sprintf( buf, "<A HREF=\"%s", urlprefix ); 234 strcat_escaped( buf, dn ); 235 sprintf( buf + strlen( buf ), "??one?(!(objectClass=dsa))\">Browse Below <EM>%s</EM></A>%s%s", 236 ( untagged != NULL ) ? untagged + 1 : vals[ 0 ], eol, eol ); 237 (*writeproc)( writeparm, buf, strlen( buf )); 238 239 ldap_value_free( vals ); 240 } 241 242 (*writeproc)( writeparm, "<HR>", 4 ); /* horizontal rule */ 243 } else { 244 (*writeproc)( writeparm, "\"", 1 ); 245 output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL ); 246 sprintf( buf, "\"%s", eol ); 247 (*writeproc)( writeparm, buf, strlen( buf )); 248 } 249 250 if ( tmpl != NULL && ( opts & LDAP_DISP_OPT_AUTOLABELWIDTH ) != 0 ) { 251 labelwidth = max_label_len( tmpl ) + 3; 252 } else { 253 labelwidth = DEF_LABEL_WIDTH;; 254 } 255 256 err = LDAP_SUCCESS; 257 258 if ( tmpl == NULL ) { 259 BerElement *ber; 260 char *attr; 261 262 ber = NULL; 263 for ( attr = ldap_first_attribute( ld, entry, &ber ); 264 NONFATAL_LDAP_ERR( err ) && attr != NULL; 265 attr = ldap_next_attribute( ld, entry, ber )) { 266 if (( vals = ldap_get_values( ld, entry, attr )) == NULL ) { 267 freevals = 0; 268 if ( defattrs != NULL ) { 269 for ( i = 0; defattrs[ i ] != NULL; ++i ) { 270 if ( strcasecmp( attr, defattrs[ i ] ) == 0 ) { 271 break; 272 } 273 } 274 if ( defattrs[ i ] != NULL ) { 275 vals = defvals[ i ]; 276 } 277 } 278 } else { 279 freevals = 1; 280 } 281 282 if ( islower( *attr )) { /* cosmetic -- upcase attr. name */ 283 *attr = toupper( *attr ); 284 } 285 286 err = do_vals2text( ld, buf, vals, attr, labelwidth, 287 LDAP_SYN_CASEIGNORESTR, writeproc, writeparm, eol, 288 rdncount, urlprefix ); 289 if ( freevals ) { 290 ldap_value_free( vals ); 291 } 292 } 293 if ( ber == NULL ) { 294 ber_free( ber, 0 ); 295 } 296 /* 297 * XXX check for errors in ldap_first_attribute/ldap_next_attribute 298 * here (but what should we do if there was one?) 299 */ 300 301 } else { 302 for ( rowp = ldap_first_tmplrow( tmpl ); 303 NONFATAL_LDAP_ERR( err ) && rowp != NULLTMPLITEM; 304 rowp = ldap_next_tmplrow( tmpl, rowp )) { 305 for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM; 306 colp = ldap_next_tmplcol( tmpl, rowp, colp )) { 307 vals = NULL; 308 if ( colp->ti_attrname == NULL || ( vals = ldap_get_values( ld, 309 entry, colp->ti_attrname )) == NULL ) { 310 freevals = 0; 311 if ( !LDAP_IS_TMPLITEM_OPTION_SET( colp, 312 LDAP_DITEM_OPT_HIDEIFEMPTY ) && defattrs != NULL 313 && colp->ti_attrname != NULL ) { 314 for ( i = 0; defattrs[ i ] != NULL; ++i ) { 315 if ( strcasecmp( colp->ti_attrname, defattrs[ i ] ) 316 == 0 ) { 317 break; 318 } 319 } 320 if ( defattrs[ i ] != NULL ) { 321 vals = defvals[ i ]; 322 } 323 } 324 } else { 325 freevals = 1; 326 if ( LDAP_IS_TMPLITEM_OPTION_SET( colp, 327 LDAP_DITEM_OPT_SORTVALUES ) && vals[ 0 ] != NULL 328 && vals[ 1 ] != NULL ) { 329 ldap_sort_values(ld, vals, ldap_sort_strcasecmp); 330 } 331 } 332 333 /* 334 * don't bother even calling do_vals2text() if no values 335 * or boolean with value false and "hide if false" option set 336 */ 337 show = ( vals != NULL && vals[ 0 ] != NULL ); 338 if ( show && LDAP_GET_SYN_TYPE( colp->ti_syntaxid ) 339 == LDAP_SYN_TYPE_BOOLEAN && LDAP_IS_TMPLITEM_OPTION_SET( 340 colp, LDAP_DITEM_OPT_HIDEIFFALSE ) && 341 toupper( vals[ 0 ][ 0 ] ) != 'T' ) { 342 show = 0; 343 } 344 345 if ( colp->ti_syntaxid == LDAP_SYN_SEARCHACTION ) { 346 if (( opts & LDAP_DISP_OPT_DOSEARCHACTIONS ) != 0 ) { 347 if ( colp->ti_attrname == NULL || ( show && 348 toupper( vals[ 0 ][ 0 ] ) == 'T' )) { 349 err = searchaction( ld, buf, base, entry, dn, colp, 350 labelwidth, rdncount, writeproc, 351 writeparm, eol, urlprefix ); 352 } 353 } 354 show = 0; 355 } 356 357 if ( show ) { 358 err = do_vals2text( ld, buf, vals, colp->ti_label, 359 labelwidth, colp->ti_syntaxid, writeproc, writeparm, 360 eol, rdncount, urlprefix ); 361 } 362 363 if ( freevals ) { 364 ldap_value_free( vals ); 365 } 366 } 367 } 368 } 369 370 if ( html && !(( opts & LDAP_DISP_OPT_HTMLBODYONLY ) != 0 )) { 371 sprintf( buf, "</BODY>%s</HTML>%s", eol, eol ); 372 (*writeproc)( writeparm, buf, strlen( buf )); 373 } 374 375 NSLDAPI_FREE( dn ); 376 if ( freebuf ) { 377 NSLDAPI_FREE( buf ); 378 } 379 380 return( err ); 381 } 382 383 384 int 385 LDAP_CALL 386 ldap_entry2text_search( 387 LDAP *ld, 388 char *dn, /* if NULL, use entry */ 389 char *base, /* if NULL, no search actions */ 390 LDAPMessage *entry, /* if NULL, use dn */ 391 struct ldap_disptmpl* tmpllist, /* if NULL, load default file */ 392 char **defattrs, 393 char ***defvals, 394 writeptype writeproc, 395 void *writeparm, 396 char *eol, 397 int rdncount, /* if 0, display full DN */ 398 unsigned long opts 399 ) 400 { 401 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_entry2text_search\n", 0, 0, 0 ); 402 403 return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs, 404 defvals, writeproc, writeparm, eol, rdncount, opts, NULL )); 405 } 406 407 408 409 int 410 LDAP_CALL 411 ldap_entry2html_search( 412 LDAP *ld, 413 char *dn, /* if NULL, use entry */ 414 char *base, /* if NULL, no search actions */ 415 LDAPMessage *entry, /* if NULL, use dn */ 416 struct ldap_disptmpl* tmpllist, /* if NULL, load default file */ 417 char **defattrs, 418 char ***defvals, 419 writeptype writeproc, 420 void *writeparm, 421 char *eol, 422 int rdncount, /* if 0, display full DN */ 423 unsigned long opts, 424 char *urlprefix 425 ) 426 { 427 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_entry2html_search\n", 0, 0, 0 ); 428 429 return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs, 430 defvals, writeproc, writeparm, eol, rdncount, opts, urlprefix )); 431 } 432 433 434 static int 435 do_entry2text_search( 436 LDAP *ld, 437 char *dn, /* if NULL, use entry */ 438 char *base, /* if NULL, no search actions */ 439 LDAPMessage *entry, /* if NULL, use dn */ 440 struct ldap_disptmpl* tmpllist, /* if NULL, no template used */ 441 char **defattrs, 442 char ***defvals, 443 writeptype writeproc, 444 void *writeparm, 445 char *eol, 446 int rdncount, /* if 0, display full DN */ 447 unsigned long opts, 448 char *urlprefix 449 ) 450 { 451 int err, freedn, html; 452 char *buf, **fetchattrs, **vals; 453 LDAPMessage *ldmp; 454 struct ldap_disptmpl *tmpl; 455 struct timeval timeout; 456 457 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { 458 return( LDAP_PARAM_ERROR ); 459 } 460 461 if ( dn == NULL && entry == NULLMSG ) { 462 err = LDAP_PARAM_ERROR; 463 LDAP_SET_LDERRNO( ld, err, NULL, NULL ); 464 return( err ); 465 } 466 467 html = ( urlprefix != NULL ); 468 469 timeout.tv_sec = SEARCH_TIMEOUT_SECS; 470 timeout.tv_usec = 0; 471 472 if (( buf = NSLDAPI_MALLOC( LDAP_DTMPL_BUFSIZ )) == NULL ) { 473 err = LDAP_NO_MEMORY; 474 LDAP_SET_LDERRNO( ld, err, NULL, NULL ); 475 return( err ); 476 } 477 478 freedn = 0; 479 tmpl = NULL; 480 481 if ( dn == NULL ) { 482 if (( dn = ldap_get_dn( ld, entry )) == NULL ) { 483 NSLDAPI_FREE( buf ); 484 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); 485 } 486 freedn = 1; 487 } 488 489 490 if ( tmpllist != NULL ) { 491 ldmp = NULLMSG; 492 493 if ( entry == NULL ) { 494 char *ocattrs[2]; 495 496 ocattrs[0] = OCATTRNAME; 497 ocattrs[1] = NULL; 498 #ifdef CLDAP 499 if ( LDAP_IS_CLDAP( ld )) 500 err = cldap_search_s( ld, dn, LDAP_SCOPE_BASE, 501 "objectClass=*", ocattrs, 0, &ldmp, NULL ); 502 else 503 #endif /* CLDAP */ 504 err = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, 505 "objectClass=*", ocattrs, 0, &timeout, &ldmp ); 506 507 if ( err == LDAP_SUCCESS ) { 508 entry = ldap_first_entry( ld, ldmp ); 509 } 510 } 511 512 if ( entry != NULL ) { 513 vals = ldap_get_values( ld, entry, OCATTRNAME ); 514 tmpl = ldap_oc2template( vals, tmpllist ); 515 if ( vals != NULL ) { 516 ldap_value_free( vals ); 517 } 518 } 519 if ( ldmp != NULL ) { 520 ldap_msgfree( ldmp ); 521 } 522 } 523 524 entry = NULL; 525 526 if ( tmpl == NULL ) { 527 fetchattrs = NULL; 528 } else { 529 fetchattrs = ldap_tmplattrs( tmpl, NULL, 1, LDAP_SYN_OPT_DEFER ); 530 } 531 532 #ifdef CLDAP 533 if ( LDAP_IS_CLDAP( ld )) 534 err = cldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*", 535 fetchattrs, 0, &ldmp, NULL ); 536 else 537 #endif /* CLDAP */ 538 err = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "objectClass=*", 539 fetchattrs, 0, &timeout, &ldmp ); 540 541 if ( freedn ) { 542 NSLDAPI_FREE( dn ); 543 } 544 if ( fetchattrs != NULL ) { 545 ldap_value_free( fetchattrs ); 546 } 547 548 if ( err != LDAP_SUCCESS || 549 ( entry = ldap_first_entry( ld, ldmp )) == NULL ) { 550 NSLDAPI_FREE( buf ); 551 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); 552 } 553 554 err = do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals, 555 writeproc, writeparm, eol, rdncount, opts, urlprefix ); 556 557 NSLDAPI_FREE( buf ); 558 ldap_msgfree( ldmp ); 559 return( err ); 560 } 561 562 563 int 564 LDAP_CALL 565 ldap_vals2text( 566 LDAP *ld, 567 char *buf, /* NULL for "use internal" */ 568 char **vals, 569 char *label, 570 int labelwidth, /* 0 means use default */ 571 unsigned long syntaxid, 572 writeptype writeproc, 573 void *writeparm, 574 char *eol, 575 int rdncount 576 ) 577 { 578 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_vals2text\n", 0, 0, 0 ); 579 580 return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid, 581 writeproc, writeparm, eol, rdncount, NULL )); 582 } 583 584 585 int 586 LDAP_CALL 587 ldap_vals2html( 588 LDAP *ld, 589 char *buf, /* NULL for "use internal" */ 590 char **vals, 591 char *label, 592 int labelwidth, /* 0 means use default */ 593 unsigned long syntaxid, 594 writeptype writeproc, 595 void *writeparm, 596 char *eol, 597 int rdncount, 598 char *urlprefix 599 ) 600 { 601 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_vals2html\n", 0, 0, 0 ); 602 603 if ( urlprefix == NULL ) { 604 urlprefix = DEF_LDAP_URL_PREFIX; 605 } 606 607 return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid, 608 writeproc, writeparm, eol, rdncount, urlprefix )); 609 } 610 611 612 static int 613 do_vals2text( 614 LDAP *ld, 615 char *buf, /* NULL for "use internal" */ 616 char **vals, 617 char *label, 618 int labelwidth, /* 0 means use default */ 619 unsigned long syntaxid, 620 writeptype writeproc, 621 void *writeparm, 622 char *eol, 623 int rdncount, 624 char *urlprefix 625 ) 626 { 627 int err, i, html, writeoutval, freebuf, notascii; 628 char *p, *s, *outval; 629 630 if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || writeproc == NULL ) { 631 return( LDAP_PARAM_ERROR ); 632 } 633 634 if ( vals == NULL ) { 635 return( LDAP_SUCCESS ); 636 } 637 638 html = ( urlprefix != NULL ); 639 640 switch( LDAP_GET_SYN_TYPE( syntaxid )) { 641 case LDAP_SYN_TYPE_TEXT: 642 case LDAP_SYN_TYPE_BOOLEAN: 643 break; /* we only bother with these two types... */ 644 default: 645 return( LDAP_SUCCESS ); 646 } 647 648 if ( labelwidth == 0 || labelwidth < 0 ) { 649 labelwidth = DEF_LABEL_WIDTH; 650 } 651 652 if ( buf == NULL ) { 653 if (( buf = NSLDAPI_MALLOC( LDAP_DTMPL_BUFSIZ )) == NULL ) { 654 err = LDAP_NO_MEMORY; 655 LDAP_SET_LDERRNO( ld, err, NULL, NULL ); 656 return( err ); 657 } 658 freebuf = 1; 659 } else { 660 freebuf = 0; 661 } 662 663 output_label( buf, label, labelwidth, writeproc, writeparm, eol, html ); 664 665 for ( i = 0; vals[ i ] != NULL; ++i ) { 666 for ( p = vals[ i ]; *p != '\0'; ++p ) { 667 if ( !isascii( *p )) { 668 break; 669 } 670 } 671 notascii = ( *p != '\0' ); 672 outval = notascii ? dgettext(TEXT_DOMAIN, 673 "(unable to display non-ASCII text value)") 674 : vals[ i ]; 675 676 writeoutval = 0; /* if non-zero, write outval after switch */ 677 678 switch( syntaxid ) { 679 case LDAP_SYN_CASEIGNORESTR: 680 ++writeoutval; 681 break; 682 683 case LDAP_SYN_RFC822ADDR: 684 if ( html ) { 685 strcpy( buf, "<DD><A HREF=\"mailto:" ); 686 strcat_escaped( buf, outval ); 687 sprintf( buf + strlen( buf ), "\">%s</A><BR>%s", outval, eol ); 688 (*writeproc)( writeparm, buf, strlen( buf )); 689 } else { 690 ++writeoutval; 691 } 692 break; 693 694 case LDAP_SYN_DN: /* for now */ 695 output_dn( buf, outval, labelwidth, rdncount, writeproc, 696 writeparm, eol, urlprefix ); 697 break; 698 699 case LDAP_SYN_MULTILINESTR: 700 if ( i > 0 && !html ) { 701 output_label( buf, label, labelwidth, writeproc, 702 writeparm, eol, html ); 703 } 704 705 p = s = outval; 706 while (( s = strchr( s, '$' )) != NULL ) { 707 *s++ = '\0'; 708 while ( ldap_utf8isspace( s )) { 709 ++s; 710 } 711 if ( html ) { 712 sprintf( buf, "<DD>%s<BR>%s", p, eol ); 713 } else { 714 sprintf( buf, "%-*s%s%s", labelwidth, " ", p, eol ); 715 } 716 (*writeproc)( writeparm, buf, strlen( buf )); 717 p = s; 718 } 719 outval = p; 720 ++writeoutval; 721 break; 722 723 case LDAP_SYN_BOOLEAN: 724 outval = toupper( outval[ 0 ] ) == 'T' ? 725 dgettext(TEXT_DOMAIN, "TRUE") : dgettext(TEXT_DOMAIN, "FALSE"); 726 ++writeoutval; 727 break; 728 729 case LDAP_SYN_TIME: 730 case LDAP_SYN_DATE: 731 outval = time2text( outval, syntaxid == LDAP_SYN_DATE ); 732 ++writeoutval; 733 break; 734 735 case LDAP_SYN_LABELEDURL: 736 if ( !notascii && ( p = strchr( outval, '$' )) != NULL ) { 737 *p++ = '\0'; 738 while ( ldap_utf8isspace( p )) { 739 ++p; 740 } 741 s = outval; 742 } else if ( !notascii && ( s = strchr( outval, ' ' )) != NULL ) { 743 *s++ = '\0'; 744 while ( ldap_utf8isspace( s )) { 745 ++s; 746 } 747 p = outval; 748 } else { 749 s = "URL"; 750 p = outval; 751 } 752 753 /* 754 * at this point `s' points to the label & `p' to the URL 755 */ 756 if ( html ) { 757 sprintf( buf, "<DD><A HREF=\"%s\">%s</A><BR>%s", p, s, eol ); 758 } else { 759 sprintf( buf, "%-*s%s%s%-*s%s%s", labelwidth, " ", 760 s, eol, labelwidth + 2, " ",p , eol ); 761 } 762 (*writeproc)( writeparm, buf, strlen( buf )); 763 break; 764 765 default: 766 sprintf( buf, dgettext(TEXT_DOMAIN, 767 " Can't display item type %ld%s"), 768 syntaxid, eol ); 769 (*writeproc)( writeparm, buf, strlen( buf )); 770 } 771 772 if ( writeoutval ) { 773 if ( html ) { 774 sprintf( buf, "<DD>%s<BR>%s", outval, eol ); 775 } else { 776 sprintf( buf, "%-*s%s%s", labelwidth, " ", outval, eol ); 777 } 778 (*writeproc)( writeparm, buf, strlen( buf )); 779 } 780 } 781 782 if ( freebuf ) { 783 NSLDAPI_FREE( buf ); 784 } 785 786 return( LDAP_SUCCESS ); 787 } 788 789 790 static int 791 max_label_len( struct ldap_disptmpl *tmpl ) 792 { 793 struct ldap_tmplitem *rowp, *colp; 794 int len, maxlen; 795 796 maxlen = 0; 797 798 for ( rowp = ldap_first_tmplrow( tmpl ); rowp != NULLTMPLITEM; 799 rowp = ldap_next_tmplrow( tmpl, rowp )) { 800 for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM; 801 colp = ldap_next_tmplcol( tmpl, rowp, colp )) { 802 if (( len = strlen( colp->ti_label )) > maxlen ) { 803 maxlen = len; 804 } 805 } 806 } 807 808 return( maxlen ); 809 } 810 811 812 static int 813 output_label( char *buf, char *label, int width, writeptype writeproc, 814 void *writeparm, char *eol, int html ) 815 { 816 char *p; 817 818 if ( html ) { 819 sprintf( buf, "<DT><B>%s</B>", label ); 820 } else { 821 auto size_t w; 822 sprintf( buf, " %s:", label ); 823 p = buf + strlen( buf ); 824 825 for (w = ldap_utf8characters(buf); w < (size_t)width; ++w) { 826 *p++ = ' '; 827 } 828 829 *p = '\0'; 830 strcat( buf, eol ); 831 } 832 833 return ((*writeproc)( writeparm, buf, strlen( buf ))); 834 } 835 836 837 static int 838 output_dn( char *buf, char *dn, int width, int rdncount, 839 writeptype writeproc, void *writeparm, char *eol, char *urlprefix ) 840 { 841 char **dnrdns; 842 int i; 843 844 if (( dnrdns = ldap_explode_dn( dn, 1 )) == NULL ) { 845 return( -1 ); 846 } 847 848 if ( urlprefix != NULL ) { 849 sprintf( buf, "<DD><A HREF=\"%s", urlprefix ); 850 strcat_escaped( buf, dn ); 851 strcat( buf, "\">" ); 852 } else if ( width > 0 ) { 853 sprintf( buf, "%-*s", width, " " ); 854 } else { 855 *buf = '\0'; 856 } 857 858 for ( i = 0; dnrdns[ i ] != NULL && ( rdncount == 0 || i < rdncount ); 859 ++i ) { 860 if ( i > 0 ) { 861 strcat( buf, ", " ); 862 } 863 strcat( buf, dnrdns[ i ] ); 864 } 865 866 if ( urlprefix != NULL ) { 867 strcat( buf, "</A><BR>" ); 868 } 869 870 ldap_value_free( dnrdns ); 871 872 strcat( buf, eol ); 873 874 return ((*writeproc)( writeparm, buf, strlen( buf ))); 875 } 876 877 878 879 #define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \ 880 ( c >= '@' && c <= 'Z' ) || \ 881 ( c == '_' ) || \ 882 ( c >= 'a' && c <= 'z' )) 883 884 static void 885 strcat_escaped( char *s1, char *s2 ) 886 { 887 char *p, *q; 888 char *hexdig = "0123456789ABCDEF"; 889 890 p = s1 + strlen( s1 ); 891 for ( q = s2; *q != '\0'; ++q ) { 892 if ( HREF_CHAR_ACCEPTABLE( *q )) { 893 *p++ = *q; 894 } else { 895 *p++ = '%'; 896 *p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ]; 897 *p++ = hexdig[ 0x0F & *q ]; 898 } 899 } 900 901 *p = '\0'; 902 } 903 904 905 #define GET2BYTENUM( p ) (( *p - '0' ) * 10 + ( *(p+1) - '0' )) 906 907 static char * 908 time2text( char *ldtimestr, int dateonly ) 909 { 910 int len; 911 struct tm t; 912 char *p, *timestr, zone, *fmterr = 913 dgettext(TEXT_DOMAIN, "badly formatted time"); 914 time_t gmttime; 915 /* CTIME for this platform doesn't use this. */ 916 #if !defined(SUNOS4) && !defined(BSDI) && !defined(LINUX1_2) && \ 917 !defined(SNI) && !defined(_WIN32) && !defined(macintosh) && !defined(LINUX) 918 char buf[26]; 919 #endif 920 921 memset( (char *)&t, 0, sizeof( struct tm )); 922 if (( len = (int)strlen( ldtimestr )) < 13 ) { 923 return( fmterr ); 924 } 925 if ( len > 15 ) { /* throw away excess from 4-digit year time string */ 926 len = 15; 927 } else if ( len == 14 ) { 928 len = 13; /* assume we have a time w/2-digit year (len=13) */ 929 } 930 931 for ( p = ldtimestr; p - ldtimestr + 1 < len; ++p ) { 932 if ( !isdigit( *p )) { 933 return( fmterr ); 934 } 935 } 936 937 p = ldtimestr; 938 t.tm_year = GET2BYTENUM( p ); p += 2; 939 if ( len == 15 ) { 940 t.tm_year = 100 * (t.tm_year - 19); 941 t.tm_year += GET2BYTENUM( p ); p += 2; 942 } 943 else { 944 /* 2 digit years...assumed to be in the range (19)70 through 945 (20)69 ...less than 70 (for now, 38) means 20xx */ 946 if(t.tm_year < 70) { 947 t.tm_year += 100; 948 } 949 } 950 t.tm_mon = GET2BYTENUM( p ) - 1; p += 2; 951 t.tm_mday = GET2BYTENUM( p ); p += 2; 952 t.tm_hour = GET2BYTENUM( p ); p += 2; 953 t.tm_min = GET2BYTENUM( p ); p += 2; 954 t.tm_sec = GET2BYTENUM( p ); p += 2; 955 956 if (( zone = *p ) == 'Z' ) { /* GMT */ 957 zone = '\0'; /* no need to indicate on screen, so we make it null */ 958 } 959 960 gmttime = gtime( &t ); 961 timestr = NSLDAPI_CTIME( &gmttime, buf, sizeof(buf) ); 962 963 timestr[ strlen( timestr ) - 1 ] = zone; /* replace trailing newline */ 964 if ( dateonly ) { 965 strcpy( timestr + 11, timestr + 20 ); 966 } 967 968 return( timestr ); 969 } 970 971 972 973 /* gtime.c - inverse gmtime */ 974 975 #if !defined( macintosh ) && !defined( _WINDOWS ) && !defined( DOS ) && !defined(XP_OS2) 976 #include <sys/time.h> 977 #endif /* !macintosh */ 978 979 /* gtime(): the inverse of localtime(). 980 This routine was supplied by Mike Accetta at CMU many years ago. 981 */ 982 983 static int dmsize[] = { 984 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 985 }; 986 987 #define dysize(y) \ 988 (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366))) 989 990 /* 991 #define YEAR(y) ((y) >= 100 ? (y) : (y) + 1900) 992 */ 993 #define YEAR(y) (((y) < 1900) ? ((y) + 1900) : (y)) 994 995 /* */ 996 997 static long gtime ( struct tm *tm ) 998 { 999 register int i, 1000 sec, 1001 mins, 1002 hour, 1003 mday, 1004 mon, 1005 year; 1006 register long result; 1007 1008 if ((sec = tm -> tm_sec) < 0 || sec > 59 1009 || (mins = tm -> tm_min) < 0 || mins > 59 1010 || (hour = tm -> tm_hour) < 0 || hour > 24 1011 || (mday = tm -> tm_mday) < 1 || mday > 31 1012 || (mon = tm -> tm_mon + 1) < 1 || mon > 12) 1013 return ((long) -1); 1014 if (hour == 24) { 1015 hour = 0; 1016 mday++; 1017 } 1018 year = YEAR (tm -> tm_year); 1019 1020 result = 0L; 1021 for (i = 1970; i < year; i++) 1022 result += dysize (i); 1023 if (dysize (year) == 366 && mon >= 3) 1024 result++; 1025 while (--mon) 1026 result += dmsize[mon - 1]; 1027 result += mday - 1; 1028 result = 24 * result + hour; 1029 result = 60 * result + mins; 1030 result = 60 * result + sec; 1031 1032 return result; 1033 } 1034 1035 static int 1036 searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, char *dn, 1037 struct ldap_tmplitem *tip, int labelwidth, int rdncount, 1038 writeptype writeproc, void *writeparm, char *eol, char *urlprefix ) 1039 { 1040 int err = LDAP_SUCCESS, lderr, i, count, html; 1041 char **vals, **members; 1042 char *value, *filtpattern, *attr, *selectname; 1043 char *retattrs[2], filter[ 256 ]; 1044 LDAPMessage *ldmp; 1045 struct timeval timeout; 1046 1047 html = ( urlprefix != NULL ); 1048 1049 for ( i = 0; tip->ti_args != NULL && tip->ti_args[ i ] != NULL; ++i ) { 1050 ; 1051 } 1052 if ( i < 3 ) { 1053 return( LDAP_PARAM_ERROR ); 1054 } 1055 attr = tip->ti_args[ 0 ]; 1056 filtpattern = tip->ti_args[ 1 ]; 1057 retattrs[ 0 ] = tip->ti_args[ 2 ]; 1058 retattrs[ 1 ] = NULL; 1059 selectname = tip->ti_args[ 3 ]; 1060 1061 vals = NULL; 1062 if ( attr == NULL ) { 1063 value = NULL; 1064 } else if ( strcasecmp( attr, "-dnb" ) == 0 ) { 1065 return( LDAP_PARAM_ERROR ); 1066 } else if ( strcasecmp( attr, "-dnt" ) == 0 ) { 1067 value = dn; 1068 } else if (( vals = ldap_get_values( ld, entry, attr )) != NULL ) { 1069 value = vals[ 0 ]; 1070 } else { 1071 value = NULL; 1072 } 1073 1074 ldap_build_filter( filter, sizeof( filter ), filtpattern, NULL, NULL, NULL, 1075 value, NULL ); 1076 1077 if ( html ) { 1078 /* 1079 * if we are generating HTML, we add an HREF link that embodies this 1080 * search action as an LDAP URL, instead of actually doing the search 1081 * now. 1082 */ 1083 sprintf( buf, "<DT><A HREF=\"%s", urlprefix ); 1084 if ( base != NULL ) { 1085 strcat_escaped( buf, base ); 1086 } 1087 strcat( buf, "??sub?" ); 1088 strcat_escaped( buf, filter ); 1089 sprintf( buf + strlen( buf ), "\"><B>%s</B></A><DD><BR>%s", 1090 tip->ti_label, eol ); 1091 if ((*writeproc)( writeparm, buf, strlen( buf )) < 0 ) { 1092 return( LDAP_LOCAL_ERROR ); 1093 } 1094 return( LDAP_SUCCESS ); 1095 } 1096 1097 timeout.tv_sec = SEARCH_TIMEOUT_SECS; 1098 timeout.tv_usec = 0; 1099 1100 #ifdef CLDAP 1101 if ( LDAP_IS_CLDAP( ld )) 1102 lderr = cldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs, 1103 0, &ldmp, NULL ); 1104 else 1105 #endif /* CLDAP */ 1106 lderr = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE, filter, 1107 retattrs, 0, &timeout, &ldmp ); 1108 1109 if ( lderr == LDAP_SUCCESS || NONFATAL_LDAP_ERR( lderr )) { 1110 if (( count = ldap_count_entries( ld, ldmp )) > 0 ) { 1111 if (( members = (char **)NSLDAPI_MALLOC( (count + 1) 1112 * sizeof(char *))) == NULL ) { 1113 err = LDAP_NO_MEMORY; 1114 } else { 1115 for ( i = 0, entry = ldap_first_entry( ld, ldmp ); 1116 entry != NULL; 1117 entry = ldap_next_entry( ld, entry ), ++i ) { 1118 members[ i ] = ldap_get_dn( ld, entry ); 1119 } 1120 members[ i ] = NULL; 1121 1122 ldap_sort_values(ld,members, ldap_sort_strcasecmp); 1123 1124 err = do_vals2text( ld, NULL, members, tip->ti_label, 1125 html ? -1 : 0, LDAP_SYN_DN, writeproc, writeparm, 1126 eol, rdncount, urlprefix ); 1127 1128 ldap_value_free( members ); 1129 } 1130 } 1131 ldap_msgfree( ldmp ); 1132 } 1133 1134 1135 if ( vals != NULL ) { 1136 ldap_value_free( vals ); 1137 } 1138 1139 return(( err == LDAP_SUCCESS ) ? lderr : err ); 1140 } 1141