1 /* spmfilter - mail filtering framework
2  * Copyright (C) 2009-2012 Axel Steiner and SpaceNet AG
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 3 of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #define THIS_MODULE "dict"
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <assert.h>
25 
26 #include "smf_internal.h"
27 #include "smf_dict.h"
28 #include "smf_list.h"
29 
30 #define _set_ptr(ptr, val) if (ptr != NULL) { *ptr = val; }
31 
32 /* Doubles the allocated size associated to a pointer */
33 /* 'size' is the current allocated size. */
_mem_double(void * ptr,int size)34 static void *_mem_double(void * ptr, int size) {
35     void * newptr ;
36 
37     newptr = calloc(2*size, 1);
38     if (newptr==NULL) {
39         return NULL ;
40     }
41     memcpy(newptr, ptr, size);
42     free(ptr);
43     return newptr ;
44 }
45 
_dict_hash(const char * key)46 unsigned _dict_hash(const char * key) {
47     int len;
48     unsigned hash;
49     int i;
50 
51     len = strlen(key);
52     for (hash=0, i=0 ; i<len ; i++) {
53         hash += (unsigned)key[i] ;
54         hash += (hash<<10);
55         hash ^= (hash>>6) ;
56     }
57     hash += (hash <<3);
58     hash ^= (hash >>11);
59     hash += (hash <<15);
60     return hash;
61 }
62 
smf_dict_new(void)63 SMFDict_T *smf_dict_new(void) {
64     SMFDict_T *dict = NULL;
65 
66     if (!(dict = (SMFDict_T *)calloc(1, sizeof(SMFDict_T))))
67         return NULL;
68 
69     dict->size = 128;
70     dict->val  = (char **)calloc(128, sizeof(char*));
71     dict->key  = (char **)calloc(128, sizeof(char*));
72     dict->hash = (unsigned int *)calloc(128, sizeof(unsigned));
73     return dict;
74 }
75 
smf_dict_free(SMFDict_T * dict)76 void smf_dict_free(SMFDict_T *dict) {
77     int i ;
78 
79     assert(dict);
80 
81     for (i=0; i<dict->size; i++) {
82         if (dict->key[i] != NULL)
83             free(dict->key[i]);
84         if (dict->val[i] != NULL)
85             free(dict->val[i]);
86     }
87 
88     free(dict->val);
89     free(dict->key);
90     free(dict->hash);
91     free(dict);
92 }
93 
smf_dict_set(SMFDict_T * dict,const char * key,const char * val)94 int smf_dict_set(SMFDict_T *dict, const char * key, const char * val) {
95     int i;
96     unsigned hash;
97 
98     assert(dict);
99     assert(key);
100     assert(val);
101 
102     /* Compute hash for this key */
103     hash = _dict_hash(key);
104 
105     /* Find if value is already in dictionary */
106     if (dict->n>0) {
107         for (i = 0; i < dict->size; i++) {
108             if (dict->key[i] == NULL)
109                 continue;
110             if (hash == dict->hash[i]) { /* Same hash value */
111                 if (!strcmp(key, dict->key[i])) {   /* Same key */
112                     /* Found a value: modify and return */
113                     if (dict->val[i]!=NULL)
114                         free(dict->val[i]);
115                     dict->val[i] = val ? strdup(val) : NULL;
116                     /* Value has been modified: return */
117                     return 0;
118                 }
119             }
120         }
121     }
122 
123     /* Add a new value */
124     /* See if dictionary needs to grow */
125     if (dict->n == dict->size) {
126         /* Reached maximum size: reallocate dictionary */
127         dict->val = (char **)_mem_double(dict->val,  dict->size * sizeof(char*));
128         dict->key = (char **)_mem_double(dict->key,  dict->size * sizeof(char*)) ;
129         dict->hash = (unsigned int *)_mem_double(dict->hash, dict->size * sizeof(unsigned)) ;
130         if ((dict->val == NULL) || (dict->key == NULL) || (dict->hash == NULL)) {
131             /* Cannot grow dictionary */
132             return -1;
133         }
134         /* Double size */
135         dict->size *= 2 ;
136     }
137 
138     /* Insert key in the first empty slot. Start at d->n and wrap at
139        d->size. Because d->n < d->size this will necessarily
140        terminate. */
141     for (i = dict->n; dict->key[i]; ) {
142         if(++i == dict->size) i = 0;
143     }
144     /* Copy key */
145     dict->key[i]  = strdup(key);
146     dict->val[i]  = val ? strdup(val) : NULL ;
147     dict->hash[i] = hash;
148     dict->n ++;
149     return 0;
150 }
151 
smf_dict_get(SMFDict_T * dict,const char * key)152 char *smf_dict_get(SMFDict_T *dict, const char * key) {
153     unsigned hash;
154     int i;
155 
156     assert(dict);
157     assert(key);
158 
159     hash = _dict_hash(key);
160     for (i = 0; i < dict->size; i++) {
161         if (dict->key[i] == NULL)
162             continue ;
163         /* Compare hash */
164         if (hash == dict->hash[i]) {
165             /* Compare string, to avoid hash collisions */
166             if (!strcmp(key, dict->key[i])) {
167                 return dict->val[i] ;
168             }
169         }
170     }
171     return NULL;
172 }
173 
smf_dict_get_ulong(SMFDict_T * dict,const char * key,int * success)174 unsigned long smf_dict_get_ulong(SMFDict_T *dict, const char * key, int *success) {
175     char *value;
176     char *endptr;
177     unsigned long lval;
178 
179     assert(dict);
180     assert(key);
181 
182     if ((value = smf_dict_get(dict, key)) == NULL) {
183         _set_ptr(success, 0);
184         return -1;
185     }
186 
187     lval = strtoul(value, &endptr, 10);
188 
189     if (endptr == value || *endptr != '\0') {
190         _set_ptr(success, 0);
191         return -1;
192     }
193 
194     _set_ptr(success, 1);
195     return lval;
196 }
197 
smf_dict_remove(SMFDict_T * dict,const char * key)198 void smf_dict_remove(SMFDict_T *dict, const char * key) {
199     unsigned hash;
200     int i;
201 
202     assert(dict);
203     assert(key);
204 
205     hash = _dict_hash(key);
206     for (i=0; i<dict->size; i++) {
207         if (dict->key[i]==NULL)
208             continue ;
209         /* Compare hash */
210         if (hash==dict->hash[i]) {
211             /* Compare string, to avoid hash collisions */
212             if (!strcmp(key, dict->key[i])) {
213                 /* Found key */
214                 break;
215             }
216         }
217     }
218     if (i>=dict->size)
219         /* Key not found */
220         return;
221 
222     free(dict->key[i]);
223     dict->key[i] = NULL ;
224     if (dict->val[i]!=NULL) {
225         free(dict->val[i]);
226         dict->val[i] = NULL;
227     }
228     dict->hash[i] = 0;
229     dict->n --;
230 }
231 
smf_dict_get_keys(SMFDict_T * dict)232 SMFList_T *smf_dict_get_keys(SMFDict_T *dict) {
233     int i = 0;
234     SMFList_T *l = NULL;
235     assert(dict);
236 
237     if (smf_list_new(&l,smf_internal_string_list_destroy)!=0)
238         return NULL;
239 
240     for (i=0 ; i<dict->size ; i++) {
241         if (dict->key[i]) {
242             if (smf_list_append(l, strdup(dict->key[i])) != 0) {
243                 smf_list_free(l);
244                 return NULL;
245             }
246         }
247     }
248     return l;
249 }
250 
smf_dict_map(SMFDict_T * dict,void (* func)(char * key,char * value,void * args),void * args)251 void smf_dict_map(SMFDict_T *dict, void(*func)(char *key,char *value, void *args), void *args) {
252     assert(dict);
253     SMFList_T *list = NULL;
254     SMFListElem_T *elem = NULL;
255     char *key = NULL;
256     char *value = NULL;
257 
258     assert(dict);
259 
260     list = smf_dict_get_keys(dict);
261     elem = cmime_list_head(list);
262     while(elem != NULL) {
263         key = (char *)smf_list_data(elem);
264         value = smf_dict_get(dict,key);
265         func(key,value,args);
266         elem = elem->next;
267     }
268 
269     smf_list_free(list);
270 }
271 
272