summaryrefslogtreecommitdiff
path: root/plugins/!NotAdopted/VypressChat/contacts.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/!NotAdopted/VypressChat/contacts.c')
-rw-r--r--plugins/!NotAdopted/VypressChat/contacts.c705
1 files changed, 705 insertions, 0 deletions
diff --git a/plugins/!NotAdopted/VypressChat/contacts.c b/plugins/!NotAdopted/VypressChat/contacts.c
new file mode 100644
index 0000000000..5274900624
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contacts.c
@@ -0,0 +1,705 @@
+/*
+ * 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: contacts.c,v 1.26 2005/03/17 11:02:43 bobas Exp $
+ */
+
+#include "miranda.h"
+
+#include "main.h"
+#include "user.h"
+#include "userlist.h"
+#include "msgloop.h"
+#include "contacts.h"
+#include "util.h"
+
+/* static data
+ */
+static vqp_link_t s_vqpLink;
+
+/* static routines
+ */
+
+static __inline const char * contact_prop_name_by_enum(
+ enum contact_property_enum property)
+{
+ const char * name = NULL;
+ switch(property) {
+ case CONTACT_COMPUTER: name = "Computer"; break;
+ case CONTACT_USER: name = "ComputerUser"; break;
+ case CONTACT_WORKGROUP: name = "Workgroup"; break;
+ case CONTACT_PLATFORM: name = "Platform"; break;
+ case CONTACT_SOFTWARE: name = "Software"; break;
+ }
+ return name;
+}
+
+/* contacts_add_queued_message:
+ * adds a message to contacts message queue while
+ * the contact is offline. contacts_set_contact_status should
+ * send these messages when contact gets online.
+ */
+static int contacts_add_queued_message(
+ HANDLE hContact, const char * text, WPARAM flags)
+{
+ DBVARIANT dbv = {0, };
+ char * new_data;
+ size_t new_data_sz;
+ DBCONTACTWRITESETTING cws;
+
+ if(!db_get(hContact, VQCHAT_PROTO, "QueuedMsgs", &dbv)) {
+ size_t text_len = strlen(text) + 1;
+
+ if(dbv.type!=DBVT_BLOB && (dbv.cpbVal < (sizeof(WPARAM) + 1)))
+ return 1;
+
+ new_data_sz = dbv.cpbVal + sizeof(flags) + text_len;
+ new_data = malloc(new_data_sz);
+
+ memcpy(new_data, dbv.pbVal, dbv.cpbVal);
+ *((WPARAM *)(new_data + dbv.cpbVal)) = flags;
+ memcpy(new_data + dbv.cpbVal + sizeof(flags), text, text_len);
+ } else {
+ size_t text_len = strlen(text) + 1;
+ new_data_sz = sizeof(flags) + text_len;
+ new_data = malloc(new_data_sz);
+
+ *((WPARAM *)new_data) = flags;
+ memcpy(new_data + sizeof(flags), text, text_len);
+ }
+
+ cws.szModule = VQCHAT_PROTO;
+ cws.szSetting = "QueuedMsgs";
+ cws.value.type = DBVT_BLOB;
+ cws.value.cpbVal = new_data_sz;
+ cws.value.pbVal = new_data;
+ CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws);
+
+ free(new_data);
+ DBFreeVariant(&dbv);
+
+ return 0;
+}
+
+/* contacts_fetch_queued_message
+ * returns queued message (or NULL if no more)
+ */
+static char * contacts_fetch_queued_message(
+ HANDLE hContact, WPARAM * p_wparam)
+{
+ DBVARIANT dbv;
+ size_t msg_sz;
+ char * text;
+
+ if(db_get(hContact, VQCHAT_PROTO, "QueuedMsgs", &dbv))
+ return NULL;
+
+ if(dbv.type != DBVT_BLOB || (dbv.cpbVal < (sizeof(WPARAM) + 1))) {
+ db_unset(hContact, VQCHAT_PROTO, "QueuedMsgs");
+ return NULL;
+ }
+
+ /* XXX: check that we have '\0' terminator at the end */
+ msg_sz = sizeof(WPARAM) + strlen(dbv.pbVal + sizeof(WPARAM)) + 1;
+ if(p_wparam)
+ *p_wparam = *(WPARAM *)dbv.pbVal;
+ text = strdup(dbv.pbVal + sizeof(WPARAM));
+
+ if(msg_sz == dbv.cpbVal) {
+ /* unset the setting if there are no more msgs
+ */
+ db_unset(hContact, VQCHAT_PROTO, "QueuedMsgs");
+ } else {
+ /* skip past this message and rewrite the blob
+ */
+ DBCONTACTWRITESETTING cws;
+
+ cws.szModule = VQCHAT_PROTO;
+ cws.szSetting = "QueuedMsgs";
+ cws.value.type = DBVT_BLOB;
+ cws.value.cpbVal = dbv.cpbVal - msg_sz;
+ cws.value.pbVal = dbv.pbVal + msg_sz;
+ CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws);
+ }
+
+ DBFreeVariant(&dbv);
+ return text;
+}
+
+/* exported routines
+ */
+void contacts_connected(vqp_link_t link)
+{
+ s_vqpLink = link;
+}
+
+void contacts_disconnected()
+{
+ s_vqpLink = NULL;
+}
+
+void contacts_hook_modules_loaded()
+{
+ contacts_set_all_offline();
+}
+
+void contacts_set_all_offline()
+{
+ HANDLE hContact;
+
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while(hContact) {
+ char * proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if(proto && !strcmp(proto, VQCHAT_PROTO)) {
+ /* dont touch chat rooms */
+ if(db_byte_get(hContact, VQCHAT_PROTO, "ChatRoom", 0) == 0)
+ contacts_set_contact_status(hContact, ID_STATUS_OFFLINE);
+ }
+
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+}
+
+HANDLE contacts_add_contact(const char * nickname, int permanent)
+{
+ HANDLE hContact;
+
+ hContact = contacts_find_contact(nickname);
+ if(hContact) {
+ /* show it on the list, if hidden */
+ db_unset(hContact, "CList", "Hidden");
+ if(permanent)
+ db_unset(hContact, "CList", "NotOnList");
+
+ contacts_set_contact_status(hContact, userlist_user_status(nickname));
+ return hContact;
+ }
+
+ /* add contact to db */
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_ADD, 0, 0);
+ if(hContact) {
+ /* add contact to VQCHAT_PROTO contact list */
+ CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)VQCHAT_PROTO);
+
+ /* init contact settings */
+ db_unset(hContact, "CList", "Hidden");
+ if(permanent) {
+ db_unset(hContact, "CList", "NotOnList");
+ } else {
+ db_byte_set(hContact, "CList", "NotOnList", 1);
+ }
+
+ contacts_set_contact_nickname(hContact, nickname);
+
+ db_byte_set(hContact, VQCHAT_PROTO, "PreferMsg",
+ db_byte_get(NULL, VQCHAT_PROTO, "ContactsPreferMsg", 0));
+ }
+
+ return hContact;
+}
+
+/* contacts_find_contact:
+ * finds contact by user nickname
+ * the returned contact is ensured to be valid
+ */
+HANDLE contacts_find_contact(const char * nickname)
+{
+ HANDLE hContact;
+
+ /* walk all the contacts and find ours */
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while(hContact) {
+ if(contacts_is_user_contact(hContact)) {
+ char * contact_nickname = contacts_get_nickname(hContact);
+
+ if(!strcmp(contact_nickname, nickname)) {
+ free(contact_nickname);
+ break;
+ }
+
+ free(contact_nickname);
+ }
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+
+ return hContact;
+}
+
+void contacts_set_contact_status(HANDLE hContact, int new_status)
+{
+ WORD prevStatus;
+
+ /* dont touch hidden contacts */
+ if(db_byte_get(hContact, "CList", "Hidden", 0) != 0)
+ return;
+
+ /* set contact status, if prev status is not the same */
+ prevStatus = db_word_get(hContact, VQCHAT_PROTO, "Status", ID_STATUS_OFFLINE);
+ if(prevStatus != new_status) {
+ db_word_set(hContact, VQCHAT_PROTO, "Status", new_status);
+
+ if(prevStatus == ID_STATUS_OFFLINE) {
+ /* send queued messages, if any */
+ char * text;
+ WPARAM wparam;
+
+ while((text = contacts_fetch_queued_message(hContact, &wparam)) != NULL) {
+ contacts_send_contact_message(hContact, text, wparam, 1);
+ free(text);
+ }
+ }
+ }
+}
+
+/* contacts_set_contact_nickname:
+ * updates contact's nickname, also updates contacts' status
+ * based on userlist's user data
+ */
+void contacts_set_contact_nickname(HANDLE hContact, const char * new_nickname)
+{
+ char * loc_new_nickname;
+ ASSERT_RETURNIFFAIL(VALIDPTR(new_nickname));
+
+ /* XXX: check for duplicates */
+
+ /* set db name */
+ loc_new_nickname = util_utf2loc(new_nickname);
+ db_string_set(hContact, VQCHAT_PROTO, "Nick", loc_new_nickname);
+ free(loc_new_nickname);
+
+ /* update contact status */
+ contacts_set_contact_status(hContact, userlist_user_status(new_nickname));
+}
+
+void contacts_set_contact_addr(HANDLE hContact, vqp_addr_t new_addr)
+{
+ /* dont touch hidden contacts */
+ if(db_byte_get(hContact, "CList", "Hidden", 0) != 0)
+ return;
+
+ if(new_addr.conn == VQP_PROTOCOL_CONN_UDP) {
+ if(db_dword_get(hContact, VQCHAT_PROTO, "IP", 0) != new_addr.node.ip)
+ db_dword_set(hContact, VQCHAT_PROTO, "IP", new_addr.node.ip);
+ } else {
+ db_unset(hContact, VQCHAT_PROTO, "IP");
+ }
+}
+
+void contacts_set_contact_about(HANDLE hContact, const char * about_text)
+{
+ char * loc_about_text;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(about_text));
+
+ /* dont touch hidden contacts */
+ if(db_byte_get(hContact, "CList", "Hidden", 0) != 0)
+ return;
+
+ /* store about text in contact */
+ loc_about_text = util_utf2loc(about_text);
+ db_string_set(hContact, VQCHAT_PROTO, "About", loc_about_text);
+ free(loc_about_text);
+}
+
+void contacts_set_contact_gender(HANDLE hContact, enum vqp_gender gender)
+{
+ ASSERT_RETURNIFFAIL(hContact!=NULL);
+ db_byte_set(hContact, VQCHAT_PROTO, "Gender", gender == VQP_GENDER_MALE ? 'M': 'F');
+}
+
+void contacts_set_contact_property(
+ HANDLE hContact, enum contact_property_enum property,
+ const char * string)
+{
+ const char * prop_name;
+ ASSERT_RETURNIFFAIL(hContact!=NULL && VALIDPTR(string));
+
+ prop_name = contact_prop_name_by_enum(property);
+ ASSERT_RETURNIFFAIL(VALIDPTR(prop_name));
+
+ db_string_set(hContact, VQCHAT_PROTO, prop_name, string);
+}
+
+char * contacts_get_contact_property(
+ HANDLE hContact, enum contact_property_enum property)
+{
+ DBVARIANT dbv;
+ const char * prop_name;
+ char * value;
+
+ prop_name = contact_prop_name_by_enum(property);
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(prop_name), strdup("unknown"));
+
+ value = NULL;
+ if(!db_get(hContact, VQCHAT_PROTO, prop_name, &dbv)) {
+ if(dbv.type == DBVT_ASCIIZ) {
+ value = strdup(dbv.pszVal);
+ }
+ DBFreeVariant(&dbv);
+ }
+ if(value == NULL)
+ value = strdup("(unknown)");
+
+ return value;
+}
+
+/* contacts_get_contact_info:
+ * queries user info from contact
+ * (see PSS_GETINFO)
+ */
+static void CALLBACK
+contacts_get_contact_info_minimal_apc(ULONG_PTR cb_data)
+{
+ HANDLE hContact = (HANDLE)(((void **)cb_data)[0]);
+ int nReplies = (int)(((void **)cb_data)[1]);
+
+ /* just send the ack */
+ ProtoBroadcastAck(VQCHAT_PROTO, hContact,
+ ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)nReplies, (LPARAM)0);
+
+ /* free cb data */
+ free((void *) cb_data);
+}
+
+int contacts_get_contact_info(HANDLE hContact, WPARAM flags)
+{
+ int nReplies = (flags & SGIF_MINIMAL) ? 1: 2;
+ void ** cb_data;
+
+ /* we can get info on user contacts only */
+ if(!contacts_is_user_contact(hContact))
+ return 1;
+
+ /* we know minimal info already */
+ cb_data = (void **) malloc(sizeof(void*) * 2);
+ cb_data[0] = (void *)hContact;
+ cb_data[1] = (void *)nReplies;
+ QueueUserAPC(contacts_get_contact_info_minimal_apc, g_hMainThread, (ULONG_PTR)cb_data);
+
+ if(!(flags & SGIF_MINIMAL)) {
+ /* we need not only minimal info: send info request msg:
+ * see msghandler.c QCS_MSG_INFO_REPLY handler rountine
+ */
+ char * r_nickname, * dst, * r_dst;
+ enum vqp_codepage cp;
+
+ dst = contacts_get_nickname(hContact);
+ cp = userlist_user_codepage(dst);
+
+ r_nickname = util_utf2vqp(cp, user_nickname());
+ r_dst = util_utf2vqp(cp, dst);
+ free(dst);
+
+ msgloop_send_to(
+ vqp_msg_info_req(s_vqpLink, r_nickname, r_dst),
+ 0, userlist_user_addr(dst));
+
+ free(r_nickname);
+ free(r_dst);
+ }
+
+ return 0; /* success */
+}
+
+/* contacts_get_contact_status:
+ * returns contact's current status
+ */
+int contacts_get_contact_status(HANDLE hContact)
+{
+ int status;
+ char * utf_nickname;
+ ASSERT_RETURNVALIFFAIL(contacts_is_user_contact(hContact), ID_STATUS_OFFLINE);
+
+ utf_nickname = contacts_get_nickname(hContact);
+ if(utf_nickname) {
+ status = userlist_user_status(utf_nickname);
+ free(utf_nickname);
+ } else {
+ status = ID_STATUS_OFFLINE;
+ }
+
+ return status;
+}
+
+/* contacts_get_nickname:
+ * return utf nickname of the contact
+ */
+char * contacts_get_nickname(HANDLE hContact)
+{
+ char * nickname;
+
+ if(contacts_is_user_contact(hContact)) {
+ DBVARIANT dbv;
+
+ db_get(hContact, VQCHAT_PROTO, "Nick", &dbv);
+ nickname = util_loc2utf(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ } else {
+ nickname = strdup("(null)");
+ }
+
+ return nickname;
+}
+
+/* contacts_input_contact_message:
+ * Writes an unread input message to contact's log.
+ *
+ * The message will get routed through protocol plugins
+ * and (hopefully) will be afterwards received by
+ * service.c:service_contact_recv_message(), which will add it to our the database
+ */
+void contacts_input_contact_message(HANDLE hContact, const char * message_text)
+{
+ CCSDATA ccs;
+ PROTORECVEVENT pre;
+
+ /* make miranda-style ansi-unicode mixed string
+ */
+ char * loc_text = util_utf2loc(message_text);
+ wchar_t * uni_text = util_utf2uni(message_text);
+ int loc_text_len = strlen(loc_text) + 1;
+ char * m_text = malloc(loc_text_len + loc_text_len * sizeof(wchar_t));
+
+ memcpy(m_text, loc_text, loc_text_len);
+ free(loc_text);
+ memcpy(m_text + loc_text_len, uni_text, loc_text_len * sizeof(wchar_t));
+ free(uni_text);
+
+ /* send message event through protocol plugins
+ */
+ pre.flags = PREF_UNICODE;
+ pre.timestamp = (DWORD)time(NULL);
+ pre.szMessage = m_text;
+ pre.lParam = 0;
+ ccs.szProtoService = PSR_MESSAGE;
+ ccs.hContact = hContact;
+ ccs.wParam = 0;
+ ccs.lParam = (LPARAM) &pre;
+
+ CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs);
+
+ /* free ansi/ucs-2 text */
+ free(m_text);
+}
+
+/* contacts_send_contact_message:
+ * sends a message to a contact
+ * for details look miranda's m_protosvc.h:PSS_MESSAGE
+ *
+ * returns:
+ * (HANDLE)hprocess
+ */
+static void CALLBACK
+contacts_send_contact_message_result_apc(ULONG_PTR data)
+{
+ HANDLE hContact = (HANDLE)(((void**)data)[0]);
+ int result = (int)(((void**)data)[1]);
+ HANDLE hProcess = (HANDLE)(((void **)data)[2]);
+
+ ProtoBroadcastAck(VQCHAT_PROTO, hContact, ACKTYPE_MESSAGE, result, hProcess, (LPARAM)0);
+
+ /* free cb data */
+ free((void*)data);
+}
+
+int contacts_send_contact_message(
+ HANDLE hContact, const char * message_text, WPARAM flags, int is_queued)
+{
+ static DWORD msg_process_handle = 0;
+
+ int i;
+ char * contact_nick, * r_nickname, * r_contact;
+ enum vqp_codepage contact_cp;
+ DWORD process;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(message_text), 0);
+
+ /* cannot send message when offline
+ * or when user has no nickname
+ */
+ contact_nick = contacts_get_nickname(hContact);
+ if(!contact_nick) {
+ /* send failure message ack */
+ void ** cb_data = malloc(sizeof(void*) * 3);
+ cb_data[0] = (void*)hContact;
+ cb_data[1] = (void*)ACKRESULT_FAILED;
+ cb_data[2] = (void*)1; /* process handle */
+ QueueUserAPC(
+ contacts_send_contact_message_result_apc,
+ g_hMainThread, (ULONG_PTR)cb_data);
+
+ return (int)cb_data[2];
+ }
+
+ /* write as queued message if we're not online
+ * or the contact is not online
+ */
+ if(user_offline() || !userlist_user_exists(contact_nick)) {
+ process = msg_process_handle ++;
+
+ contacts_add_queued_message(hContact, message_text, flags);
+
+ /* we've sent this message successfully (not really) */
+ void ** cb_data = malloc(sizeof(void*) * 3);
+ cb_data[0] = (void*)hContact;
+ cb_data[1] = (void*)(int)ACKRESULT_SUCCESS;
+ cb_data[2] = (void*)process;
+ QueueUserAPC(
+ contacts_send_contact_message_result_apc,
+ g_hMainThread, (ULONG_PTR)cb_data);
+
+ free(contact_nick);
+ return process;
+ }
+
+ /* ok, the user is online and we can send the message
+ */
+ contact_cp = userlist_user_codepage(contact_nick);
+ r_contact = util_utf2vqp(contact_cp, contact_nick);
+ r_nickname = util_utf2vqp(contact_cp, user_nickname());
+
+ /* vypress chat 2.0 has abandoned private chats, -
+ * use private messages; also check the "PreferMsg" option
+ */
+ if((userlist_user_swversion(contact_nick) >= VQP_MAKE_SWVERSION(2, 0))
+ || (db_byte_get(hContact, VQCHAT_PROTO, "PreferMsg", 0)
+ && userlist_user_can_receive_msg(contact_nick))
+ ) {
+ char * r_msg_text = util_utf2vqp(contact_cp, message_text);
+
+ msgloop_send_to(
+ vqp_msg_message(s_vqpLink, r_nickname, r_contact, r_msg_text, 0),
+ 0, userlist_user_addr(contact_nick));
+ free(r_msg_text);
+ } else {
+ char ** msg_lines;
+ if(!userlist_user_is_chat_open(contact_nick)) {
+ /* mark chat as opened */
+ userlist_user_set_chat_open(contact_nick, 1);
+
+ /* send "Open private chat" message */
+ msgloop_send_to(
+ vqp_msg_private_open(s_vqpLink, r_nickname, r_contact, user_gender()),
+ 0, userlist_user_addr(contact_nick));
+ }
+
+ /* send message text, after spliting it into multiple lines
+ * and skipping the empty ones
+ */
+ msg_lines = util_split_multiline(message_text, 0);
+ for(i = 0; msg_lines[i]; i++) {
+ char * r_text = util_utf2vqp(contact_cp, msg_lines[i]);
+
+ msgloop_send_to(
+ vqp_msg_private_text(s_vqpLink, r_nickname, r_contact, r_text, 0),
+ 0, userlist_user_addr(contact_nick));
+ free(r_text);
+ }
+ util_free_str_list(msg_lines);
+ }
+
+ if(!is_queued) {
+ /* add APC to send protocol ack, to notify that we've sent the message
+ * (if this message was queued, there's no sense to send the ack, it was sent already)
+ */
+ process = msg_process_handle ++;
+
+ void ** cb_data = malloc(sizeof(void*) * 3);
+ cb_data[0] = (void*)hContact;
+ cb_data[1] = (void*)(int)ACKRESULT_SUCCESS;
+ cb_data[2] = (void*)process;
+ QueueUserAPC(
+ contacts_send_contact_message_result_apc,
+ g_hMainThread, (ULONG_PTR)cb_data);
+ } else {
+ process = 0;
+ }
+
+ /* free strings */
+ free(contact_nick);
+ free(r_contact);
+ free(r_nickname);
+
+ return process; /* success */
+}
+
+/* contacts_send_beep:
+ * sends a beep to the user
+ */
+void contacts_send_beep(HANDLE hContact)
+{
+ char * contact_nick;
+ ASSERT_RETURNIFFAIL(hContact!=NULL);
+
+ contact_nick = contacts_get_nickname(hContact);
+ if(contact_nick) {
+ enum vqp_codepage contact_cp = userlist_user_codepage(contact_nick);
+ char * r_nickname = util_utf2vqp(contact_cp, user_nickname()),
+ * r_contact = util_utf2vqp(contact_cp, contact_nick);
+
+ /* send the beep message */
+ msgloop_send_to(
+ vqp_msg_beep_signal(s_vqpLink, r_nickname, r_contact),
+ 0, userlist_user_addr(contact_nick));
+ free(r_nickname);
+ free(r_contact);
+ }
+}
+
+/* contacts_is_user_contact:
+ * returns non-0 if the contact is VQCHAT_PROTO user contact
+ */
+int contacts_is_user_contact(HANDLE hContact)
+{
+ char * proto;
+ DBVARIANT dbv;
+
+ ASSERT_RETURNVALIFFAIL(hContact!=NULL, 0);
+
+ /* check contact protocol */
+ proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if(!proto || strcmp(proto, VQCHAT_PROTO))
+ return 0;
+
+ /* check that the contact has Nick set */
+ if(db_get(hContact, VQCHAT_PROTO, "Nick", &dbv)) return 0;
+ if(dbv.type != DBVT_ASCIIZ) return 0;
+ DBFreeVariant(&dbv);
+
+ /* check if this is a chatroom */
+ if(db_byte_get(hContact, VQCHAT_PROTO, "ChatRoom", 0))
+ return 0;
+
+ return 1;
+}
+
+int contacts_is_chatroom_contact(HANDLE hContact)
+{
+ char * proto;
+
+ ASSERT_RETURNVALIFFAIL(hContact!=NULL, 0);
+
+ /* check contact protocol */
+ proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if(!proto || strcmp(proto, VQCHAT_PROTO))
+ return 0; /* not VQCHAT_PROTO protocol contact */
+
+ /* check that the contact has "ChatRoom" byte set */
+ return db_byte_get(hContact, VQCHAT_PROTO, "ChatRoom", 0)!=0;
+}
+