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