diff --git a/Makefile b/Makefile index 11a112b..bd9b42a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC=cc CFLAGS=-c -Wall -Wextra -Werror -fPIC -ggdb -O2 -fno-omit-frame-pointer -fno-strict-aliasing -rdynamic -LDFLAGS=-lreadline -lssl -lcrypto -lrt -lz -ggdb -rdynamic +LDFLAGS=-lreadline -lssl -lcrypto -lrt -lz -lconfig -ggdb -rdynamic LD=cc SRC=main.c loop.c interface.c net.c mtproto-common.c mtproto-client.c queries.c structures.c diff --git a/loop.c b/loop.c index b7833a7..8644877 100644 --- a/loop.c +++ b/loop.c @@ -43,6 +43,7 @@ extern char *default_username; extern char *auth_token; +extern int test_dc; void set_default_username (const char *s); int default_dc_num; @@ -188,7 +189,7 @@ void read_dc (int auth_file_fd, int id, unsigned ver) { } void empty_auth_file (void) { - struct dc *DC = alloc_dc (1, strdup (TG_SERVER), 443); + struct dc *DC = alloc_dc (1, strdup (test_dc ? TG_SERVER_TEST : TG_SERVER), 443); assert (DC); dc_working_num = 1; auth_state = 0; diff --git a/main.c b/main.c index 0bd5580..bfe9e62 100644 --- a/main.c +++ b/main.c @@ -16,6 +16,7 @@ Copyright Vitaly Valtman 2013 */ +#define _GNU_SOURCE #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include "loop.h" #include "mtproto-client.h" @@ -38,12 +40,12 @@ #define PROGNAME "telegram-client" #define VERSION "0.01" -#define CONFIG_DIRECTORY ".telegram/" -#define CONFIG_FILE CONFIG_DIRECTORY "config" -#define AUTH_KEY_FILE CONFIG_DIRECTORY "auth" -#define STATE_FILE CONFIG_DIRECTORY "state" -#define SECRET_CHAT_FILE CONFIG_DIRECTORY "secret" -#define DOWNLOADS_DIRECTORY "downloads/" +#define CONFIG_DIRECTORY ".telegram" +#define CONFIG_FILE "config" +#define AUTH_KEY_FILE "auth" +#define STATE_FILE "state" +#define SECRET_CHAT_FILE "secret" +#define DOWNLOADS_DIRECTORY "downloads" #define CONFIG_DIRECTORY_MODE 0700 @@ -52,9 +54,16 @@ "# Feel free to put something here\n" char *default_username; -int setup_mode; char *auth_token; int msg_num_mode; +char *config_filename; +char *prefix; +int test_dc; +char *auth_file_name; +char *state_file_name; +char *secret_chat_file_name; +char *downloads_directory; +char *config_directory; void set_default_username (const char *s) { if (default_username) { @@ -63,11 +72,6 @@ void set_default_username (const char *s) { default_username = strdup (s); } -void set_setup_mode (void) { - setup_mode = 1; -} - - /* {{{ TERMINAL */ tcflag_t old_lflag; cc_t old_vtime; @@ -100,7 +104,7 @@ char *get_home_directory (void) { return current_passwd->pw_dir; } } - return 0; + return ""; } char *get_config_directory (void) { @@ -115,63 +119,47 @@ char *get_config_directory (void) { } char *get_config_filename (void) { - char *config_filename; - int length = strlen (get_home_directory ()) + strlen (CONFIG_FILE) + 2; - - config_filename = (char *) calloc (length, sizeof (char)); - sprintf (config_filename, "%s/" CONFIG_FILE, get_home_directory ()); return config_filename; } char *get_auth_key_filename (void) { - char *auth_key_filename; - int length = strlen (get_home_directory ()) + strlen (AUTH_KEY_FILE) + 2; - - auth_key_filename = (char *) calloc (length, sizeof (char)); - sprintf (auth_key_filename, "%s/" AUTH_KEY_FILE, get_home_directory ()); - return auth_key_filename; + return auth_file_name; } char *get_state_filename (void) { - char *state_filename; - int length = strlen (get_home_directory ()) + strlen (STATE_FILE) + 2; - - state_filename = (char *) calloc (length, sizeof (char)); - sprintf (state_filename, "%s/" STATE_FILE, get_home_directory ()); - return state_filename; + return state_file_name; } char *get_secret_chat_filename (void) { - char *secret_chat_filename; - int length = strlen (get_home_directory ()) + strlen (SECRET_CHAT_FILE) + 2; - - secret_chat_filename = (char *) calloc (length, sizeof (char)); - sprintf (secret_chat_filename, "%s/" SECRET_CHAT_FILE, get_home_directory ()); - return secret_chat_filename; + return secret_chat_file_name; } -char *get_downloads_directory (void) -{ - char *downloads_directory; - int length = strlen (get_config_directory ()) + strlen (DOWNLOADS_DIRECTORY) + 2; - - downloads_directory = (char *) calloc (length, sizeof (char)); - sprintf (downloads_directory, "%s/" DOWNLOADS_DIRECTORY, get_config_directory ()); - +char *get_downloads_directory (void) { return downloads_directory; } +char *make_full_path (char *s) { + if (*s != '/') { + char *t = s; + assert (asprintf (&s, "%s/%s", get_home_directory (), s) >= 0); + free (t); + } + return s; +} + void running_for_first_time (void) { + if (config_filename) { + return; // Do not create custom config file + } + assert (asprintf (&config_filename, "%s/%s/%s", get_home_directory (), CONFIG_DIRECTORY, CONFIG_FILE) >= 0); + config_filename = make_full_path (config_filename); + struct stat *config_file_stat = NULL; int config_file_fd; char *config_directory = get_config_directory (); - char *config_filename = get_config_filename (); - char *downloads_directory = get_downloads_directory (); + //char *downloads_directory = get_downloads_directory (); - if (mkdir (config_directory, CONFIG_DIRECTORY_MODE) != 0) { - return; - } else { - printf ("\nRunning " PROGNAME " for first time!!\n"); + if (!mkdir (config_directory, CONFIG_DIRECTORY_MODE)) { printf ("[%s] created\n", config_directory); } @@ -192,21 +180,92 @@ void running_for_first_time (void) { exit (EXIT_FAILURE); } close (config_file_fd); - int auth_file_fd = open (get_auth_key_filename (), O_CREAT | O_RDWR, 0600); + /*int auth_file_fd = open (get_auth_key_filename (), O_CREAT | O_RDWR, 0600); int x = -1; assert (write (auth_file_fd, &x, 4) == 4); close (auth_file_fd); - printf ("[%s] created\n", config_filename); + printf ("[%s] created\n", config_filename);*/ /* create downloads directory */ - if (mkdir (downloads_directory, 0755) !=0) { + /*if (mkdir (downloads_directory, 0755) !=0) { perror ("creating download directory"); exit (EXIT_FAILURE); + }*/ + } +} + +void parse_config_val (config_t *conf, char **s, char *param_name, const char *default_name, const char *path) { + static char buf[1000]; + int l = 0; + if (prefix) { + l = strlen (prefix); + memcpy (buf, prefix, l); + buf[l ++] = '.'; + } + *s = 0; + const char *r = 0; + strcpy (buf + l, param_name); + config_lookup_string (conf, buf, &r); + if (r) { + if (path) { + assert (asprintf (s, "%s/%s", path, r) >= 0); + } else { + *s = strdup (r); + } + } else { + if (path) { + assert (asprintf (s, "%s/%s", path, default_name) >= 0); + } else { + *s = strdup (default_name); } } +} - set_setup_mode (); +void parse_config (void) { + config_filename = make_full_path (config_filename); + + config_t conf; + config_init (&conf); + if (config_read_file (&conf, config_filename) != CONFIG_TRUE) { + fprintf (stderr, "Can not read config '%s': error '%s' on the line %d\n", config_filename, config_error_text (&conf), config_error_line (&conf)); + exit (2); + } + + if (!prefix) { + config_lookup_string (&conf, "default_profile", (void *)&prefix); + } + + static char buf[1000]; + int l = 0; + if (prefix) { + l = strlen (prefix); + memcpy (buf, prefix, l); + buf[l ++] = '.'; + } + test_dc = 0; + strcpy (buf + l, "test"); + config_lookup_bool (&conf, buf, &test_dc); + + if (!msg_num_mode) { + strcpy (buf + l, "msg_num"); + config_lookup_bool (&conf, buf, &msg_num_mode); + } + + parse_config_val (&conf, &config_directory, "config_directory", CONFIG_DIRECTORY, 0); + config_directory = make_full_path (config_directory); + + parse_config_val (&conf, &auth_file_name, "auth_file", AUTH_KEY_FILE, config_directory); + parse_config_val (&conf, &state_file_name, "state_file", STATE_FILE, config_directory); + parse_config_val (&conf, &secret_chat_file_name, "secret", SECRET_CHAT_FILE, config_directory); + parse_config_val (&conf, &downloads_directory, "downloads", DOWNLOADS_DIRECTORY, config_directory); + + if (!mkdir (config_directory, CONFIG_DIRECTORY_MODE)) { + printf ("[%s] created\n", config_directory); + } + if (!mkdir (downloads_directory, CONFIG_DIRECTORY_MODE)) { + printf ("[%s] created\n", downloads_directory); + } } void inner_main (void) { @@ -214,7 +273,7 @@ void inner_main (void) { } void usage (void) { - printf ("%s [-u username] [-h] [-k public key name]\n", PROGNAME); + printf ("%s [-u username] [-h] [-k public key name] [-N] [-v]\n", PROGNAME); exit (1); } @@ -224,7 +283,7 @@ extern int default_dc_num; void args_parse (int argc, char **argv) { int opt = 0; - while ((opt = getopt (argc, argv, "u:hk:vn:N")) != -1) { + while ((opt = getopt (argc, argv, "u:hk:vn:Nc:p:")) != -1) { switch (opt) { case 'u': set_default_username (optarg); @@ -235,12 +294,16 @@ void args_parse (int argc, char **argv) { case 'v': verbosity ++; break; - case 'n': - default_dc_num = atoi (optarg); - break; case 'N': msg_num_mode ++; break; + case 'c': + config_filename = strdup (optarg); + break; + case 'p': + prefix = strdup (optarg); + assert (strlen (prefix) <= 100); + break; case 'h': default: usage (); @@ -266,18 +329,20 @@ void sig_handler (int signum) { int main (int argc, char **argv) { signal (SIGSEGV, sig_handler); signal (SIGABRT, sig_handler); - running_for_first_time (); - + + args_parse (argc, argv); printf ( "Telegram-client version " TG_VERSION ", Copyright (C) 2013 Vitaly Valtman\n" "Telegram-client comes with ABSOLUTELY NO WARRANTY; for details type `show_license'.\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions; type `show_license' for details.\n" ); + running_for_first_time (); + parse_config (); + get_terminal_attributes (); - args_parse (argc, argv); inner_main (); diff --git a/mtproto-client.c b/mtproto-client.c index 3166308..23b4063 100644 --- a/mtproto-client.c +++ b/mtproto-client.c @@ -1361,8 +1361,9 @@ int process_rpc_message (struct connection *c UU, struct encrypted_message *enc, double st = get_server_time (DC); assert (this_server_time >= st - 300 && this_server_time <= st + 30); //assert (enc->msg_id > server_last_msg_id && (enc->msg_id & 3) == 1); - if (verbosity >= 2) { + if (verbosity >= 1) { logprintf ( "received mesage id %016llx\n", enc->msg_id); + hexdump_in (); } server_last_msg_id = enc->msg_id; @@ -1390,10 +1391,10 @@ int rpc_execute (struct connection *c, int op, int len) { if (verbosity) { logprintf ( "outbound rpc connection #%d : received rpc answer %d with %d content bytes\n", c->fd, op, len); } - if (op < 0) { +/* if (op < 0) { assert (read_in (c, Response, Response_len) == Response_len); return 0; - } + }*/ if (len >= MAX_RESPONSE_SIZE/* - 12*/ || len < 0/*12*/) { logprintf ( "answer too long (%d bytes), skipping\n", len); diff --git a/net.c b/net.c index 9ae8215..a17e3bb 100644 --- a/net.c +++ b/net.c @@ -146,7 +146,7 @@ int read_in (struct connection *c, void *data, int len) { int x = 0; while (len) { int y = c->in_head->wptr - c->in_head->rptr; - if (y >= len) { + if (y > len) { memcpy (data, c->in_head->rptr, len); c->in_head->rptr += len; c->in_bytes -= len; @@ -185,6 +185,7 @@ int read_in_lookup (struct connection *c, void *data, int len) { memcpy (data, b->rptr, y); x += y; data += y; + len -= y; b = b->next; } } @@ -405,7 +406,7 @@ void hexdump_buf (struct connection_buffer *b) { void try_rpc_read (struct connection *c) { assert (c->in_head); - if (verbosity >= 4) { + if (verbosity >= 1) { hexdump_buf (c->in_head); } diff --git a/net.h b/net.h index 6061e7b..343f0a7 100644 --- a/net.h +++ b/net.h @@ -23,7 +23,7 @@ struct dc; #include "queries.h" #define TG_SERVER "173.240.5.1" -//#define TG_SERVER "95.142.192.66" +#define TG_SERVER_TEST "173.240.5.253" #define TG_APP_HASH "36722c72256a24c1225de00eb6a1ca74" #define TG_APP_ID 2899 #define TG_BUILD "203" diff --git a/queries.c b/queries.c index 85b4a54..bd7de57 100644 --- a/queries.c +++ b/queries.c @@ -1159,6 +1159,9 @@ int send_file_on_answer (struct query *q UU) { } int send_encr_file_on_answer (struct query *q UU) { + if (prefetch_int () != (int)CODE_messages_sent_encrypted_file) { + hexdump_in (); + } assert (fetch_int () == (int)CODE_messages_sent_encrypted_file); struct message *M = q->extra; M->date = fetch_int (); @@ -1649,40 +1652,9 @@ void load_next_part (struct download *D); int download_on_answer (struct query *q) { assert (fetch_int () == (int)CODE_upload_file); unsigned x = fetch_int (); + assert (x); struct download *D = q->extra; if (D->fd == -1) { - static char buf[100]; - sprintf (buf, "%s/tmp_%ld_%ld", get_downloads_directory (), lrand48 (), lrand48 ()); - int l = strlen (buf); - switch (x) { - case CODE_storage_file_unknown: - break; - case CODE_storage_file_jpeg: - sprintf (buf + l, "%s", ".jpg"); - break; - case CODE_storage_file_gif: - sprintf (buf + l, "%s", ".gif"); - break; - case CODE_storage_file_png: - sprintf (buf + l, "%s", ".png"); - break; - case CODE_storage_file_mp3: - sprintf (buf + l, "%s", ".mp3"); - break; - case CODE_storage_file_mov: - sprintf (buf + l, "%s", ".mov"); - break; - case CODE_storage_file_partial: - sprintf (buf + l, "%s", ".part"); - break; - case CODE_storage_file_mp4: - sprintf (buf + l, "%s", ".mp4"); - break; - case CODE_storage_file_webp: - sprintf (buf + l, "%s", ".webp"); - break; - } - D->name = strdup (buf); D->fd = open (D->name, O_CREAT | O_WRONLY, 0640); } fetch_int (); // mtime @@ -1719,7 +1691,27 @@ struct query_methods download_methods = { void load_next_part (struct download *D) { if (!D->offset) { + static char buf[1000]; + if (!D->id) { + sprintf (buf, "%s/download_%lld_%d", get_downloads_directory (), D->volume, D->local_id); + } else { + sprintf (buf, "%s/download_%lld", get_downloads_directory (), D->id); + } + D->name = strdup (buf); + struct stat st; + if (stat (buf, &st) >= 0) { + D->offset = st.st_size; + if (D->offset >= D->size) { + cur_downloading_bytes += D->size; + cur_downloaded_bytes += D->offset; + rprintf ("Already downloaded\n"); + end_load (D); + return; + } + } + cur_downloading_bytes += D->size; + cur_downloaded_bytes += D->offset; update_prompt (); } clear_packet ();