1 /********************************************************************\
2   * BitlBee -- An IRC to other IM-networks gateway                     *
3   *                                                                    *
4   * Copyright 2002-2013 Wilmer van der Gaast and others                *
5   \********************************************************************/
6 
7 /* Some stuff to register, handle and save user preferences             */
8 
9 /*
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19 
20   You should have received a copy of the GNU General Public License with
21   the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22   if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23   Fifth Floor, Boston, MA  02110-1301  USA
24 */
25 
26 #define BITLBEE_CORE
27 #include "bitlbee.h"
28 
29 /* Used to use NULL for this, but NULL is actually a "valid" value. */
30 char *SET_INVALID = "nee";
31 
set_add(set_t ** head,const char * key,const char * def,set_eval eval,void * data)32 set_t *set_add(set_t **head, const char *key, const char *def, set_eval eval, void *data)
33 {
34 	set_t *s = set_find(head, key);
35 
36 	/* Possibly the setting already exists. If it doesn't exist yet,
37 	   we create it. If it does, we'll just change the default. */
38 	if (!s) {
39 		if ((s = *head)) {
40 			/* Sorted insertion. Special-case insertion at the start. */
41 			if (strcmp(key, s->key) < 0) {
42 				s = g_new0(set_t, 1);
43 				s->next = *head;
44 				*head = s;
45 			} else {
46 				while (s->next && strcmp(key, s->next->key) > 0) {
47 					s = s->next;
48 				}
49 				set_t *last_next = s->next;
50 				s->next = g_new0(set_t, 1);
51 				s = s->next;
52 				s->next = last_next;
53 			}
54 		} else {
55 			s = *head = g_new0(set_t, 1);
56 		}
57 		s->key = g_strdup(key);
58 	}
59 
60 	if (s->def) {
61 		g_free(s->def);
62 		s->def = NULL;
63 	}
64 	if (def) {
65 		s->def = g_strdup(def);
66 	}
67 
68 	s->eval = eval;
69 	s->data = data;
70 
71 	return s;
72 }
73 
set_find(set_t ** head,const char * key)74 set_t *set_find(set_t **head, const char *key)
75 {
76 	set_t *s = *head;
77 
78 	while (s) {
79 		if (g_strcasecmp(s->key, key) == 0 ||
80 		    (s->old_key && g_strcasecmp(s->old_key, key) == 0)) {
81 			break;
82 		}
83 		s = s->next;
84 	}
85 
86 	return s;
87 }
88 
set_getstr(set_t ** head,const char * key)89 char *set_getstr(set_t **head, const char *key)
90 {
91 	set_t *s = set_find(head, key);
92 
93 	if (!s || (!s->value && !s->def)) {
94 		return NULL;
95 	}
96 
97 	return set_value(s);
98 }
99 
set_getint(set_t ** head,const char * key)100 int set_getint(set_t **head, const char *key)
101 {
102 	char *s = set_getstr(head, key);
103 	int i = 0;
104 
105 	if (!s) {
106 		return 0;
107 	}
108 
109 	if (sscanf(s, "%d", &i) != 1) {
110 		return 0;
111 	}
112 
113 	return i;
114 }
115 
set_getbool(set_t ** head,const char * key)116 int set_getbool(set_t **head, const char *key)
117 {
118 	char *s = set_getstr(head, key);
119 
120 	if (!s) {
121 		return 0;
122 	}
123 
124 	return bool2int(s);
125 }
126 
set_isvisible(set_t * set)127 int set_isvisible(set_t *set)
128 {
129 	/* the default value is not stored in value, only in def */
130 	return !((set->flags & SET_HIDDEN) ||
131 	         ((set->flags & SET_HIDDEN_DEFAULT) &&
132 	          (set->value == NULL)));
133 }
134 
set_setstr(set_t ** head,const char * key,char * value)135 int set_setstr(set_t **head, const char *key, char *value)
136 {
137 	set_t *s = set_find(head, key);
138 	char *nv = value;
139 
140 	if (!s) {
141 		/*
142 		Used to do this, but it never really made sense.
143 		s = set_add( head, key, NULL, NULL, NULL );
144 		*/
145 		return 0;
146 	}
147 
148 	if (value == NULL && (s->flags & SET_NULL_OK) == 0) {
149 		return 0;
150 	}
151 
152 	/* Call the evaluator. For invalid values, evaluators should now
153 	   return SET_INVALID, but previously this was NULL. Try to handle
154 	   that too if NULL is not an allowed value for this setting. */
155 	if (s->eval && ((nv = s->eval(s, value)) == SET_INVALID ||
156 	                ((s->flags & SET_NULL_OK) == 0 && nv == NULL))) {
157 		return 0;
158 	}
159 
160 	if (s->value) {
161 		g_free(s->value);
162 		s->value = NULL;
163 	}
164 
165 	/* If there's a default setting and it's equal to what we're trying to
166 	   set, stick with s->value = NULL. Otherwise, remember the setting. */
167 	if (!s->def || (g_strcmp0(nv, s->def) != 0)) {
168 		s->value = g_strdup(nv);
169 	}
170 
171 	if (nv != value) {
172 		g_free(nv);
173 	}
174 
175 	return 1;
176 }
177 
set_setint(set_t ** head,const char * key,int value)178 int set_setint(set_t **head, const char *key, int value)
179 {
180 	char *s = g_strdup_printf("%d", value);
181 	int retval = set_setstr(head, key, s);
182 
183 	g_free(s);
184 	return retval;
185 }
186 
set_del(set_t ** head,const char * key)187 void set_del(set_t **head, const char *key)
188 {
189 	set_t *s = *head, *t = NULL;
190 
191 	while (s) {
192 		if (g_strcasecmp(s->key, key) == 0) {
193 			break;
194 		}
195 		s = (t = s)->next;
196 	}
197 	if (s) {
198 		if (t) {
199 			t->next = s->next;
200 		} else {
201 			*head = s->next;
202 		}
203 
204 		g_free(s->key);
205 		g_free(s->old_key);
206 		g_free(s->value);
207 		g_free(s->def);
208 		g_free(s);
209 	}
210 }
211 
set_reset(set_t ** head,const char * key)212 int set_reset(set_t **head, const char *key)
213 {
214 	set_t *s;
215 
216 	s = set_find(head, key);
217 	if (s) {
218 		return set_setstr(head, key, s->def);
219 	}
220 
221 	return 0;
222 }
223 
set_eval_int(set_t * set,char * value)224 char *set_eval_int(set_t *set, char *value)
225 {
226 	char *s = value;
227 
228 	/* Allow a minus at the first position. */
229 	if (*s == '-') {
230 		s++;
231 	}
232 
233 	for (; *s; s++) {
234 		if (!g_ascii_isdigit(*s)) {
235 			return SET_INVALID;
236 		}
237 	}
238 
239 	return value;
240 }
241 
set_eval_bool(set_t * set,char * value)242 char *set_eval_bool(set_t *set, char *value)
243 {
244 	return is_bool(value) ? value : SET_INVALID;
245 }
246 
set_eval_list(set_t * set,char * value)247 char *set_eval_list(set_t *set, char *value)
248 {
249 	GSList *options = set->eval_data, *opt;
250 
251 	for (opt = options; opt; opt = opt->next) {
252 		if (strcmp(value, opt->data) == 0) {
253 			return value;
254 		}
255 	}
256 
257 	/* TODO: It'd be nice to show the user a list of allowed values,
258 	         but we don't have enough context here to do that. May
259 	         want to fix that. */
260 
261 	return NULL;
262 }
263 
set_eval_to_char(set_t * set,char * value)264 char *set_eval_to_char(set_t *set, char *value)
265 {
266 	char *s = g_new(char, 3);
267 
268 	if (*value == ' ') {
269 		strcpy(s, " ");
270 	} else {
271 		sprintf(s, "%c ", *value);
272 	}
273 
274 	return s;
275 }
276 
set_eval_oauth(set_t * set,char * value)277 char *set_eval_oauth(set_t *set, char *value)
278 {
279 	account_t *acc = set->data;
280 
281 	if (bool2int(value) && strcmp(acc->pass, PASSWORD_PENDING) == 0) {
282 		*acc->pass = '\0';
283 	}
284 
285 	return set_eval_bool(set, value);
286 }
287