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