1/*++
2/* NAME
3/*	attr_print0 3
4/* SUMMARY
5/*	send attributes over byte stream
6/* SYNOPSIS
7/*	#include <attr.h>
8/*
9/*	int	attr_print0(fp, flags, type, name, ..., ATTR_TYPE_END)
10/*	VSTREAM	fp;
11/*	int	flags;
12/*	int	type;
13/*	char	*name;
14/*
15/*	int	attr_vprint0(fp, flags, ap)
16/*	VSTREAM	fp;
17/*	int	flags;
18/*	va_list	ap;
19/* DESCRIPTION
20/*	attr_print0() takes zero or more (name, value) simple attributes
21/*	and converts its input to a byte stream that can be recovered with
22/*	attr_scan0(). The stream is not flushed.
23/*
24/*	attr_vprint0() provides an alternate interface that is convenient
25/*	for calling from within variadic functions.
26/*
27/*	Attributes are sent in the requested order as specified with the
28/*	attr_print0() argument list. This routine satisfies the formatting
29/*	rules as outlined in attr_scan0(3).
30/*
31/*	Arguments:
32/* .IP fp
33/*	Stream to write the result to.
34/* .IP flags
35/*	The bit-wise OR of zero or more of the following.
36/* .RS
37/* .IP ATTR_FLAG_MORE
38/*	After sending the requested attributes, leave the output stream in
39/*	a state that is usable for more attribute sending operations on
40/*	the same output attribute list.
41/*	By default, attr_print0() automatically appends an attribute list
42/*	terminator when it has sent the last requested attribute.
43/* .RE
44/* .IP type
45/*	The type determines the arguments that follow.
46/* .RS
47/* .IP "ATTR_TYPE_INT (char *, int)"
48/*	This argument is followed by an attribute name and an integer.
49/* .IP "ATTR_TYPE_LONG (char *, long)"
50/*	This argument is followed by an attribute name and a long integer.
51/* .IP "ATTR_TYPE_STR (char *, char *)"
52/*	This argument is followed by an attribute name and a null-terminated
53/*	string.
54/* .IP "ATTR_TYPE_DATA (char *, ssize_t, char *)"
55/*	This argument is followed by an attribute name, an attribute value
56/*	length, and an attribute value pointer.
57/* .IP "ATTR_TYPE_FUNC (ATTR_PRINT_SLAVE_FN, void *)"
58/*	This argument is followed by a function pointer and generic data
59/*	pointer. The caller-specified function returns whatever the
60/*	specified attribute printing function returns.
61/* .IP "ATTR_TYPE_HASH (HTABLE *)"
62/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
63/*	The content of the table is sent as a sequence of string-valued
64/*	attributes with names equal to the table lookup keys.
65/* .IP ATTR_TYPE_END
66/*	This terminates the attribute list.
67/* .RE
68/* DIAGNOSTICS
69/*	The result value is 0 in case of success, VSTREAM_EOF in case
70/*	of trouble.
71/*
72/*	Panic: interface violation. All system call errors are fatal.
73/* SEE ALSO
74/*	attr_scan0(3) recover attributes from byte stream
75/* LICENSE
76/* .ad
77/* .fi
78/*	The Secure Mailer license must be distributed with this software.
79/* AUTHOR(S)
80/*	Wietse Venema
81/*	IBM T.J. Watson Research
82/*	P.O. Box 704
83/*	Yorktown Heights, NY 10598, USA
84/*--*/
85
86/* System library. */
87
88#include <sys_defs.h>
89#include <stdarg.h>
90#include <string.h>
91
92/* Utility library. */
93
94#include <msg.h>
95#include <mymalloc.h>
96#include <vstream.h>
97#include <htable.h>
98#include <attr.h>
99#include <base64_code.h>
100
101#define STR(x) vstring_str(x)
102#define LEN(x) VSTRING_LEN(x)
103
104/* attr_vprint0 - send attribute list to stream */
105
106int     attr_vprint0(VSTREAM *fp, int flags, va_list ap)
107{
108    const char *myname = "attr_print0";
109    int     attr_type;
110    char   *attr_name;
111    unsigned int_val;
112    unsigned long long_val;
113    char   *str_val;
114    HTABLE_INFO **ht_info_list;
115    HTABLE_INFO **ht;
116    ssize_t len_val;
117    static VSTRING *base64_buf;
118    ATTR_PRINT_SLAVE_FN print_fn;
119    void   *print_arg;
120
121    /*
122     * Sanity check.
123     */
124    if (flags & ~ATTR_FLAG_ALL)
125	msg_panic("%s: bad flags: 0x%x", myname, flags);
126
127    /*
128     * Iterate over all (type, name, value) triples, and produce output on
129     * the fly.
130     */
131    while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) {
132	switch (attr_type) {
133	case ATTR_TYPE_INT:
134	    attr_name = va_arg(ap, char *);
135	    vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
136	    int_val = va_arg(ap, int);
137	    vstream_fprintf(fp, "%u", (unsigned) int_val);
138	    VSTREAM_PUTC('\0', fp);
139	    if (msg_verbose)
140		msg_info("send attr %s = %u", attr_name, int_val);
141	    break;
142	case ATTR_TYPE_LONG:
143	    attr_name = va_arg(ap, char *);
144	    vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
145	    long_val = va_arg(ap, unsigned long);
146	    vstream_fprintf(fp, "%lu", (unsigned long) long_val);
147	    VSTREAM_PUTC('\0', fp);
148	    if (msg_verbose)
149		msg_info("send attr %s = %lu", attr_name, long_val);
150	    break;
151	case ATTR_TYPE_STR:
152	    attr_name = va_arg(ap, char *);
153	    vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
154	    str_val = va_arg(ap, char *);
155	    vstream_fwrite(fp, str_val, strlen(str_val) + 1);
156	    if (msg_verbose)
157		msg_info("send attr %s = %s", attr_name, str_val);
158	    break;
159	case ATTR_TYPE_DATA:
160	    attr_name = va_arg(ap, char *);
161	    vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
162	    len_val = va_arg(ap, ssize_t);
163	    str_val = va_arg(ap, char *);
164	    if (base64_buf == 0)
165		base64_buf = vstring_alloc(10);
166	    base64_encode(base64_buf, str_val, len_val);
167	    vstream_fwrite(fp, STR(base64_buf), LEN(base64_buf) + 1);
168	    if (msg_verbose)
169		msg_info("send attr %s = [data %ld bytes]",
170			 attr_name, (long) len_val);
171	    break;
172	case ATTR_TYPE_FUNC:
173	    print_fn = va_arg(ap, ATTR_PRINT_SLAVE_FN);
174	    print_arg = va_arg(ap, void *);
175	    print_fn(attr_print0, fp, flags | ATTR_FLAG_MORE, print_arg);
176	    break;
177	case ATTR_TYPE_HASH:
178	    ht_info_list = htable_list(va_arg(ap, HTABLE *));
179	    for (ht = ht_info_list; *ht; ht++) {
180		vstream_fwrite(fp, ht[0]->key, strlen(ht[0]->key) + 1);
181		vstream_fwrite(fp, ht[0]->value, strlen(ht[0]->value) + 1);
182		if (msg_verbose)
183		    msg_info("send attr name %s value %s",
184			     ht[0]->key, ht[0]->value);
185	    }
186	    myfree((char *) ht_info_list);
187	    break;
188	default:
189	    msg_panic("%s: unknown type code: %d", myname, attr_type);
190	}
191    }
192    if ((flags & ATTR_FLAG_MORE) == 0)
193	VSTREAM_PUTC('\0', fp);
194    return (vstream_ferror(fp));
195}
196
197int     attr_print0(VSTREAM *fp, int flags,...)
198{
199    va_list ap;
200    int     ret;
201
202    va_start(ap, flags);
203    ret = attr_vprint0(fp, flags, ap);
204    va_end(ap);
205    return (ret);
206}
207
208#ifdef TEST
209
210 /*
211  * Proof of concept test program.  Mirror image of the attr_scan0 test
212  * program.
213  */
214#include <msg_vstream.h>
215
216int     main(int unused_argc, char **argv)
217{
218    HTABLE *table = htable_create(1);
219
220    msg_vstream_init(argv[0], VSTREAM_ERR);
221    msg_verbose = 1;
222    htable_enter(table, "foo-name", mystrdup("foo-value"));
223    htable_enter(table, "bar-name", mystrdup("bar-value"));
224    attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE,
225		ATTR_TYPE_INT, ATTR_NAME_INT, 4711,
226		ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234L,
227		ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
228		ATTR_TYPE_DATA, ATTR_NAME_DATA, strlen("whoopee"), "whoopee",
229		ATTR_TYPE_HASH, table,
230		ATTR_TYPE_END);
231    attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE,
232		ATTR_TYPE_INT, ATTR_NAME_INT, 4711,
233		ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234L,
234		ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
235		ATTR_TYPE_DATA, ATTR_NAME_DATA, strlen("whoopee"), "whoopee",
236		ATTR_TYPE_END);
237    if (vstream_fflush(VSTREAM_OUT) != 0)
238	msg_fatal("write error: %m");
239
240    htable_free(table, myfree);
241    return (0);
242}
243
244#endif
245