diff options
Diffstat (limited to 'src/sbsessions.c')
-rw-r--r-- | src/sbsessions.c | 597 |
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; + } + +} |