1 /*        $NetBSD: dict_inline.c,v 1.5 2025/02/25 19:15:51 christos Exp $       */
2 
3 /*++
4 /* NAME
5 /*        dict_inline 3
6 /* SUMMARY
7 /*        dictionary manager interface for inline table
8 /* SYNOPSIS
9 /*        #include <dict_inline.h>
10 /*
11 /*        DICT      *dict_inline_open(name, open_flags, dict_flags)
12 /*        const char *name;
13 /*        int       open_flags;
14 /*        int       dict_flags;
15 /* DESCRIPTION
16 /*        dict_inline_open() opens a read-only, in-memory table.
17 /*        Example: "\fBinline:{\fIkey_1=value_1, ..., key_n=value_n\fR}".
18 /*        The longer form with { key = value } allows values that
19 /*        contain whitespace or comma.
20 /* SEE ALSO
21 /*        dict(3) generic dictionary manager
22 /* LICENSE
23 /* .ad
24 /* .fi
25 /*        The Secure Mailer license must be distributed with this software.
26 /* AUTHOR(S)
27 /*        Wietse Venema
28 /*        IBM T.J. Watson Research
29 /*        P.O. Box 704
30 /*        Yorktown Heights, NY 10598, USA
31 /*
32 /*        Wietse Venema
33 /*        Google, Inc.
34 /*        111 8th Avenue
35 /*        New York, NY 10011, USA
36 /*--*/
37 
38 /* System library. */
39 
40 #include <sys_defs.h>
41 #include <string.h>
42 
43 /* Utility library. */
44 
45 #include <msg.h>
46 #include <mymalloc.h>
47 #include <stringops.h>
48 #include <dict.h>
49 #include <dict_ht.h>
50 #include <dict_inline.h>
51 
52 /* Application-specific. */
53 
54 /* dict_inline_open - open inline table */
55 
dict_inline_open(const char * name,int open_flags,int dict_flags)56 DICT   *dict_inline_open(const char *name, int open_flags, int dict_flags)
57 {
58     DICT   *dict;
59     char   *cp, *saved_name = 0;
60     size_t  len;
61     char   *nameval, *vname, *value;
62     const char *err = 0;
63     char   *free_me = 0;
64     int     count = 0;
65 
66     /*
67      * Clarity first. Let the optimizer worry about redundant code.
68      */
69 #define DICT_INLINE_RETURN(x) do { \
70               DICT *__d = (x); \
71               if (saved_name != 0) \
72                     myfree(saved_name); \
73               if (free_me != 0) \
74                     myfree(free_me); \
75               return (__d); \
76           } while (0)
77 
78     /*
79      * Sanity checks.
80      */
81     if (open_flags != O_RDONLY)
82           DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
83                                                     open_flags, dict_flags,
84                                           "%s:%s map requires O_RDONLY access mode",
85                                                     DICT_TYPE_INLINE, name));
86 
87     /*
88      * UTF-8 syntax check.
89      */
90     if (DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)
91           && allascii(name) == 0
92           && valid_utf8_stringz(name) == 0)
93           DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
94                                                     open_flags, dict_flags,
95                                                     "bad UTF-8 syntax: \"%s:%s\"; "
96                                                     "need \"%s:{name=value...}\"",
97                                                     DICT_TYPE_INLINE, name,
98                                                     DICT_TYPE_INLINE));
99 
100     /*
101      * Parse the table into its constituent name=value pairs.
102      */
103     if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0
104           || *(cp = saved_name = mystrndup(name + 1, len - 2)) == 0)
105           DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
106                                                     open_flags, dict_flags,
107                                                     "bad syntax: \"%s:%s\"; "
108                                                     "need \"%s:{name=value...}\"",
109                                                     DICT_TYPE_INLINE, name,
110                                                     DICT_TYPE_INLINE));
111 
112     /*
113      * Reuse the "internal" dictionary type.
114      */
115     dict = dict_open3(DICT_TYPE_HT, name, open_flags, dict_flags);
116     dict_type_override(dict, DICT_TYPE_INLINE);
117     while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
118           if (nameval[0] == CHARS_BRACE[0])
119               err = free_me = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_STRIP);
120           if (err != 0 || (err = split_qnameval(nameval, &vname, &value)) != 0)
121               break;
122 
123           if ((dict->flags & DICT_FLAG_SRC_RHS_IS_FILE) != 0) {
124               VSTRING *base64_buf;
125 
126               if ((base64_buf = dict_file_to_b64(dict, value)) == 0) {
127                     err = free_me = dict_file_get_error(dict);
128                     break;
129               }
130               value = vstring_str(base64_buf);
131           }
132           /* No duplicate checks. See comments in dict_thash.c. */
133           dict->update(dict, vname, value);
134           count += 1;
135     }
136     if (err != 0 || count == 0) {
137           dict->close(dict);
138           DICT_INLINE_RETURN(dict_surrogate(DICT_TYPE_INLINE, name,
139                                                     open_flags, dict_flags,
140                                                     "%s: \"%s:%s\"; "
141                                                     "need \"%s:{name=%s...}\"",
142                                                     err != 0 ? err : "empty table",
143                                                     DICT_TYPE_INLINE, name,
144                                                     DICT_TYPE_INLINE,
145                                           (dict_flags & DICT_FLAG_SRC_RHS_IS_FILE) ?
146                                                     "filename" : "value"));
147     }
148     dict->owner.status = DICT_OWNER_TRUSTED;
149 
150     dict_file_purge_buffers(dict);
151     DICT_INLINE_RETURN(DICT_DEBUG (dict));
152 }
153