diff --git a/mtproto-client.c b/mtproto-client.c index 01618c6..3bc63c1 100644 --- a/mtproto-client.c +++ b/mtproto-client.c @@ -890,7 +890,7 @@ void work_update (struct mtproto_connection *self, long long msg_id UU) { assert (M); fetch_pts (self); self->unread_messages ++; - event_update_new_message(M); + event_update_new_message (tg, M); //print_message (M); //update_prompt (); break; diff --git a/purple-plugin/telegram-purple.c b/purple-plugin/telegram-purple.c index ed651c2..c66b7cf 100644 --- a/purple-plugin/telegram-purple.c +++ b/purple-plugin/telegram-purple.c @@ -73,8 +73,10 @@ void tg_cli_log_cb(const char* format, va_list ap) purple_debug_info(PLUGIN_ID, "%s", buffer); } -void on_new_message(struct message *M); -void peer_allocated_handler(void *user); +void on_new_message (struct telegram *instance, struct message *M); +void peer_allocated_handler (struct telegram *instance, void *user); +void telegram_on_phone_registration (struct telegram *instance); +void elegram_on_client_registration (struct telegram *instance); /** * Returns the base icon name for the given buddy and account. @@ -99,26 +101,7 @@ static void tgprpl_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * info } /** - * Request a verification key, save the returned verification_hash in the account settings - * for later usage and inform the user. -static void login_request_verification(PurpleAccount *acct) -{ - // TODO: we should find a way to request the key - // only once. - purple_debug_info(PLUGIN_ID, "No code provided, requesting new authentication code.\n"); - char *new_hash = network_request_registration(); - purple_debug_info(PLUGIN_ID, "Saving verification_hash: '%s'", new_hash); - purple_account_set_string(acct, "verification_hash", new_hash); - - purple_notify_message(_telegram_protocol, PURPLE_NOTIFY_MSG_INFO, "Please Verify", - "You need to verify this device, please enter the code struct telegram has sent to you by SMS.", - NULL, NULL, NULL); -} - */ - -/** - * Handle a failed verification, by removing the invalid sms code and - * notifying the user + * Handle a failed verification by removing the invalid sms code and notifying the user */ static void login_verification_fail(PurpleAccount *acct) { @@ -179,89 +162,6 @@ static void tgprpl_has_input(struct telegram *tg) } } -static void tgprpl_on_state_change(struct telegram *instance, int state, void *data) -{ - telegram_conn *conn = instance->extra; - switch (state) { - case STATE_PHONE_NOT_REGISTERED: - // TODO: Request first and last name - // TODO: Fetch PurpleAccount and don't use global - purple_debug_info(PLUGIN_ID, "Phone is not registered, registering...\n"); - const char *first_name = purple_account_get_string(conn->pa, "first_name", NULL); - const char *last_name = purple_account_get_string(conn->pa, "last_name", NULL); - const char *code = purple_account_get_string(conn->pa, "verification_key", NULL); - const char *hash = purple_account_get_string(conn->pa, "verification_hash", NULL); - purple_debug_info(PLUGIN_ID, "code: %s\n", code); - purple_debug_info(PLUGIN_ID, "verification_hash: %s\n", hash); - if (!first_name || !last_name || !strlen(first_name) > 0 || !strlen(last_name) > 0) { - purple_notify_message(_telegram_protocol, PURPLE_NOTIFY_MSG_INFO, "Registration Needed", - "Enter your first and last name to register this phone number with the telegram network.", - NULL, NULL, NULL); - return; - } - - do_send_code_result_auth (instance, code, hash, first_name, last_name); - break; - - case STATE_PHONE_CODE_NOT_ENTERED: { - char *hash = data; - const char *code = purple_account_get_string(conn->pa, "verification_key", NULL); - do_send_code_result(instance, code, hash); - // TODO: Request SMS code - } - break; - - case STATE_CLIENT_CODE_NOT_ENTERED: { - char *hash = data; - const char *code = purple_account_get_string(conn->pa, "verification_key", NULL); - //const char *hash = purple_account_get_string(conn->pa, "verification_hash", NULL); - do_send_code_result(instance, code, hash); - // enter SMS code - } - break; - - case STATE_READY: - // ready - purple_debug_info(PLUGIN_ID, "Logged in...\n"); - purple_connection_set_state(conn->gc, PURPLE_CONNECTED); - char const *username = purple_account_get_username(conn->pa); - purple_connection_set_display_name(conn->gc, username); - purple_blist_add_account(conn->pa); - - tggroup = purple_find_group("struct telegram"); - if (tggroup == NULL) { - purple_debug_info(PLUGIN_ID, "PurpleGroup = NULL, creating"); - tggroup = purple_group_new("struct telegram"); - purple_blist_add_group(tggroup, NULL); - } - - on_update_new_message(on_new_message); - on_peer_allocated(peer_allocated_handler); - - // get all current contacts - purple_debug_info(PLUGIN_ID, "Fetching all current contacts...\n"); - do_update_contact_list(instance); - - purple_debug_info(PLUGIN_ID, "Fetching all current chats...\n"); - do_get_dialog_list(instance); - - // get new messages - purple_debug_info(PLUGIN_ID, "Fetching new messages...\n"); - do_get_difference(instance); - - tgprpl_has_output(instance); - //telegram_flush_queries(instance); - break; - - case STATE_ERROR: { - const char* err = data; - logprintf("Connection errored: %s\n", err); - - } - break; - } -} - static void init_dc_settings(PurpleAccount *acc, struct dc *DC) { DC->port = purple_account_get_int(acc, "port", TELEGRAM_DEFAULT_PORT); @@ -293,6 +193,101 @@ void telegram_on_proxy_close(struct telegram *instance, int fd UU) purple_input_remove (conn->wh); } +void telegram_on_phone_registration (struct telegram *instance) +{ + telegram_conn *conn = instance->extra; + + // TODO: Request first and last name + // TODO: Fetch PurpleAccount and don't use global + purple_debug_info(PLUGIN_ID, "Phone is not registered, registering...\n"); + const char *first_name = purple_account_get_string(conn->pa, "first_name", NULL); + const char *last_name = purple_account_get_string(conn->pa, "last_name", NULL); + const char *code = purple_account_get_string(conn->pa, "verification_key", NULL); + + if (!first_name || !last_name || !strlen(first_name) > 0 || !strlen(last_name) > 0) { + purple_notify_message(_telegram_protocol, PURPLE_NOTIFY_MSG_INFO, "Registration Needed", + "Enter your first and last name to register this phone number with the telegram network.", + NULL, NULL, NULL); + return; + } + + do_send_code_result_auth (instance, code, first_name, last_name); +} + +void client_registration_entered (gpointer data, const gchar *code) +{ + struct telegram *tg = data; + do_send_code_result (tg, code); + tgprpl_has_output (tg); +} + +void client_registration_canceled (gpointer data) +{ + struct telegram *tg = data; + // TODO: disconnect and exit +} + +void telegram_on_client_registration (struct telegram *instance) +{ + purple_debug_info(PLUGIN_ID, "Client is not registered, registering...\n"); + telegram_conn *conn = instance->extra; + + 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 + FALSE, // multiline + FALSE, // masked + "code", // hint + "OK", // ok_text + G_CALLBACK(client_registration_entered), // ok_cb + "Cancel", // cancel_text + G_CALLBACK(client_registration_canceled), // cancel_cb + conn->pa, // account + NULL, // who + NULL, // conv + conn->tg // user_data + ); +} + +void telegram_on_ready (struct telegram *instance) +{ + purple_debug_info(PLUGIN_ID, "telegram_on_ready().\n"); + telegram_conn *conn = instance->extra; + + purple_connection_set_state(conn->gc, PURPLE_CONNECTED); + purple_connection_set_display_name(conn->gc, purple_account_get_username(conn->pa)); + + purple_blist_add_account(conn->pa); + tggroup = purple_find_group("Telegram"); + if (tggroup == NULL) { + purple_debug_info (PLUGIN_ID, "PurpleGroup = NULL, creating"); + tggroup = purple_group_new ("struct telegram"); + purple_blist_add_group (tggroup, NULL); + } + + // get all current contacts + purple_debug_info(PLUGIN_ID, "Fetching all current contacts...\n"); + do_update_contact_list(instance); + + purple_debug_info(PLUGIN_ID, "Fetching all current chats...\n"); + do_get_dialog_list(instance); + + // get new messages + purple_debug_info(PLUGIN_ID, "Fetching new messages...\n"); + do_get_difference(instance); + + tgprpl_has_output(instance); +} + +void telegram_on_disconnected (struct telegram *tg) +{ + logprintf ("telegram_on_disconnected()\n"); + assert (0); +} + /** * A proxy connection was created by purple * @@ -314,31 +309,17 @@ void tgprpl_login_on_connected(gpointer *data, gint fd, const gchar *error_messa conn->rh = purple_input_add(fd, PURPLE_INPUT_READ, tgprpl_input_cb, tg); telegram_set_proxy(tg, fd); +} - // // load all settings: the known network topology, secret keys, logs and configuration file paths - // purple_debug_info(PLUGIN_ID, "parse_config()\n"); - // parse_config (); - // purple_debug_info(PLUGIN_ID, "set_default_username()\n"); - //set_default_username (username); - - // Connect to the network - // Assure phone number registration - /* - if (!network_phone_is_registered()) { - if (code && strlen(code) > 0 && hash && strlen(hash) > 0) { - int registered = network_verify_phone_registration(code, hash, first_name, last_name); - if (registered) { - store_config(); - } else { - login_verification_fail(acct); - return; - } - } else { - login_request_verification(acct); - return; - } - } - */ +struct telegram_config tgconf = { + "/home/dev-jessie/.telegram", + NULL, // on output + telegram_on_proxy_request, + telegram_on_proxy_close, + telegram_on_phone_registration, + telegram_on_client_registration, + telegram_on_ready, + telegram_on_disconnected, // Assure client registration /* @@ -362,7 +343,9 @@ void tgprpl_login_on_connected(gpointer *data, gint fd, const gchar *error_messa } } */ -} + on_new_message, + peer_allocated_handler +}; /** * This must be implemented. @@ -380,9 +363,7 @@ static void tgprpl_login(PurpleAccount * acct) // TODO: fetch current home directory // use this as root - struct telegram *tg = telegram_new(&DC, username, "/home/dev-jessie/.telegram", - telegram_on_proxy_request, telegram_on_proxy_close); - telegram_add_state_change_listener(tg, tgprpl_on_state_change); + struct telegram *tg = telegram_new (&DC, username, &tgconf); telegram_restore_session(tg); telegram_conn *conn = g_new0(telegram_conn, 1); @@ -396,7 +377,7 @@ static void tgprpl_login(PurpleAccount * acct) telegram_network_connect(tg); } -void on_new_message(struct message *M) +void on_new_message(struct telegram *tg, struct message *M) { purple_debug_info(PLUGIN_ID, "New Message: %s\n", M->message); // TODO: this should probably be freed again somwhere @@ -439,7 +420,7 @@ static PurpleChat *blist_find_chat_by_id(PurpleConnection *gc, const char *id) } -void peer_allocated_handler(void *usr) +void peer_allocated_handler(struct telegram *tg, void *usr) { peer_t *user = usr; gchar *name = g_strdup_printf("%d", get_peer_id(user->id)); diff --git a/queries.c b/queries.c index 811a169..3c89ea9 100644 --- a/queries.c +++ b/queries.c @@ -75,7 +75,7 @@ extern int sync_from_start; int sync_from_start = 0; void telegram_flush_queries (struct telegram *instance) { - instance->on_output(instance); + instance->config->on_output(instance); } void out_peer_id (struct mtproto_connection *self, peer_id_t id); @@ -153,8 +153,10 @@ struct query *send_query (struct dc *DC, int ints, void *data, struct query_meth logprintf ( "%lld %lld\n", q->msg_id, queries_tree->x->msg_id); } } + queries_tree = tree_insert_query (queries_tree, q, lrand48 ()); - logprintf("queries_num: %d\n", ++ mtc->queries_num); + struct mtproto_connection *mtp = query_get_mtproto(q); + logprintf("queries_num: %d\n", ++ mtp->queries_num); q->ev.alarm = (void *)alarm_query; q->ev.timeout = get_double_time () + QUERY_TIMEOUT; diff --git a/telegram.c b/telegram.c index e214cfc..fda4e58 100755 --- a/telegram.c +++ b/telegram.c @@ -18,51 +18,23 @@ /* * New message received */ -void (*on_msg_handler)(struct message *M); -void on_update_new_message(void (*on_msg)(struct message *M)) +void event_update_new_message(struct telegram *instance, struct message *M) { - on_msg_handler = on_msg; -} -void event_update_new_message(struct message *M) -{ - if (on_msg_handler) { - on_msg_handler(M); + if (instance->config->on_msg_handler) { + instance->config->on_msg_handler(instance, M); } } /* * Peer allocated */ -void (*on_peer_allocated_handler)(void *peer); -void on_peer_allocated(void (*handler)(void *peer)) +void event_peer_allocated(struct telegram *instance, void *peer) { - on_peer_allocated_handler = handler; -} -void event_peer_allocated(void *peer) -{ - if (on_peer_allocated_handler) { - on_peer_allocated_handler(peer); + if (instance->config->on_peer_allocated_handler) { + instance->config->on_peer_allocated_handler(instance, peer); } } -/* - * State changed - */ -GList *change_listeners = NULL; -void telegram_add_state_change_listener(struct telegram *instance, state_listener_t listener) -{ - instance->change_state_listeners = g_list_append(instance->change_state_listeners, listener); -} -void telegram_change_state(struct telegram *instance, int state, void *data) -{ - logprintf("telegram connection state changed to: %d\n", state); - instance->session_state = state; - GList *curr = instance->change_state_listeners; - do { - ((state_listener_t)curr->data)(instance, state, data); - } while ((curr = g_list_next(change_listeners)) != NULL); -} - /** * Calculate the configuration path for the given config file and the given instance * @@ -82,8 +54,9 @@ char *telegram_get_config(struct telegram *instance, char *config) * the authorization and registration steps needed to connect the client to the telegram network, * and will either trigger RPC queries or callbacks to the GUI to request input from the user. */ -void on_state_change(struct telegram *instance, int state, void *data) +void telegram_change_state (struct telegram *instance, int state, void *data) { + instance->session_state = state; logprintf("on_state_change: %d\n", state); switch (state) { case STATE_ERROR: { @@ -139,11 +112,9 @@ void on_state_change(struct telegram *instance, int state, void *data) // close old connection and mark it for destruction mtproto_close (instance->connection); - if (instance->proxy_request_cb) { - // tell the proxy to close all connections - instance->proxy_close_cb (instance, - instance->connection->connection->fd); - } + assert (instance->config->proxy_request_cb); + // tell the proxy to close all connections + instance->config->proxy_close_cb (instance, instance->connection->connection->fd); // start a new connection to the demanded data center. The pointer to the // currently working dc should have already been updated by the @@ -154,20 +125,16 @@ void on_state_change(struct telegram *instance, int state, void *data) } } -struct telegram *telegram_new(struct dc *DC, const char* login, const char *config_path, - void (*proxy_request_cb)(struct telegram *instance, const char *ip, int port), - void (*proxy_close_cb)(struct telegram *instance, int fd)) +struct telegram *telegram_new(struct dc *DC, const char* login, struct telegram_config *config) { struct telegram *this = talloc0(sizeof(struct telegram)); this->protocol_data = NULL; this->auth.DC_list[0] = DC; - this->change_state_listeners = NULL; this->bl = talloc0 (sizeof(struct binlog)); - this->proxy_request_cb = proxy_request_cb; - this->proxy_close_cb = proxy_close_cb; + this->config = config; this->login = g_strdup(login); - this->config_path = g_strdup_printf("%s/%s", config_path, login); + this->config_path = g_strdup_printf("%s/%s", config->base_config_path, login); this->download_path = telegram_get_config(this, "downloads"); this->auth_path = telegram_get_config(this, "auth"); this->state_path = telegram_get_config(this, "state"); @@ -180,14 +147,12 @@ struct telegram *telegram_new(struct dc *DC, const char* login, const char *conf logprintf("%s\n", this->state_path); logprintf("%s\n", this->secret_path); - telegram_add_state_change_listener(this, on_state_change); telegram_change_state(this, STATE_INITIALISED, NULL); return this; } void telegram_free(struct telegram *this) { - g_list_free(this->change_state_listeners); g_free(this->login); g_free(this->config_path); g_free(this->download_path); @@ -279,8 +244,8 @@ void telegram_network_connect(struct telegram *instance) assert(0); } struct dc *DC_working = telegram_get_working_dc (instance); - assert (instance->proxy_request_cb); - instance->proxy_request_cb (instance, DC_working->ip, DC_working->port); + assert (instance->config->proxy_request_cb); + instance->config->proxy_request_cb (instance, DC_working->ip, DC_working->port); } /** diff --git a/telegram.h b/telegram.h index 59efb8f..8e04e0d 100644 --- a/telegram.h +++ b/telegram.h @@ -83,6 +83,75 @@ struct binlog { int s[1000]; }; +struct telegram; + +/** + * Contains all options and pointer to callback functions required by telegram + */ +struct telegram_config { + + /** + * The base path containing the telegram configuration + */ + const char* base_config_path; + + /** + * Called when there is pending network output + */ + void (*on_output)(struct telegram *instance); + + /** + * A callback function that delivers a connections to the given hostname + * and port by calling telegram_set_proxy. This is useful for tunelling + * the connection through a proxy server. + */ + void (*proxy_request_cb)(struct telegram *instance, const char *ip, int port); + + /** + * A callback function that is called once the proxy connection is no longer + * needed. This is useful for freeing all used resources. + */ + void (*proxy_close_cb)(struct telegram *instance, int fd); + + /** + * A callback function that is called when a phone registration is required. + * + * This callback must query first name, last name and the + * authentication code from the user and call do_send_code_result_auth once done + */ + void (*on_phone_registration_required) (struct telegram *instance); + + /** + * A callback function that is called when a client registration is required. + * + * This callback must query the authentication code from the user and + * call do_send_code_result once done + */ + void (*on_client_registration_required) (struct telegram *instance); + + /** + * A callback function that is called when telegram is ready + */ + void (*on_ready) (struct telegram *instance); + + /** + * A callback function that is called when telegram is disconnected + */ + void (*on_disconnected) (struct telegram *instance); + + /** + * A callback function that is called when a new message was allocated. This is useful + * for adding new messages to the GUI. + */ + void (*on_msg_handler)(struct telegram *instance, struct message *M); + + /** + * A callback function that is called when a new peer was allocated. This is useful + * for populating the GUI with new peers. + */ + void (*on_peer_allocated_handler)(struct telegram *instance, void *peer); +}; + /** * A telegram session * @@ -101,6 +170,7 @@ struct telegram { char *secret_path; int session_state; + struct telegram_config *config; /* * protocol state @@ -108,8 +178,6 @@ struct telegram { struct protocol_state proto; struct authorization_state auth; - GList *change_state_listeners; - /* * connection */ @@ -120,12 +188,6 @@ struct telegram { */ struct binlog *bl; - /* - * callbacks - */ - void (*on_output)(struct telegram *instance); - void (*proxy_request_cb)(struct telegram *instance, const char *ip, int port); - void (*proxy_close_cb)(struct telegram *instance, int fd); void *extra; }; @@ -133,18 +195,11 @@ struct telegram { /** * Create a new telegram application * - * @param DC The initial data center to use - * @param login The phone number to use as login name - * @param config_path The configuration path used to store the content - * @param proxy_request_cb A callback function that delivers a connections to the given hostname - * and port by calling telegram_set_proxy. This is useful for tunelling - * the connection through a proxy server - * @param proxy_close_cb A callback function that is called once the proxy connection is no longer - * needed. This is useful for freeing all used resources + * @param DC The initial data center to use + * @param login The phone number to use as login name + * @param config Contains all callbacks used for the telegram instance */ -struct telegram *telegram_new(struct dc *DC, const char* login, const char* config_path, - void (*proxy_request_cb)(struct telegram *instance, const char *ip, int port), - void (*proxy_close_cb)(struct telegram *instance, int fd)); +struct telegram *telegram_new(struct dc *DC, const char* login, struct telegram_config *config); /** * Resume the session to @@ -175,23 +230,6 @@ struct dc *telegram_get_working_dc(struct telegram *instance); * Events */ -/** - * Handler to process a state change - * - * @param instance The telegram instance that changed its state - * @param state The changed state - * @param data Extra data that depends on switched state - */ -typedef void (*state_listener_t)(struct telegram *instance, int state, void *data); - -/** - * Execute this listener when the state has changed - * - * @param instance The telegram instance - * @param listener The listener to execute - */ -void telegram_add_state_change_listener(struct telegram *instance, state_listener_t listener); - /** * Change the state of the given telegram instance and execute all event handlers * @@ -283,14 +321,12 @@ void session_update_contact_list(); /* * Events */ -void on_update_new_message(void (*on_msg)(struct message *M)); -void event_update_new_message(struct message *M); +void event_update_new_message(struct telegram *instance, struct message *M); /* * Load known users and chats on connect */ -void on_peer_allocated(void (*handler)(void *peer)); -void event_peer_allocated(void *peer); +void event_peer_allocated(struct telegram *instance, void *peer); /** * Set a function to use as a handle to read from a network resource