aboutsummaryrefslogtreecommitdiff
path: root/src/sbsessions.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sbsessions.c')
-rw-r--r--src/sbsessions.c597
1 files changed, 597 insertions, 0 deletions
diff --git a/src/sbsessions.c b/src/sbsessions.c
new file mode 100644
index 0000000..0ce353c
--- /dev/null
+++ b/src/sbsessions.c
@@ -0,0 +1,597 @@
+/*
+ * sbsessions.c
+ *
+ * SB session (=>IM window) management (but not the protocol nor UI)
+ *
+ * (c) 2002-2005 Thomas White <taw27@srcf.ucam.org>
+ * Part of TuxMessenger - GTK+-based MSN Messenger client
+ *
+ * This package 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; version 2 dated June, 1991.
+ *
+ * This package 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 package; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#include "messagewindow.h"
+#include "sbsessions.h"
+#include "sbprotocol.h"
+#include "msnprotocol.h"
+#include "contactlist.h"
+#include "msngenerics.h"
+#include "options.h"
+#include "msnp2p.h"
+#include "error.h"
+#include "routines.h"
+#include "fonttrans.h"
+
+SbSession *sessions = NULL;
+
+static SbSession *sbsessions_lastsession() {
+
+ SbSession *session = sessions;
+
+ while ( session ) {
+
+ assert(session != NULL);
+
+ if ( session->next == NULL ) {
+ return session;
+ } else {
+ session = session->next;
+ }
+
+ }
+
+ return NULL; /* If there were no sessions at all. */
+
+}
+
+static void sbsessions_inituser(SbUser *new_user) {
+
+}
+
+static int sbsessions_ready_callback(SbSession *session) {
+
+ if ( !session->ready ) {
+ debug_print("SS %8p: Not ready after 60 seconds - deleting.\n", session);
+ sbsessions_destroy(session);
+ return FALSE;
+ }
+ session->ready_timeout = 0;
+
+ return FALSE; /* Don't repeat */
+
+}
+
+
+static SbSession *sbsessions_new(const char *username, SbSessionSource source) {
+
+ SbSession *new_session;
+ SbSession *last_session;
+
+ new_session = malloc(sizeof(SbSession));
+ new_session->users = malloc(sizeof(SbUser));
+ assert(new_session->users != NULL);
+
+ new_session->messagewindow = NULL;
+
+ new_session->users->next = NULL;
+ new_session->users->username = strdup(username);
+ sbsessions_inituser(new_session->users);
+
+ new_session->ready = FALSE;
+ new_session->num_users = 1;
+ new_session->rbufsize = 0;
+ new_session->wbufsize = 0;
+ new_session->rbuffer = NULL;
+ new_session->wbuffer = NULL;
+ new_session->roffset = 0;
+ new_session->roffset = 0;
+ new_session->am_typing = 0;
+ new_session->am_typing_callback = 0;
+ new_session->threeway_intent = NULL;
+ new_session->multipackets = NULL;
+ new_session->trid = 1; /* Reset in sbprotocol_connect */
+ new_session->cached = NULL;
+
+ new_session->source = source;
+ new_session->rcallback = 0;
+ new_session->wcallback = 0;
+
+ /* 60 seconds to get ready. */
+ new_session->ready_timeout = gtk_timeout_add(60000, (GtkFunction)sbsessions_ready_callback, new_session);
+
+ /* Link it into the list. */
+ last_session = sbsessions_lastsession();
+ if ( last_session != NULL ) {
+ assert(last_session->next == NULL);
+ last_session->next = new_session;
+ } else {
+ sessions = new_session;
+ }
+ new_session->next = NULL;
+
+ return new_session;
+
+}
+
+SbSession *sbsessions_create_local(const char *username) {
+
+ SbSession *session = sbsessions_new(username, SESSION_SOURCE_LOCAL);
+
+ session->neg_trid = msnprotocol_initiatesb();
+ debug_print("SS %8p: New local session for user %s.\n", session, username);
+
+ return session;
+
+}
+
+/* Called when the user clicks on a name in the contact list. */
+SbSession *sbsessions_create_remote(char *username, char *switchboardaddress, char *sessionid, char *authchallenge) {
+
+ SbSession *session = sbsessions_new(username, SESSION_SOURCE_REMOTE);
+
+ session->neg_trid = 0;
+ sbprotocol_initiate_remote(session, switchboardaddress, sessionid, authchallenge);
+ debug_print("SS %8p: New remote session for user %s.\n", session, username);
+
+ return session;
+
+}
+
+/* Create a three-way session in "one go". */
+SbSession *sbsessions_create_threeway(char *username1, char *username2) {
+
+ SbSession *session = sbsessions_new(username1, SESSION_SOURCE_LOCAL);
+
+ session->threeway_intent = strdup(username2);
+ session->neg_trid = msnprotocol_initiatesb();
+ debug_print("SS %8p: New local session for user %s. Intending to invite %s too.\n", session, username1, username2);
+
+ return session;
+
+}
+
+void sbsessions_plug(SbSession *session, MessageWindow *messagewindow) {
+ session->messagewindow = messagewindow;
+}
+
+/* Unplug any SB sessions which are plugged into a given IM window */
+void sbsessions_unplug(MessageWindow *messagewindow) {
+
+ SbSession *session = sessions;
+
+ while ( session != NULL ) {
+ if ( session->messagewindow == messagewindow ) {
+ session->messagewindow = NULL;
+ }
+ session = session->next;
+ }
+
+}
+
+/* Find a headless session with this username. */
+SbSession *sbsessions_find_headless(const char *username) {
+
+ SbSession *session = sessions;
+
+ while ( session != NULL ) {
+ if ( (session->num_users == 1) && (strcmp(username, session->users->username) == 0) && (session->messagewindow == NULL) ) {
+ return session;
+ }
+ session = session->next;
+ }
+
+ return NULL;
+
+}
+
+/* Find a session with just this user in, and noone about to randomly join (hopefully...) */
+SbSession *sbsessions_find_single_safe(const char *username) {
+
+ SbSession *session = sessions;
+
+ while ( session != NULL ) {
+ if ( (session->num_users == 1) && (strcmp(username, session->users->username) == 0) ) {
+ if ( session->threeway_intent == NULL ) {
+ return session;
+ }
+ }
+ session = session->next;
+ }
+
+ return NULL;
+
+}
+
+
+/* SbUsers are unique to a particular SbSession. Find the parent SbSession
+ given the SbUser. Could just keep a note of the parent record in the
+ SbUser record, but sbsessions_stoptyping needs to know if the SbUser
+ is still attached to (any) SbSession. */
+SbSession *sbsessions_find_user(SbUser *target_user) {
+
+ SbSession *session;
+
+ session = sessions;
+ while ( session != NULL ) {
+ SbUser *user = session->users;
+ while ( user != NULL ) {
+ if ( user == target_user ) {
+ return session;
+ }
+ user = user->next;
+ }
+ session = session->next;
+ }
+
+ debug_print("SS %8p: Couldn't find user in all sessions.\n", NULL);
+ return NULL;
+
+}
+
+/* Return a session's record given "TrID". */
+SbSession *sbsessions_find_trid(unsigned int trid) {
+
+ SbSession *session;
+
+ session = sessions;
+ while ( session != NULL ) {
+
+ assert(session != NULL);
+
+ if ( session->neg_trid == trid ) {
+ return session;
+ } else {
+ session = session->next;
+ }
+
+ }
+
+ debug_print("SS %8p: Failed to find session negotiated with TrId %i\n", NULL, trid);
+ return NULL;
+
+}
+
+SbUser *sbsessions_find_username(SbSession *session, const char *username) {
+
+ SbUser *user;
+
+ assert(session != NULL);
+ assert(session->users != NULL);
+
+ user = session->users;
+ while ( user != NULL ) {
+
+ assert(user != NULL);
+
+ /* Case-insensitive here. Username may have different case depending
+ on its source, since the servers seem to change usernames to
+ lower case but clients might not in (e.g.) TypingUser controls. */
+ if ( strcasecmp(user->username, username) == 0 ) {
+ return user;
+ } else {
+ user = user->next;
+ }
+
+ }
+
+ return NULL;
+
+}
+
+/* Locate a user on a given SB session from their DP SHA1D field. */
+SbUser *sbsessions_find_dpsha1d(SbSession *session, char *sha1d) {
+
+ SbUser *user;
+
+ assert(session != NULL);
+ assert(session->users != NULL);
+
+ user = session->users;
+ while ( user != NULL ) {
+
+ assert(user != NULL);
+
+ if ( strcmp(user->dpsha1d, sha1d) == 0 ) {
+ return user;
+ } else {
+ user = user->next;
+ }
+
+ }
+
+ return NULL;
+
+}
+
+void sbsessions_destroy(SbSession *session) {
+
+ SbSession *prev_session;
+ SbUser *user;
+ CachedMsg *cached;
+
+ /* Unplug the session and close the socket. */
+ messagewindow_unplug(session);
+ sbprotocol_close(session);
+
+ /* Remove the session from the list. */
+ prev_session = sessions;
+ if ( prev_session != session ) {
+ while ( prev_session != NULL ) {
+ assert(prev_session != NULL);
+ if ( prev_session->next == session ) {
+ break;
+ } else {
+ prev_session = prev_session->next;
+ }
+ }
+ assert(prev_session->next == session);
+ /* Link it out of the list. */
+ prev_session->next = session->next;
+ } else {
+ /* This session was the first on the list. */
+ assert(sessions == session); /* Can't fail... */
+ sessions = session->next; /* Which may be NULL if the list is now empty. */
+ }
+
+ /* Drop all MSNP2P sessions in this SB session. */
+ msnp2p_abortall(session);
+
+ /* Drop any cached messages, and inform the user. */
+ cached = session->cached;
+ while ( cached != NULL ) {
+
+ CachedMsg *next = cached->next;
+ if ( !session->messagewindow ) {
+ messagewindow_mitigate(session);
+ }
+ messagewindow_reportdropped(session->messagewindow, cached->message, cached->length);
+ free(cached->message);
+ free(cached);
+ cached = next;
+ messagewindow_unplug(session); /* This session doesn't exist, remember? */
+
+ }
+
+ /* Remove all the users. */
+ user = session->users;
+ while ( user != NULL ) {
+
+ SbUser *next_user = user->next;
+ free(user->username);
+ free(user);
+ user = next_user;
+
+ }
+
+ /* Wind up the low-level business. */
+ if ( session->rcallback != 0 ) {
+ gdk_input_remove(session->rcallback);
+ }
+ session->rcallback = 0;
+ if ( session->wcallback != 0 ) {
+ gdk_input_remove(session->wcallback);
+ }
+ session->wcallback = 0;
+
+ if ( session->ready_timeout != 0 ) {
+ gtk_timeout_remove(session->ready_timeout);
+ }
+ session->ready_timeout = 0;
+
+ if ( session->am_typing_callback != 0 ) {
+ gtk_timeout_remove(session->am_typing_callback);
+ session->am_typing = 0;
+ session->am_typing_callback = 0;
+ }
+
+ if ( session->rbuffer != NULL ) {
+ free(session->rbuffer);
+ }
+ if ( session->wbuffer != NULL ) {
+ free(session->wbuffer);
+ }
+
+ free(session);
+
+}
+
+SbUser *sbsessions_lastuser(SbSession *session) {
+
+ SbUser *user;
+
+ user = session->users;
+ while ( user != NULL ) {
+
+ assert(user != NULL);
+
+ if ( user->next == NULL ) {
+ return user;
+ } else {
+ user = user->next;
+ }
+
+ }
+
+ debug_print("SS %8p: Reached end of sbsessions_lastuser! This doesn't happen!\n", session);
+ return NULL;
+
+}
+
+static SbUser *sbsessions_addnewuser(SbSession *session, char *username) {
+
+ SbUser *new_user;
+ SbUser *previous_user;
+
+ new_user = malloc(sizeof(SbUser));
+ assert(new_user != NULL);
+ new_user->next = NULL;
+ new_user->username = strdup(username);
+ sbsessions_inituser(new_user);
+
+ previous_user = sbsessions_lastuser(session);
+ assert(previous_user->next == NULL);
+ previous_user->next = new_user;
+
+ session->num_users++;
+
+ return new_user;
+
+}
+
+/* Deal with any user joining a session (whether by JOI or IRO). */
+void sbsessions_joined(SbSession *session, char *username, char *friendlyname) {
+
+ SbUser *user;
+
+ /* "friendlyname" is from the JOI message. This user might not be in any of the
+ contact lists. Now would be a good time to check, and if not, create a
+ temporary record for them. */
+ if ( contactlist_friendlyname(username) == NULL ) {
+ debug_print("SS %8p: Creating temporary user record for '%s'/'%s'\n", session, username, friendlyname);
+ contactlist_tldetails(CONTACT_SOURCE_RNG, username, friendlyname, ONLINE_NLN);
+ }
+
+ user = sbsessions_find_username(session, username);
+ if ( user == NULL ) {
+
+ /* User is new - probably got invited by someone else */
+ debug_print("SS %8p: Couldn't find user %s in SB record - adding them.\n", session, username);
+
+ user = sbsessions_addnewuser(session, username);
+
+ }
+
+ if ( session->messagewindow != NULL ) {
+ messagewindow_joined(session->messagewindow, username);
+ }
+
+}
+
+/* Deal with any user leaving a session. */
+void sbsessions_left(SbSession *session, char *username) {
+
+ SbUser *user;
+
+ user = sbsessions_find_username(session, username);
+ if ( user == NULL ) {
+ debug_print("SS %8p: Couldn't find leaving user %s in SB record!\n", session, username);
+ return;
+ }
+
+ if ( session->num_users == 1 ) {
+ sbprotocol_leavesession(session);
+ } else {
+
+ /* "Normal" situation. Remove user record, their DP and status bar. */
+
+ SbUser *find_user;
+
+ session->num_users--;
+ if ( session->messagewindow != NULL ) {
+ messagewindow_removeuser(session->messagewindow, username);
+ }
+
+ /* Find the preceding user in the session and link this user out. */
+ find_user = session->users;
+ if ( find_user == user ) {
+ /* User was the first in the list. */
+ session->users = user->next;
+ } else {
+
+ while ( find_user != NULL ) {
+
+ assert(find_user != NULL);
+
+ if ( find_user->next == user ) {
+ find_user->next = user->next;
+ break;
+ } else {
+ find_user = find_user->next;
+ }
+
+ }
+
+ }
+ free(user->username);
+ free(user);
+
+ }
+
+}
+
+static int sbsessions_amtyping_callback(SbSession *session) {
+
+ session->am_typing = 0;
+ session->am_typing_callback = 0;
+
+ return FALSE; /* Don't repeat */
+
+}
+
+void sbsessions_am_typing(SbSession *session) {
+
+ if ( !session->ready ) {
+ debug_print("SS %8p: Not ready - not sending TypingUser.\n", session);
+ return;
+ }
+
+ /* Calculate timeout then send TypingUser control. */
+ if ( session->am_typing == 0 ) {
+
+ sbprotocol_sendtypingcontrol(session);
+ session->am_typing = 1;
+ session->am_typing_callback = gtk_timeout_add(5000, (GtkFunction)sbsessions_amtyping_callback, session);
+
+ }
+
+}
+
+/* Check a session is ready for I/O. */
+int sbsessions_sessionready(SbSession *session) {
+
+ if ( session == NULL ) {
+ return FALSE;
+ }
+
+ return session->ready;
+
+}
+
+/* Delete all sessions. */
+void sbsessions_destroy_all() {
+
+ SbSession *session;
+ SbSession *next;
+
+ debug_print("SB %8p: destroying all sessions.\n", NULL);
+
+ session = sessions;
+ while ( session != NULL ) {
+ next = session->next;
+ sbsessions_destroy(session);
+ session = next;
+ }
+
+}