Remove deprecated option ('hide_offline_buddies')
[mcabber.git] / mcabber / src / main.c
blob0fe89830d6903df901c85414c56c97512599d51b
1 /*
2 * main.c
4 * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
5 * Parts of this file come from Cabber <cabber@ajmacias.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <signal.h>
28 #include <termios.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <glib.h>
32 #include <config.h>
34 #include "caps.h"
35 #include "screen.h"
36 #include "settings.h"
37 #include "roster.h"
38 #include "commands.h"
39 #include "histolog.h"
40 #include "hooks.h"
41 #include "utils.h"
42 #include "pgp.h"
43 #include "otr.h"
44 #include "fifo.h"
45 #include "xmpp.h"
47 #ifdef ENABLE_HGCSET
48 # include "hgcset.h"
49 #endif
51 #ifndef WAIT_ANY
52 # define WAIT_ANY -1
53 #endif
55 static unsigned int terminate_ui;
56 GMainLoop *main_loop = NULL;
58 static struct termios *backup_termios;
60 char *mcabber_version(void)
62 char *ver;
63 #ifdef HGCSET
64 ver = g_strdup_printf("%s (%s)", PACKAGE_VERSION, HGCSET);
65 #else
66 ver = g_strdup(PACKAGE_VERSION);
67 #endif
68 return ver;
71 static void mcabber_terminate(const char *msg)
73 fifo_deinit();
74 xmpp_disconnect();
75 scr_TerminateCurses();
77 // Restore term settings, if needed.
78 if (backup_termios)
79 tcsetattr(fileno(stdin), TCSAFLUSH, backup_termios);
81 if (msg)
82 fprintf(stderr, "%s\n", msg);
83 printf("Bye!\n");
84 exit(EXIT_SUCCESS);
87 void sig_handler(int signum)
89 if (signum == SIGCHLD) {
90 int status;
91 pid_t pid;
92 do {
93 pid = waitpid (WAIT_ANY, &status, WNOHANG);
94 // Check the exit status value if 'eventcmd_checkstatus' is set
95 if (settings_opt_get_int("eventcmd_checkstatus")) {
96 if (pid > 0) {
97 // exit status 2 -> beep
98 if (WIFEXITED(status) && WEXITSTATUS(status) == 2) {
99 scr_Beep();
103 } while (pid > 0);
104 signal(SIGCHLD, sig_handler);
105 } else if (signum == SIGTERM) {
106 mcabber_terminate("Killed by SIGTERM");
107 } else if (signum == SIGINT) {
108 mcabber_terminate("Killed by SIGINT");
109 #ifdef USE_SIGWINCH
110 } else if (signum == SIGWINCH) {
111 ungetch(KEY_RESIZE);
112 #endif
113 } else {
114 scr_LogPrint(LPRINT_LOGNORM, "Caught signal: %d", signum);
118 // ask_password(what)
119 // Return the password, or NULL.
120 // The string must be freed after use.
121 static char *ask_password(const char *what)
123 char *password, *p;
124 size_t passsize = 128;
125 struct termios orig, new;
127 password = g_new0(char, passsize);
129 /* Turn echoing off and fail if we can't. */
130 if (tcgetattr(fileno(stdin), &orig) != 0) return NULL;
131 backup_termios = &orig;
133 new = orig;
134 new.c_lflag &= ~ECHO;
135 if (tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0) return NULL;
137 /* Read the password. */
138 printf("Please enter %s: ", what);
139 if (fgets(password, passsize, stdin) == NULL) return NULL;
141 /* Restore terminal. */
142 tcsetattr(fileno(stdin), TCSAFLUSH, &orig);
143 printf("\n");
144 backup_termios = NULL;
146 for (p = (char*)password; *p; p++)
148 for ( ; p > (char*)password ; p--)
149 if (*p == '\n' || *p == '\r') *p = 0;
151 return password;
154 static void credits(void)
156 const char *v_fmt = "MCabber %s -- Email: mcabber [at] lilotux [dot] net\n";
157 char *v = mcabber_version();
158 printf(v_fmt, v);
159 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8, v_fmt, v);
160 g_free(v);
163 static void compile_options(void)
165 puts("Installation data directory: " DATA_DIR "\n");
166 #ifdef HAVE_UNICODE
167 puts("Compiled with unicode support.");
168 #endif
169 #ifdef MODULES_ENABLE
170 puts ("Compiled with modules support.");
171 #endif
172 #ifdef HAVE_GPGME
173 puts("Compiled with GPG support.");
174 #endif
175 #ifdef HAVE_LIBOTR
176 puts("Compiled with OTR support.");
177 #endif
178 #ifdef WITH_ENCHANT
179 puts("Compiled with Enchant support.");
180 #endif
181 #ifdef WITH_ASPELL
182 puts("Compiled with Aspell support.");
183 #endif
184 #ifdef ENABLE_DEBUG
185 puts("Compiled with debugging support.");
186 #endif
189 static void main_init_pgp(void)
191 #ifdef HAVE_GPGME
192 const char *pk, *pp;
193 char *typed_passwd = NULL;
194 char *p;
195 bool pgp_invalid = FALSE;
196 bool pgp_agent;
197 int retries;
199 p = getenv("GPG_AGENT_INFO");
200 pgp_agent = (p && strchr(p, ':'));
202 pk = settings_opt_get("pgp_private_key");
203 pp = settings_opt_get("pgp_passphrase");
205 if (settings_opt_get("pgp_passphrase_retries"))
206 retries = settings_opt_get_int("pgp_passphrase_retries");
207 else
208 retries = 2;
210 if (!pk) {
211 scr_LogPrint(LPRINT_LOGNORM, "WARNING: unknown PGP private key");
212 pgp_invalid = TRUE;
213 } else if (!(pp || pgp_agent)) {
214 // Request PGP passphrase
215 pp = typed_passwd = ask_password("PGP passphrase");
217 gpg_init(pk, pp);
218 // Erase password from the settings array
219 if (pp) {
220 memset((char*)pp, 0, strlen(pp));
221 if (typed_passwd)
222 g_free(typed_passwd);
223 else
224 settings_set(SETTINGS_TYPE_OPTION, "pgp_passphrase", NULL);
226 if (!pgp_agent && pk && pp && gpg_test_passphrase()) {
227 // Let's check the pasphrase
228 int i;
229 for (i = 1; retries < 0 || i <= retries; i++) {
230 typed_passwd = ask_password("PGP passphrase"); // Ask again...
231 if (typed_passwd) {
232 gpg_set_passphrase(typed_passwd);
233 memset(typed_passwd, 0, strlen(typed_passwd));
234 g_free(typed_passwd);
236 if (!gpg_test_passphrase())
237 break; // Ok
239 if (i > retries)
240 pgp_invalid = TRUE;
242 if (pgp_invalid)
243 scr_LogPrint(LPRINT_LOGNORM, "WARNING: PGP key/pass invalid");
244 #else /* not HAVE_GPGME */
245 scr_LogPrint(LPRINT_LOGNORM, "WARNING: not compiled with PGP support");
246 #endif /* HAVE_GPGME */
249 void mcabber_set_terminate_ui(void)
251 terminate_ui = TRUE;
254 gboolean mcabber_loop()
256 keycode kcode;
258 if (terminate_ui) {
259 g_main_loop_quit(main_loop);
260 return FALSE;
262 scr_DoUpdate();
263 scr_Getch(&kcode);
265 while (kcode.value != ERR) {
266 process_key(kcode);
267 scr_DoUpdate();
268 scr_Getch(&kcode);
270 scr_CheckAutoAway(FALSE);
272 if (update_roster)
273 scr_DrawRoster();
275 hk_mainloop();
276 return TRUE;
279 int main(int argc, char **argv)
281 char *configFile = NULL;
282 const char *optstring;
283 int optval, optval2;
284 int ret;
286 credits();
288 signal(SIGTERM, sig_handler);
289 signal(SIGINT, sig_handler);
290 signal(SIGCHLD, sig_handler);
291 #ifdef USE_SIGWINCH
292 signal(SIGWINCH, sig_handler);
293 #endif
294 signal(SIGPIPE, SIG_IGN);
296 /* Parse command line options */
297 while (1) {
298 int c = getopt(argc, argv, "hVf:");
299 if (c == -1) {
300 break;
301 } else
302 switch (c) {
303 case 'h':
304 case '?':
305 printf("Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
306 return (c == 'h' ? 0 : -1);
307 case 'V':
308 compile_options();
309 return 0;
310 case 'f':
311 configFile = g_strdup(optarg);
312 break;
316 if (optind < argc) {
317 fprintf(stderr, "Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
318 return -1;
321 /* Initialize command system, roster and default key bindings */
322 cmd_init();
323 roster_init();
324 settings_init();
325 scr_init_bindings();
326 caps_init();
327 /* Initialize charset */
328 scr_InitLocaleCharSet();
330 /* Parsing config file... */
331 ret = cfg_read_file(configFile, TRUE);
332 /* free() configFile if it has been allocated during options parsing */
333 g_free(configFile);
334 /* Leave if there was an error in the config. file */
335 if (ret == -2)
336 exit(EXIT_FAILURE);
338 optstring = settings_opt_get("tracelog_file");
339 if (optstring)
340 ut_InitDebug(settings_opt_get_int("tracelog_level"), optstring);
342 /* If no password is stored, we ask for it before entering
343 ncurses mode -- unless the username is unknown. */
344 if (settings_opt_get("jid") && !settings_opt_get("password")) {
345 const char *p;
346 char *pwd;
347 p = settings_opt_get("server");
348 if (p)
349 printf("Server: %s\n", p);
350 p = settings_opt_get("jid");
351 if (p)
352 printf("User JID: %s\n", p);
354 pwd = ask_password("Jabber password");
355 settings_set(SETTINGS_TYPE_OPTION, "password", pwd);
356 g_free(pwd);
359 /* Initialize PGP system
360 We do it before ncurses initialization because we may need to request
361 a passphrase. */
362 if (settings_opt_get_int("pgp"))
363 main_init_pgp();
365 /* Initialize N-Curses */
366 scr_LogPrint(LPRINT_DEBUG, "Initializing N-Curses...");
367 scr_InitCurses();
368 scr_DrawMainWindow(TRUE);
370 optval = (settings_opt_get_int("logging") > 0);
371 optval2 = (settings_opt_get_int("load_logs") > 0);
372 if (optval || optval2)
373 hlog_enable(optval, settings_opt_get("logging_dir"), optval2);
375 #if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
376 /* Initialize spelling */
377 if (settings_opt_get_int("spell_enable")) {
378 spellcheck_init();
380 #endif
382 optstring = settings_opt_get("events_command");
383 if (optstring)
384 hk_ext_cmd_init(optstring);
386 optstring = settings_opt_get("roster_display_filter");
387 if (optstring)
388 scr_RosterDisplay(optstring);
389 // Empty filter isn't allowed...
390 if (!buddylist_get_filter())
391 scr_RosterDisplay("*");
393 chatstates_disabled = settings_opt_get_int("disable_chatstates");
395 /* Initialize FIFO named pipe */
396 fifo_init(settings_opt_get("fifo_name"));
398 /* Load previous roster state */
399 hlog_load_state();
401 main_loop = g_main_loop_new(NULL, TRUE);
403 if (ret < 0) {
404 scr_LogPrint(LPRINT_NORMAL, "No configuration file has been found.");
405 scr_ShowBuddyWindow();
406 } else {
407 /* Connection */
408 xmpp_connect();
411 scr_LogPrint(LPRINT_DEBUG, "Entering into main loop...");
413 g_timeout_add(10, mcabber_loop, NULL);
414 g_main_loop_run(main_loop);
416 scr_TerminateCurses();
417 #ifdef MODULES_ENABLE
418 cmd_deinit();
419 #endif
420 fifo_deinit();
421 #ifdef HAVE_LIBOTR
422 otr_terminate();
423 #endif
424 xmpp_disconnect();
425 #ifdef HAVE_GPGME
426 gpg_terminate();
427 #endif
428 #if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
429 /* Deinitialize spelling */
430 if (settings_opt_get_int("spell_enable"))
431 spellcheck_deinit();
432 #endif
433 /* Save pending message state */
434 hlog_save_state();
435 caps_free();
437 printf("\n\nThanks for using mcabber!\n");
439 return 0;
442 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */