Mercurial > ~mikael > mcabber > hg
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... */ |