source: libabac/abac_keyid_map.c @ 7764378

abac0-leak
Last change on this file since 7764378 was 65e3c6b, checked in by Mei <mei@…>, 11 years ago

1) fixed an off by one bug in expand_key

  • Property mode set to 100644
File size: 8.1 KB
Line 
1/* abac_keyid_map.c */
2
3
4#include "abac.h"
5#include "abac_util.h"
6#include "uthash.h"
7
8/*
9 * A mapping entry that maps key to valus (both char *s).  These can be hashed
10 * because of the UT_hash_handle - see uthash.h.
11 */
12struct abac_keyid_mapping_t {
13    char *key;
14    char *value;
15    UT_hash_handle hh;
16};
17
18/*
19 * A map from keyids -> nicknames and nicknames to keys.  These are managed by
20 * the libabac reference counting system, hence the refcount
21 */
22struct abac_keyid_map_t {
23    abac_keyid_mapping_t *keys;         /* Key to nickname map */
24    abac_keyid_mapping_t *nicknames;    /* Nickname to key map */
25    int refcount;
26};
27
28/*
29 * Create a new mapping entry from key(k) to value (v).  It must be freed using
30 * abac_keyid_mapping_free.
31 */
32abac_keyid_mapping_t *abac_keyid_mapping_new(char *k, char *v) {
33    abac_keyid_mapping_t *m = NULL;
34
35    if ( !k || ! v) return NULL;
36    m = abac_xmalloc(sizeof(abac_keyid_mapping_t));
37    m->key = abac_xstrdup(k);
38    m->value = abac_xstrdup(v);
39
40    return m;
41}
42
43/*
44 * Free the given mapping.  These are not reference counted, so free the key
45 * and value, and then the mapping memory
46 */
47void abac_keyid_mapping_free(abac_keyid_mapping_t *m) {
48    if ( m->key ) free(m->key);
49    if ( m->value ) free(m->value);
50    free(m);
51}
52
53/*
54 * Create a new keyid map.  These are reference counted and must be freed using
55 * abac_keyid_map_free.
56 */
57abac_keyid_map_t *abac_keyid_map_new() {
58    abac_keyid_map_t *m = abac_xmalloc(sizeof(abac_keyid_map_t));
59
60    m->keys = NULL;
61    m->nicknames = NULL;
62    m->refcount = 1;
63    return m;
64}
65
66/*
67 * Make a new, independent copy of the old keymap. This allocates new memory
68 * with a new reference count.  abac_keyid_map_free must be called on it.
69 */
70abac_keyid_map_t *abac_keyid_map_clone(abac_keyid_map_t *old) {
71    abac_keyid_map_t *m = abac_xmalloc(sizeof(abac_keyid_map_t));
72    abac_keyid_mapping_t *me = NULL;
73
74    m->keys = NULL;
75    m->nicknames = NULL;
76    m->refcount = 1;
77
78    for ( me = old->keys; me ; me = me->hh.next) 
79        abac_keyid_map_add_nickname(m, me->key, me->value);
80
81    return m;
82}
83
84/*
85 * Add a reference to the old map and return it.  The reference count has been
86 * incremented, so the underlying memory will not be freed until
87 * abac_keyid_map_free is called once for each reference.
88 */
89abac_keyid_map_t *abac_keyid_map_dup(abac_keyid_map_t *old) {
90    old->refcount ++;
91    return old;
92}
93
94/*
95 * Free the reference-counted map m.  Decrement the reference count.  If and
96 * only if the reference count is 0 or less, delete all the associated memory.
97 */
98void abac_keyid_map_free(abac_keyid_map_t *m) {
99    abac_keyid_mapping_t *me = NULL;
100
101    if ( --m->refcount > 0) return;
102
103    while ( (me = m->keys) ) {
104        HASH_DEL(m->keys, me);
105        abac_keyid_mapping_free(me);
106    }
107    while ( (me = m->nicknames) ) {
108        HASH_DEL(m->nicknames, me);
109        abac_keyid_mapping_free(me);
110    }
111    free(m);
112}
113
114/*
115 * Return the nickname associated with this keyid, if nay.  The caller is
116 * responsible for freeing the returned string.
117 */
118char *abac_keyid_map_key_to_nickname(abac_keyid_map_t *m, char *key) {
119    abac_keyid_mapping_t *me = NULL;
120
121    if ( !key || !m) return 0;
122    HASH_FIND_STR(m->keys, key, me);
123
124    if ( me ) return abac_xstrdup(me->value);
125    else return NULL;
126}
127
128/*
129 * Return the keyid associated with this nickname, if nay.  The caller is
130 * responsible for freeing the returned string.
131 */
132char *abac_keyid_map_nickname_to_key(abac_keyid_map_t *m, char *nick) {
133    abac_keyid_mapping_t *me = NULL;
134
135    if ( !nick || !m ) return 0;
136    HASH_FIND_STR(m->nicknames, nick, me);
137
138    if ( me ) return abac_xstrdup(me->value);
139    else return NULL;
140}
141
142/*
143 * Remove this keyid from both mappings.
144 */
145int abac_keyid_map_remove_keyid(abac_keyid_map_t *m, char *key) {
146    abac_keyid_mapping_t *me = NULL;
147    abac_keyid_mapping_t *nne = NULL;
148
149    HASH_FIND_STR(m->keys, key, me);
150    if ( !me ) return 0;
151    HASH_FIND_STR(m->nicknames, me->value, nne);
152    /* delete from keys */
153    HASH_DEL(m->keys, me);
154    abac_keyid_mapping_free(me);
155    /* If we found a nickname, delete that too */
156    if ( nne ) { 
157        HASH_DEL(m->nicknames, nne);
158        abac_keyid_mapping_free(nne);
159    }
160    return 1;
161}
162
163/*
164 * If this keyid is not mapped to a nickname, add a mapping from key to
165 * nickname.  If the nickname is already assigned to a key, disambiguate it by
166 * adding trailing numbers.  If more than 1000 tries are made to disambiguate,
167 * give up.
168 */
169int abac_keyid_map_add_nickname(abac_keyid_map_t *m, char *key, char *nick) {
170    abac_keyid_mapping_t *me = NULL;
171    char *name = NULL;
172    char *p = NULL;
173    int i =0;
174
175    if ( !key || !nick) return 0;
176    if ( (p = abac_keyid_map_key_to_nickname(m, key))) {
177        free(p);
178        return 0;
179    }
180
181    if ( !(name = abac_xmalloc(strlen(nick)+10))) return 0;
182    sprintf(name, "%s", nick);
183
184    while (abac_keyid_map_nickname_to_key(m, name) && i < 1000) 
185        sprintf(name, "%s%05d", nick, i++);
186
187    if ( i < 1000 ) {
188        me = abac_keyid_mapping_new(key, name);
189        HASH_ADD_KEYPTR(hh, m->keys, me->key, strlen(me->key), me);
190        me = abac_keyid_mapping_new(name, key);
191        HASH_ADD_KEYPTR(hh, m->nicknames, me->key, strlen(me->key), me);
192    }
193    free(name);
194    return (i < 1000);
195}
196
197/*
198 * Merge the mappings in src to the mappings in dest.  If overwrite is true,
199 * src mappings always overwrite dest mappings, otherwise the dest mappings
200 * remain.  Calls abac_keyid_map_add_nickname internally, so nicknames in src
201 * that are also in dest are disambiguated.
202 */
203void abac_keyid_map_merge(abac_keyid_map_t *dest, abac_keyid_map_t *src, 
204        int overwrite) {
205    abac_keyid_mapping_t *me = NULL;
206    if ( !dest || !src ) return;
207
208    for (me = src->keys; me; me = me->hh.next) {
209        char *n = abac_keyid_map_key_to_nickname(dest, me->key);
210        if ( n ) {
211            free(n);
212            if ( overwrite) {
213                abac_keyid_map_remove_keyid(dest,me->key);
214                abac_keyid_map_add_nickname(dest, me->key, me->value);
215            }
216        } else {
217            abac_keyid_map_add_nickname(dest, me->key, me->value);
218        }
219    }
220}
221
222/*
223 * Utility function to identify separator characters in a role.
224 */
225static int is_sep(char *c) {
226    switch (*c) {
227        case ' ':
228        case '.':
229            return 1;
230        default:
231            return 0;
232    }
233}
234
235/*
236 * Break s into sections by separator characters (see is_sep) and copy them
237 * into a return value.  If lookup finds a replacement for a section, the
238 * replacement is used, otherwise the original string is used.  Using different
239 * lookup functions implements expand_key and expand_name.  In either case, the
240 * caller is responsible for freeing the returned value.
241 */
242static char *abac_keyid_map_replace(abac_keyid_map_t *m, char *s, 
243        char *(*lookup)(abac_keyid_map_t *, char *)) {
244    int lim = strlen(s);
245    int i; 
246    int sz = 0;
247    char *rv = NULL;
248    char *start = s;
249    char old = '\0';
250    char *repl = NULL;
251    char *newrv = NULL;
252    int newlen = 0;
253
254    for ( i = 0; i < lim; i++ ) {
255        if ( is_sep(s+i) ) {
256            old = s[i];
257            s[i] = '\0';
258           
259            if ( !(repl = lookup(m, start)) ) repl = start;
260
261            newlen = strlen(repl)+1;
262
263            if ( !(newrv = abac_xrealloc(rv, sz + newlen +1)) ) {
264                if ( rv ) free(rv);
265                return NULL;
266            } else {
267                rv = newrv;
268                if (sz == 0 ) 
269                    rv[0] = '\0';
270                sz += newlen;
271            }
272            strncat(rv, repl, newlen);
273            rv[sz-1] = old;
274            rv[sz] = '\0';
275            s[i] = old;
276            start = s+i+1;
277        }
278    }
279    if ( start != s+i ) {
280        if ( !(repl = lookup(m, start)) ) repl = start;
281
282        newlen = strlen(repl);
283
284        if ( !(newrv = abac_xrealloc(rv, sz + newlen +1)) ) {
285            if ( rv ) free(rv);
286            return NULL;
287        } else {
288            rv = newrv;
289            if ( sz == 0) 
290                rv[0] = '\0';
291            sz += newlen;
292        }
293        strncat(rv, repl, newlen);
294    }
295    return rv;
296}
297
298/*
299 * Break s up into sections and replace any keyids that have nicknames with
300 * those nicknames.  The caller is responsible for freeing the return value.
301 */
302char *abac_keyid_map_expand_key(abac_keyid_map_t *m, char *s) {
303    return abac_keyid_map_replace(m , s, abac_keyid_map_key_to_nickname);
304}
305
306/*
307 * Break s up into sections and replace any nicknames that have keyids with
308 * those keyids.  The caller is responsible for freeing the return value.
309 */
310char *abac_keyid_map_expand_nickname(abac_keyid_map_t *m, char *s) {
311    return abac_keyid_map_replace(m , s, abac_keyid_map_nickname_to_key);
312}
Note: See TracBrowser for help on using the repository browser.