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