Slightly working version

This commit is contained in:
Vysheng 2014-11-11 20:21:14 +03:00
commit 8f69d8d11d
17 changed files with 2531 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.o
*.so
tg/

120
Makefile Normal file
View file

@ -0,0 +1,120 @@
#
# Telegram Flags
#
srcdir=.
CFLAGS=-g -Wall -Wextra -Werror -Wno-unused-parameter
LDFLAGS=-L/usr/local/lib
CPPFLAGS=-I/usr/local/include -Itg
DEFS=
COMPILE_FLAGS=${CFLAGS} ${CPPFLAGS} ${DEFS} -Wall -Wextra -Wno-deprecated-declarations -fno-strict-aliasing -fno-omit-frame-pointer -ggdb
EXTRA_LIBS=-lcrypto -lz -lm
LOCAL_LDFLAGS=-rdynamic -ggdb ${EXTRA_LIBS}
LINK_FLAGS=${LDFLAGS} ${LOCAL_LDFLAGS}
INCLUDE=-I. -I${srcdir}
CC=cc
OBJECTS=tgp-net.o tgp-timers.o msglog.o telegram-base.o
.SUFFIXES:
.SUFFIXES: .c .h .o
#
# Plugin Flags
#
LIBS_PURPLE = $(shell pkg-config --libs purple)
CFLAGS_PURPLE = $(shell pkg-config --cflags purple)
PLUGIN_DIR_PURPLE:=$(shell pkg-config --variable=plugindir purple)
DATA_ROOT_DIR_PURPLE:=$(shell pkg-config --variable=datarootdir purple)
ARCH = ""
ifeq ($(ARCH),i686)
ARCHFLAGS = -m32
else ifeq ($(ARCH),x86_64)
ARCHFLAGS = -m64
else
ARCHFLAGS =
endif
LD = $(CC)
PRPL_C_SRCS = telegram-purple.c
PRPL_C_OBJS = $(PRPL_C_SRCS:.c=.o)
PRPL_LIBNAME = telegram-purple.so
PRPL_INCLUDE = -I. -I./purple-plugin
PRPL_LDFLAGS = $(ARCHFLAGS) -shared
STRIP = strip
PRPL_CFLAGS = \
$(ARCHFLAGS) \
-fPIC \
-DPURPLE_PLUGINS \
-DPIC \
-DDEBUG \
-g \
$(CFLAGS_PURPLE)
#
# Telegram Objects
#
.c.o :
${CC} -fPIC -DPIC ${CFLAGS_PURPLE} ${COMPILE_FLAGS} ${INCLUDE} -c $< -o $@
# ${OBJECTS}: ${HEADERS}
#telegram: ${OBJECTS}
# ${CC} ${OBJECTS} ${LINK_FLAGS} -o $@
#
# Plugin Objects
#
$(PRPL_C_OBJS): $(PRPL_C_SRCS)
$(CC) -c $(PRPL_INCLUDE) $(PRPL_CFLAGS) $(CFLAGS) $(CPPFLAGS) -ggdb -o $@ $<
$(PRPL_LIBNAME): $(OBJECTS) $(PRPL_C_OBJS) tg/libs/libtgl.a
$(LD) $(PRPL_LDFLAGS) $(LDFLAGS) $(PRPL_INCLUDE) $(LIBS_PURPLE) $(EXTRA_LIBS) -o $@ $^
.PHONY: all
all: ${PRPL_LIBNAME}
plugin: $(PRPL_LIBNAME)
.PHONY: strip
strip: $(PRPL_LIBNAME)
$(STRIP) --strip-unneeded $(PRPL_LIBNAME)
# TODO: Find a better place for server.pub
install: $(PRPL_LIBNAME)
install -D $(PRPL_LIBNAME) $(DESTDIR)$(PLUGIN_DIR_PURPLE)/$(PRPL_LIBNAME)
install -D tg-server.pub /etc/telegram-purple/server.pub
install -D imgs/telegram16.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/16/telegram.png
install -D imgs/telegram22.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/22/telegram.png
install -D imgs/telegram48.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/48/telegram.png
.PHONY: uninstall
uninstall:
rm -f $(DESTDIR)$(PLUGIN_DIR_PURPLE)/$(PRPL_LIBNAME)
rm -f /etc/telegram-purple/server.pub
rm -f $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/16/telegram.png
rm -f $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/22/telegram.png
rm -f $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/48/telegram.png
.PHONY: run
run: install
pidgin -d | grep 'telegram\|plugin\|proxy'
.PHONY: purge
purge: uninstall
rm -rf $(HOME)/.telegram-purple
.PHONY: debug
debug: install
ddd pidgin
clean:
rm -rf *.so *.a *.o telegram config.log config.status $(PRPL_C_OBJS) $(PRPL_LIBNAME) > /dev/null || echo "all clean"

BIN
imgs/telegram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
imgs/telegram16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

BIN
imgs/telegram22.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
imgs/telegram48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

79
msglog.c Normal file
View file

