Mercurial > ~mikael > mcabber > hg
comparison mcabber/mcabber/settings.c @ 1668:41c26b7d2890
Install mcabber headers
* Change mcabber headers naming scheme
* Move 'src/' -> 'mcabber/'
* Add missing include <mcabber/config.h>'s
* Create and install clean config.h version in 'include/'
* Move "dirty" config.h version to 'mcabber/'
* Add $(top_srcdir) to compiler include path
* Update modules HOWTO
author | Myhailo Danylenko <isbear@ukrpost.net> |
---|---|
date | Mon, 18 Jan 2010 15:36:19 +0200 |
parents | mcabber/src/settings.c@64a7428afcb3 |
children | 552da310b83e |
comparison
equal
deleted
inserted
replaced
1667:8af0e0ad20ad | 1668:41c26b7d2890 |
---|---|
1 /* | |
2 * settings.c -- Configuration stuff | |
3 * | |
4 * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or (at | |
9 * your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, but | |
12 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
19 * USA | |
20 */ | |
21 | |
22 #include <stdio.h> | |
23 #include <stdlib.h> | |
24 #include <string.h> | |
25 | |
26 #include "config.h" | |
27 #include "settings.h" | |
28 #include "commands.h" | |
29 #include "logprint.h" | |
30 #include "otr.h" | |
31 #include "utils.h" | |
32 #include "xmpp.h" | |
33 #include "main.h" | |
34 | |
35 // Maximum line length | |
36 // (probably best to use the same value as INPUTLINE_LENGTH) | |
37 #define CONFLINE_LENGTH 1024 | |
38 | |
39 static GHashTable *option; | |
40 static GHashTable *alias; | |
41 static GHashTable *binding; | |
42 | |
43 #ifdef HAVE_GPGME /* PGP settings */ | |
44 static GHashTable *pgpopt; | |
45 | |
46 typedef struct { | |
47 gchar *pgp_keyid; /* KeyId the contact is supposed to use */ | |
48 guint pgp_disabled; /* If TRUE, PGP is disabled for outgoing messages */ | |
49 guint pgp_force; /* If TRUE, PGP is used w/o negotiation */ | |
50 } T_pgpopt; | |
51 #endif | |
52 | |
53 #ifdef HAVE_LIBOTR | |
54 static GHashTable *otrpolicy; | |
55 static enum otr_policy default_policy; | |
56 #endif | |
57 | |
58 static inline GHashTable *get_hash(guint type) | |
59 { | |
60 if (type == SETTINGS_TYPE_OPTION) return option; | |
61 else if (type == SETTINGS_TYPE_ALIAS) return alias; | |
62 else if (type == SETTINGS_TYPE_BINDING) return binding; | |
63 #ifdef HAVE_LIBOTR | |
64 else if (type == SETTINGS_TYPE_OTR) return otrpolicy; | |
65 #endif | |
66 return NULL; | |
67 } | |
68 | |
69 /* -- */ | |
70 | |
71 void settings_init(void) | |
72 { | |
73 option = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free); | |
74 alias = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free); | |
75 binding = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free); | |
76 #ifdef HAVE_GPGME | |
77 pgpopt = g_hash_table_new(&g_str_hash, &g_str_equal); | |
78 #endif | |
79 #ifdef HAVE_LIBOTR | |
80 otrpolicy = g_hash_table_new(&g_str_hash, &g_str_equal); | |
81 #endif | |
82 } | |
83 | |
84 // cfg_read_file(filename, mainfile) | |
85 // Read and parse config file "filename". If filename is NULL, | |
86 // try to open the configuration file at the default locations. | |
87 // mainfile must be set to TRUE for the startup config file. | |
88 // If mainfile is TRUE, the permissions of the configuration file will | |
89 // be fixed if they're insecure. | |
90 // | |
91 int cfg_read_file(char *filename, guint mainfile) | |
92 { | |
93 static unsigned int runtime; | |
94 FILE *fp; | |
95 char *buf; | |
96 char *line, *eol; | |
97 unsigned int ln = 0; | |
98 int err = 0; | |
99 | |
100 if (!filename) { | |
101 // Use default config file locations | |
102 char *home; | |
103 GString *sfilename; | |
104 | |
105 if (!mainfile) { | |
106 scr_LogPrint(LPRINT_LOGNORM, "No file name provided"); | |
107 return -1; | |
108 } | |
109 | |
110 home = getenv("HOME"); | |
111 if (!home) { | |
112 scr_LogPrint(LPRINT_LOG, "Can't find home dir!"); | |
113 fprintf(stderr, "Can't find home dir!\n"); | |
114 err = -1; | |
115 goto cfg_read_file_return; | |
116 } | |
117 sfilename = g_string_new(""); | |
118 g_string_printf(sfilename, "%s/.mcabber/mcabberrc", home); | |
119 if ((fp = fopen(sfilename->str, "r")) == NULL) { | |
120 // 2nd try... | |
121 g_string_printf(sfilename, "%s/.mcabberrc", home); | |
122 if ((fp = fopen(sfilename->str, "r")) == NULL) { | |
123 fprintf(stderr, "Cannot open config file!\n"); | |
124 g_string_free(sfilename, TRUE); | |
125 err = -1; | |
126 goto cfg_read_file_return; | |
127 } | |
128 } | |
129 // Check configuration file permissions | |
130 // As it could contain sensitive data, we make it user-readable only. | |
131 checkset_perm(sfilename->str, TRUE); | |
132 scr_LogPrint(LPRINT_LOGNORM, "Reading %s", sfilename->str); | |
133 // Check mcabber dir. Here we just warn, we don't change the modes. | |
134 g_string_printf(sfilename, "%s/.mcabber/", home); | |
135 checkset_perm(sfilename->str, FALSE); | |
136 g_string_free(sfilename, TRUE); | |
137 } else { | |
138 // filename was specified | |
139 if ((fp = fopen(filename, "r")) == NULL) { | |
140 const char *msg = "Cannot open configuration file"; | |
141 if (mainfile) | |
142 perror(msg); | |
143 else | |
144 scr_LogPrint(LPRINT_LOGNORM, "%s (%s).", msg, filename); | |
145 err = -2; | |
146 goto cfg_read_file_return; | |
147 } | |
148 // Check configuration file permissions (see above) | |
149 // We don't change the permissions if that's not the main file. | |
150 if (mainfile) | |
151 checkset_perm(filename, TRUE); | |
152 scr_LogPrint(LPRINT_LOGNORM, "Reading %s", filename); | |
153 } | |
154 | |
155 buf = g_new(char, CONFLINE_LENGTH+1); | |
156 | |
157 while (fgets(buf+1, CONFLINE_LENGTH, fp) != NULL) { | |
158 // The first char is reserved to add a '/', to make a command line | |
159 line = buf+1; | |
160 ln++; | |
161 | |
162 // Strip leading spaces | |
163 while (isspace(*line)) | |
164 line++; | |
165 | |
166 // Make eol point to the last char of the line | |
167 for (eol = line ; *eol ; eol++) | |
168 ; | |
169 if (eol > line) | |
170 eol--; | |
171 | |
172 // Strip trailing spaces | |
173 while (eol > line && isspace(*eol)) | |
174 *eol-- = 0; | |
175 | |
176 // Ignore empty lines and comments | |
177 if ((*line == '\n') || (*line == '\0') || (*line == '#')) | |
178 continue; | |
179 | |
180 // We only allow assignments line, except for commands "pgp", "source", | |
181 // "color", "load" and "otrpolicy", unless we're in runtime (i.e. not startup). | |
182 if (runtime || | |
183 (strchr(line, '=') != NULL) || | |
184 startswith(line, "pgp ", FALSE) || | |
185 startswith(line, "source ", FALSE) || | |
186 startswith(line, "color ", FALSE) || | |
187 #ifdef MODULES_ENABLE | |
188 startswith(line, "load ", FALSE) || | |
189 #endif | |
190 startswith(line, "otrpolicy", FALSE)) { | |
191 // Only accept a few "safe" commands | |
192 if (!runtime && | |
193 !startswith(line, "set ", FALSE) && | |
194 !startswith(line, "bind ", FALSE) && | |
195 !startswith(line, "alias ", FALSE) && | |
196 !startswith(line, "pgp ", FALSE) && | |
197 !startswith(line, "source ", FALSE) && | |
198 !startswith(line, "color ", FALSE) && | |
199 #ifdef MODULES_ENABLE | |
200 !startswith(line, "load ", FALSE) && | |
201 #endif | |
202 !startswith(line, "otrpolicy ", FALSE)) { | |
203 scr_LogPrint(LPRINT_LOGNORM, "Error in configuration file (l. %d): " | |
204 "this command can't be used here", ln); | |
205 err++; | |
206 continue; | |
207 } | |
208 // Set the leading COMMAND_CHAR to build a command line | |
209 // and process the command | |
210 *(--line) = COMMAND_CHAR; | |
211 if (process_command(line, TRUE) == 255) | |
212 mcabber_set_terminate_ui(); | |
213 } else { | |
214 scr_LogPrint(LPRINT_LOGNORM, "Error in configuration file (l. %d): " | |
215 "this is not an assignment", ln); | |
216 err++; | |
217 } | |
218 } | |
219 g_free(buf); | |
220 fclose(fp); | |
221 | |
222 if (filename) | |
223 scr_LogPrint(LPRINT_LOGNORM, "Loaded %s.", filename); | |
224 | |
225 cfg_read_file_return: | |
226 // If we're done with the main file parsing, we can assume that | |
227 // the next time this function is called will be at run time. | |
228 if (mainfile) | |
229 runtime = TRUE; | |
230 return err; | |
231 } | |
232 | |
233 // parse_assigment(assignment, pkey, pval) | |
234 // Read assignment and split it to key, value | |
235 // | |
236 // If this is an assignment, the function will return TRUE and | |
237 // set *pkey and *pval (*pval is set to NULL if value field is empty). | |
238 // | |
239 // If this isn't a assignment (no = char), the function will set *pval | |
240 // to NULL and return FALSE. | |
241 // | |
242 // The caller should g_free() *pkey and *pval (if not NULL) after use. | |
243 guint parse_assigment(gchar *assignment, gchar **pkey, gchar **pval) | |
244 { | |
245 char *key, *val, *t, *p; | |
246 | |
247 *pkey = *pval = NULL; | |
248 | |
249 key = assignment; | |
250 // Remove leading spaces in option name | |
251 while ((!isalnum(*key)) && (*key != '=') && *key) { | |
252 //if (!isblank(*key)) | |
253 // scr_LogPrint("Error in assignment parsing!"); | |
254 key++; | |
255 } | |
256 if (!*key) return FALSE; // Empty assignment | |
257 | |
258 if (*key == '=') { | |
259 //scr_LogPrint("Cannot parse assignment!"); | |
260 return FALSE; | |
261 } | |
262 // Ok, key points to the option name | |
263 | |
264 for (val = key+1 ; *val && (*val != '=') ; val++) | |
265 if (!isalnum(*val) && !isblank(*val) && (*val != '_') && (*val != '-')) { | |
266 // Key should only have alnum chars... | |
267 //scr_LogPrint("Error in assignment parsing!"); | |
268 return FALSE; | |
269 } | |
270 // Remove trailing spaces in option name: | |
271 for (t = val-1 ; t > key && isblank(*t) ; t--) | |
272 ; | |
273 // Check for embedded whitespace characters | |
274 for (p = key; p < t; p++) { | |
275 if (isblank(*p)) { | |
276 //scr_LogPrint("Error in assignment parsing!" | |
277 // " (Name should not contain space chars)"); | |
278 return FALSE; | |
279 } | |
280 } | |
281 | |
282 *pkey = g_strndup(key, t+1-key); | |
283 | |
284 if (!*val) return FALSE; // Not an assignment | |
285 | |
286 // Remove leading and trailing spaces in option value: | |
287 for (val++; *val && isblank(*val) ; val++) ; | |
288 for (t = val ; *t ; t++) ; | |
289 for (t-- ; t >= val && isblank(*t) ; t--) ; | |
290 | |
291 if (t < val) return TRUE; // no value (variable reset for example) | |
292 | |
293 // If the value begins and ends with quotes ("), these quotes are | |
294 // removed and whitespace is not stripped | |
295 if ((t>val) && (*val == '"' && *t == '"')) { | |
296 val++; | |
297 t--; | |
298 } | |
299 *pval = g_strndup(val, t+1-val); | |
300 return TRUE; | |
301 } | |
302 | |
303 void settings_set(guint type, const gchar *key, const gchar *value) | |
304 { | |
305 GHashTable *hash; | |
306 | |
307 hash = get_hash(type); | |
308 if (!hash) | |
309 return; | |
310 | |
311 if (!value) { | |
312 g_hash_table_remove(hash, key); | |
313 } else { | |
314 g_hash_table_insert(hash, g_strdup(key), g_strdup(value)); | |
315 } | |
316 } | |
317 | |
318 void settings_del(guint type, const gchar *key) | |
319 { | |
320 settings_set(type, key, NULL); | |
321 } | |
322 | |
323 const gchar *settings_get(guint type, const gchar *key) | |
324 { | |
325 GHashTable *hash; | |
326 | |
327 hash = get_hash(type); | |
328 if (!hash) | |
329 return NULL; | |
330 | |
331 return g_hash_table_lookup(hash, key); | |
332 } | |
333 | |
334 int settings_get_int(guint type, const gchar *key) | |
335 { | |
336 const gchar *setval = settings_get(type, key); | |
337 | |
338 if (setval) return atoi(setval); | |
339 return 0; | |
340 } | |
341 | |
342 // settings_get_status_msg(status) | |
343 // Return a string with the current status message: | |
344 // - if there is a user-defined message ("message" option), | |
345 // return this message | |
346 // - if there is a user-defined message for the given status (and no | |
347 // generic user message), it is returned | |
348 // - if no message is found, return NULL | |
349 const gchar *settings_get_status_msg(enum imstatus status) | |
350 { | |
351 const gchar *rstatus = settings_opt_get("message"); | |
352 | |
353 if (rstatus) return rstatus; | |
354 | |
355 switch(status) { | |
356 case available: | |
357 rstatus = settings_opt_get("message_avail"); | |
358 break; | |
359 | |
360 case freeforchat: | |
361 rstatus = settings_opt_get("message_free"); | |
362 break; | |
363 | |
364 case dontdisturb: | |
365 rstatus = settings_opt_get("message_dnd"); | |
366 break; | |
367 | |
368 case notavail: | |
369 rstatus = settings_opt_get("message_notavail"); | |
370 break; | |
371 | |
372 case away: | |
373 rstatus = settings_opt_get("message_away"); | |
374 break; | |
375 | |
376 default: // offline, invisible | |
377 break; | |
378 } | |
379 return rstatus; | |
380 } | |
381 | |
382 // settings_foreach(type, pfunction, param) | |
383 // Call pfunction(key, value, param) for each setting with requested type. | |
384 void settings_foreach(guint type, void (*pfunc)(char *k, char *v, void *param), | |
385 void *param) | |
386 { | |
387 GHashTable *hash; | |
388 | |
389 hash = get_hash(type); | |
390 if (!hash) | |
391 return; | |
392 | |
393 g_hash_table_foreach(hash, (GHFunc)pfunc, param); | |
394 } | |
395 | |
396 | |
397 // default_muc_nickname() | |
398 // Return the user's default nickname | |
399 // The caller should free the string after use | |
400 char *default_muc_nickname(const char *roomid) | |
401 { | |
402 char *nick; | |
403 | |
404 nick = (char*)xmpp_get_bookmark_nick(roomid); | |
405 if (nick) | |
406 return g_strdup(nick); | |
407 | |
408 // We try the "nickname" option, then the username part of the jid. | |
409 nick = (char*)settings_opt_get("nickname"); | |
410 if (nick) | |
411 return g_strdup(nick); | |
412 | |
413 nick = jid_get_username(settings_opt_get("jid")); | |
414 return nick; | |
415 } | |
416 | |
417 | |
418 /* PGP settings */ | |
419 | |
420 // settings_pgp_setdisabled(jid, value) | |
421 // Enable/disable PGP encryption for jid. | |
422 // (Set value to TRUE to disable encryption) | |
423 void settings_pgp_setdisabled(const char *bjid, guint value) | |
424 { | |
425 #ifdef HAVE_GPGME | |
426 T_pgpopt *pgpdata; | |
427 pgpdata = g_hash_table_lookup(pgpopt, bjid); | |
428 if (!pgpdata) { | |
429 // If value is 0, we do not need to create a structure (that's | |
430 // the default value). | |
431 if (value) { | |
432 pgpdata = g_new0(T_pgpopt, 1); | |
433 pgpdata->pgp_disabled = value; | |
434 g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata); | |
435 } | |
436 } else { | |
437 pgpdata->pgp_disabled = value; | |
438 // We could remove the key/value if pgp_disabled is 0 and | |
439 // pgp_keyid is NULL, actually. | |
440 } | |
441 #endif | |
442 } | |
443 | |
444 // settings_pgp_getdisabled(jid) | |
445 // Return TRUE if PGP encryption should be disabled for jid. | |
446 guint settings_pgp_getdisabled(const char *bjid) | |
447 { | |
448 #ifdef HAVE_GPGME | |
449 T_pgpopt *pgpdata; | |
450 pgpdata = g_hash_table_lookup(pgpopt, bjid); | |
451 if (pgpdata) | |
452 return pgpdata->pgp_disabled; | |
453 else | |
454 return FALSE; // Default: not disabled | |
455 #else | |
456 return TRUE; // No PGP support, let's say it's disabled. | |
457 #endif | |
458 } | |
459 | |
460 // settings_pgp_setforce(jid, value) | |
461 // Force (or not) PGP encryption for jid. | |
462 // When value is TRUE, PGP support will be assumed for the remote client. | |
463 void settings_pgp_setforce(const char *bjid, guint value) | |
464 { | |
465 #ifdef HAVE_GPGME | |
466 T_pgpopt *pgpdata; | |
467 pgpdata = g_hash_table_lookup(pgpopt, bjid); | |
468 if (!pgpdata) { | |
469 // If value is 0, we do not need to create a structure (that's | |
470 // the default value). | |
471 if (value) { | |
472 pgpdata = g_new0(T_pgpopt, 1); | |
473 pgpdata->pgp_force = value; | |
474 g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata); | |
475 } | |
476 } else { | |
477 pgpdata->pgp_force = value; | |
478 } | |
479 if (value && pgpdata && !pgpdata->pgp_keyid) | |
480 scr_LogPrint(LPRINT_NORMAL, "Warning: the Key Id is not set!"); | |
481 #endif | |
482 } | |
483 | |
484 // settings_pgp_getforce(jid) | |
485 // Return TRUE if PGP enforcement is set for jid. | |
486 guint settings_pgp_getforce(const char *bjid) | |
487 { | |
488 #ifdef HAVE_GPGME | |
489 T_pgpopt *pgpdata; | |
490 pgpdata = g_hash_table_lookup(pgpopt, bjid); | |
491 if (pgpdata) | |
492 return pgpdata->pgp_force; | |
493 else | |
494 return FALSE; // Default | |
495 #else | |
496 return FALSE; // No PGP support | |
497 #endif | |
498 } | |
499 | |
500 // settings_pgp_setkeyid(jid, keyid) | |
501 // Set the PGP KeyId for user jid. | |
502 // Use keyid = NULL to erase the previous KeyId. | |
503 void settings_pgp_setkeyid(const char *bjid, const char *keyid) | |
504 { | |
505 #ifdef HAVE_GPGME | |
506 T_pgpopt *pgpdata; | |
507 pgpdata = g_hash_table_lookup(pgpopt, bjid); | |
508 if (!pgpdata) { | |
509 // If keyid is NULL, we do not need to create a structure (that's | |
510 // the default value). | |
511 if (keyid) { | |
512 pgpdata = g_new0(T_pgpopt, 1); | |
513 pgpdata->pgp_keyid = g_strdup(keyid); | |
514 g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata); | |
515 } | |
516 } else { | |
517 g_free(pgpdata->pgp_keyid); | |
518 if (keyid) | |
519 pgpdata->pgp_keyid = g_strdup(keyid); | |
520 else | |
521 pgpdata->pgp_keyid = NULL; | |
522 // We could remove the key/value if pgp_disabled is 0 and | |
523 // pgp_keyid is NULL, actually. | |
524 } | |
525 #endif | |
526 } | |
527 | |
528 // settings_pgp_getkeyid(jid) | |
529 // Get the PGP KeyId for user jid. | |
530 const char *settings_pgp_getkeyid(const char *bjid) | |
531 { | |
532 #ifdef HAVE_GPGME | |
533 T_pgpopt *pgpdata; | |
534 pgpdata = g_hash_table_lookup(pgpopt, bjid); | |
535 if (pgpdata) | |
536 return pgpdata->pgp_keyid; | |
537 #endif | |
538 return NULL; | |
539 } | |
540 | |
541 /* otr settings */ | |
542 | |
543 #ifdef HAVE_LIBOTR | |
544 static void remove_default_policies(char *k, char *policy, void *defaultp) | |
545 { | |
546 if (*(enum otr_policy *)policy == *(enum otr_policy *)defaultp) { | |
547 g_free((enum otr_policy *) policy); | |
548 g_hash_table_remove(otrpolicy, k); | |
549 } | |
550 } | |
551 #endif | |
552 | |
553 void settings_otr_setpolicy(const char *bjid, guint value) | |
554 { | |
555 #ifdef HAVE_LIBOTR | |
556 enum otr_policy *otrdata; | |
557 | |
558 if (!bjid) { | |
559 default_policy = value; | |
560 /* refresh hash */ | |
561 settings_foreach(SETTINGS_TYPE_OTR, &remove_default_policies, &value); | |
562 return; | |
563 } | |
564 | |
565 otrdata = g_hash_table_lookup(otrpolicy, bjid); | |
566 | |
567 if (value == default_policy) { | |
568 if (otrdata) { | |
569 g_free(otrdata); | |
570 g_hash_table_remove(otrpolicy, bjid); | |
571 } | |
572 } else if (otrdata) { | |
573 *otrdata = value; | |
574 } else { | |
575 otrdata = g_new(enum otr_policy, 1); | |
576 *otrdata = value; | |
577 g_hash_table_insert(otrpolicy, g_strdup(bjid), otrdata); | |
578 } | |
579 #endif | |
580 } | |
581 | |
582 guint settings_otr_getpolicy(const char *bjid) | |
583 { | |
584 #ifdef HAVE_LIBOTR | |
585 enum otr_policy *otrdata; | |
586 if (!bjid) | |
587 return default_policy; | |
588 | |
589 otrdata = g_hash_table_lookup(otrpolicy, bjid); | |
590 if (otrdata) | |
591 return *otrdata; | |
592 else | |
593 return default_policy; | |
594 #else | |
595 return 0; | |
596 #endif | |
597 } | |
598 | |
599 guint get_max_history_blocks(void) | |
600 { | |
601 int max_num_of_blocks = settings_opt_get_int("max_history_blocks"); | |
602 if (max_num_of_blocks < 0) | |
603 max_num_of_blocks = 0; | |
604 else if (max_num_of_blocks == 1) | |
605 max_num_of_blocks = 2; | |
606 return (guint)max_num_of_blocks; | |
607 } | |
608 | |
609 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */ |