/*
This file is part of telegram-client.
Telegram-client is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Telegram-client is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this telegram-client. If not, see .
Copyright Vitaly Valtman 2013
*/
#define _GNU_SOURCE
#define READLINE_CALLBACKS
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "net.h"
#include "mtproto-client.h"
#include "queries.h"
#include "telegram.h"
#include "loop.h"
#include "binlog.h"
//
#include "purple-plugin/telegram-purple.h"
//
char *get_auth_key_filename (void);
char *get_state_filename (void);
int zero[512];
void write_dc (int auth_file_fd, struct dc *DC) {
debug("writing to auth_file: auth_file_fd: %d, port: %d, ip: %s\n", auth_file_fd, DC->port, DC->ip);
assert (write (auth_file_fd, &DC->port, 4) == 4);
int l = strlen (DC->ip);
assert (write (auth_file_fd, &l, 4) == 4);
assert (write (auth_file_fd, DC->ip, l) == l);
if (DC->flags & 1) {
assert (write (auth_file_fd, &DC->auth_key_id, 8) == 8);
assert (write (auth_file_fd, DC->auth_key, 256) == 256);
} else {
assert (write (auth_file_fd, zero, 256 + 8) == 256 + 8);
}
assert (write (auth_file_fd, &DC->server_salt, 8) == 8);
assert (write (auth_file_fd, &DC->has_auth, 4) == 4);
}
void write_auth_file (struct authorization_state *state, const char *filename) {
debug("Writing to auth_file: %s\n", filename);
int auth_file_fd = open (filename, O_CREAT | O_RDWR, 0600);
assert (auth_file_fd >= 0);
int x = DC_SERIALIZED_MAGIC_V2;
assert (write (auth_file_fd, &x, 4) == 4);
x = MAX_DC_ID;
assert (write (auth_file_fd, &x, 4) == 4);
assert (write (auth_file_fd, &state->dc_working_num, 4) == 4);
assert (write (auth_file_fd, &state->auth_state, 4) == 4);
int i;
for (i = 0; i <= MAX_DC_ID; i++) {
if (state->DC_list[i]) {
x = 1;
assert (write (auth_file_fd, &x, 4) == 4);
write_dc (auth_file_fd, state->DC_list[i]);
} else {
x = 0;
assert (write (auth_file_fd, &x, 4) == 4);
}
}
assert (write (auth_file_fd, &state->our_id, 4) == 4);
close (auth_file_fd);
}
void read_dc (int auth_file_fd, int id, unsigned ver, struct dc *DC_list[]) {
int port = 0;
assert (read (auth_file_fd, &port, 4) == 4);
int l = 0;
assert (read (auth_file_fd, &l, 4) == 4);
assert (l >= 0);
char *ip = talloc (l + 1);
assert (read (auth_file_fd, ip, l) == l);
ip[l] = 0;
struct dc *DC = alloc_dc (DC_list, id, ip, port);
assert (read (auth_file_fd, &DC->auth_key_id, 8) == 8);
assert (read (auth_file_fd, &DC->auth_key, 256) == 256);
assert (read (auth_file_fd, &DC->server_salt, 8) == 8);
if (DC->auth_key_id) {
DC->flags |= 1;
}
if (ver != DC_SERIALIZED_MAGIC) {
assert (read (auth_file_fd, &DC->has_auth, 4) == 4);
} else {
DC->has_auth = 0;
}
}
void empty_auth_file (const char *filename) {
struct authorization_state state;
memset(state.DC_list, 0, 11 * sizeof(void *));
debug("empty_auth_file()\n");
alloc_dc (state.DC_list, TG_DC_NUM, tstrdup (TG_SERVER), TG_PORT);
state.dc_working_num = TG_DC_NUM;
state.auth_state = 0;
write_auth_file (&state, filename);
}
/**
* Read the auth-file and return the read authorization state
*
* When the given file doesn't exist, create a new empty
* file containing the default authorization state at this
* path
*/
struct authorization_state read_auth_file (const char *filename) {
debug("read_auth_file()\n");
struct authorization_state state;
memset(state.DC_list, 0, 11 * sizeof(void *));
int auth_file_fd = open (filename, O_RDWR, 0600);
debug("fd: %d\n", auth_file_fd);
if (auth_file_fd < 0) {
debug("auth_file does not exist, creating empty...\n");
empty_auth_file (filename);
}
auth_file_fd = open (filename, O_RDWR, 0600);
assert (auth_file_fd >= 0);
// amount of data centers
unsigned x;
// magic number of file
unsigned m;
if (read (auth_file_fd, &m, 4) < 4 || (m != DC_SERIALIZED_MAGIC && m != DC_SERIALIZED_MAGIC_V2)) {
debug("Invalid File content, wrong Magic numebr\n");
close (auth_file_fd);
empty_auth_file (filename);
return state;
}
assert (read (auth_file_fd, &x, 4) == 4);
assert (x <= MAX_DC_ID);
assert (read (auth_file_fd, &state.dc_working_num, 4) == 4);
assert (read (auth_file_fd, &state.auth_state, 4) == 4);
debug ("dc_working_num=%d, auth_state=%d \n", state.dc_working_num, state.auth_state);
if (m == DC_SERIALIZED_MAGIC) {
state.auth_state = 700;
}
int i;
for (i = 0; i <= (int)x; i++) {
int y;
assert (read (auth_file_fd, &y, 4) == 4);
if (y) {
read_dc (auth_file_fd, i, m, state.DC_list);
debug("loaded dc[%d] - port: %d, ip: %s, auth_key_id: %lli, server_salt: %lli, has_auth: %d\n",
i, state.DC_list[i]->port, state.DC_list[i]->ip, state.DC_list[i]->auth_key_id,
state.DC_list[i]->server_salt, state.DC_list[i]->has_auth);
} else {
debug("loaded dc[%d] - NULL\n", i);
}
}
int l = read (auth_file_fd, &state.our_id, 4);
if (l < 4) {
assert (!l);
}
close (auth_file_fd);
struct dc *DC_working = state.DC_list[state.dc_working_num];
if (m == DC_SERIALIZED_MAGIC) {
DC_working->has_auth = 1;
}
debug("loaded authorization state - our_id: %d, auth_state: %d, dc_working_num: %d \n", state.our_id, state.auth_state, state.dc_working_num);
return state;
}
struct protocol_state read_state_file (const char *filename) {
debug("read_state_file()\n");
struct protocol_state state = {0, 0, 0, 0};
int state_file_fd = open (filename, O_CREAT | O_RDWR, 0600);
if (state_file_fd < 0) {
return state;
}
int version, magic;
if (read (state_file_fd, &magic, 4) < 4) { close (state_file_fd); return state; }
if (magic != (int)STATE_FILE_MAGIC) { close (state_file_fd); return state; }
if (read (state_file_fd, &version, 4) < 4) { close (state_file_fd); return state; }
assert (version >= 0);
int x[4];
if (read (state_file_fd, x, 16) < 16) {
close (state_file_fd);
return state;
}
state.pts = x[0];
state.qts = x[1];
state.seq = x[2];
state.last_date = x[3];
close (state_file_fd);
debug("loaded session state - pts: %d, qts: %d, seq: %d, last_date: %d.\n", state.pts,
state.qts, state.seq, state.last_date);
return state;
}
void write_state_file (struct protocol_state *state, const char* filename) {
int state_file_fd = open (filename, O_CREAT | O_RDWR, 0600);
if (state_file_fd < 0) {
return;
}
int x[6];
x[0] = STATE_FILE_MAGIC;
x[1] = 0;
x[2] = state->pts;
x[3] = state->qts;
x[4] = state->seq;
x[5] = state->last_date;
assert (write (state_file_fd, x, 24) == 24);
close (state_file_fd);
}
void read_secret_chat_file (struct telegram *instance, const char *file) {
struct binlog *bl = instance->bl;
int fd = open (file, O_CREAT | O_RDWR, 0600);
if (fd < 0) {
return;
}
int x[2];
if (read (fd, x, 8) < 8) {
close (fd); return;
}
if (x[0] != (int)SECRET_CHAT_FILE_MAGIC) { close (fd); return; }
int version = x[1];
assert (version >= 0);
int cc;
assert (read (fd, &cc, 4) == 4);
int i;
for (i = 0; i < cc; i++) {
peer_t *P = talloc0 (sizeof (*P));
struct secret_chat *E = &P->encr_chat;
int t;
assert (read (fd, &t, 4) == 4);
P->id = MK_ENCR_CHAT (t);
assert (read (fd, &P->flags, 4) == 4);
assert (read (fd, &t, 4) == 4);
assert (t > 0);
P->print_name = talloc (t + 1);
assert (read (fd, P->print_name, t) == t);
P->print_name[t] = 0;
peer_insert_name (bl, P);
assert (read (fd, &E->state, 4) == 4);
assert (read (fd, &E->user_id, 4) == 4);
assert (read (fd, &E->admin_id, 4) == 4);
assert (read (fd, &E->ttl, 4) == 4);
assert (read (fd, &E->access_hash, 8) == 8);
if (E->state != sc_waiting) {
E->g_key = talloc (256);
assert (read (fd, E->g_key, 256) == 256);
E->nonce = talloc (256);
assert (read (fd, E->nonce, 256) == 256);
}
assert (read (fd, E->key, 256) == 256);
assert (read (fd, &E->key_fingerprint, 8) == 8);
insert_encrypted_chat (bl, P);
}
if (version >= 1) {
assert (read (fd, &instance->encr_root, 4) == 4);
if (instance->encr_root) {
assert (read (fd, &instance->encr_param_version, 4) == 4);
instance->encr_prime = talloc (256);
assert (read (fd, instance->encr_prime, 256) == 256);
}
}
close (fd);
}
// TODO: Refactor
void write_secret_chat_file (struct telegram *instance, const char *filename) {
struct binlog *bl = instance->bl;
int fd = open (filename, O_CREAT | O_RDWR, 0600);
if (fd < 0) {
return;
}
int x[2];
x[0] = SECRET_CHAT_FILE_MAGIC;
x[1] = 1;
assert (write (fd, x, 8) == 8);
int i;
int cc = 0;
for (i = 0; i < bl->peer_num; i++) if (get_peer_type (bl->Peers[i]->id) == PEER_ENCR_CHAT) {
if (bl->Peers[i]->encr_chat.state != sc_none && bl->Peers[i]->encr_chat.state != sc_deleted) {
cc ++;
}
}
assert (write (fd, &cc, 4) == 4);
for (i = 0; i < bl->peer_num; i++) if (get_peer_type (bl->Peers[i]->id) == PEER_ENCR_CHAT) {
if (bl->Peers[i]->encr_chat.state != sc_none && bl->Peers[i]->encr_chat.state != sc_deleted) {
int t = get_peer_id (bl->Peers[i]->id);
assert (write (fd, &t, 4) == 4);
t = bl->Peers[i]->flags;
assert (write (fd, &t, 4) == 4);
t = strlen (bl->Peers[i]->print_name);
assert (write (fd, &t, 4) == 4);
assert (write (fd, bl->Peers[i]->print_name, t) == t);
assert (write (fd, &bl->Peers[i]->encr_chat.state, 4) == 4);
assert (write (fd, &bl->Peers[i]->encr_chat.user_id, 4) == 4);
assert (write (fd, &bl->Peers[i]->encr_chat.admin_id, 4) == 4);
assert (write (fd, &bl->Peers[i]->encr_chat.ttl, 4) == 4);
assert (write (fd, &bl->Peers[i]->encr_chat.access_hash, 8) == 8);
if (bl->Peers[i]->encr_chat.state != sc_waiting) {
assert (write (fd, bl->Peers[i]->encr_chat.g_key, 256) == 256);
}
if (bl->Peers[i]->encr_chat.state != sc_waiting) {
assert (write (fd, bl->Peers[i]->encr_chat.nonce, 256) == 256);
}
assert (write (fd, bl->Peers[i]->encr_chat.key, 256) == 256);
assert (write (fd, &bl->Peers[i]->encr_chat.key_fingerprint, 8) == 8);
}
}
assert (write (fd, &instance->encr_root, 4) == 4);
if (instance->encr_root) {
assert (write (fd, &instance->encr_param_version, 4) == 4);
assert (write (fd, instance->encr_prime, 256) == 256);
}
close (fd);
}