@ -0,0 +1,79 @@
#include <stdio.h>
#include <stdarg.h>
#include <debug.h>
#include "telegram-purple.h"
#ifdef DEBUG
#define COLOR_GREY "\033[37;1m"
#define COLOR_YELLOW "\033[33;1m"
#define COLOR_RED "\033[0;31m"
#define COLOR_REDB "\033[1;31m"
#define COLOR_GREEN "\033[32;1m"
#define COLOR_NORMAL "\033[0m"
#else
#define COLOR_GREY ""
#define COLOR_YELLOW ""
#define COLOR_RED ""
#define COLOR_REDB ""
#define COLOR_GREEN ""
#define COLOR_NORMAL ""
#endif
void hexdump (int *in_ptr, int *in_end) {
// TODO: figure out how to log hexdumps to purple log
int *ptr = in_ptr;
while (ptr < in_end) {
++ ptr;
//printf (" %08x", *(ptr ++));
}
//printf ("\n");
}
void log_level_printf (const char* format, va_list ap, int level, char *color)
{
char buffer[256];
vsnprintf(buffer, sizeof(buffer), format, ap);
purple_debug(level, PLUGIN_ID, "%s%s%s ", color, buffer, COLOR_NORMAL);
}
void debug(const char* format, ...)
{
va_list ap;
va_start (ap, format);
log_level_printf (format, ap, PURPLE_DEBUG_MISC, COLOR_NORMAL);
va_end (ap);
}
void info(const char* format, ...)
{
va_list ap;
va_start (ap, format);
log_level_printf (format, ap, PURPLE_DEBUG_INFO, COLOR_GREEN);
va_end (ap);
}
void warning(const char* format, ...)
{
va_list ap;
va_start (ap, format);
log_level_printf (format, ap, PURPLE_DEBUG_WARNING, COLOR_YELLOW);
va_end (ap);
}
void failure(const char* format, ...)
{
va_list ap;
va_start (ap, format);
log_level_printf (format, ap, PURPLE_DEBUG_ERROR, COLOR_YELLOW);
va_end (ap);
}
void fatal(const char* format, ...)
{
va_list ap;
va_start (ap, format);
log_level_printf (format, ap, PURPLE_DEBUG_FATAL, COLOR_REDB);
va_end (ap);
info ("\n");
}

14
msglog.h Normal file
View file

@ -0,0 +1,14 @@
#include <stdarg.h>
/**
* Set a custom logging callback to use instead of regular printing
* to stdout
*/
void hexdump (int *in_ptr, int *in_end);
void debug(const char* format, ...);
void info(const char* format, ...);
void warning(const char* format, ...);
void failure(const char* format, ...);
void fatal(const char* format, ...);

372
telegram-base.c Normal file
View file

