1 /*
2 This file is part of telegram-cli.
3
4 Telegram-cli is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 Telegram-cli is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this telegram-cli. If not, see <http://www.gnu.org/licenses/>.
16
17 Copyright Vitaly Valtman 2013-2015
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #ifdef USE_PYTHON
25 # include "python-tg.h"
26 #endif
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <pwd.h>
33 #include <termios.h>
34 #include <unistd.h>
35 #include <assert.h>
36 #ifdef __FreeBSD__
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #endif
40 #include <readline/readline.h>
41
42 #include <sys/stat.h>
43 #include <sys/socket.h>
44 #include <sys/un.h>
45 #include <time.h>
46 #include <fcntl.h>
47
48 #ifdef HAVE_EXECINFO_H
49 #include <execinfo.h>
50 #endif
51 #include <signal.h>
52 #ifdef HAVE_LIBCONFIG
53 #include <libconfig.h>
54 #endif
55
56 #include <grp.h>
57 #include <arpa/inet.h>
58 #include <netinet/in.h>
59
60 #include "telegram.h"
61 #include "loop.h"
62 #include "interface.h"
63 #include <tgl/tools.h>
64 #include <getopt.h>
65 #include <tgl/mtproto-key.h>
66
67 #ifdef USE_LUA
68 # include "lua-tg.h"
69 #endif
70
71
72 #include <tgl/tgl.h>
73
74 #define PROGNAME "telegram-cli"
75 #define VERSION "0.07"
76
77 #define CONFIG_DIRECTORY "." PROG_NAME
78 #define CONFIG_FILE "config"
79 #define AUTH_KEY_FILE "auth"
80 #define STATE_FILE "state"
81 #define SECRET_CHAT_FILE "secret"
82 #define DOWNLOADS_DIRECTORY "downloads"
83 #define BINLOG_FILE "binlog"
84
85
86 #define CONFIG_DIRECTORY_MODE 0700
87
88 #define DEFAULT_CONFIG_CONTENTS \
89 "# This is an empty config file\n" \
90 "# Feel free to put something here\n"
91
92 int bot_mode;
93 int verbosity;
94 int msg_num_mode;
95 char *default_username;
96 char *config_filename;
97 char *prefix;
98 char *auth_file_name;
99 char *state_file_name;
100 char *secret_chat_file_name;
101 char *downloads_directory;
102 char *config_directory;
103 char *config_directory_force;
104 char *binlog_file_name;
105 char *lua_file;
106 char *python_file;
107 int binlog_enabled;
108 extern int log_level;
109 int sync_from_start;
110 int allow_weak_random;
111 int disable_colors;
112 int readline_disabled;
113 int disable_output;
114 int reset_authorization;
115 int port;
116 int use_ids;
117 int ipv6_enabled;
118 char *start_command;
119 int disable_link_preview;
120 int enable_json;
121 int alert_sound;
122 int auto_mark_read;
123 int exit_code;
124 int permanent_msg_id_mode;
125 int permanent_peer_id_mode;
126 char *home_directory;
127
128 struct tgl_state *TLS;
129
set_default_username(const char * s)130 void set_default_username (const char *s) {
131 if (default_username) {
132 tfree_str (default_username);
133 }
134 default_username = tstrdup (s);
135 }
136
137
138 /* {{{ TERMINAL */
139 static struct termios term_in, term_out;
140 static int term_set_in;
141 static int term_set_out;
142
get_terminal_attributes(void)143 void get_terminal_attributes (void) {
144 if (tcgetattr (STDIN_FILENO, &term_in) < 0) {
145 } else {
146 term_set_in = 1;
147 }
148 if (tcgetattr (STDOUT_FILENO, &term_out) < 0) {
149 } else {
150 term_set_out = 1;
151 }
152 }
153
set_terminal_attributes(void)154 void set_terminal_attributes (void) {
155 if (term_set_in) {
156 if (tcsetattr (STDIN_FILENO, 0, &term_in) < 0) {
157 perror ("tcsetattr()");
158 }
159 }
160 if (term_set_out) {
161 if (tcsetattr (STDOUT_FILENO, 0, &term_out) < 0) {
162 perror ("tcsetattr()");
163 }
164 }
165 }
166 /* }}} */
167
str_empty(char * str)168 int str_empty (char *str) {
169 return ((str == NULL) || (strlen(str) < 1));
170 }
171
get_home_directory(void)172 char *get_home_directory (void) {
173 if (home_directory) { return home_directory; }
174 home_directory = getenv("TELEGRAM_HOME");
175 if (!str_empty (home_directory)) { return home_directory = tstrdup (home_directory); }
176 home_directory = getenv("HOME");
177 if (!str_empty (home_directory)) { return home_directory = tstrdup (home_directory); }
178 struct passwd *current_passwd;
179 uid_t user_id;
180 setpwent ();
181 user_id = getuid ();
182 while ((current_passwd = getpwent ())) {
183 if (current_passwd->pw_uid == user_id) {
184 home_directory = tstrdup (current_passwd->pw_dir);
185 break;
186 }
187 }
188 endpwent ();
189 if (str_empty (home_directory)) { home_directory = tstrdup ("."); }
190 return home_directory;
191 }
192
get_config_directory(void)193 char *get_config_directory (void) {
194 char *config_directory;
195 config_directory = getenv("TELEGRAM_CONFIG_DIR");
196 if (!str_empty (config_directory)) { return tstrdup (config_directory); }
197 // XDG: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
198 config_directory = getenv("XDG_CONFIG_HOME");
199 if (!str_empty (config_directory)) {
200 tasprintf (&config_directory, "%s/" PROG_NAME, config_directory);
201 // :TODO: someone check whether it could be required to pass tasprintf
202 // a tstrdup()ed config_directory instead; works for me without.
203 // should work b/c this scope's lifespan encompasses tasprintf()
204 return config_directory;
205 }
206 tasprintf (&config_directory, "%s/" CONFIG_DIRECTORY, get_home_directory ());
207 return config_directory;
208 }
209
get_config_filename(void)210 char *get_config_filename (void) {
211 return config_filename;
212 }
213
get_auth_key_filename(void)214 char *get_auth_key_filename (void) {
215 return auth_file_name;
216 }
217
get_state_filename(void)218 char *get_state_filename (void) {
219 return state_file_name;
220 }
221
get_secret_chat_filename(void)222 char *get_secret_chat_filename (void) {
223 return secret_chat_file_name;
224 }
225
get_downloads_directory(void)226 char *get_downloads_directory (void) {
227 return downloads_directory;
228 }
229
get_binlog_file_name(void)230 char *get_binlog_file_name (void) {
231 return binlog_file_name;
232 }
233
make_full_path(char * s)234 char *make_full_path (char *s) {
235 if (*s != '/') {
236 char *t = s;
237 tasprintf (&s, "%s/%s", get_home_directory (), s);
238 tfree_str (t);
239 }
240 return s;
241 }
242
check_type_sizes(void)243 void check_type_sizes (void) {
244 if (sizeof (int) != 4u) {
245 logprintf ("sizeof (int) isn't equal 4.\n");
246 exit (1);
247 }
248 if (sizeof (char) != 1u) {
249 logprintf ("sizeof (char) isn't equal 1.\n");
250 exit (1);
251 }
252 }
253
running_for_first_time(void)254 void running_for_first_time (void) {
255 check_type_sizes ();
256 if (!str_empty (config_filename)) {
257 return; // Do not create custom config file
258 }
259 if (str_empty (config_directory)) {
260 config_directory = get_config_directory ();
261 }
262 tasprintf (&config_filename, "%s/%s", config_directory, CONFIG_FILE);
263 config_filename = make_full_path (config_filename);
264 if (!disable_output) {
265 printf ("I: config dir=[%s]\n", config_directory);
266 }
267 // printf ("I: config file=[%s]\n", config_filename);
268
269 int config_file_fd;
270 //char *config_directory = get_config_directory ();
271 //char *downloads_directory = get_downloads_directory ();
272
273 if (!mkdir (config_directory, CONFIG_DIRECTORY_MODE)) {
274 if (!disable_output) {
275 printf ("[%s] created\n", config_directory);
276 }
277 }
278
279 tfree_str (config_directory);
280 config_directory = NULL;
281 // see if config file is there
282 if (access (config_filename, R_OK) != 0) {
283 // config file missing, so touch it
284 config_file_fd = open (config_filename, O_CREAT | O_RDWR, 0600);
285 if (config_file_fd == -1) {
286 perror ("open[config_file]");
287 printf ("I: config_file=[%s]\n", config_filename);
288 exit (EXIT_FAILURE);
289 }
290 if (write (config_file_fd, DEFAULT_CONFIG_CONTENTS, strlen (DEFAULT_CONFIG_CONTENTS)) <= 0) {
291 perror ("write[config_file]");
292 exit (EXIT_FAILURE);
293 }
294 close (config_file_fd);
295 }
296 }
297
298 #ifdef HAVE_LIBCONFIG
parse_config_val(config_t * conf,char ** s,char * param_name,const char * default_name,const char * path)299 void parse_config_val (config_t *conf, char **s, char *param_name, const char *default_name, const char *path) {
300 static char buf[1000];
301 int l = 0;
302 if (prefix) {
303 l = strlen (prefix);
304 memcpy (buf, prefix, l);
305 buf[l ++] = '.';
306 }
307 *s = 0;
308 const char *r = 0;
309 strcpy (buf + l, param_name);
310 config_lookup_string (conf, buf, &r);
311 if (r) {
312 if (path && *r != '/') {
313 tasprintf (s, "%s/%s", path, r);
314 } else {
315 *s = tstrdup (r);
316 }
317 } else {
318 if (path && default_name) {
319 tasprintf (s, "%s/%s", path, default_name);
320 } else {
321 *s = default_name ? tstrdup (default_name) : 0;
322 }
323 }
324 }
325
parse_config(void)326 void parse_config (void) {
327 //config_filename = make_full_path (config_filename);
328
329 config_t conf;
330 config_init (&conf);
331 if (config_read_file (&conf, config_filename) != CONFIG_TRUE) {
332 fprintf (stderr, "Can not read config '%s': error '%s' on the line %d\n", config_filename, config_error_text (&conf), config_error_line (&conf));
333 exit (2);
334 }
335
336 if (!prefix) {
337 config_lookup_string (&conf, "default_profile", (void *)&prefix);
338 }
339
340 static char buf[1000];
341 int l = 0;
342 if (prefix) {
343 l = strlen (prefix);
344 memcpy (buf, prefix, l);
345 buf[l ++] = '.';
346 }
347
348 int test_mode = 0;
349 strcpy (buf + l, "test");
350 config_lookup_bool (&conf, buf, &test_mode);
351 if (test_mode) {
352 tgl_set_test_mode (TLS);
353 }
354
355 strcpy (buf + l, "log_level");
356 long long t = log_level;
357 config_lookup_int (&conf, buf, (void *)&t);
358 log_level = t;
359
360 if (!msg_num_mode) {
361 strcpy (buf + l, "msg_num");
362 config_lookup_bool (&conf, buf, &msg_num_mode);
363 }
364
365 if( config_directory_force ){
366 config_directory = config_directory_force;
367
368 if( !disable_output ){
369 printf( "Config directory force: [%s]\n", config_directory_force );
370 }
371 }else{
372 parse_config_val (&conf, &config_directory, "config_directory", CONFIG_DIRECTORY, 0);
373 config_directory = make_full_path (config_directory);
374 }
375
376 parse_config_val (&conf, &auth_file_name, "auth_file", AUTH_KEY_FILE, config_directory);
377 parse_config_val (&conf, &downloads_directory, "downloads", DOWNLOADS_DIRECTORY, config_directory);
378
379 if (!lua_file) {
380 parse_config_val (&conf, &lua_file, "lua_script", 0, config_directory);
381 }
382
383 if (!python_file) {
384 parse_config_val (&conf, &python_file, "python_script", 0, config_directory);
385 }
386
387 #if 0
388 strcpy (buf + l, "binlog_enabled");
389 config_lookup_bool (&conf, buf, &binlog_enabled);
390 #else
391 binlog_enabled = 0;
392 #endif
393
394 int pfs_enabled = 0;
395 strcpy (buf + l, "pfs_enabled");
396 config_lookup_bool (&conf, buf, &pfs_enabled);
397 if (pfs_enabled) {
398 tgl_enable_pfs (TLS);
399 }
400
401 if (binlog_enabled) {
402 parse_config_val (&conf, &binlog_file_name, "binlog", BINLOG_FILE, config_directory);
403 tgl_set_binlog_mode (TLS, 1);
404 tgl_set_binlog_path (TLS, binlog_file_name);
405 } else {
406 tgl_set_binlog_mode (TLS, 0);
407 parse_config_val (&conf, &state_file_name, "state_file", STATE_FILE, config_directory);
408 parse_config_val (&conf, &secret_chat_file_name, "secret", SECRET_CHAT_FILE, config_directory);
409 //tgl_set_auth_file_path (auth_file_name);
410 }
411 tgl_set_download_directory (TLS, downloads_directory);
412
413 if (!mkdir (config_directory, CONFIG_DIRECTORY_MODE)) {
414 if (!disable_output) {
415 printf ("[%s] created\n", config_directory);
416 }
417 }
418 if (!mkdir (downloads_directory, CONFIG_DIRECTORY_MODE)) {
419 if (!disable_output) {
420 printf ("[%s] created\n", downloads_directory);
421 }
422 }
423 tfree_str (config_directory);
424 config_directory = NULL;
425 config_destroy (&conf);
426 }
427 #else
parse_config(void)428 void parse_config (void) {
429 if (!disable_output) {
430 printf ("libconfig not enabled\n");
431 }
432 tasprintf (&downloads_directory, "%s/%s/%s", get_home_directory (), CONFIG_DIRECTORY, DOWNLOADS_DIRECTORY);
433
434 if (binlog_enabled) {
435 tasprintf (&binlog_file_name, "%s/%s/%s", get_home_directory (), CONFIG_DIRECTORY, BINLOG_FILE);
436 tgl_set_binlog_mode (TLS, 1);
437 tgl_set_binlog_path (TLS, binlog_file_name);
438 } else {
439 tgl_set_binlog_mode (TLS, 0);
440 //tgl_set_auth_file_path (auth_file_name;
441 tasprintf (&auth_file_name, "%s/%s/%s", get_home_directory (), CONFIG_DIRECTORY, AUTH_KEY_FILE);
442 tasprintf (&state_file_name, "%s/%s/%s", get_home_directory (), CONFIG_DIRECTORY, STATE_FILE);
443 tasprintf (&secret_chat_file_name, "%s/%s/%s", get_home_directory (), CONFIG_DIRECTORY, SECRET_CHAT_FILE);
444 }
445 tgl_set_download_directory (TLS, downloads_directory);
446 if (!mkdir (downloads_directory, CONFIG_DIRECTORY_MODE)) {
447 if (!disable_output) {
448 printf ("[%s] created\n", downloads_directory);
449 }
450 }
451 }
452 #endif
453
inner_main(void)454 void inner_main (void) {
455 loop ();
456 }
457
usage(void)458 void usage (void) {
459 printf ("%s Usage\n", PROGNAME);
460
461 printf (" --phone/-u specify username (would not be asked during authorization)\n");
462 printf (" --rsa-key/-k specify location of public key (possible multiple entries)\n");
463 printf (" --verbosity/-v increase verbosity (0-ERROR 1-WARNIN 2-NOTICE 3+-DEBUG-levels)\n");
464 printf (" --enable-msg-id/-N message num mode\n");
465 #ifdef HAVE_LIBCONFIG
466 printf (" --config/-c config file name\n");
467 printf (" --force-config-dir/-x set force config dir instead of profile\n");
468 printf (" --profile/-p use specified profile\n");
469 #else
470 #if 0
471 printf (" --enable-binlog/-B enable binlog\n");
472 #endif
473 #endif
474 printf (" --log-level/-l log level\n");
475 printf (" --sync-from-start/-f during authorization fetch all messages since registration\n");
476 printf (" --disable-auto-accept/-E disable auto accept of encrypted chats\n");
477 #ifdef USE_LUA
478 printf (" --lua-script/-s lua script file\n");
479 #endif
480 printf (" --wait-dialog-list/-W send dialog_list query and wait for answer before reading input\n");
481 printf (" --disable-colors/-C disable color output\n");
482 printf (" --disable-readline/-R disable readline\n");
483 printf (" --alert/-A enable bell notifications\n");
484 printf (" --auto-mark-read/-M mark read conversation after sending a message\n");
485 printf (" --daemonize/-d daemon mode\n");
486 printf (" --logname/-L <log-name> log file name\n");
487 printf (" --username/-U <user-name> change uid after start\n");
488 printf (" --groupname/-G <group-name> change gid after start\n");
489 printf (" --disable-output/-D disable output\n");
490 printf (" --tcp-port/-P <port> port to listen for input commands\n");
491 printf (" --unix-socket/-S <socket-name> unix socket to create\n");
492 printf (" --exec/-e <command> make a single command end then exit safely\n");
493 printf (" --disable-names/-I use user and chat IDs in updates instead of names\n");
494 printf (" --enable-ipv6/-6 use ipv6 (may be unstable)\n");
495 printf (" --help/-h prints this help\n");
496 printf (" --accept-any-tcp accepts tcp connections from any src (only loopback by default)\n");
497 printf (" --disable-link-preview disables server-side previews to links\n");
498 printf (" --bot/-b bot mode\n");
499 #ifdef USE_JSON
500 printf (" --json prints answers and values in json format\n");
501 #endif
502 #ifdef USE_PYTHON
503 printf (" --python-script/-Z <script-name> python script file\n");
504 #endif
505 printf (" --permanent-msg-ids use permanent msg ids\n");
506 printf (" --permanent-peer-ids use permanent peer ids\n");
507
508 exit (1);
509 }
510
511 //extern char *rsa_public_key_name;
512 //extern int default_dc_num;
513
514
515
516
517 char *log_net_file;
518 FILE *log_net_f;
519
520 int register_mode;
521 int disable_auto_accept;
522 int wait_dialog_list;
523
524 char *logname;
525 int daemonize=0;
526
527
reopen_logs(void)528 void reopen_logs (void) {
529 int fd;
530 fflush (stdout);
531 fflush (stderr);
532 if ((fd = open ("/dev/null", O_RDWR, 0)) != -1) {
533 dup2 (fd, 0);
534 dup2 (fd, 1);
535 dup2 (fd, 2);
536 if (fd > 2) {
537 close (fd);
538 }
539 }
540 if (logname && (fd = open (logname, O_WRONLY|O_APPEND|O_CREAT, 0640)) != -1) {
541 dup2 (fd, 1);
542 dup2 (fd, 2);
543 if (fd > 2) {
544 close (fd);
545 }
546 }
547 }
548
549
sigusr1_handler(const int sig)550 static void sigusr1_handler (const int sig) {
551 fprintf(stderr, "got SIGUSR1, rotate logs.\n");
552 reopen_logs ();
553 signal (SIGUSR1, sigusr1_handler);
554 }
555
sighup_handler(const int sig)556 static void sighup_handler (const int sig) {
557 fprintf(stderr, "got SIGHUP.\n");
558 signal (SIGHUP, sighup_handler);
559 }
560
561 char *set_user_name;
562 char *set_group_name;
563 int accept_any_tcp;
564 char *bot_hash;
565
change_user_group()566 int change_user_group () {
567 char *username = set_user_name;
568 char *groupname = set_group_name;
569 struct passwd *pw;
570 /* lose root privileges if we have them */
571 if (getuid() == 0 || geteuid() == 0) {
572 if (username == 0 || *username == '\0') {
573 username = "telegramd";
574 }
575 if ((pw = getpwnam (username)) == 0) {
576 fprintf (stderr, "change_user_group: can't find the user %s to switch to\n", username);
577 return -1;
578 }
579 gid_t gid = pw->pw_gid;
580 if (setgroups (1, &gid) < 0) {
581 fprintf (stderr, "change_user_group: failed to clear supplementary groups list: %m\n");
582 return -1;
583 }
584
585 if (groupname) {
586 struct group *g = getgrnam (groupname);
587 if (g == NULL) {
588 fprintf (stderr, "change_user_group: can't find the group %s to switch to\n", groupname);
589 return -1;
590 }
591 gid = g->gr_gid;
592 }
593
594 if (setgid (gid) < 0) {
595 fprintf (stderr, "change_user_group: setgid (%d) failed. %m\n", (int) gid);
596 return -1;
597 }
598
599 if (setuid (pw->pw_uid) < 0) {
600 fprintf (stderr, "change_user_group: failed to assume identity of user %s\n", username);
601 return -1;
602 } else {
603 pw = getpwuid(getuid());
604 setenv("USER", pw->pw_name, 1);
605 setenv("HOME", pw->pw_dir, 1);
606 setenv("SHELL", pw->pw_shell, 1);
607 }
608 }
609 return 0;
610 }
611
612 char *unix_socket;
613
args_parse(int argc,char ** argv)614 void args_parse (int argc, char **argv) {
615 TLS = tgl_state_alloc ();
616
617 static struct option long_options[] = {
618 {"debug-allocator", no_argument, 0, 1000 },
619 {"phone", required_argument, 0, 'u'},
620 {"rsa-key", required_argument, 0, 'k'},
621 {"verbosity", no_argument, 0, 'v'},
622 {"enable-msg-id", no_argument, 0, 'N'},
623 #ifdef HAVE_LIBCONFIG
624 {"config", required_argument, 0, 'c'},
625 {"force-config-dir", required_argument, 0, 'X'},
626 {"profile", required_argument, 0, 'p'},
627 #else
628 #if 0
629 {"enable-binlog", no_argument, 0, 'B'},
630 #endif
631 #endif
632 {"log-level", required_argument, 0, 'l'},
633 {"sync-from-start", no_argument, 0, 'f'},
634 {"disable-auto-accept", no_argument, 0, 'E'},
635 {"allow-weak-random", no_argument, 0, 'w'},
636 #ifdef USE_LUA
637 {"lua-script", required_argument, 0, 's'},
638 #endif
639 {"wait-dialog-list", no_argument, 0, 'W'},
640 {"disable-colors", no_argument, 0, 'C'},
641 {"disable-readline", no_argument, 0, 'R'},
642 {"alert", no_argument, 0, 'A'},
643 {"auto-mark-read", no_argument, 0, 'M'},
644 {"daemonize", no_argument, 0, 'd'},
645 {"logname", required_argument, 0, 'L'},
646 {"username", required_argument, 0, 'U'},
647 {"groupname", required_argument, 0, 'G'},
648 {"disable-output", no_argument, 0, 'D'},
649 {"reset-authorization", no_argument, 0, 'q'},
650 {"tcp-port", required_argument, 0, 'P'},
651 {"unix-socket", required_argument, 0, 'S'},
652 {"exec", required_argument, 0, 'e'},
653 {"disable-names", no_argument, 0, 'I'},
654 {"enable-ipv6", no_argument, 0, '6'},
655 {"bot", optional_argument, 0, 'b'},
656 {"help", no_argument, 0, 'h'},
657 {"accept-any-tcp", no_argument, 0, 1001},
658 {"disable-link-preview", no_argument, 0, 1002},
659 {"json", no_argument, 0, 1003},
660 {"python-script", required_argument, 0, 'Z'},
661 {"permanent-msg-ids", no_argument, 0, 1004},
662 {"permanent-peer-ids", no_argument, 0, 1005},
663 {0, 0, 0, 0 }
664 };
665
666
667
668 int opt = 0;
669 while ((opt = getopt_long (argc, argv, "u:hk:vNl:fEwWCRAMdL:DU:G:qP:X:S:e:I6b"
670 #ifdef HAVE_LIBCONFIG
671 "c:p:"
672 #else
673 #if 0
674 "B"
675 #endif
676 #endif
677 #ifdef USE_LUA
678 "s:"
679 #endif
680 #ifdef USE_PYTHON
681 "Z:"
682 #endif
683 , long_options, NULL
684
685 )) != -1) {
686 switch (opt) {
687 case 'b':
688 bot_mode ++;
689 if (optarg) {
690 bot_hash = optarg;
691 }
692 break;
693 case 1000:
694 tgl_allocator = &tgl_allocator_debug;
695 break;
696 case 1001:
697 accept_any_tcp = 1;
698 break;
699 case 'u':
700 set_default_username (optarg);
701 break;
702 case 'k':
703 //rsa_public_key_name = tstrdup (optarg);
704 tgl_set_rsa_key (TLS, optarg);
705 break;
706 case 'v':
707 tgl_incr_verbosity (TLS);
708 verbosity ++;
709 break;
710 case 'N':
711 msg_num_mode ++;
712 break;
713 #ifdef HAVE_LIBCONFIG
714 case 'c':
715 config_filename = tstrdup (optarg);
716 break;
717 case 'X':
718 config_directory_force = tstrdup (optarg);
719 break;
720 case 'p':
721 prefix = optarg;
722 assert (strlen (prefix) <= 100);
723 break;
724 #else
725 #if 0
726 case 'B':
727 binlog_enabled = 1;
728 break;
729 #endif
730 #endif
731 case 'l':
732 log_level = atoi (optarg);
733 break;
734 case 'f':
735 sync_from_start = 1;
736 break;
737 case 'E':
738 disable_auto_accept = 1;
739 break;
740 case 'w':
741 allow_weak_random = 1;
742 break;
743 #ifdef USE_LUA
744 case 's':
745 lua_file = strdup (optarg);
746 break;
747 #endif
748 case 'W':
749 wait_dialog_list = 1;
750 break;
751 #ifdef USE_PYTHON
752 case 'Z':
753 python_file = strdup (optarg);
754 break;
755 #endif
756 case 'C':
757 disable_colors ++;
758 break;
759 case 'R':
760 readline_disabled ++;
761 break;
762 case 'A':
763 alert_sound = 1;
764 break;
765 case 'M':
766 auto_mark_read = 1;
767 break;
768 case 'd':
769 daemonize ++;
770 break;
771 case 'L':
772 logname = optarg;
773 break;
774 case 'U':
775 set_user_name = optarg;
776 break;
777 case 'G':
778 set_group_name = optarg;
779 break;
780 case 'D':
781 disable_output ++;
782 break;
783 case 'q':
784 reset_authorization ++;
785 break;
786 case 'P':
787 port = atoi (optarg);
788 break;
789 case 'S':
790 unix_socket = optarg;
791 break;
792 case 'e':
793 start_command = optarg;
794 break;
795 case 'I':
796 use_ids ++;
797 break;
798 case '6':
799 ipv6_enabled = 1;
800 break;
801 case 1002:
802 disable_link_preview = 2;
803 break;
804 case 1003:
805 enable_json = 1;
806 break;
807 case 1004:
808 permanent_msg_id_mode = 1;
809 break;
810 case 1005:
811 permanent_peer_id_mode = 1;
812 break;
813 case 'h':
814 default:
815 usage ();
816 break;
817 }
818 }
819 }
820
821 #ifdef HAVE_EXECINFO_H
print_backtrace(void)822 void print_backtrace (void) {
823 void *buffer[255];
824 const int calls = backtrace (buffer, sizeof (buffer) / sizeof (void *));
825 backtrace_symbols_fd (buffer, calls, 1);
826 }
827 #else
print_backtrace(void)828 void print_backtrace (void) {
829 if (write (1, "No libexec. Backtrace disabled\n", 32) < 0) {
830 // Sad thing
831 }
832 }
833 #endif
834
835 int sfd;
836 int usfd;
837
termination_signal_handler(int signum)838 void termination_signal_handler (int signum) {
839 if (!readline_disabled) {
840 rl_free_line_state ();
841 rl_cleanup_after_signal ();
842 }
843
844 if (write (1, "SIGNAL received\n", 18) < 0) {
845 // Sad thing
846 }
847
848 if (unix_socket) {
849 unlink (unix_socket);
850 }
851
852 if (usfd > 0) {
853 close (usfd);
854 }
855 if (sfd > 0) {
856 close (sfd);
857 }
858 print_backtrace ();
859
860 exit (EXIT_FAILURE);
861 }
862
863 volatile int sigterm_cnt;
864
sig_term_handler(int signum)865 void sig_term_handler (int signum __attribute__ ((unused))) {
866 signal (signum, termination_signal_handler);
867 //set_terminal_attributes ();
868 if (write (1, "SIGTERM/SIGINT received\n", 25) < 0) {
869 // Sad thing
870 }
871 //if (TLS && TLS->ev_base) {
872 // event_base_loopbreak (TLS->ev_base);
873 //}
874 sigterm_cnt ++;
875 }
876
do_halt(int error)877 void do_halt (int error) {
878 int retval;
879 if (daemonize) {
880 return;
881 }
882
883 if (!readline_disabled) {
884 rl_free_line_state ();
885 rl_cleanup_after_signal ();
886 }
887
888 if (write (1, "halt\n", 5) < 0) {
889 // Sad thing
890 }
891
892 if (unix_socket) {
893 unlink (unix_socket);
894 }
895
896 if (usfd > 0) {
897 close (usfd);
898 }
899 if (sfd > 0) {
900 close (sfd);
901 }
902
903 if (exit_code) {
904 retval = exit_code;
905 } else {
906 retval = error ? EXIT_FAILURE : EXIT_SUCCESS;
907 }
908
909 exit (retval);
910 }
911
main(int argc,char ** argv)912 int main (int argc, char **argv) {
913 signal (SIGSEGV, termination_signal_handler);
914 signal (SIGABRT, termination_signal_handler);
915 signal (SIGBUS, termination_signal_handler);
916 signal (SIGQUIT, termination_signal_handler);
917 signal (SIGFPE, termination_signal_handler);
918
919 signal (SIGPIPE, SIG_IGN);
920
921 signal (SIGTERM, sig_term_handler);
922 signal (SIGINT, sig_term_handler);
923
924 rl_catch_signals = 0;
925
926
927 log_level = 10;
928
929 args_parse (argc, argv);
930
931 change_user_group ();
932
933 if (port > 0) {
934 struct sockaddr_in serv_addr;
935 int yes = 1;
936 sfd = socket (AF_INET, SOCK_STREAM, 0);
937 if (sfd < 0) {
938 perror ("socket");
939 exit(1);
940 }
941
942 if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
943 perror("setsockopt");
944 exit(1);
945 }
946 memset (&serv_addr, 0, sizeof (serv_addr));
947
948 serv_addr.sin_family = AF_INET;
949 serv_addr.sin_addr.s_addr = accept_any_tcp ? INADDR_ANY : htonl (0x7f000001);
950 serv_addr.sin_port = htons (port);
951
952 if (bind (sfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
953 perror ("bind");
954 exit(1);
955 }
956
957 listen (sfd, 5);
958 } else {
959 sfd = -1;
960 }
961
962 if (unix_socket) {
963 assert (strlen (unix_socket) < 100);
964 struct sockaddr_un serv_addr;
965
966 usfd = socket (AF_UNIX, SOCK_STREAM, 0);
967 if (usfd < 0) {
968 perror ("socket");
969 exit(1);
970 }
971
972 memset (&serv_addr, 0, sizeof (serv_addr));
973
974 serv_addr.sun_family = AF_UNIX;
975
976 snprintf (serv_addr.sun_path, sizeof(serv_addr.sun_path), "%s", unix_socket);
977
978 if (bind (usfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
979 perror ("bind");
980 exit(1);
981 }
982
983 listen (usfd, 5);
984 } else {
985 usfd = -1;
986 }
987
988 if (daemonize) {
989 signal (SIGHUP, sighup_handler);
990 reopen_logs ();
991 }
992 signal (SIGUSR1, sigusr1_handler);
993
994 if (!disable_output) {
995 printf (
996 "Telegram-cli version " TELEGRAM_CLI_VERSION ", Copyright (C) 2013-2015 Vitaly Valtman\n"
997 "Telegram-cli comes with ABSOLUTELY NO WARRANTY; for details type `show_license'.\n"
998 "This is free software, and you are welcome to redistribute it\n"
999 "under certain conditions; type `show_license' for details.\n"
1000 "Telegram-cli uses libtgl version " TGL_VERSION "\n"
1001 #ifndef TGL_AVOID_OPENSSL
1002 "Telegram-cli includes software developed by the OpenSSL Project\n"
1003 "for use in the OpenSSL Toolkit. (http://www.openssl.org/)\n"
1004 #endif
1005 #ifdef USE_PYTHON
1006 "Telegram-cli uses libpython version " PY_VERSION "\n"
1007 #endif
1008 );
1009 }
1010 running_for_first_time ();
1011 parse_config ();
1012
1013 #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__))
1014 tgl_set_rsa_key (TLS, "/usr/local/etc/" PROG_NAME "/server.pub");
1015 #else
1016 tgl_set_rsa_key (TLS, "/etc/" PROG_NAME "/server.pub");
1017 #endif
1018 tgl_set_rsa_key (TLS, "tg-server.pub");
1019
1020 tgl_set_rsa_key_direct (TLS, tglmp_get_default_e (), tglmp_get_default_key_len (), tglmp_get_default_key ());
1021
1022 get_terminal_attributes ();
1023
1024 #ifdef USE_LUA
1025 if (lua_file) {
1026 lua_init (lua_file);
1027 }
1028 #endif
1029 #ifdef USE_PYTHON
1030 if (python_file) {
1031 py_init (python_file);
1032 }
1033 #endif
1034
1035
1036 inner_main ();
1037
1038 return 0;
1039 }
1040