Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 697 lines (552 sloc) 16.329 kb
87b5f18 Initial commit from 0.3.1 tarball (BSD compliant version without GLIB re...
Daniel (dmilith) Dettlaff authored
1 /* $Id: ekg.c 4601 2008-09-04 16:02:33Z darkjames $ */
2
3 /*
4 * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
5 * Robert J. Wo¼ny <speedy@ziew.org>
6 * Pawe³ Maziarz <drg@infomex.pl>
7 * Adam Osuchowski <adwol@polsl.gliwice.pl>
8 * Dawid Jarosz <dawjar@poczta.onet.pl>
9 * Wojciech Bojdo³ <wojboj@htcon.pl>
10 * Piotr Domagalski <szalik@szalik.net>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License Version 2 as
14 * published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "ekg2-config.h"
27
28 #ifndef __FreeBSD__
29 #define _XOPEN_SOURCE 600
30 #define __EXTENSIONS__
31 #endif
32
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37
38 #include <sys/un.h>
39
40 #include <sys/ioctl.h>
41
42 #include <sys/stat.h>
43
44 #define __USE_BSD
45 #include <sys/time.h>
46
47 #include <sys/resource.h> /* rlimit */
48
49 #include <sys/select.h>
50
51 #include <errno.h>
52 #include <fcntl.h>
53 #ifdef HAVE_GETOPT_LONG
54 # include <getopt.h>
55 #else
56 # include "compat/getopt.h"
57 #endif
58 #include <limits.h>
59 #include <locale.h>
60
61 #include <stdio.h>
62
63 #include <signal.h>
64 #include <stdarg.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <time.h>
68 #include <unistd.h>
69
70 #include "commands.h"
71 #include "debug.h"
72 #include "plugins.h"
73 #ifndef HAVE_STRLCPY
74 # include "compat/strlcpy.h"
75 #endif
76 #include "sessions.h"
77 #include "recode.h"
78 #include "stuff.h"
79 #include "themes.h"
80 #include "userlist.h"
81 #include "vars.h"
82 #include "windows.h"
83 #include "xmalloc.h"
84
85 #include "queries.h"
86
87 /* tez sprawdzic includy */
88
89 EXPORTNOT int ekg_watches_removed;
90 static char argv0[100];
91
92 static int stderr_backup = -1;
93
94 int no_mouse = 0;
95
96 char *events_all[] = { "protocol-message", "event_avail", "event_away", "event_na", "event_online", "event_descr", NULL };
97
98 static void config_postread() {
99 query_emit_id(NULL, CONFIG_POSTINIT);
100
101 /* legacyconfig.c */
102 #if ! USE_UNICODE
103 if (!xstrcasecmp(config_console_charset, "UTF-8")) {
104 print("config_error", _("Warning, nl_langinfo(CODESET) reports that you are using utf-8 encoding, but you didn't compile ekg2 with (experimental/untested) --enable-unicode\n"
105 "\tPlease compile ekg2 with --enable-unicode or change your enviroment setting to use not utf-8 but iso-8859-1 maybe? (LC_ALL/LC_CTYPE)\n"));
106 }
107 #endif
108
109 print("remote_console_charset_using", config_console_charset);
110 }
111
112 /* configfile.c */
113 static int config_read(const char *filename) { return -1; } /* XXX, czyli przeczytac co jeszcze trzeba przeslac... */
114
115 /* sessions.c */
116 static int session_read(const char *filename) { return -1; }
117
118 static int metacontact_read() { return -1; }
119 static void metacontact_init() { }
120 static void metacontacts_destroy() { }
121
122 void *metacontacts;
123 void *metacontact_find_prio(void *m) { return NULL; }
124
125 void *newconferences;
126 void *newconference_find(session_t *s, const char *name) { return NULL; }
127 static void newconferences_destroy() { }
128
129 void *conferences;
130 static void conferences_destroy() { }
131
132 /* themes.c */
133 void changed_theme(const char *var) { }
134
135 const char *prepare_path(const char *filename, int do_mkdir) { return NULL; } /* completion z tego korzysta, zdalnie i tak nie dziala */
136 int session_check(session_t *s, int need_private, const char *protocol) { return 0; } /* dla ncursesa */
137
138 void ekg_loop() {
139 static int lock;
140
141 struct timeval stv;
142 fd_set rd, wd;
143 int ret, maxfd;
144
145 struct timeval tv;
146
147 struct timer *t; /* timery */
148 list_t l; /* watche */
149
150 if (lock) {
151 debug_error("ekg_loop() locked!\n");
152 return;
153 }
154 lock = 1;
155
156 gettimeofday(&tv, NULL);
157
158 /* przejrzyj timery u¿ytkownika, ui, skryptów */
159 for (t = timers; t; t = t->next) {
160 if (tv.tv_sec > t->ends.tv_sec || (tv.tv_sec == t->ends.tv_sec && tv.tv_usec >= t->ends.tv_usec)) {
161 int ispersist = t->persist;
162
163 if (ispersist) {
164 memcpy(&t->ends, &tv, sizeof(tv));
165 t->ends.tv_sec += (t->period / 1000);
166 t->ends.tv_usec += ((t->period % 1000) * 1000);
167 if (t->ends.tv_usec >= 1000000) {
168 t->ends.tv_usec -= 1000000;
169 t->ends.tv_sec++;
170 }
171 }
172
173 if ((t->function(0, t->data) == -1) || !ispersist)
174 t = timers_removei(t);
175
176 }
177 }
178
179 for (l = watches; l; l = l->next) {
180 watch_t *w = l->data;
181
182 if (w && w->removed == 1) {
183 w->removed = 0;
184 watch_free(w);
185 }
186 }
187
188 /* zerknij na wszystkie niezbêdne deskryptory */
189
190 FD_ZERO(&rd);
191 FD_ZERO(&wd);
192
193 for (maxfd = 0, l = watches; l; l = l->next) {
194 watch_t *w = l->data;
195 if (!w)
196 continue;
197
198 if (w->fd > maxfd)
199 maxfd = w->fd;
200 if ((w->type & WATCH_READ))
201 FD_SET(w->fd, &rd);
202 if ((w->type & WATCH_WRITE)) {
203 if (w->buf && !w->buf->len) continue; /* if we have WATCH_WRITE_LINE and there's nothink to send, ignore this */
204 FD_SET(w->fd, &wd);
205 }
206 }
207
208 stv.tv_sec = 1;
209 stv.tv_usec = 0;
210
211 for (t = timers; t; t = t->next) {
212 int usec = 0;
213
214 /* zeby uniknac przekrecenia licznika mikrosekund przy
215 * wiekszych czasach, pomijamy dlugie timery */
216 if (t->ends.tv_sec - tv.tv_sec > 1)
217 continue;
218
219 /* zobacz, ile zostalo do wywolania timera */
220 usec = (t->ends.tv_sec - tv.tv_sec) * 1000000 + (t->ends.tv_usec - tv.tv_usec);
221
222 /* jesli wiecej niz sekunda, to nie ma znacznia */
223 if (usec >= 1000000)
224 continue;
225
226 /* jesli mniej niz aktualny timeout, zmniejsz */
227 if (stv.tv_sec * 1000000 + stv.tv_usec > usec) {
228 stv.tv_sec = 0;
229 stv.tv_usec = usec;
230 }
231 }
232
233 /* na wszelki wypadek sprawd¼ warto¶ci */
234 if (stv.tv_sec != 1)
235 stv.tv_sec = 0;
236 if (stv.tv_usec < 0)
237 stv.tv_usec = 1;
238
239 /* sprawd¼, co siê dzieje */
240 ret = select(maxfd + 1, &rd, &wd, NULL, &stv);
241
242 /* je¶li wyst±pi³ b³±d, daj znaæ */
243 if (ret == -1) {
244 /* jaki¶ plugin doda³ do watchów z³y deskryptor. ¿eby
245 * ekg mog³o dzia³aæ dalej, sprawd¼my który to i go
246 * usuñmy z listy. */
247 if (errno == EBADF) {
248 list_t l;
249
250 for (l = watches; l; l = l->next) {
251 watch_t *w = l->data;
252 struct stat st;
253
254 if (w && fstat(w->fd, &st)) {
255 debug("select(): bad file descriptor: fd=%d, type=%d, plugin=%s\n", w->fd, w->type, (w->plugin) ? w->plugin->name : ("none"));
256 watch_free(w);
257 }
258 }
259 } else if (errno != EINTR)
260 debug("select() failed: %s\n", strerror(errno));
261 lock = 0;
262 return;
263 }
264
265 for (l = watches; l; l = l->next) {
266 watch_t *w = l->data;
267
268 if (!w)
269 continue;
270
271 if (((w->type == WATCH_WRITE) && FD_ISSET(w->fd, &wd)) ||
272 ((w->type == WATCH_READ) && FD_ISSET(w->fd, &rd)))
273 {
274 watch_handle(w);
275 }
276 }
277
278 if (ekg_watches_removed > 0) {
279 debug("ekg_loop() Removed %d watches this loop, let's cleanup calling: list_cleanup() ...\n", ekg_watches_removed);
280 list_cleanup(&watches);
281 ekg_watches_removed = 0;
282 }
283 lock = 0;
284 }
285
286 static void handle_sighup()
287 {
288 ekg_exit();
289 }
290
291 static void handle_sigsegv()
292 {
293 signal(SIGSEGV, SIG_DFL);
294
295 if (stderr_backup && stderr_backup != -1)
296 dup2(stderr_backup, 2);
297
298 /* wy³±cz plugin ui, ¿eby odda³ terminal */
299 if (ui_plugin)
300 plugin_unload(ui_plugin);
301
302 fprintf(stderr,
303 "\r\n"
304 "\r\n"
305 "*** Naruszenie ochrony pamiêci ***\r\n"
306 "\r\n"
307 "\r\n"
308 "Je¶li zostanie utworzony plik core.%d, spróbuj uruchomiæ\r\n"
309 "polecenie:\r\n"
310 "\r\n"
311 " gdb %s core.%d\r\n"
312 "\n"
313 "zanotowaæ kilka ostatnich linii, a nastêpnie zanotowaæ wynik polecenia\r\n"
314 ",,bt''. Dziêki temu autorzy dowiedz± siê, w którym miejscu wyst±pi³ b³±d\r\n"
315 "i najprawdopodobniej pozwoli to unikn±æ tego typu sytuacji w przysz³o¶ci.\r\n"
316 "Wiêcej szczegó³ów w dokumentacji, w pliku ,,gdb.txt''.\r\n"
317 "\r\n",
318 (int) getpid(),
319 argv0, (int) getpid());
320
321 raise(SIGSEGV); /* niech zrzuci core */
322 }
323
324 /*
325 * handle_stderr()
326 *
327 * wy¶wietla to, co uzbiera siê na stderr.
328 */
329 static WATCHER_LINE(handle_stderr) /* sta³y */
330 {
331 if (type) {
332 close(fd);
333 return 0;
334 }
335
336 /* XXX */
337 /* print("stderr", watch); */
338 return 0;
339 }
340
341 EXPORTNOT void ekg_debug_handler(int level, const char *format, va_list ap) {
342 static string_t line = NULL;
343 char *tmp;
344
345 int is_UI = 0;
346 char *theme_format;
347
348 if (!(tmp = vsaprintf(format, ap)))
349 return;
350
351 if (line) {
352 string_append(line, tmp);
353 xfree(tmp);
354
355 if (line->len == 0 || line->str[line->len - 1] != '\n')
356 return;
357
358 line->str[line->len - 1] = '\0'; /* remove '\n' */
359 tmp = string_free(line, 0);
360 line = NULL;
361 } else {
362 const size_t tmplen = strlen(tmp);
363 if (tmplen == 0 || tmp[tmplen - 1] != '\n') {
364 line = string_init(tmp);
365 xfree(tmp);
366 return;
367 }
368 tmp[tmplen - 1] = 0; /* remove '\n' */
369 }
370
371 switch(level) {
372 case 0: theme_format = "remote_debug"; break;
373 case DEBUG_IO: theme_format = "remote_iodebug"; break;
374 case DEBUG_IORECV: theme_format = "remote_iorecvdebug"; break;
375 case DEBUG_FUNCTION: theme_format = "remote_fdebug"; break;
376 case DEBUG_ERROR: theme_format = "remote_edebug"; break;
377 case DEBUG_WHITE: theme_format = "remote_wdebug"; break;
378 case DEBUG_WARN: theme_format = "remote_warndebug"; break;
379 case DEBUG_OK: theme_format = "remote_okdebug"; break;
380 default: theme_format = "remote_debug"; break;
381 }
382
383 query_emit_id(NULL, UI_IS_INITIALIZED, &is_UI);
384
385 if (is_UI && window_debug) {
386 print_window_w(window_debug, EKG_WINACT_NONE, theme_format, tmp);
387 }
388 else
389 fprintf(stderr, "%s\n", tmp);
390 #ifdef STDERR_DEBUG /* STDERR debug */
391 #endif
392
393 xfree(tmp);
394 }
395
396 struct option ekg_options[] = {
397 { "charset", required_argument, 0, 'c' },
398 { "password", required_argument, 0, 'p' },
399 { "test", required_argument, 0, 'T' },
400 { "frontend", required_argument, 0, 'F' },
401 { "no-mouse", no_argument, 0, 'm' },
402 { "unicode", no_argument, 0, 'U' },
403 { "help", no_argument, 0, 'h' },
404 { "version", no_argument, 0, 'v' },
405
406 { 0, 0, 0, 0 }
407 };
408
409 #define EKG_USAGE N_( \
410 "Usage: %s [OPTIONS] REMOTE-ENDPOINT\n" \
411 " -c, --charset=CHARSET forces charset to use\n" \
412 " -p, --password=PASSWD sets password\n" \
413 " -T, --test=NAME same as -F, but runs in test mode - for debugging\n" \
414 " -F, --frontend=NAME uses NAME frontend (default is ncurses)\n" \
415 " -m, --no-mouse does not load mouse support\n" \
416 " -U, --unicode forces unicode support\n" \
417 \
418 " -h, --help displays this help message\n" \
419 " -v, --version displays program version and exits\n" \
420 "\n" )
421
422
423 extern int remote_connect(const char *path);
424 extern int remote_connect2(int fd, const char *password);
425 extern int remote_connect3();
426 extern void remote_print_stats();
427
428 extern int remote_mail_count;
429
430 static QUERY(remote_irc_topic_helper) {
431 char **top = va_arg(ap, char **);
432 char **setby = va_arg(ap, char **);
433 char **modes = va_arg(ap, char **);
434
435 *top = xstrdup(window_current->irctopic);
436 *setby = xstrdup(window_current->irctopicby);
437 *modes = xstrdup(window_current->ircmode);
438
439 return 0;
440 }
441
442 static QUERY(remote_mail_count_helper) {
443 int *__count = va_arg(ap, int *);
444
445 *__count = remote_mail_count;
446 return 0;
447 }
448
449 int main(int argc, char **argv)
450 {
451 int c;
452 char *frontend = NULL;
453 int testonly = 0;
454
455 char *remote;
456 int remotefd;
457 char *config_password = NULL;
458
459 struct rlimit rlim;
460
461 /* zostaw po sobie core */
462 rlim.rlim_cur = RLIM_INFINITY;
463 rlim.rlim_max = RLIM_INFINITY;
464 setrlimit(RLIMIT_CORE, &rlim);
465
466 setlocale(LC_ALL, "");
467 tzset();
468
469 srand(time(NULL));
470
471 strlcpy(argv0, argv[0], sizeof(argv0));
472
473 signal(SIGSEGV, handle_sigsegv);
474 signal(SIGHUP, handle_sighup);
475 signal(SIGTERM, handle_sighup);
476 signal(SIGALRM, SIG_IGN);
477 signal(SIGPIPE, SIG_IGN);
478 signal(SIGCHLD, SIG_IGN); /* nie interesuja nas dzieci... nie powinnismy miec zadnego :) */
479
480 while ((c = getopt_long(argc, argv, "c:p:T:F:mUhv", ekg_options, NULL)) != -1)
481 {
482 switch (c) {
483 case 'c':
484 xfree(config_console_charset); /* XXX, sensowniej!, /me chce zeby sie wyswietlalo co nl_langinfo() zwrocilo, a co my podalismy. */
485 config_console_charset = xstrdup(optarg);
486 break;
487
488 case 'p':
489 config_password = optarg;
490 break;
491
492 case 'T':
493 testonly = 1;
494 frontend = optarg;
495 break;
496
497 case 'F':
498 frontend = optarg;
499 break;
500
501 case 'm':
502 no_mouse = 1;
503 break;
504
505 case 'U':
506 #ifdef USE_UNICODE
507 config_use_unicode = 1;
508 #else
509 fprintf(stderr, _("EKG2 compiled without unicode support. This just can't work!\n"));
510 return 1;
511 #endif
512 break;
513
514 case 'h':
515 printf(_(EKG_USAGE), argv[0]);
516 return 0;
517
518 case 'v':
519 printf("ekg2-%s (compiled on %s)\n", VERSION, compile_time());
520 return 0;
521
522 case '?':
523 /* supported by getopt */
524 fprintf(stdout, _("To get more information, start program with --help.\n"));
525 return 1;
526
527 default:
528 break;
529 }
530 }
531
532 if (!testonly && optind + 1 != argc) {
533 fprintf(stdout, _("no destination\n")); /* netcat-like :) */
534 return 1;
535 }
536
537 command_init();
538 variable_init();
539 theme_init();
540 /* windows: */
541 window_debug = window_new(NULL, NULL, -1); /* debugowanie */
542 window_status = window_new(NULL, NULL, 1); /* okno stanu */
543 window_current = window_status; /* XXX!!! */
544
545 if (testonly) {
546 remote = NULL; /* silence gcc warning */
547 goto _test;
548 }
549 remote = argv[optind];
550 if ((remotefd = remote_connect(remote)) == -1) {
551 perror("remote_connect()"); /* XXX, ladniejsze */
552 return 1;
553 }
554
555 printf("Connected to: %s, fd: %d\n", remote, remotefd);
556
557 if (!remote_connect2(remotefd, config_password)) {
558 perror("remote_connect2()"); /* XXX, ladniejsze */
559 return 1;
560 }
561
562 _test:
563 in_autoexec = 1;
564
565 query_connect_id(NULL, IRC_TOPIC, remote_irc_topic_helper, NULL);
566 query_connect_id(NULL, MAIL_COUNT, remote_mail_count_helper, NULL);
567
568 if (frontend)
569 plugin_load(frontend);
570
571 #ifdef HAVE_NCURSES
572 if (!ui_plugin)
573 plugin_load("ncurses");
574 #endif
575 #ifdef HAVE_GTK
576 if (!ui_plugin)
577 plugin_load("gtk");
578 #endif
579 #ifdef HAVE_READLINE
580 if (!ui_plugin)
581 plugin_load("readline");
582 #endif
583 if (!ui_plugin || ui_plugin->pclass != PLUGIN_UI) {
584 fprintf(stderr, "No UI-PLUGIN!\n");
585 ekg_exit();
586 return 0; /* never here */
587 }
588
589 if (testonly) {
590 ekg_exit();
591 return 0; /* never here */
592 }
593
594 query_emit_id(NULL, SESSION_EVENT); /* XXX, dla ncures, zeby sie statusbar odswiezyl */
595
596 config_read(NULL);
597
598 if (!remote_connect3()) {
599 perror("remote_connect3()"); /* XXX ladniejsze */
600 ekg_exit();
601 return 1;
602 }
603
604 in_autoexec = 0;
605
606 /* wypada³oby obserwowaæ stderr */
607 {
608 int fd[2];
609
610 if (!pipe(fd)) {
611 fcntl(fd[0], F_SETFL, O_NONBLOCK);
612 fcntl(fd[1], F_SETFL, O_NONBLOCK);
613 watch_add_line(NULL, fd[0], WATCH_READ_LINE, handle_stderr, NULL);
614 stderr_backup = fcntl(2, F_DUPFD, 0);
615 dup2(fd[1], 2);
616 }
617 }
618
619 if (config_display_welcome)
620 print("remote_welcome", remote);
621
622 /* protocol_init(); */ /* ekg2-remote: ignored, however we should debug_wtf() if we recv some strange query.. */
623 metacontact_init();
624 session_read(NULL);
625
626 config_postread();
627
628 /* status window takes first session if not set before*/
629 if (!session_current && sessions)
630 session_current = sessions;
631
632 if (session_current != window_current->session)
633 window_current->session = session_current;
634 window_debug->session = window_current->session;
635
636 metacontact_read(); /* read the metacontacts info */
637
638 /* jesli jest emit: ui-loop (plugin-side) to dajemy mu kontrole, jesli nie
639 * to wywolujemy normalnie sami ekg_loop() w petelce */
640 if (query_emit_id(NULL, UI_LOOP) != -1) {
641 /* krêæ imprezê */
642 while (1) {
643 ekg_loop();
644 }
645 }
646
647 ekg_exit();
648
649 return 0;
650 }
651
652 void ekg_exit() {
653 int i;
654
655 for (i = 0; i < SEND_NICKS_MAX; i++) {
656 xfree(send_nicks[i]);
657 send_nicks[i] = NULL;
658 }
659 send_nicks_count = 0;
660
661 if (ui_plugin)
662 plugin_unload(ui_plugin);
663
664 remote_plugins_destroy();
665 watches_destroy();
666
667 /* XXX, think about sequence of unloading. */
668
669 conferences_destroy();
670 newconferences_destroy();
671 metacontacts_destroy();
672 sessions_free();
673 theme_free();
674 variables_destroy();
675 commands_destroy();
676 timers_destroy();
677 binding_free();
678 remote_recode_destroy();
679
680 windows_destroy();
681 queries_destroy();
682 close(stderr_backup);
683
684 remote_print_stats();
685
686 exit(0);
687 }
688
689 /*
690 * Local Variables:
691 * mode: c
692 * c-file-style: "k&r"
693 * c-basic-offset: 8
694 * indent-tabs-mode: t
695 * End:
696 */
Something went wrong with that request. Please try again.