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