@ -0,0 +1,372 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <assert.h>
#include <tgl.h>
#include <binlog.h>
#include <glib.h>
#include <request.h>
#include "telegram-purple.h"
#include "msglog.h"
#define DC_SERIALIZED_MAGIC 0x868aa81d
#define STATE_FILE_MAGIC 0x28949a93
#define SECRET_CHAT_FILE_MAGIC 0x37a1988a
void read_state_file (struct tgl_state *TLS) {
char *name = 0;
if (asprintf (&name, "%s/%s", TLS->base_path, "state") < 0) {
return;
}
int state_file_fd = open (name, O_CREAT | O_RDWR, 0600);
free (name);
if (state_file_fd < 0) {
return;
}
int version, magic;
if (read (state_file_fd, &magic, 4) < 4) { close (state_file_fd); return; }
if (magic != (int)STATE_FILE_MAGIC) { close (state_file_fd); return; }
if (read (state_file_fd, &version, 4 || version < 0) < 4) { close (state_file_fd); return; }
int x[4];
if (read (state_file_fd, x, 16) < 16) {
close (state_file_fd);
return;
}
int pts = x[0];
int qts = x[1];
int seq = x[2];
int date = x[3];
close (state_file_fd);
bl_do_set_seq (TLS, seq);
bl_do_set_pts (TLS, pts);
bl_do_set_qts (TLS, qts);
bl_do_set_date (TLS, date);
}
void write_state_file (struct tgl_state *TLS) {
int wseq;
int wpts;
int wqts;
int wdate;
wseq = TLS->seq; wpts = TLS->pts; wqts = TLS->qts; wdate = TLS->date;
char *name = 0;
if (asprintf (&name, "%s/%s", TLS->base_path, "state") < 0) {
return;
}
int state_file_fd = open (name, O_CREAT | O_RDWR, 0600);
free (name);
if (state_file_fd < 0) {
return;
}
int x[6];
x[0] = STATE_FILE_MAGIC;
x[1] = 0;
x[2] = wpts;
x[3] = wqts;
x[4] = wseq;
x[5] = wdate;
assert (write (state_file_fd, x, 24) == 24);
close (state_file_fd);
}
void write_dc (struct tgl_dc *DC, void *extra) {
int auth_file_fd = *(int *)extra;
if (!DC) {
int x = 0;
assert (write (auth_file_fd, &x, 4) == 4);
return;
} else {
int x = 1;
assert (write (auth_file_fd, &x, 4) == 4);
}
assert (DC->has_auth);
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);
assert (write (auth_file_fd, &DC->auth_key_id, 8) == 8);
assert (write (auth_file_fd, DC->auth_key, 256) == 256);
}
void write_auth_file (struct tgl_state *TLS) {
char *name = 0;
if (asprintf (&name, "%s/%s", TLS->base_path, "auth") < 0) {
return;
}
int auth_file_fd = open (name, O_CREAT | O_RDWR, 0600);
free (name);
if (auth_file_fd < 0) { return; }
int x = DC_SERIALIZED_MAGIC;
assert (write (auth_file_fd, &x, 4) == 4);
assert (write (auth_file_fd, &TLS->max_dc_num, 4) == 4);
assert (write (auth_file_fd, &TLS->dc_working_num, 4) == 4);
tgl_dc_iterator_ex (TLS, write_dc, &auth_file_fd);
assert (write (auth_file_fd, &TLS->our_id, 4) == 4);
close (auth_file_fd);
}
void read_dc (struct tgl_state *TLS, int auth_file_fd, int id, unsigned ver) {
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 && l < 100);
char ip[100];
assert (read (auth_file_fd, ip, l) == l);
ip[l] = 0;
long long auth_key_id;
static unsigned char auth_key[256];
assert (read (auth_file_fd, &auth_key_id, 8) == 8);
assert (read (auth_file_fd, auth_key, 256) == 256);
//bl_do_add_dc (id, ip, l, port, auth_key_id, auth_key);
bl_do_dc_option (TLS, id, 2, "DC", l, ip, port);
bl_do_set_auth_key_id (TLS, id, auth_key);
bl_do_dc_signed (TLS, id);
}
void empty_auth_file (struct tgl_state *TLS) {
if (TLS->test_mode) {
bl_do_dc_option (TLS, 1, 0, "", strlen (TG_SERVER_TEST_1), TG_SERVER_TEST_1, 443);
bl_do_dc_option (TLS, 2, 0, "", strlen (TG_SERVER_TEST_2), TG_SERVER_TEST_2, 443);
bl_do_dc_option (TLS, 3, 0, "", strlen (TG_SERVER_TEST_3), TG_SERVER_TEST_3, 443);
bl_do_set_working_dc (TLS, TG_SERVER_TEST_DEFAULT);
} else {
bl_do_dc_option (TLS, 1, 0, "", strlen (TG_SERVER_1), TG_SERVER_1, 443);
bl_do_dc_option (TLS, 2, 0, "", strlen (TG_SERVER_2), TG_SERVER_2, 443);
bl_do_dc_option (TLS, 3, 0, "", strlen (TG_SERVER_3), TG_SERVER_3, 443);
bl_do_dc_option (TLS, 4, 0, "", strlen (TG_SERVER_4), TG_SERVER_4, 443);
bl_do_dc_option (TLS, 5, 0, "", strlen (TG_SERVER_5), TG_SERVER_5, 443);
bl_do_set_working_dc (TLS, TG_SERVER_DEFAULT);;
}
}
void read_auth_file (struct tgl_state *TLS) {
char *name = 0;
if (asprintf (&name, "%s/%s", TLS->base_path, "auth") < 0) {
return;
}
int auth_file_fd = open (name, O_CREAT | O_RDWR, 0600);
free (name);
if (auth_file_fd < 0) {
empty_auth_file (TLS);
return;
}
assert (auth_file_fd >= 0);
unsigned x;
unsigned m;
if (read (auth_file_fd, &m, 4) < 4 || (m != DC_SERIALIZED_MAGIC)) {
close (auth_file_fd);
empty_auth_file (TLS);
return;
}
assert (read (auth_file_fd, &x, 4) == 4);
assert (x > 0);
int dc_working_num;
assert (read (auth_file_fd, &dc_working_num, 4) == 4);
int i;
for (i = 0; i <= (int)x; i++) {
int y;
assert (read (auth_file_fd, &y, 4) == 4);
if (y) {
read_dc (TLS, auth_file_fd, i, m);
}
}
bl_do_set_working_dc (TLS, dc_working_num);
int our_id;
int l = read (auth_file_fd, &our_id, 4);
if (l < 4) {
assert (!l);
}
if (our_id) {
bl_do_set_our_id (TLS, our_id);
}
close (auth_file_fd);
}
void telegram_export_authorization (struct tgl_state *TLS);
void export_auth_callback (struct tgl_state *TLS, void *extra, int success) {
assert (success);
telegram_export_authorization (TLS);
}
void telegram_export_authorization (struct tgl_state *TLS) {
int i;
for (i = 0; i <= TLS->max_dc_num; i++) if (TLS->DC_list[i] && !tgl_signed_dc (TLS, TLS->DC_list[i])) {
tgl_do_export_auth (TLS, i, export_auth_callback, (void*)(long)TLS->DC_list[i]);
return;
}
write_auth_file (TLS);
telegram_on_ready (TLS);
}
static void request_code (struct tgl_state *TLS);
static void request_name_and_code (struct tgl_state *TLS);
static void code_receive_result (struct tgl_state *TLS, void *extra, int success, struct tgl_user *U) {
if (!success) {
debug ("Bad code...\n");
request_code (TLS);
} else {
telegram_export_authorization (TLS);
}
}
static void code_auth_receive_result (struct tgl_state *TLS, void *extra, int success, struct tgl_user *U) {
if (!success) {
debug ("Bad code...\n");
request_name_and_code (TLS);
} else {
telegram_export_authorization (TLS);
}
}
static void request_code_entered (gpointer data, const gchar *code) {
struct tgl_state *TLS = data;
telegram_conn *conn = TLS->ev_base;
char const *username = purple_account_get_username(conn->pa);
tgl_do_send_code_result (TLS, username, conn->hash, code, code_receive_result, 0) ;
}
static void request_code_canceled (gpointer data) {
struct tgl_state *TLS = data;
telegram_conn *conn = TLS->ev_base;
purple_connection_error_reason(conn->gc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, "registration canceled");
}
static void request_name_code_entered (PurpleConnection* gc, PurpleRequestFields* fields) {
telegram_conn *conn = purple_connection_get_protocol_data(gc);
struct tgl_state *TLS = conn->TLS;
char const *username = purple_account_get_username(conn->pa);
const char* first = purple_request_fields_get_string(fields, "first_name");
const char* last = purple_request_fields_get_string(fields, "last_name");
const char* code = purple_request_fields_get_string(fields, "code");
if (!first || !last || !code) {
request_name_and_code (TLS);
return;
}
tgl_do_send_code_result_auth (TLS, username, conn->hash, code, first, last, code_auth_receive_result, NULL);
}
static void request_code (struct tgl_state *TLS) {
debug ("Client is not registered, registering...\n");
telegram_conn *conn = TLS->ev_base;
purple_request_input (
conn->gc, // handle (the PurpleAccount)
"Telegram Code", // title
"Enter Telegram Code", // primary
"Telegram wants to verify your identity, please enter the code, that you have received via SMS.", // secondary
NULL, // default_value
0, // multiline
0, // masked
"code", // hint
"OK", // ok_text
G_CALLBACK(request_code_entered), // ok_cb
"Cancel", // cancel_text
G_CALLBACK(request_code_canceled), // cancel_cb
conn->pa, // account
NULL, // who
NULL, // conv
TLS // user_data
);
}
static void request_name_and_code (struct tgl_state *TLS) {
telegram_conn *conn = TLS->ev_base;
debug ("Phone is not registered, registering...\n");
PurpleRequestFields* fields = purple_request_fields_new();
PurpleRequestField* field = 0;
PurpleRequestFieldGroup* group = purple_request_field_group_new("Registration");
field = purple_request_field_string_new("first_name", "First Name", "", 0);
purple_request_field_group_add_field(group, field);
field = purple_request_field_string_new("last_name", "Last Name", "", 0);
purple_request_field_group_add_field(group, field);
purple_request_fields_add_group(fields, group);
group = purple_request_field_group_new("Authorization");
field = purple_request_field_string_new("code", "Telegram Code", "", 0);
purple_request_field_group_add_field(group, field);
purple_request_fields_add_group(fields, group);
purple_request_fields(conn->gc, "Register", "Please register your phone number.", NULL, fields, "Ok",
G_CALLBACK( request_name_code_entered ), "Cancel", NULL, conn->pa, NULL, NULL, conn->gc);
}
static void sign_in_callback (struct tgl_state *TLS, void *extra, int success, int registered, const char *mhash) {
assert (success); // TODO proper error handle
telegram_conn *conn = TLS->ev_base;
conn->hash = strdup (mhash);
if (registered) {
request_code (TLS);
} else {
request_name_and_code (TLS);
}
}
static void telegram_send_sms (struct tgl_state *TLS) {
if (tgl_signed_dc (TLS, TLS->DC_working)) {
telegram_export_authorization (TLS);
return;
}
telegram_conn *conn = TLS->ev_base;
char const *username = purple_account_get_username(conn->pa);
tgl_do_send_code (TLS, username, sign_in_callback, 0);
}
static int all_authorized (struct tgl_state *TLS) {
int i;
for (i = 0; i <= TLS->max_dc_num; i++) if (TLS->DC_list[i]) {
if (!tgl_authorized_dc (TLS, TLS->DC_list[i])) {
return 0;
}
}
return 1;
}
static int check_all_authorized (gpointer arg) {
struct tgl_state *TLS = arg;
if (all_authorized (TLS)) {
telegram_send_sms (TLS);
return FALSE;
} else {
return TRUE;
}
}
void telegram_login (struct tgl_state *TLS) {
read_auth_file (TLS);
read_state_file (TLS);
if (all_authorized (TLS)) {
telegram_send_sms (TLS);
return;
}
purple_timeout_add (100, check_all_authorized, TLS);
}

