comparison mcabber/mcabber/main.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/main.c@2536a4b5e370
children f02e7076ccec
comparison
equal deleted inserted replaced
1667:8af0e0ad20ad 1668:41c26b7d2890
1 /*
2 * main.c
3 *
4 * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net>
5 * Parts of this file come from Cabber <cabber@ajmacias.com>
6 *
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.
11 *
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.
16 *
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
21 */
22
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>
33 #include <poll.h>
34
35 #include "caps.h"
36 #include "screen.h"
37 #include "settings.h"
38 #include "roster.h"
39 #include "commands.h"
40 #include "histolog.h"
41 #include "hooks.h"
42 #include "utils.h"
43 #include "pgp.h"
44 #include "otr.h"
45 #include "fifo.h"
46 #include "xmpp.h"
47
48 #ifdef ENABLE_HGCSET
49 # include "hgcset.h"
50 #endif
51
52 #ifndef WAIT_ANY
53 # define WAIT_ANY -1
54 #endif
55
56 static unsigned int terminate_ui;
57 GMainContext *main_context;
58
59 static gboolean update_screen = TRUE;
60
61 static struct termios *backup_termios;
62
63 char *mcabber_version(void)
64 {
65 char *ver;
66 #ifdef HGCSET
67 ver = g_strdup_printf("%s (%s)", PACKAGE_VERSION, HGCSET);
68 #else
69 ver = g_strdup(PACKAGE_VERSION);
70 #endif
71 return ver;
72 }
73
74 static void mcabber_terminate(const char *msg)
75 {
76 fifo_deinit();
77 xmpp_disconnect();
78 scr_TerminateCurses();
79
80 // Restore term settings, if needed.
81 if (backup_termios)
82 tcsetattr(fileno(stdin), TCSAFLUSH, backup_termios);
83
84 if (msg)
85 fprintf(stderr, "%s\n", msg);
86 printf("Bye!\n");
87 exit(EXIT_SUCCESS);
88 }
89
90 void sig_handler(int signum)
91 {
92 if (signum == SIGCHLD) {
93 int status;
94 pid_t pid;
95 do {
96 pid = waitpid (WAIT_ANY, &status, WNOHANG);
97 // Check the exit status value if 'eventcmd_checkstatus' is set
98 if (settings_opt_get_int("eventcmd_checkstatus")) {
99 if (pid > 0) {
100 // exit status 2 -> beep
101 if (WIFEXITED(status) && WEXITSTATUS(status) == 2) {
102 scr_Beep();
103 }
104 }
105 }
106 } while (pid > 0);
107 signal(SIGCHLD, sig_handler);
108 } else if (signum == SIGTERM) {
109 mcabber_terminate("Killed by SIGTERM");
110 } else if (signum == SIGINT) {
111 mcabber_terminate("Killed by SIGINT");
112 #ifdef USE_SIGWINCH
113 } else if (signum == SIGWINCH) {
114 ungetch(KEY_RESIZE);
115 #endif
116 } else {
117 scr_LogPrint(LPRINT_LOGNORM, "Caught signal: %d", signum);
118 }
119 }
120
121 // ask_password(what)
122 // Return the password, or NULL.
123 // The string must be freed after use.
124 static char *ask_password(const char *what)
125 {
126 char *password, *p;
127 size_t passsize = 128;
128 struct termios orig, new;
129
130 password = g_new0(char, passsize);
131
132 /* Turn echoing off and fail if we can't. */
133 if (tcgetattr(fileno(stdin), &orig) != 0) return NULL;
134 backup_termios = &orig;
135
136 new = orig;
137 new.c_lflag &= ~ECHO;
138 if (tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0) return NULL;
139
140 /* Read the password. */
141 printf("Please enter %s: ", what);
142 if (fgets(password, passsize, stdin) == NULL) return NULL;
143
144 /* Restore terminal. */
145 tcsetattr(fileno(stdin), TCSAFLUSH, &orig);
146 printf("\n");
147 backup_termios = NULL;
148
149 for (p = (char*)password; *p; p++)
150 ;
151 for ( ; p > (char*)password ; p--)
152 if (*p == '\n' || *p == '\r') *p = 0;
153
154 return password;
155 }
156
157 static void credits(void)
158 {
159 const char *v_fmt = "MCabber %s -- Email: mcabber [at] lilotux [dot] net\n";
160 char *v = mcabber_version();
161 printf(v_fmt, v);
162 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8, v_fmt, v);
163 g_free(v);
164 }
165
166 static void compile_options(void)
167 {
168 puts("Installation data directory: " DATA_DIR "\n");
169 #ifdef HAVE_UNICODE
170 puts("Compiled with unicode support.");
171 #endif
172 #ifdef MODULES_ENABLE
173 puts ("Compiled with modules support.");
174 #endif
175 #ifdef HAVE_GPGME
176 puts("Compiled with GPG support.");
177 #endif
178 #ifdef HAVE_LIBOTR
179 puts("Compiled with OTR support.");
180 #endif
181 #ifdef WITH_ENCHANT
182 puts("Compiled with Enchant support.");
183 #endif
184 #ifdef WITH_ASPELL
185 puts("Compiled with Aspell support.");
186 #endif
187 #ifdef ENABLE_DEBUG
188 puts("Compiled with debugging support.");
189 #endif
190 }
191
192 static void main_init_pgp(void)
193 {
194 #ifdef HAVE_GPGME
195 const char *pk, *pp;
196 char *typed_passwd = NULL;
197 char *p;
198 bool pgp_invalid = FALSE;
199 bool pgp_agent;
200 int retries;
201
202 p = getenv("GPG_AGENT_INFO");
203 pgp_agent = (p && strchr(p, ':'));
204
205 pk = settings_opt_get("pgp_private_key");
206 pp = settings_opt_get("pgp_passphrase");
207
208 if (settings_opt_get("pgp_passphrase_retries"))
209 retries = settings_opt_get_int("pgp_passphrase_retries");
210 else
211 retries = 2;
212
213 if (!pk) {
214 scr_LogPrint(LPRINT_LOGNORM, "WARNING: unknown PGP private key");
215 pgp_invalid = TRUE;
216 } else if (!(pp || pgp_agent)) {
217 // Request PGP passphrase
218 pp = typed_passwd = ask_password("PGP passphrase");
219 }
220 gpg_init(pk, pp);
221 // Erase password from the settings array
222 if (pp) {
223 memset((char*)pp, 0, strlen(pp));
224 if (typed_passwd)
225 g_free(typed_passwd);
226 else
227 settings_set(SETTINGS_TYPE_OPTION, "pgp_passphrase", NULL);
228 }
229 if (!pgp_agent && pk && pp && gpg_test_passphrase()) {
230 // Let's check the pasphrase
231 int i;
232 for (i = 1; retries < 0 || i <= retries; i++) {
233 typed_passwd = ask_password("PGP passphrase"); // Ask again...
234 if (typed_passwd) {
235 gpg_set_passphrase(typed_passwd);
236 memset(typed_passwd, 0, strlen(typed_passwd));
237 g_free(typed_passwd);
238 }
239 if (!gpg_test_passphrase())
240 break; // Ok
241 }
242 if (i > retries)
243 pgp_invalid = TRUE;
244 }
245 if (pgp_invalid)
246 scr_LogPrint(LPRINT_LOGNORM, "WARNING: PGP key/pass invalid");
247 #else /* not HAVE_GPGME */
248 scr_LogPrint(LPRINT_LOGNORM, "WARNING: not compiled with PGP support");
249 #endif /* HAVE_GPGME */
250 }
251
252 void mcabber_set_terminate_ui(void)
253 {
254 terminate_ui = TRUE;
255 }
256
257 typedef struct {
258 GSource source;
259 GPollFD pollfd;
260 } mcabber_source_t;
261
262 static gboolean mcabber_source_prepare(GSource *source, gint *timeout)
263 {
264 *timeout = -1;
265 return FALSE;
266 }
267
268 static gboolean mcabber_source_check(GSource *source)
269 {
270 mcabber_source_t *mc_source = (mcabber_source_t *) source;
271 gushort revents = mc_source->pollfd.revents;
272 if (revents)
273 return TRUE;
274 return FALSE;
275 }
276
277 static gboolean keyboard_activity(void)
278 {
279 keycode kcode;
280
281 if (terminate_ui) {
282 return FALSE;
283 }
284 scr_DoUpdate();
285 scr_Getch(&kcode);
286
287 while (kcode.value != ERR) {
288 process_key(kcode);
289 update_screen = TRUE;
290 scr_Getch(&kcode);
291 }
292 scr_CheckAutoAway(FALSE);
293
294 return TRUE;
295 }
296
297 static gboolean mcabber_source_dispatch(GSource *source, GSourceFunc callback,
298 gpointer udata) {
299 return keyboard_activity();
300 }
301
302 static GSourceFuncs mcabber_source_funcs = {
303 mcabber_source_prepare,
304 mcabber_source_check,
305 mcabber_source_dispatch,
306 NULL,
307 NULL,
308 NULL
309 };
310
311 int main(int argc, char **argv)
312 {
313 char *configFile = NULL;
314 const char *optstring;
315 int optval, optval2;
316 int ret;
317
318 credits();
319
320 signal(SIGTERM, sig_handler);
321 signal(SIGINT, sig_handler);
322 signal(SIGCHLD, sig_handler);
323 #ifdef USE_SIGWINCH
324 signal(SIGWINCH, sig_handler);
325 #endif
326 signal(SIGPIPE, SIG_IGN);
327
328 /* Parse command line options */
329 while (1) {
330 int c = getopt(argc, argv, "hVf:");
331 if (c == -1) {
332 break;
333 } else
334 switch (c) {
335 case 'h':
336 case '?':
337 printf("Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
338 return (c == 'h' ? 0 : -1);
339 case 'V':
340 compile_options();
341 return 0;
342 case 'f':
343 configFile = g_strdup(optarg);
344 break;
345 }
346 }
347
348 if (optind < argc) {
349 fprintf(stderr, "Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
350 return -1;
351 }
352
353 /* Initialize command system, roster and default key bindings */
354 cmd_init();
355 roster_init();
356 settings_init();
357 scr_init_bindings();
358 caps_init();
359 /* Initialize charset */
360 scr_InitLocaleCharSet();
361
362 /* Parsing config file... */
363 ret = cfg_read_file(configFile, TRUE);
364 /* free() configFile if it has been allocated during options parsing */
365 g_free(configFile);
366 /* Leave if there was an error in the config. file */
367 if (ret == -2)
368 exit(EXIT_FAILURE);
369
370 optstring = settings_opt_get("tracelog_file");
371 if (optstring)
372 ut_InitDebug(settings_opt_get_int("tracelog_level"), optstring);
373
374 /* If no password is stored, we ask for it before entering
375 ncurses mode -- unless the username is unknown. */
376 if (settings_opt_get("jid") && !settings_opt_get("password")) {
377 const char *p;
378 char *pwd;
379 p = settings_opt_get("server");
380 if (p)
381 printf("Server: %s\n", p);
382 p = settings_opt_get("jid");
383 if (p)
384 printf("User JID: %s\n", p);
385
386 pwd = ask_password("Jabber password");
387 settings_set(SETTINGS_TYPE_OPTION, "password", pwd);
388 g_free(pwd);
389 }
390
391 /* Initialize PGP system
392 We do it before ncurses initialization because we may need to request
393 a passphrase. */
394 if (settings_opt_get_int("pgp"))
395 main_init_pgp();
396
397 /* Initialize N-Curses */
398 scr_LogPrint(LPRINT_DEBUG, "Initializing N-Curses...");
399 scr_InitCurses();
400 scr_DrawMainWindow(TRUE);
401
402 optval = (settings_opt_get_int("logging") > 0);
403 optval2 = (settings_opt_get_int("load_logs") > 0);
404 if (optval || optval2)
405 hlog_enable(optval, settings_opt_get("logging_dir"), optval2);
406
407 #if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
408 /* Initialize spelling */
409 if (settings_opt_get_int("spell_enable")) {
410 spellcheck_init();
411 }
412 #endif
413
414 optstring = settings_opt_get("events_command");
415 if (optstring)
416 hk_ext_cmd_init(optstring);
417
418 optstring = settings_opt_get("roster_display_filter");
419 if (optstring)
420 scr_RosterDisplay(optstring);
421 // Empty filter isn't allowed...
422 if (!buddylist_get_filter())
423 scr_RosterDisplay("*");
424
425 chatstates_disabled = settings_opt_get_int("disable_chatstates");
426
427 /* Initialize FIFO named pipe */
428 fifo_init(settings_opt_get("fifo_name"));
429
430 /* Load previous roster state */
431 hlog_load_state();
432
433 main_context = g_main_context_default();
434
435 if (ret < 0) {
436 scr_LogPrint(LPRINT_NORMAL, "No configuration file has been found.");
437 scr_ShowBuddyWindow();
438 } else {
439 /* Connection */
440 xmpp_connect();
441 }
442
443 { // add keypress processing source
444 GSource *mc_source = g_source_new(&mcabber_source_funcs,
445 sizeof(mcabber_source_t));
446 GPollFD *mc_pollfd = &(((mcabber_source_t *)mc_source)->pollfd);
447 mc_pollfd->fd = STDIN_FILENO;
448 mc_pollfd->events = POLLIN|POLLERR|POLLPRI;
449 mc_pollfd->revents = 0;
450 g_source_add_poll(mc_source, mc_pollfd);
451 g_source_attach(mc_source, main_context);
452
453 scr_LogPrint(LPRINT_DEBUG, "Entering into main loop...");
454
455 while(!terminate_ui) {
456 if (g_main_context_iteration(main_context, TRUE) == FALSE)
457 keyboard_activity();
458 if (update_roster)
459 scr_DrawRoster();
460 if(update_screen)
461 scr_DoUpdate();
462 }
463
464 g_source_destroy(mc_source);
465 g_source_unref(mc_source);
466 }
467
468 scr_TerminateCurses();
469 #ifdef MODULES_ENABLE
470 cmd_deinit();
471 #endif
472 fifo_deinit();
473 #ifdef HAVE_LIBOTR
474 otr_terminate();
475 #endif
476 xmpp_disconnect();
477 #ifdef HAVE_GPGME
478 gpg_terminate();
479 #endif
480 #if defined(WITH_ENCHANT) || defined(WITH_ASPELL)
481 /* Deinitialize spelling */
482 if (settings_opt_get_int("spell_enable"))
483 spellcheck_deinit();
484 #endif
485 /* Save pending message state */
486 hlog_save_state();
487 caps_free();
488
489 printf("\n\nThanks for using mcabber!\n");
490
491 return 0;
492 }
493
494 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */