Slightly working version
This commit is contained in:
commit
8f69d8d11d
17 changed files with 2531 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
tg/
|
120
Makefile
Normal file
120
Makefile
Normal 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
BIN
imgs/telegram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
imgs/telegram16.png
Normal file
BIN
imgs/telegram16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 671 B |
BIN
imgs/telegram22.png
Normal file
BIN
imgs/telegram22.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
BIN
imgs/telegram48.png
Normal file
BIN
imgs/telegram48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
79
msglog.c
Normal file
79
msglog.c
Normal 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
14
msglog.h
Normal 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
372
telegram-base.c
Normal 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
9
telegram-base.h
Normal 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
1082
telegram-purple.c
Normal file
File diff suppressed because it is too large
Load diff
70
telegram-purple.h
Normal file
70
telegram-purple.h
Normal 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
8
tg-server.pub
Normal 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
584
tgp-net.c
Normal 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
88
tgp-net.h
Normal 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
75
tgp-timers.c
Normal 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
27
tgp-timers.h
Normal 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
|
Loading…
Add table
Reference in a new issue