9
telegram-base.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef __TELEGRAM_BASE_H__
#define __TELEGRAM_BASE_H__
void read_state_file (struct tgl_state *TLS);
void read_auth_file (struct tgl_state *TLS);
void write_auth_file (struct tgl_state *TLS);
void write_state_file (struct tgl_state *TLS);
void telegram_login (struct tgl_state *TLS);
#endif

1082
telegram-purple.c Normal file

File diff suppressed because it is too large Load diff

70
telegram-purple.h Normal file
View file

@ -0,0 +1,70 @@
/**
* This program 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.
*
* This program 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 program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#ifndef __TG_PURPLE_H__
#define __TG_PURPLE_H__
#define PLUGIN_ID "prpl-telegram"
#define TELEGRAM_AUTH_MODE_PHONE "phone"
#define TELEGRAM_AUTH_MODE_SMS "sms"
#define TG_AUTHOR "Christopher Althaus <althaus.christopher@gmail.com>, Markus Endres <endresma45241@th-nuernberg.de>, Matthias Jentsch <mtthsjntsch@gmail.com>. Based on telegram-cli by Vitaly Valtman."
#define TG_DESCRIPTION "Adds support for Telegram."
#define TG_VERSION "0.3.3"
#define TG_BUILD "8"
#include <glib.h>
#include <notify.h>
#include <plugin.h>
#include <version.h>
#include <account.h>
#include <connection.h>
typedef struct {
struct tgl_state *TLS;
/*
* Used during login
*/
char *hash;
PurpleAccount *pa;
PurpleConnection *gc;
/**
* Whether the state of the protocol has changed since the last save
*/
int updated;
/**
* Queue of all new messages that need to be added to a chat
*/
GQueue *new_messages;
/**
* Queue of all joined chats
*/
GHashTable *joining_chats;
guint timer;
} telegram_conn;
struct download_desc {
int type;
void *data;
};
void telegram_on_ready (struct tgl_state *TLS);
#endif

