/* * Miranda-IM Vypress Chat/quickChat plugins * Copyright (C) Saulius Menkevicius * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: user.c,v 1.24 2005/04/11 18:49:44 bobas Exp $ */ #include "miranda.h" #include "main.h" #include "msgloop.h" #include "msghandler.h" #include "user.h" #include "userlist.h" #include "contacts.h" #include "chatroom.h" #include "options.h" #include "chanlist.h" #include "service.h" #include "util.h" /* static data */ static char * s_userNickname; static char * s_awayMsg; static int s_userStatus; static char * s_userChanlist; static enum vqp_gender s_userGender; static vqp_uuid_t s_userUuid; static enum vqp_codepage s_userCodepage; static vqp_link_t s_vqpLink; /* static routines */ /* user_closest_supported_status: * we do not support every possible user status mode * in this plugin, thus must select the closest status * * XXX: do the actual approximation, instead of defaulting to ID_STATUS_ONLINE */ static int user_closest_supported_status(int status) { switch(status) { case ID_STATUS_OFFLINE: case ID_STATUS_AWAY: case ID_STATUS_DND: case ID_STATUS_NA: case ID_STATUS_ONLINE: break; default: status = ID_STATUS_ONLINE; break; } return status; } /* user_validate_settings: * checks if we have valid settings to connect to network, * and shows configuration dialog, if wanted */ static int user_validate_settings(int show_config_dlg) { ASSERT_RETURNVALIFFAIL(VALIDPTR(s_userNickname), FALSE); if(!strlen(s_userNickname)) { if(show_config_dlg) options_show_user_options(); return FALSE; } #ifdef VYPRESSCHAT /* we must ensure to have a non-null UUID in order to connect */ if(vqp_uuid_is_nil(&s_userUuid)) { if(show_config_dlg) options_show_user_options(); return FALSE; } #endif return TRUE; } static vqp_link_t user_open_link() { DWORD * list; unsigned long * broadcast_masks; size_t list_sz, i; vqp_link_t link; /* setup broadcast mask list */ list = db_dword_list(NULL, VQCHAT_PROTO, "BroadcastMasks", &list_sz); broadcast_masks = malloc(sizeof(unsigned long) * (list_sz + 1)); for(i = 0; i < list_sz; i++) broadcast_masks[i] = list[i]; broadcast_masks[i] = 0UL; free(list); /* connect to the network */ link = vqp_link_open( VQCHAT_VQP_PROTO, db_dword_get(NULL, VQCHAT_PROTO, "ProtoOpt", VQCHAT_VQP_DEF_PROTO_OPT), (db_byte_get(NULL, VQCHAT_PROTO, "ProtoConn", 0) == 0) ? VQP_PROTOCOL_CONN_UDP : VQP_PROTOCOL_CONN_IPX, 0, broadcast_masks, db_dword_get(NULL, VQCHAT_PROTO, "Multicast", VQCHAT_VQP_DEF_MULTICAST), db_word_get(NULL, VQCHAT_PROTO, "Port", VQCHAT_VQP_DEF_PORT), NULL ); free(broadcast_masks); return link; } /* exported routines */ void user_init() { s_userNickname = strdup(""); s_userStatus = ID_STATUS_OFFLINE; s_awayMsg = strdup(""); s_userChanlist = NULL; vqp_uuid_create_nil(&s_userUuid); } void user_uninit() { free(s_userNickname); s_userNickname = NULL; free(s_awayMsg); s_awayMsg = NULL; chanlist_free(s_userChanlist); s_userChanlist = NULL; s_userStatus = ID_STATUS_OFFLINE; } enum vqp_status user_vqp_status_by_status(int status) { switch(status) { case ID_STATUS_AWAY: return VQP_STATUS_AWAY; case ID_STATUS_DND: return VQP_STATUS_DND; case ID_STATUS_NA: return VQP_STATUS_NOTAVAILABLE; case ID_STATUS_ONLINE: return VQP_STATUS_AVAILABLE; } return VQP_STATUS_AVAILABLE; } int user_status_by_vqp_status(enum vqp_status user_status) { switch(user_status) { case VQP_STATUS_AVAILABLE: return ID_STATUS_ONLINE; case VQP_STATUS_AWAY: return ID_STATUS_AWAY; case VQP_STATUS_DND: return ID_STATUS_DND; case VQP_STATUS_NOTAVAILABLE: return ID_STATUS_NA; default: break; } return ID_STATUS_ONLINE; } const char * user_status_name(int status) { switch(status) { case ID_STATUS_AWAY: return "Away"; case ID_STATUS_DND: return "Do Not Disturb"; case ID_STATUS_NA: return "Not Available"; case ID_STATUS_ONLINE: return "Online"; } return "(unknown)"; } /* user_hook_modules_loaded: * invoked from ME_SYSTEM_MODULESLOADED hook after all of miranda's modules * have loaded */ void user_hook_modules_loaded() { user_validate_settings(TRUE); } /* user_set_nickname: * sets user nickname * * returns: * non-0 on failure (e.g. !strlen(new_nickname)) * 0 on success */ int user_set_nickname(const char * new_nickname, int store_in_db) { ASSERT_RETURNVALIFFAIL(VALIDPTR(new_nickname), 1); ASSERT_RETURNVALIFFAIL(VALIDPTR(s_userNickname), 1); /* check if we have a valid nickname */ if(strlen(new_nickname) == 0) return 1; /* update my settings in DB */ if(store_in_db) db_string_set(NULL, VQCHAT_PROTO, "Nick", new_nickname); /* check that the new nickname is not the same */ if(!strcmp(new_nickname, s_userNickname)) return 0; /* send message to network */ if(!user_offline()) { /* send nickname change message */ char * r_nickname = util_utf2vqp(user_codepage(), user_nickname()), * r_new_nickname = util_utf2vqp(user_codepage(), new_nickname); msgloop_send( vqp_msg_nick_change(s_vqpLink, r_nickname, r_new_nickname, user_gender()), 0); free(r_nickname); free(r_new_nickname); /* update chatrooms */ chatroom_user_name_change(s_userNickname, new_nickname); } /* change nickname */ free(s_userNickname); s_userNickname = strdup(new_nickname); return 0; /* success */ } const char * user_nickname() { ASSERT_RETURNVALIFFAIL(VALIDPTR(s_userNickname), "no-nickname"); return s_userNickname; } int user_is_my_nickname(const char * nickname) { ASSERT_RETURNVALIFFAIL(VALIDPTR(nickname), FALSE); ASSERT_RETURNVALIFFAIL(VALIDPTR(s_userNickname), FALSE); return !strcmp(s_userNickname, nickname); } /* user_set_status: * sets specified user status * (if previous status was ID_STATUS_OFFLINE, then it tries to connect, * et vice-versa) * * returns: * zero, : status change completed (thus caller needs to send ProtoAck) * non-zero: no status change took place */ int user_set_status(int new_status) { new_status = user_closest_supported_status(new_status); if(new_status==s_userStatus) return 1; /* no need to change status */ /* check if we need to disconnect */ if(new_status==ID_STATUS_OFFLINE) { /* stop message loop */ msgloop_stop(); /* notify chatroom module that we've diconnected * (this will make the user leave all the channels) */ chatroom_disconnected(); /* cleanup user list */ userlist_disconnected(); /* close link */ vqp_link_close(s_vqpLink); /* set all contacts offline */ contacts_set_all_offline(); /* notify modules */ contacts_disconnected(); service_disconnected(); /* set status */ s_userStatus = ID_STATUS_OFFLINE; return 0; /* status change succeeded */ } /* check if we need to connect */ if(s_userStatus==ID_STATUS_OFFLINE) { /* check if we have valid settings to connect to network */ if(!user_validate_settings(FALSE)) { /* stay in offline mode */ ProtoBroadcastAck( VQCHAT_PROTO, NULL, ACKTYPE_LOGIN, ACKRESULT_SUCCESS, (HANDLE)s_userStatus, s_userStatus); /* deny status change */ ProtoBroadcastAck( VQCHAT_PROTO, NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, (LPARAM)LOGINERR_BADUSERID); return 1; /* mode change failure */ } s_vqpLink = user_open_link(); if(!s_vqpLink) { /* stay in offline mode */ ProtoBroadcastAck( VQCHAT_PROTO, NULL, ACKTYPE_LOGIN, ACKRESULT_SUCCESS, (HANDLE)s_userStatus, s_userStatus); /* send an ack to notify that there was a network failure */ ProtoBroadcastAck( VQCHAT_PROTO, NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, (LPARAM)LOGINERR_NONETWORK); return 1; /* mode change failure */ } /* set new status */ s_userStatus = new_status; /* start message loop so we can send and receive * messages asynchronuously, in a separate thread */ msgloop_start(s_vqpLink, msghandler_apc); /* notify chatroom module, that we've connected * (this will init user channel list and join the #Main) */ chatroom_connected(s_vqpLink); /* init user list * (this should go after chatroom_connected(), as userlist_connected() * will send a refresh req) */ userlist_connected(s_vqpLink); /* notify modules */ service_connected(s_vqpLink); contacts_connected(s_vqpLink); } else { char * r_nickname, * r_awaymsg; /* simply change our status */ /* send status change msg */ r_nickname = util_utf2vqp(user_codepage(), user_nickname()); r_awaymsg = util_utf2vqp(user_codepage(), user_awaymsg()); msgloop_send( vqp_msg_status_change( s_vqpLink, r_nickname, user_vqp_status_by_status(new_status), user_gender(), r_awaymsg), 0 ); free(r_nickname); free(r_awaymsg); /* set status */ s_userStatus = new_status; /* notify chatrooms */ chatroom_user_status_change(new_status); } return 0; /* status change succeeded */ } /* user_status: * returns user's status in miranda's ID_STATUS_* ints */ int user_status() { return s_userStatus; } enum vqp_gender user_gender() { return s_userGender; } void user_set_gender(enum vqp_gender new_gender, int store_in_db) { if(s_userGender != new_gender) { s_userGender = new_gender; if(store_in_db) { db_byte_set( NULL, VQCHAT_PROTO, "Gender", s_userGender == VQP_GENDER_MALE ? 'M': 'F'); } } } /* user_p_uuid: * returns user's active uuid (universally unique identifier) */ const vqp_uuid_t * user_p_uuid() { return &s_userUuid; } /* user_set_uuid: * updates user's active uuid, store_in_db should be set to TRUE, * if it should be saved to dababase settings */ void user_set_uuid(const vqp_uuid_t * p_new_uuid, int store_in_db) { ASSERT_RETURNIFFAIL(VALIDPTR(p_new_uuid)); if(!vqp_uuid_is_equal(p_new_uuid, &s_userUuid)) { s_userUuid = *p_new_uuid; if(store_in_db) { char * uuid_str = vqp_uuid_to_string(&s_userUuid); db_string_set(NULL, VQCHAT_PROTO, "Uuid", uuid_str); free(uuid_str); } } } enum vqp_codepage user_codepage() { return s_userCodepage; } void user_set_codepage(enum vqp_codepage codepage, int store_in_db) { if(codepage != s_userCodepage) { s_userCodepage = codepage; if(store_in_db) { db_byte_set( NULL, VQCHAT_PROTO, "Codepage", codepage==VQP_CODEPAGE_LOCALE ? 0: 1); } } } void user_set_awaymsg(const char * msg_text) { ASSERT_RETURNIFFAIL(VALIDPTR(msg_text) && VALIDPTR(s_awayMsg)); free(s_awayMsg); s_awayMsg = strdup(msg_text); } const char * user_awaymsg() { return s_awayMsg; } /* user_set_chanlist: * stores specified chanlist as my chanlist * * (chanlist can be == *user_p_chanlist()) */ void user_set_chanlist(const char * chanlist, int store_in_db) { if(chanlist != s_userChanlist) { /* copy the specified chanlist */ chanlist_free(s_userChanlist); s_userChanlist = chanlist_copy(chanlist); } if(store_in_db) { /* in case of empty chanlist it will store "" string, * otherwise -- the s_userChanlist string */ db_string_set(NULL, VQCHAT_PROTO, "Chanlist", s_userChanlist ? s_userChanlist: ""); } } /* user_p_chanlist: * returns pointer to user's chanlist (which can be modified) */ char ** user_p_chanlist() { return &s_userChanlist; }