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