8
tg-server.pub Normal file
View file

@ -0,0 +1,8 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
-----END RSA PUBLIC KEY-----

584
tgp-net.c Normal file
View file

@ -0,0 +1,584 @@
/*
This file is part of tgl-library
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright Vitaly Valtman 2013-2014
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <poll.h>
#include <openssl/rand.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <time.h>
#include "tgp-net.h"
//#include "include.h"
#include <tgl.h>
#include <tgl-inner.h>
//#include "mtproto-client.h"
//#include "mtproto-common.h"
//#include "tree.h"
//#include "tools.h"
#include <glib.h>
#include <eventloop.h>
#include "telegram-purple.h"
#ifndef POLLRDHUP
#define POLLRDHUP 0
#endif
//double get_utime (int clock_id);
//extern struct mtproto_methods auth_methods;
static void fail_connection (struct connection *c);
#define PING_TIMEOUT 10
static void start_ping_timer (struct connection *c);
static int ping_alarm (gpointer arg) {
struct connection *c = arg;
struct tgl_state *TLS = c->TLS;
vlogprintf (E_DEBUG + 2,"ping alarm\n");
assert (c->state == conn_ready || c->state == conn_connecting);
if (tglt_get_double_time () - c->last_receive_time > 6 * PING_TIMEOUT) {
vlogprintf (E_WARNING, "fail connection: reason: ping timeout\n");
c->state = conn_failed;
fail_connection (c);
return FALSE;
} else if (tglt_get_double_time () - c->last_receive_time > 3 * PING_TIMEOUT && c->state == conn_ready) {
tgl_do_send_ping (c->TLS, c);
return TRUE;
} else {
return TRUE;
}
}
static void stop_ping_timer (struct connection *c) {
purple_timeout_remove (c->ping_ev);
}
static void start_ping_timer (struct connection *c) {
c->ping_ev = purple_timeout_add_seconds (PING_TIMEOUT, ping_alarm, c);
}
static void restart_connection (struct connection *c);
static int fail_alarm (gpointer arg) {
struct connection *c = arg;
c->in_fail_timer = 0;
restart_connection (c);
return FALSE;
}
static void start_fail_timer (struct connection *c) {
if (c->in_fail_timer) { return; }
c->in_fail_timer = 1;
c->fail_ev = purple_timeout_add_seconds (10, fail_alarm, c);
}
static struct connection_buffer *new_connection_buffer (int size) {
struct connection_buffer *b = malloc (sizeof (*b));
memset (b, 0, sizeof (*b));
b->start = malloc (size);
b->end = b->start + size;
b->rptr = b->wptr = b->start;
return b;
}
static void delete_connection_buffer (struct connection_buffer *b) {
free (b->start);
free (b);
}
static void conn_try_write (gpointer arg, gint source, PurpleInputCondition cond);
int tgln_write_out (struct connection *c, const void *_data, int len) {
struct tgl_state *TLS = c->TLS;
vlogprintf (E_DEBUG, "write_out: %d bytes\n", len);
const unsigned char *data = _data;
if (!len) { return 0; }
assert (len > 0);
int x = 0;
if (!c->out_bytes) {
assert (c->write_ev == -1);
c->write_ev = purple_input_add (c->fd, PURPLE_INPUT_WRITE, conn_try_write, c);
}
if (!c->out_head) {
struct connection_buffer *b = new_connection_buffer (1 << 20);
c->out_head = c->out_tail = b;
}
while (len) {
if (c->out_tail->end - c->out_tail->wptr >= len) {
memcpy (c->out_tail->wptr, data, len);
c->out_tail->wptr += len;
c->out_bytes += len;
return x + len;
} else {
int y = c->out_tail->end - c->out_tail->wptr;
assert (y < len);
memcpy (c->out_tail->wptr, data, y);
x += y;
len -= y;
data += y;
struct connection_buffer *b = new_connection_buffer (1 << 20);
c->out_tail->next = b;
b->next = 0;
c->out_tail = b;
c->out_bytes += y;
}
}
return x;
}
int tgln_read_in (struct connection *c, void *_data, int len) {
unsigned char *data = _data;
if (!len) { return 0; }
assert (len > 0);
if (len > c->in_bytes) {
len = c->in_bytes;
}
int x = 0;
while (len) {
int y = c->in_head->wptr - c->in_head->rptr;
if (y > len) {
memcpy (data, c->in_head->rptr, len);
c->in_head->rptr += len;
c->in_bytes -= len;
return x + len;
} else {
memcpy (data, c->in_head->rptr, y);
c->in_bytes -= y;
x += y;
data += y;
len -= y;
void *old = c->in_head;
c->in_head = c->in_head->next;
if (!c->in_head) {
c->in_tail = 0;
}
delete_connection_buffer (old);
}
}
return x;
}
int tgln_read_in_lookup (struct connection *c, void *_data, int len) {
unsigned char *data = _data;
if (!len || !c->in_bytes) { return 0; }
assert (len > 0);
if (len > c->in_bytes) {
len = c->in_bytes;
}
int x = 0;
struct connection_buffer *b = c->in_head;
while (len) {
int y = b->wptr - b->rptr;
if (y >= len) {
memcpy (data, b->rptr, len);
return x + len;
} else {
memcpy (data, b->rptr, y);
x += y;
data += y;
len -= y;
b = b->next;
}
}
return x;
}
void tgln_flush_out (struct connection *c) {
}
//#define MAX_CONNECTIONS 100
//static struct connection *Connections[MAX_CONNECTIONS];
//static int max_connection_fd;
static void rotate_port (struct connection *c) {
switch (c->port) {
case 443:
c->port = 80;
break;
case 80:
c->port = 25;
break;
case 25:
c->port = 443;
break;
}
}
static void try_read (struct connection *c);
static void try_write (struct connection *c);
static void conn_try_read (gpointer arg, gint source, PurpleInputCondition cond) {
struct connection *c = arg;
struct tgl_state *TLS = c->TLS;
vlogprintf (E_DEBUG + 1, "Try read. Fd = %d\n", c->fd);
try_read (c);
}
static void conn_try_write (gpointer arg, gint source, PurpleInputCondition cond) {
struct connection *c = arg;
struct tgl_state *TLS = c->TLS;
if (c->state == conn_connecting) {
c->state = conn_ready;
c->methods->ready (TLS, c);
}
try_write (c);
if (!c->out_bytes) {
purple_input_remove (c->write_ev);
c->write_ev = -1;
}
}
static void net_on_connected (gpointer arg, gint fd, const gchar *error_message) {
struct connection *c = arg;
struct tgl_state *TLS = c->TLS;
vlogprintf (E_DEBUG - 2, "connect result: %d\n", fd);
if (fd == -1) {
fail_connection (c);
return;
}
c->fd = fd;
c->read_ev = purple_input_add (fd, PURPLE_INPUT_READ, conn_try_read, c);
char byte = 0xef;
assert (tgln_write_out (c, &byte, 1) == 1);
c->last_receive_time = tglt_get_double_time ();
start_ping_timer (c);
}
struct connection *tgln_create_connection (struct tgl_state *TLS, const char *host, int port, struct tgl_session *session, struct tgl_dc *dc, struct mtproto_methods *methods) {
struct connection *c = malloc (sizeof (*c));
memset (c, 0, sizeof (*c));
c->TLS = TLS;
c->fd = -1;
c->state = conn_connecting;
c->last_receive_time = tglt_get_double_time ();
c->ip = strdup (host);
c->flags = 0;
c->port = port;
c->ping_ev = -1;
c->fail_ev = -1;
c->write_ev = -1;
c->read_ev = -1;
c->dc = dc;
c->session = session;
c->methods = methods;
telegram_conn *conn = TLS->ev_base;
c->prpl_data = purple_proxy_connect (conn->gc, conn->pa, host, port, net_on_connected, c);
return c;
}
static void restart_connection (struct connection *c) {
struct tgl_state *TLS = c->TLS;
if (c->last_connect_time == time (0)) {
start_fail_timer (c);
return;
}
telegram_conn *conn = TLS->ev_base;
c->prpl_data = purple_proxy_connect (conn->gc, conn->pa, c->ip, c->port, net_on_connected, c);
}
static void fail_connection (struct connection *c) {
struct tgl_state *TLS = c->TLS;
if (c->state == conn_ready) {
stop_ping_timer (c);
}
if (c->write_ev >= 0) {
purple_input_remove (c->write_ev);
c->write_ev = -1;
}
if (c->read_ev >= 0) {
purple_input_remove (c->write_ev);
c->read_ev = -1;
}
rotate_port (c);
struct connection_buffer *b = c->out_head;
while (b) {
struct connection_buffer *d = b;
b = b->next;
delete_connection_buffer (d);
}
b = c->in_head;
while (b) {
struct connection_buffer *d = b;
b = b->next;
delete_connection_buffer (d);
}
c->out_head = c->out_tail = c->in_head = c->in_tail = 0;
c->state = conn_failed;
c->out_bytes = c->in_bytes = 0;
if (c->state == conn_ready) {
telegram_conn *conn = TLS->ev_base;
purple_connection_error_reason(conn->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, "connection fail");
}
c->prpl_data = NULL; // Did not find any destroy code. What should be done here?
vlogprintf (E_NOTICE, "Lost connection to server... %s:%d\n", c->ip, c->port);
restart_connection (c);
}
//extern FILE *log_net_f;
static void try_write (struct connection *c) {
struct tgl_state *TLS = c->TLS;
vlogprintf (E_DEBUG, "try write: fd = %d\n", c->fd);
int x = 0;
while (c->out_head) {
int r = write (c->fd, c->out_head->rptr, c->out_head->wptr - c->out_head->rptr);
if (r >= 0) {
x += r;
c->out_head->rptr += r;
if (c->out_head->rptr != c->out_head->wptr) {
break;
}
struct connection_buffer *b = c->out_head;
c->out_head = b->next;
if (!c->out_head) {
c->out_tail = 0;
}
delete_connection_buffer (b);
} else {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
vlogprintf (E_NOTICE, "fail_connection: write_error %m\n");
fail_connection (c);
return;
} else {
break;
}
}
}
vlogprintf (E_DEBUG, "Sent %d bytes to %d\n", x, c->fd);
c->out_bytes -= x;
}
static void try_rpc_read (struct connection *c) {
assert (c->in_head);
struct tgl_state *TLS = c->TLS;
while (1) {
if (c->in_bytes < 1) { return; }
unsigned len = 0;
unsigned t = 0;
assert (tgln_read_in_lookup (c, &len, 1) == 1);
if (len >= 1 && len <= 0x7e) {
if (c->in_bytes < (int)(1 + 4 * len)) { return; }
} else {
if (c->in_bytes < 4) { return; }
assert (tgln_read_in_lookup (c, &len, 4) == 4);
len = (len >> 8);
if (c->in_bytes < (int)(4 + 4 * len)) { return; }
len = 0x7f;
}
if (len >= 1 && len <= 0x7e) {
assert (tgln_read_in (c, &t, 1) == 1);
assert (t == len);
assert (len >= 1);
} else {
assert (len == 0x7f);
assert (tgln_read_in (c, &len, 4) == 4);
len = (len >> 8);
assert (len >= 1);
}
len *= 4;
int op;
assert (tgln_read_in_lookup (c, &op, 4) == 4);
c->methods->execute (TLS, c, op, len);
}
}
static void try_read (struct connection *c) {
struct tgl_state *TLS = c->TLS;
vlogprintf (E_DEBUG, "try read: fd = %d\n", c->fd);
if (!c->in_tail) {
c->in_head = c->in_tail = new_connection_buffer (1 << 20);
}
#ifdef EVENT_V1
struct timeval tv = {5, 0};
event_add (c->read_ev, &tv);
#endif
int x = 0;
while (1) {
int r = read (c->fd, c->in_tail->wptr, c->in_tail->end - c->in_tail->wptr);
if (r > 0) {
c->last_receive_time = tglt_get_double_time ();
stop_ping_timer (c);
start_ping_timer (c);
}
if (r >= 0) {
c->in_tail->wptr += r;
x += r;
if (c->in_tail->wptr != c->in_tail->end) {
break;
}
struct connection_buffer *b = new_connection_buffer (1 << 20);
c->in_tail->next = b;
c->in_tail = b;
} else {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
vlogprintf (E_NOTICE, "fail_connection: read_error %m\n");
fail_connection (c);
return;
} else {
break;
}
}
}
vlogprintf (E_DEBUG, "Received %d bytes from %d\n", x, c->fd);
c->in_bytes += x;
if (x) {
try_rpc_read (c);
}
}
/*
int tgl_connections_make_poll_array (struct pollfd *fds, int max) {
int _max = max;
int i;
for (i = 0; i <= max_connection_fd; i++) {
if (Connections[i] && Connections[i]->state == conn_failed) {
restart_connection (Connections[i]);
}
if (Connections[i] && Connections[i]->state != conn_failed) {
assert (max > 0);
struct connection *c = Connections[i];
fds[0].fd = c->fd;
fds[0].events = POLLERR | POLLHUP | POLLRDHUP | POLLIN;
if (c->out_bytes || c->state == conn_connecting) {
fds[0].events |= POLLOUT;
}
fds ++;
max --;
}
}
return _max - max;
}
void tgl_connections_poll_result (struct pollfd *fds, int max) {
int i;
for (i = 0; i < max; i++) {
struct connection *c = Connections[fds[i].fd];
if (fds[i].revents & POLLIN) {
try_read (c);
}
if (fds[i].revents & (POLLHUP | POLLERR | POLLRDHUP)) {
vlogprintf (E_NOTICE, "fail_connection: events_mask=0x%08x\n", fds[i].revents);
fail_connection (c);
} else if (fds[i].revents & POLLOUT) {
if (c->state == conn_connecting) {
vlogprintf (E_DEBUG, "connection ready\n");
c->state = conn_ready;
c->last_receive_time = tglt_get_double_time ();
}
if (c->out_bytes) {
try_write (c);
}
}
}
}*/
static void incr_out_packet_num (struct connection *c) {
c->out_packet_num ++;
}
static struct tgl_dc *get_dc (struct connection *c) {
return c->dc;
}
static struct tgl_session *get_session (struct connection *c) {
return c->session;
}
static void tgln_free (struct connection *c) {
if (c->ip) { free (c->ip); }
struct connection_buffer *b = c->out_head;
while (b) {
struct connection_buffer *d = b;
b = b->next;
delete_connection_buffer (d);
}
b = c->in_head;
while (b) {
struct connection_buffer *d = b;
b = b->next;
delete_connection_buffer (d);
}
if (c->ping_ev >= 0) {
purple_timeout_remove (c->ping_ev);
c->ping_ev = -1;
}
if (c->fail_ev >= 0) {
purple_timeout_remove (c->fail_ev);
c->fail_ev = -1;
}
if (c->read_ev >= 0) {
purple_input_remove (c->read_ev);
}
if (c->write_ev >= 0) {
purple_input_remove (c->write_ev);
}
c->fd = -1;
}
struct tgl_net_methods tgp_conn_methods = {
.write_out = tgln_write_out,
.read_in = tgln_read_in,
.read_in_lookup = tgln_read_in_lookup,
.flush_out = tgln_flush_out,
.incr_out_packet_num = incr_out_packet_num,
.get_dc = get_dc,
.get_session = get_session,
.create_connection = tgln_create_connection,
.free = tgln_free
};

88
tgp-net.h Normal file
View file

@ -0,0 +1,88 @@
/*
This file is part of tgl-library
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright Vitaly Valtman 2013-2014
*/
#ifndef __NET_H__
#define __NET_H__
struct connection_buffer {
unsigned char *start;
unsigned char *end;
unsigned char *rptr;
unsigned char *wptr;
struct connection_buffer *next;
};
enum conn_state {
conn_none,
conn_connecting,
conn_ready,
conn_failed,
conn_stopped
};
struct connection {
int fd;
char *ip;
int port;
int flags;
enum conn_state state;
int ipv6[4];
struct connection_buffer *in_head;
struct connection_buffer *in_tail;
struct connection_buffer *out_head;
struct connection_buffer *out_tail;
int in_bytes;
int out_bytes;
int packet_num;
int out_packet_num;
int last_connect_time;
int in_fail_timer;
struct mtproto_methods *methods;
struct tgl_state *TLS;
struct tgl_session *session;
struct tgl_dc *dc;
void *extra;
int ping_ev;
int fail_ev;
int read_ev;
int write_ev;
double last_receive_time;
void *prpl_data;
};
//extern struct connection *Connections[];
int tgln_write_out (struct connection *c, const void *data, int len);
void tgln_flush_out (struct connection *c);
int tgln_read_in (struct connection *c, void *data, int len);
int tgln_read_in_lookup (struct connection *c, void *data, int len);
//void tgln_insert_msg_id (struct tgl_session *S, long long id);
extern struct tgl_net_methods tgp_conn_methods;
//void create_all_outbound_connections (void);
//struct connection *create_connection (const char *host, int port, struct tgl_session *session, struct connection_methods *methods);
//struct tgl_dc *tgln_alloc_dc (int id, char *ip, int port);
//void tgln_dc_create_session (struct tgl_dc *DC, struct mtproto_methods *methods);
struct connection *tgln_create_connection (struct tgl_state *TLS, const char *host, int port, struct tgl_session *session, struct tgl_dc *dc, struct mtproto_methods *methods);
#define GET_DC(c) (c->session->dc)
#endif

75
tgp-timers.c Normal file
View file

@ -0,0 +1,75 @@
/*
This file is part of tgl-library
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright Vitaly Valtman 2013-2014
*/
#include <tgl.h>
#include <stdlib.h>
#include <glib.h>
#include <eventloop.h>
struct tgl_timer {
struct tgl_state *TLS;
void (*cb)(struct tgl_state *, void *);
void *arg;
int fd;
};
static int timer_alarm (gpointer arg) {
struct tgl_timer *t = arg;
t->cb (t->TLS, t->arg);
return FALSE;
}
static struct tgl_timer *tgl_timer_alloc (struct tgl_state *TLS, void (*cb)(struct tgl_state *TLS, void *arg), void *arg) {
struct tgl_timer *t = malloc (sizeof (*t));
t->TLS = TLS;
t->cb = cb;
t->arg = arg;
t->fd = -1;
return t;
}
static void tgl_timer_insert (struct tgl_timer *t, double p) {
if (p < 0) { p = 0; }
if (p < 1) {
t->fd = purple_timeout_add (1000 * p, timer_alarm, t);
} else {
t->fd = purple_timeout_add_seconds (p, timer_alarm, t);
}
}
static void tgl_timer_delete (struct tgl_timer *t) {
if (t->fd >= 0) {
purple_timeout_remove (t->fd);
t->fd = -1;
}
}
static void tgl_timer_free (struct tgl_timer *t) {
if (t->fd >= 0) {
tgl_timer_delete (t);
}
free (t);
}
struct tgl_timer_methods tgp_timers = {
.alloc = tgl_timer_alloc,
.insert = tgl_timer_insert,
.delete = tgl_timer_delete,
.free = tgl_timer_free
};

27
tgp-timers.h Normal file
View file

@ -0,0 +1,27 @@
/*
This file is part of tgl-library
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright Vitaly Valtman 2013-2014
*/
#ifndef __TGL_TIMERS_H__
#define __TGL_TIMERS_H__
#include "tgl.h"
extern struct tgl_timer_methods tgp_timers;
#endif