summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2013-04-02 13:54:21 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2013-04-02 13:54:21 +0000
commitff5a775b94465b30897964630af600fe5915fc51 (patch)
treef79e36c79faf7095ca6f02f6b7fea0f6e81bf085
parent8d3307adf7ba64b75fb4de363f873c97286b0e9b (diff)
VypressChat added (not adopted)
git-svn-id: http://svn.miranda-ng.org/main/trunk@4285 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
-rw-r--r--plugins/!NotAdopted/VypressChat/Resource.h97
-rw-r--r--plugins/!NotAdopted/VypressChat/chanlist.c390
-rw-r--r--plugins/!NotAdopted/VypressChat/chanlist.h43
-rw-r--r--plugins/!NotAdopted/VypressChat/chatroom.c1154
-rw-r--r--plugins/!NotAdopted/VypressChat/chatroom.h63
-rw-r--r--plugins/!NotAdopted/VypressChat/contacts.c705
-rw-r--r--plugins/!NotAdopted/VypressChat/contacts.h61
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/hashtable.c287
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/hashtable.h195
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h71
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/m_chat.h424
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/memwatch.c2664
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/memwatch.h707
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c21
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h8
-rw-r--r--plugins/!NotAdopted/VypressChat/contrib/unicows.dllbin0 -> 258352 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/docs/AUTHORS1
-rw-r--r--plugins/!NotAdopted/VypressChat/docs/COPYING340
-rw-r--r--plugins/!NotAdopted/VypressChat/docs/CREDITS10
-rw-r--r--plugins/!NotAdopted/VypressChat/docs/ChangeLog50
-rw-r--r--plugins/!NotAdopted/VypressChat/docs/NEWS0
-rw-r--r--plugins/!NotAdopted/VypressChat/docs/README79
-rw-r--r--plugins/!NotAdopted/VypressChat/docs/TODO36
-rw-r--r--plugins/!NotAdopted/VypressChat/docs/dbformat.txt38
-rw-r--r--plugins/!NotAdopted/VypressChat/iconpack.rc40
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/contact_bell.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/quickchat.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/quickchat_away.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/quickchat_channel.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/quickchat_dnd.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/quickchat_large.icobin0 -> 766 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/quickchat_na.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/quickchat_offline.icobin0 -> 1406 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/quickchat_online.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/vypresschat.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/vypresschat_away.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/vypresschat_channel.icobin0 -> 1406 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/vypresschat_dnd.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/vypresschat_large.icobin0 -> 2238 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/vypresschat_na.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/vypresschat_offline.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/icons/vypresschat_online.icobin0 -> 318 bytes
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/COPYING340
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/PROTOCOL348
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/README7
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/link.c611
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/link.h106
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/makefile21
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/message.c1218
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/message.h57
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/protocol-direct-connections97
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/uuid.c108
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/uuid.h27
-rw-r--r--plugins/!NotAdopted/VypressChat/libvqproto/vqproto.h595
-rw-r--r--plugins/!NotAdopted/VypressChat/main.c132
-rw-r--r--plugins/!NotAdopted/VypressChat/main.h108
-rw-r--r--plugins/!NotAdopted/VypressChat/makefile243
-rw-r--r--plugins/!NotAdopted/VypressChat/miranda.h56
-rw-r--r--plugins/!NotAdopted/VypressChat/msghandler.c839
-rw-r--r--plugins/!NotAdopted/VypressChat/msghandler.h33
-rw-r--r--plugins/!NotAdopted/VypressChat/msgloop.c248
-rw-r--r--plugins/!NotAdopted/VypressChat/msgloop.h35
-rw-r--r--plugins/!NotAdopted/VypressChat/options.c947
-rw-r--r--plugins/!NotAdopted/VypressChat/options.h33
-rw-r--r--plugins/!NotAdopted/VypressChat/pthread.c58
-rw-r--r--plugins/!NotAdopted/VypressChat/pthread.h30
-rw-r--r--plugins/!NotAdopted/VypressChat/resource.rc258
-rw-r--r--plugins/!NotAdopted/VypressChat/service.c730
-rw-r--r--plugins/!NotAdopted/VypressChat/service.h36
-rw-r--r--plugins/!NotAdopted/VypressChat/skin.c44
-rw-r--r--plugins/!NotAdopted/VypressChat/skin.h33
-rw-r--r--plugins/!NotAdopted/VypressChat/user.c511
-rw-r--r--plugins/!NotAdopted/VypressChat/user.h70
-rw-r--r--plugins/!NotAdopted/VypressChat/userlist.c718
-rw-r--r--plugins/!NotAdopted/VypressChat/userlist.h73
-rw-r--r--plugins/!NotAdopted/VypressChat/util.c376
-rw-r--r--plugins/!NotAdopted/VypressChat/util.h63
77 files changed, 16693 insertions, 0 deletions
diff --git a/plugins/!NotAdopted/VypressChat/Resource.h b/plugins/!NotAdopted/VypressChat/Resource.h
new file mode 100644
index 0000000000..4c16f9e22f
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/Resource.h
@@ -0,0 +1,97 @@
+/*
+ * 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: Resource.h,v 1.15 2005/04/11 20:23:58 bobas Exp $
+ */
+
+/* icons
+ */
+#define IDI_VQCHAT_PROTO 1001
+#define IDI_VQCHAT_PROTO_LARGE 1002
+#define IDI_VQCHAT_PROTO_ONLINE 1010
+#define IDI_VQCHAT_PROTO_OFFLINE 1011
+#define IDI_CONTACT_BELL 1100
+#define IDI_CHATROOM 1200
+
+/* user configuration dialog
+ */
+#define IDD_USER 2000
+#define IDC_USER_EDIT_NICKNAME 2010
+#define IDC_USER_COMBO_GENDER 2020
+#define IDC_USER_EDIT_UUID 2030
+#define IDC_USER_MSGONALERTBEEP 2040
+#define IDC_USER_NICKNAMEONTOPIC 2050
+#define IDC_USER_NEWPREFERMSG 2060
+
+/* network connection configuration dialog
+ */
+#define IDD_CONN 3000
+#define IDC_CONN_EDIT_REFRESH 3010
+#define IDC_CONN_EDIT_PORT 3020
+#define IDC_CONN_BTN_DEF_PORT 3030
+#define IDC_CONN_EDIT_SCOPE 3040
+#define IDC_CONN_BTN_DEF_MULTICAST 3050
+#define IDC_CONN_IP_MULTICAST 3060
+#define IDC_CONN_CHECK_UTF8 3070
+#define IDC_CONN_RADIO_MULTICAST 3080
+#define IDC_CONN_RADIO_BROADCAST 3090
+#define IDC_CONN_RADIO_IPX 3100
+#define IDC_CONN_BCAST_LIST 3110
+#define IDC_CONN_BCAST_INPUT 3120
+#define IDC_CONN_BCAST_ADD 3130
+#define IDC_CONN_BCAST_REMOVE 3140
+#define IDC_CONN_LABEL_UTF8 3200
+
+/* channel settings dialog
+ */
+#define IDD_CS 4000
+#define IDC_CS_EDIT_NAME 4001
+#define IDC_CS_EDIT_TOPIC 4002
+#define IDC_CS_BTN_SET 4004
+
+/* join channel dialog
+ */
+#define IDD_JC 5000
+#define IDC_JC_COMBO_CHANNEL 5001
+
+/* set nickname dialog
+ */
+#define IDD_SN 6000
+#define IDC_SN_EDIT_NICKNAME 6001
+
+#define IDC_STATIC -1
+
+/* "user details: basic info" property page
+ */
+#define IDD_USERINFO 7000
+#define IDC_USERINFO_COMPUTER 7010
+#define IDC_USERINFO_USER 7020
+#define IDC_USERINFO_NODE 7030
+#define IDC_USERINFO_PLATFORM 7040
+#define IDC_USERINFO_WORKGROUP 7050
+#define IDC_USERINFO_SOFTWARE 7060
+#define IDC_USERINFO_CHANNELS 7070
+
+/* "user details: <protocol> options" property page
+ */
+#define IDD_USEROPT 8000
+#define IDC_USEROPT_NICKNAME 8010
+#define IDC_USEROPT_SET 8020
+#define IDC_USEROPT_PREFERS 8030
+#define IDC_USEROPT_LOCKNICK 8040
+
diff --git a/plugins/!NotAdopted/VypressChat/chanlist.c b/plugins/!NotAdopted/VypressChat/chanlist.c
new file mode 100644
index 0000000000..6634afeba3
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/chanlist.c
@@ -0,0 +1,390 @@
+/*
+ * 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: chanlist.c,v 1.10 2005/03/07 14:26:54 bobas Exp $
+ */
+
+#include "miranda.h"
+
+#include "main.h"
+#include "chanlist.h"
+
+/* internal routines
+ */
+
+/* chanlist_find_channel:
+ * Returns a pointer to channel in a channel list
+ * (or a NULL, if not found)
+ * *p_cl_len will be set to the length of channel list.
+ * *p_ch_len will be set to the length of a channel without the '#'.
+ */
+static const char *
+chanlist_find_channel(
+ const char * chanlist, const char * channel,
+ int * p_cl_len, int * p_ch_len)
+{
+ char * cl_channel;
+ int cl_len, ch_len;
+
+ if(!chanlist)
+ return NULL;
+
+ /* get chanlist and channel lengths */
+ cl_len = strlen(chanlist);
+ ASSERT_RETURNVALIFFAIL(cl_len >= 2 && chanlist[0]=='#', NULL);
+ if(p_cl_len)
+ *p_cl_len = cl_len;
+
+ ch_len = strlen(channel);
+ ASSERT_RETURNVALIFFAIL(ch_len!=0, NULL);
+ if(p_ch_len)
+ *p_ch_len = ch_len;
+
+ /* the list doesn't contain a channel if it's smaller than
+ * the channel itself
+ */
+ if(cl_len-1 < ch_len)
+ return NULL;
+
+ /* find the channel in the list */
+ cl_channel = strstr(chanlist, channel);
+
+ /* there must be a '#' before channel name */
+ if(cl_channel==NULL || *(cl_channel - 1) != '#')
+ return NULL;
+
+ /* and it must be last on the channel list, or end up with '#' */
+ if(cl_channel[ch_len]!='\0' && cl_channel[ch_len]!='#')
+ return NULL;
+
+ /* ok, the channel was found */
+ return cl_channel;
+}
+
+/* exported routines
+ */
+
+/* chanlist_is_valid:
+ * returns if the chanlist is in valid format
+ */
+int chanlist_is_valid(const char * chanlist, int no_empty_channels)
+{
+ int cl_len;
+
+ /* chanlist can be empty -- NULL */
+ if(chanlist==NULL)
+ return 1;
+
+ /* non-empty chanlist must be with length >= 0,
+ * and must begin with a '#'
+ */
+ cl_len = strlen(chanlist);
+ if(cl_len < 2 || chanlist[0]!='#')
+ return 0;
+
+ if(no_empty_channels) {
+ /* check that there are no 0-length channel names
+ * in the list
+ */
+ const char * prev = chanlist, * next;
+ do {
+ next = strchr(prev + 1, '#');
+ if(next && (next - prev) == 1)
+ return 0;
+
+ prev = next;
+ } while(next);
+ }
+
+ return 1; /* this chanlist is supposedly valid */
+}
+
+/* chanlist_add:
+ * Creates a channel list (if chanlist==NULL) with a new channel
+ * or add a new one to the end.
+ * Checks if the channel is already in the list.
+ * The result channel list string should look like:
+ * "#channel1#channel2...#channelN".
+ * An empty channel list is a (char*)NULL.
+ */
+char * chanlist_add(char * chanlist, const char * channel)
+{
+ char * new_chanlist;
+ int cl_len, ch_len;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), chanlist);
+
+ if(chanlist_find_channel(chanlist, channel, NULL, NULL))
+ return chanlist;
+
+ /* the chanlist doesn't contain the new channel:
+ * append it to the end
+ */
+
+ ch_len = strlen(channel);
+ ASSERT_RETURNVALIFFAIL(ch_len!=0, chanlist);
+
+ if(chanlist) {
+ /* get the length of channel list, note that an empty chanlist
+ * MUST be (char*)NULL, and an non-empty chanlist must be at
+ * least 2 chars in length ("#a") and have '#' at the beginning
+ */
+ cl_len = strlen(chanlist);
+ ASSERT_RETURNVALIFFAIL(
+ cl_len >= 2 && chanlist[0]=='#', chanlist);
+ } else {
+ cl_len = 0;
+ }
+
+ /* allocate space for a previous channel list, plus a # character
+ * and new channel, and a terminator
+ */
+ new_chanlist = malloc((cl_len + ch_len + 1 + 1) * sizeof(char));
+ ASSERT_RETURNVALIFFAIL(new_chanlist!=NULL, chanlist);
+
+ if(chanlist) {
+ /* strcpy(new_chanlist, chanlist); */
+ memcpy(new_chanlist, chanlist, cl_len);
+ free(chanlist);
+ }
+
+ new_chanlist[cl_len] = '#'; /* strcat(new_chanlist, "#"); */
+
+ /* strcat(new_chanlist, channel); */
+ memcpy(new_chanlist + cl_len + 1, channel, ch_len + 1);
+
+ return new_chanlist;
+}
+
+/* chanlist_remove:
+ * Removes a channel from chanlist and frees the resulting
+ * chanlist, if it becomes empty (thus returning the (char*)NULL)
+ */
+char * chanlist_remove(char * chanlist, const char * channel)
+{
+ char * cl_channel;
+ int cl_len, ch_len;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), chanlist);
+
+ if(chanlist==NULL)
+ return NULL;
+
+ cl_channel = (char*)chanlist_find_channel(
+ chanlist, channel, &cl_len, &ch_len);
+
+ if(cl_channel == NULL)
+ return chanlist;
+
+ /* check if we need to free the list, (if it was the only channel) */
+ if(cl_len == ch_len + 1) {
+ free(chanlist);
+ return NULL;
+ }
+
+ /* if the channel was the last on the list, we put a terminator '\0'
+ * and we're finished */
+ if((cl_channel - chanlist) + ch_len == cl_len) {
+ *(cl_channel - 1) = '\0'; /* put '\0' on channel's '#' char */
+ return chanlist;
+ }
+
+ /* we need to move channels after cl_channel in the chanlist
+ * to the place of cl_channel (including the '\0' terminator) */
+ memcpy(cl_channel, cl_channel + ch_len + 1,
+ cl_len - (cl_channel - chanlist) - ch_len);
+
+ return chanlist;
+}
+
+/* chanlist_shift:
+ * removes the first channel from the list and returns
+ * the chanlist without the channel
+ *
+ * params:
+ * @p_chanlist - chanlist to shift the channel off
+ * returns:
+ * NULL, if the chanlist is empty
+ * malloc'ed channel name shifted from the chanlist
+ */
+char * chanlist_shift(char ** p_chanlist)
+{
+ char * next, * new_chanlist, * channel;
+
+ /* check that the channel is valid */
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(p_chanlist), NULL);
+ if(!chanlist_is_valid(*p_chanlist, 0))
+ return NULL;
+
+ /* check if chanlist is empty */
+ if(*p_chanlist==NULL) return NULL;
+
+ /* get pointer to the next channel in the list */
+ next = strchr(*p_chanlist + 1, '#');
+
+ /* make a copy of the rest as new chanlist */
+ new_chanlist = chanlist_copy(next);
+
+ /* finish channel name with a '\0' */
+ if(next) *next = '\0';
+ channel = *p_chanlist;
+ memmove(channel, channel + 1, strlen(channel));
+
+ *p_chanlist = new_chanlist;
+ return channel;
+}
+
+/* chanlist_merge:
+ * Merges two chanlists, the result adds to chanlist_dst
+ * and it is what returns.
+ *
+ * Note that chanlist_src might get modified in the process
+ * thus it is non-const, but it is kept unmodified after return.
+ */
+static int
+chanlist_merge_enum_fn(const char * channel, void * enum_data)
+{
+ char ** p_dst_chanlist = (char **)enum_data;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), 0);
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(p_dst_chanlist), 0);
+
+ *p_dst_chanlist = chanlist_add(*p_dst_chanlist, channel);
+
+ return 1; /* keep enumerating */
+}
+
+char * chanlist_merge(char * chanlist_dst, const char * chanlist_src)
+{
+ chanlist_enum(chanlist_src, chanlist_merge_enum_fn, &chanlist_dst);
+
+ return chanlist_dst;
+}
+
+/* chanlist_contains:
+ * Returns non-0, if the chanlist contains the specified channel.
+ */
+int chanlist_contains(const char * chanlist, const char * channel)
+{
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), 0);
+
+ return chanlist_find_channel(chanlist, channel, NULL, NULL) != NULL;
+}
+
+/* chanlist_enum:
+ * Enumerates chanlist.
+ * The enum function should return non-0 to keep enumeration going,
+ * or 0 to stop enumeration.
+ */
+void chanlist_enum(
+ const char * orig_chanlist, chanlist_enum_fn enum_fn,
+ void * enum_data)
+{
+ char * cl_channel, * cl_next_block;
+ int cl_len;
+ char * chanlist = chanlist_copy(orig_chanlist);
+
+ ASSERT_RETURNIFFAIL(enum_fn!=NULL);
+
+ /* do no enumeration if chanlist is empty */
+ if(chanlist == NULL)
+ return;
+
+ /* get chanlist length */
+ cl_len = strlen(chanlist);
+
+ /* the length must be at least 2 chars ("#a"), and begin with a '#' */
+ ASSERT_RETURNIFFAIL(cl_len >= 2 && chanlist[0]=='#');
+
+ /* ok, walk the channel list.. */
+ cl_channel = chanlist + 1;
+ do {
+ /* get address of the next channel's '#' character */
+ cl_next_block = strchr(cl_channel, '#');
+ if(cl_next_block) {
+ /* temporary add a terminator and invoke the callback */
+ *cl_next_block = '\0';
+ enum_fn(cl_channel, enum_data);
+
+ /* remove the terminator */
+ *cl_next_block = '#';
+ } else {
+ /* the channel is the last on the list: invoke cb */
+ enum_fn(cl_channel, enum_data);
+ }
+
+ /* skip to next channel */
+ if(cl_next_block)
+ cl_channel = cl_next_block + 1;
+ else cl_channel = NULL;
+
+ } while(cl_channel);
+
+ /* free the chanlist copy */
+ chanlist_free(chanlist);
+}
+
+/* chanlist_make_vqp_chanlist:
+ * allocates a vqp chanlist (the same as chanlist, only with '#' at the end)
+ * and always non-NULL
+ */
+char * chanlist_make_vqp_chanlist(const char * chanlist)
+{
+ int cl_len;
+ char * vqp_chanlist;
+
+ cl_len = chanlist ? strlen(chanlist): 0;
+
+ vqp_chanlist = malloc(cl_len + 2);
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(vqp_chanlist), NULL);
+
+ if(chanlist)
+ memcpy(vqp_chanlist, chanlist, cl_len);
+
+ /* append the '#' and '\0' terminator at the end */
+ vqp_chanlist[cl_len] = '#';
+ vqp_chanlist[cl_len + 1] = '\0';
+
+ return vqp_chanlist;
+}
+
+/* chanlist_parse_vqp_chanlist:
+ * makes a chanlist from vqp chanlist format
+ */
+char * chanlist_parse_vqp_chanlist(const char * vqp_chanlist)
+{
+ int vqp_cl_len;
+ char * chanlist;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(vqp_chanlist), NULL);
+
+ vqp_cl_len = strlen(vqp_chanlist);
+ ASSERT_RETURNVALIFFAIL(vqp_cl_len != 0, NULL);
+
+ /* vqp_chanlist must begin and end with '#' */
+ ASSERT_RETURNVALIFFAIL(
+ vqp_chanlist[0]=='#' && vqp_chanlist[vqp_cl_len-1]=='#', NULL);
+
+ /* make the chanlist (copy everything, except the last '#') */
+ chanlist = malloc(vqp_cl_len);
+ memcpy(chanlist, vqp_chanlist, vqp_cl_len - 1);
+ chanlist[vqp_cl_len - 1] ='\0';
+
+ return chanlist;
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/chanlist.h b/plugins/!NotAdopted/VypressChat/chanlist.h
new file mode 100644
index 0000000000..63c2c73b67
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/chanlist.h
@@ -0,0 +1,43 @@
+/*
+ * 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: chanlist.h,v 1.5 2005/02/26 20:23:17 bobas Exp $
+ */
+
+
+#ifndef __CHANLIST_H
+#define __CHANLIST_H
+
+#define chanlist_free(chanlist) if(chanlist) free(chanlist);
+int chanlist_is_valid(const char * chanlist, int no_empty_channels);
+#define chanlist_copy(chanlist) ((chanlist) ? strdup(chanlist): NULL)
+
+char * chanlist_add(char * chanlist, const char * channel);
+char * chanlist_remove(char * chanlist, const char * channel);
+char * chanlist_shift(char ** p_chanlist);
+char * chanlist_merge(char * chanlist_dst, const char * chanlist_src);
+int chanlist_contains(const char * chanlist, const char * channel);
+
+typedef int (*chanlist_enum_fn)(const char * channel, void * enum_data);
+void chanlist_enum(const char * chanlist, chanlist_enum_fn enum_fn, void * enum_data);
+
+char * chanlist_make_vqp_chanlist(const char * chanlist);
+char * chanlist_parse_vqp_chanlist(const char * vqp_chanlist);
+
+#endif
+
diff --git a/plugins/!NotAdopted/VypressChat/chatroom.c b/plugins/!NotAdopted/VypressChat/chatroom.c
new file mode 100644
index 0000000000..22086c6c8a
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/chatroom.c
@@ -0,0 +1,1154 @@
+/*
+ * 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: chatroom.c,v 1.44 2005/03/17 11:02:43 bobas Exp $
+ */
+
+#include <time.h>
+#include "miranda.h"
+#include "contrib/hashtable.h"
+#include "contrib/strhashfunc.h"
+#include "libvqproto/vqproto.h"
+
+#include "main.h"
+#include "user.h"
+#include "userlist.h"
+#include "chatroom.h"
+#include "msgloop.h"
+#include "chanlist.h"
+#include "util.h"
+#include "contacts.h"
+#include "resource.h"
+
+/* struct defs
+ */
+
+#define VQCHAT_WM_INITCS (WM_USER + 1)
+
+#define CHATROOM_NORMAL_STATUS "Normal"
+
+struct chatroom_status_entry {
+ char * topic;
+};
+
+/* static data
+ */
+static BOOL s_fHaveChat; /* TRUE, if we have chat.dll in miranda */
+static HANDLE s_hGcUserEvent, s_hGcMenuBuild;
+static struct hashtable
+ * s_channelHash;
+static HWND s_hwndChannelSettingsDlg;
+static vqp_link_t s_vqpLink;
+static UINT_PTR s_chatroom_connected_join_timer;
+
+/* static routines
+ */
+
+static void chatroom_free_channelhash_value(void * value)
+{
+ struct chatroom_status_entry * entry = (struct chatroom_status_entry *)value;
+ if(entry->topic)
+ free(entry->topic);
+ free(entry);
+}
+
+static char * chatroom_make_name(const char * channel)
+{
+ int len;
+ char * channel_name;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), NULL);
+
+ len = strlen(channel);
+ channel_name = malloc(len + 2);
+ channel_name[0] = '#';
+ memcpy(channel_name + 1, channel, len + 1);
+
+ return channel_name;
+}
+
+static void chatroom_send_net_join(const char * channel)
+{
+ char * r_channel, * r_nickname;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(channel));
+
+ r_channel = util_utf2vqp(user_codepage(), channel);
+ r_nickname = util_utf2vqp(user_codepage(), user_nickname());
+
+ /* send a join msg */
+ msgloop_send(
+ vqp_msg_channel_join(
+ s_vqpLink, r_channel, r_nickname, user_vqp_status(), user_gender(),
+ VQCHAT_VQP_SWVERSION, user_p_uuid(), user_codepage(), VQCHAT_VQP_DEF_COLOR),
+ 0);
+
+ /* send request who-is-here-on-this-channel? */
+ msgloop_send(vqp_msg_channel_whohere_req(s_vqpLink, r_channel, r_nickname), 0);
+
+ free(r_channel);
+ free(r_nickname);
+}
+
+static void chatroom_send_net_part(const char * channel)
+{
+ char * r_channel = util_utf2vqp(user_codepage(), channel),
+ * r_nickname = util_utf2vqp(user_codepage(), user_nickname());
+
+ msgloop_send(vqp_msg_channel_leave(s_vqpLink, r_channel, r_nickname, user_gender()), 1);
+ free(r_channel);
+ free(r_nickname);
+}
+
+static void chatroom_svc_gc_event_user_message(
+ const char * channel, const char * message)
+{
+ /* chatroom_svc_gc_event_user_message:
+ * handles a message the user has typed
+ * and emits it onto the channel window and onto the
+ * network
+ */
+ char ** msg_lines;
+ int i;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(channel) && VALIDPTR(message));
+
+ /* split the message into multiple lines (and skip the empty ones) */
+ msg_lines = util_split_multiline(message, 0);
+
+ /* emit message lines to the network */
+ for(i = 0; msg_lines[i]; i++) {
+ char * r_channel, * r_nickname, * r_text;
+ enum vqp_codepage cp = user_codepage();
+
+ /* emit this text on the channel window */
+ chatroom_channel_user_text(channel, user_nickname(), msg_lines[i], 0);
+
+ /* send the message to the network */
+ msgloop_send(
+ vqp_msg_channel_text(
+ s_vqpLink, r_channel = util_utf2vqp(cp, channel),
+ r_nickname = util_utf2vqp(cp, user_nickname()),
+ r_text = util_utf2vqp(cp, msg_lines[i]), 0),
+ 0);
+ free(r_channel);
+ free(r_nickname);
+ free(r_text);
+ }
+
+ /* free the string list */
+ util_free_str_list(msg_lines);
+}
+
+static void chatroom_svc_gc_event_user_nicklistmenu(GCHOOK * pHook)
+{
+ PROTOSEARCHRESULT psr;
+ ADDCONTACTSTRUCT acs;
+ char * dst;
+
+ switch(pHook->dwData) {
+ case 1: /* "Beep" */
+ dst = util_loc2utf(pHook->pszUID);
+ if(!user_is_my_nickname(dst)) {
+ char * r_dst, * r_nickname;
+ enum vqp_codepage cp = userlist_user_codepage(dst);
+ r_nickname = util_utf2vqp(cp, user_nickname());
+ r_dst = util_utf2vqp(cp, dst);
+
+ msgloop_send_to(
+ vqp_msg_beep_signal(s_vqpLink, r_nickname, r_dst),
+ 0, userlist_user_addr(dst));
+ free(r_nickname);
+ free(r_dst);
+ }
+ free(dst);
+ break;
+
+ case 2: /* "Add User" */
+ memset(&psr, 0, sizeof(psr));
+ psr.cbSize = sizeof(psr);
+ psr.nick = pHook->pszUID;
+ memset(&acs, 0, sizeof(acs));
+ acs.handleType = HANDLE_SEARCHRESULT;
+ acs.szProto = VQCHAT_PROTO;
+ acs.psr = &psr;
+
+ CallService(MS_ADDCONTACT_SHOW, (WPARAM)NULL, (LPARAM)&acs);
+ break;
+ }
+}
+
+static BOOL CALLBACK chatroom_channel_settings_wndproc(
+ HWND hdlg, UINT nMsg,
+ WPARAM wParam, LPARAM lParam)
+{
+ char * channel, * topic;
+
+ switch(nMsg) {
+ case WM_INITDIALOG:
+ /* set dialog icon */
+ SendMessage(
+ hdlg, WM_SETICON, ICON_SMALL,
+ (LPARAM)LoadIcon(g_hDllInstance,
+ MAKEINTRESOURCE(IDI_CHATROOM)));
+ SendMessage(
+ hdlg, WM_SETICON, ICON_BIG,
+ (LPARAM)(HICON)LoadImage(
+ g_hDllInstance, MAKEINTRESOURCE(IDI_VQCHAT_PROTO_LARGE),
+ IMAGE_ICON, GetSystemMetrics(SM_CXICON),
+ GetSystemMetrics(SM_CYICON), LR_SHARED)
+ );
+ break;
+
+ case VQCHAT_WM_INITCS:
+ channel = (char *)lParam;
+
+ util_SetDlgItemTextUtf(hdlg, IDC_CS_EDIT_NAME, channel);
+ util_SetDlgItemTextUtf(
+ hdlg, IDC_CS_EDIT_TOPIC, chatroom_channel_get_topic(channel));
+
+ SendDlgItemMessageW(hdlg, IDC_CS_EDIT_TOPIC, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
+
+ /* make dialog title */
+ topic = malloc(strlen(channel) + 64);
+ if(topic) {
+ sprintf(topic, "#%s channel settings", channel);
+ util_SetWindowTextUtf(hdlg, topic);
+ free(topic);
+ }
+
+ /* disable the 'set-topic' button */
+ EnableWindow(GetDlgItem(hdlg, IDC_CS_BTN_SET), FALSE);
+ break;
+
+ case WM_COMMAND:
+ switch(HIWORD(wParam)) {
+ case BN_CLICKED:
+ switch(LOWORD(wParam)) {
+ case IDC_CS_BTN_SET:
+ case IDOK:
+ /* set channel topic */
+ channel = util_GetDlgItemTextUtf(hdlg, IDC_CS_EDIT_NAME);
+ topic = util_GetDlgItemTextUtf(hdlg, IDC_CS_EDIT_TOPIC);
+ if(channel && topic) {
+ if(db_byte_get(NULL, VQCHAT_PROTO, "NicknameOnTopic", 0)) {
+ char * new_topic = malloc(strlen(topic)
+ + strlen(user_nickname()) + 8);
+ sprintf(new_topic, "%s (%s)",
+ topic, user_nickname());
+ chatroom_channel_topic_change(
+ channel, new_topic, 1, 1);
+ free(new_topic);
+ } else {
+ chatroom_channel_topic_change(channel, topic, 1, 1);
+ }
+ }
+ if(topic) free(topic);
+ if(channel) free(channel);
+
+ /* disable the 'set-topic' button */
+ EnableWindow(GetDlgItem(hdlg, IDC_CS_BTN_SET), FALSE);
+
+ /* close the window (if pressed OK btn) */
+ if(LOWORD(wParam) == IDOK)
+ SendMessage(hdlg, WM_CLOSE, 0,0);
+ break;
+ }
+ break;
+
+ case EN_CHANGE:
+ switch(LOWORD(wParam)) {
+ case IDC_CS_EDIT_TOPIC:
+ /* enable the set-topic button on topic
+ * entry change */
+ EnableWindow(GetDlgItem(hdlg, IDC_CS_BTN_SET), TRUE);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hdlg);
+ s_hwndChannelSettingsDlg = NULL;
+ break;
+ }
+
+ return (FALSE);
+}
+
+static void chatroom_svc_gc_event_user_logmenu(GCHOOK * pHook)
+{
+ char * channel;
+
+ switch(pHook->dwData) {
+ case 1:
+ channel = util_loc2utf(pHook->pDest->pszID);
+ chatroom_channel_show_settings_dlg(channel);
+ free(channel);
+ break;
+ }
+}
+
+static int chatroom_svc_gc_event(WPARAM wParam, LPARAM lParam)
+{
+ GCHOOK * pHook = (GCHOOK *)lParam;
+ HANDLE hContact;
+ char * channel, * text, * dst;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(pHook) && VALIDPTR(pHook->pDest), 0);
+
+ if(strcmp(pHook->pDest->pszModule, VQCHAT_PROTO))
+ return 0;
+
+ switch(pHook->pDest->iType) {
+ case GC_USER_MESSAGE:
+ channel = util_loc2utf(pHook->pDest->pszID);
+ text = util_loc2utf(pHook->pszText);
+
+ chatroom_svc_gc_event_user_message(channel, text);
+ free(channel);
+ free(text);
+ break;
+
+ case GC_USER_CHANMGR:
+ channel = util_loc2utf(pHook->pDest->pszID);
+ chatroom_channel_show_settings_dlg(channel);
+ free(channel);
+ break;
+
+ case GC_USER_PRIVMESS:
+ dst = util_loc2utf(pHook->pszUID);
+ if(!user_is_my_nickname(dst)) {
+ hContact = contacts_add_contact(dst, 0);
+ if(hContact)
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, 0);
+ }
+ free(dst);
+ break;
+
+ case GC_USER_NICKLISTMENU:
+ chatroom_svc_gc_event_user_nicklistmenu(pHook);
+ break;
+
+ case GC_USER_LOGMENU:
+ chatroom_svc_gc_event_user_logmenu(pHook);
+ break;
+ }
+
+ return 0;
+}
+
+static int chatroom_svc_gc_buildmenu(WPARAM wParam, LPARAM lParam)
+{
+ static struct gc_item usermenu_items[] = {
+ { "Alert Beep", 1, MENU_ITEM, FALSE },
+ { "", 0, MENU_POPUPSEPARATOR, FALSE },
+ { "&Add User", 2, MENU_ITEM, FALSE }
+ };
+ static struct gc_item chansettings_items[] = {
+ { "Channel &settings", 1, MENU_ITEM, FALSE }
+ };
+
+ GCMENUITEMS * gcmi = (GCMENUITEMS *)lParam;
+
+ if(gcmi) {
+ if(!lstrcmpi(gcmi->pszModule, VQCHAT_PROTO)) {
+ char * dst = util_loc2utf(gcmi->pszUID);
+
+ switch(gcmi->Type) {
+ case MENU_ON_NICKLIST:
+ gcmi->nItems = 3;
+ gcmi->Item = usermenu_items;
+
+ /* disable 'Add User' if already added */
+ gcmi->Item[2].bDisabled =
+ user_is_my_nickname(dst)
+ || contacts_find_contact(dst)!=NULL;
+ break;
+
+ case MENU_ON_LOG:
+ gcmi->nItems = 1;
+ gcmi->Item = chansettings_items;
+ break;
+ }
+ free(dst);
+ }
+ }
+
+ return 0;
+}
+
+static __inline void chatroom_setup_event(
+ GCDEST * pDest, GCEVENT * pEvent, const char * channel, int event_type)
+{
+ pDest->pszModule = VQCHAT_PROTO;
+ pDest->pszID = (char *)channel;
+ pDest->iType = event_type;
+
+ memset(pEvent, 0, sizeof(GCEVENT));
+ pEvent->cbSize = sizeof(GCEVENT);
+ pEvent->pDest = pDest;
+ pEvent->time = time(NULL);
+}
+static __inline char * chatroom_escape_message(const char * message)
+{
+ int msg_len = strlen(message);
+ char * escaped = malloc(msg_len * 2 + 1); /* to be safe */
+ char * p_escaped = escaped;
+
+ do {
+ if(*message == '%')
+ *(p_escaped++) = '%';
+
+ *(p_escaped++) = *message;
+ } while(*(message++));
+
+ return escaped;
+}
+
+static void chatroom_flush_channel_hash()
+{
+ if(s_channelHash)
+ hashtable_destroy(s_channelHash, 1);
+
+ s_channelHash = create_hashtable(
+ 32, hashtable_strhashfunc, hashtable_strequalfunc,
+ chatroom_free_channelhash_value);
+}
+
+/* exported routines
+ */
+void chatroom_init()/*{{{*/
+{
+ s_fHaveChat = FALSE;
+ s_channelHash = NULL;
+ s_hwndChannelSettingsDlg = NULL;
+ s_chatroom_connected_join_timer = 0;
+}/*}}}*/
+
+void chatroom_uninit()/*{{{*/
+{
+ if(s_hwndChannelSettingsDlg) {
+ SendMessage(s_hwndChannelSettingsDlg, WM_CLOSE, 0, 0);
+ s_hwndChannelSettingsDlg = NULL;
+ }
+
+ if(s_channelHash) {
+ hashtable_destroy(s_channelHash, 1);
+ s_channelHash = NULL;
+ }
+
+ if(s_fHaveChat) {
+ /* unhook all the hooks */
+ UnhookEvent(s_hGcUserEvent);
+ UnhookEvent(s_hGcMenuBuild);
+ }
+
+ if(s_chatroom_connected_join_timer) {
+ KillTimer(NULL, s_chatroom_connected_join_timer);
+ s_chatroom_connected_join_timer = 0;
+ }
+}/*}}}*/
+
+void chatroom_hook_modules_loaded()/*{{{*/
+{
+ /* check if we have m3x's Chat module in miranda */
+ s_fHaveChat = ServiceExists(MS_GC_REGISTER);
+
+ if(s_fHaveChat) {
+ /* register with the Chat module */
+ GCREGISTER gcr;
+ gcr.cbSize = sizeof(gcr);
+ gcr.dwFlags = GC_CHANMGR;
+ gcr.pszModule = VQCHAT_PROTO;
+ gcr.pszModuleDispName = VQCHAT_PROTO_NAME;
+ gcr.iMaxText = VQP_MAX_PACKET_SIZE; /* approximation */
+ gcr.nColors = 0;
+ gcr.pColors = NULL; /* hope this works */
+ CallService(MS_GC_REGISTER, 0, (LPARAM)&gcr);
+
+ /* hook to hooks */
+ s_hGcUserEvent = HookEvent(ME_GC_EVENT, chatroom_svc_gc_event);
+ s_hGcMenuBuild = HookEvent(ME_GC_BUILDMENU,
+ chatroom_svc_gc_buildmenu);
+ } else {
+ /* ask the user to get the Chat module */
+ if(IDYES == MessageBox(NULL,
+ "The " VQCHAT_PROTO_NAME " depends on another plugin"
+ " \"Chat\".\nDo you want to download it from the"
+ " Miranda IM web site now?",
+ "Information",
+ MB_YESNO|MB_ICONINFORMATION)
+ )
+ {
+ CallService(MS_UTILS_OPENURL, 1,
+ (LPARAM)"http://www.miranda-im.org/download/"
+ "details.php?action=viewfile&id=1309");
+ }
+
+ }
+}/*}}}*/
+
+/* chatroom_module_installed:
+ * returns TRUE if the "Chat" module is installed
+ */
+int chatroom_module_installed()/*{{{*/
+{
+ return s_fHaveChat;
+}/*}}}*/
+
+static int chatroom_connected_join_enum(const char * channel, void * data)/*{{{*/
+{
+ chatroom_channel_join(channel, 1);
+ return 1; /* keep enumerating */
+}/*}}}*/
+
+static void CALLBACK chatroom_connected_join_timer_cb(/*{{{*/
+ HWND hWnd, UINT nMsg, UINT_PTR nEvent, DWORD dwTime)
+{
+ /* join all of user's channels */
+ chanlist_enum(*user_p_chanlist(), chatroom_connected_join_enum, NULL);
+
+ /* this is a single-shot timer */
+ KillTimer(0, s_chatroom_connected_join_timer);
+ s_chatroom_connected_join_timer = 0;
+}/*}}}*/
+
+void chatroom_connected(vqp_link_t link)/*{{{*/
+{
+ char * r_src;
+
+ s_vqpLink = link;
+
+ /* create/flush channel status entry hash */
+ chatroom_flush_channel_hash();
+
+ /* join all of user's channels after a second
+ */
+ s_chatroom_connected_join_timer
+ = SetTimer(NULL, 0, 500, chatroom_connected_join_timer_cb);
+
+ /* join the Main channel (not-really, just for the effect)
+ * (in case the user doesn't have it on the his chanlist)
+ */
+ chatroom_send_net_join(VQCHAT_MAIN_CHANNEL);
+
+ /* set active status update */
+ r_src = util_utf2vqp(user_codepage(), user_nickname());
+ msgloop_send(vqp_msg_active_change(s_vqpLink, r_src, VQCHAT_UNDEF_ACTIVE), 0);
+ free(r_src);
+}/*}}}*/
+
+static int chatroom_disconnected_part_enum(const char * channel, void * data)
+{
+ chatroom_channel_part(channel, 1);
+ return 1; /* keep enumerating */
+}
+
+void chatroom_disconnected()
+{
+ /* part the #Main channel, - this will leave the network */
+ chatroom_send_net_part(VQCHAT_MAIN_CHANNEL);
+
+ /* leave all the channels in user's chanlist */
+ chanlist_enum(*user_p_chanlist(), chatroom_disconnected_part_enum, NULL);
+
+ s_vqpLink = NULL;
+}
+
+/* chatroom_is_valid_channel:
+ * validates that channel name is valid
+ * returns:
+ * 0 if invalid,
+ * non-0 if valid
+ */
+int chatroom_is_valid_channel(const char * channel)
+{
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), 0);
+
+ /* channel name must be of non-0 length
+ * and channel name must not contain a '#' char
+ */
+ return strlen(channel)!=0 && strchr(channel, '#')==NULL;
+}
+
+/* chatroom_channel_join:
+ * Join the specified channel, and add it to the user's chanlist
+ * (if not there already).
+ * The connected_event param is set to non-0 only when joining the
+ * channels after connecting to the network.
+ */
+static int chatroom_channel_join_userlist_enum_fn(
+ const char * user_name, void * channel)
+{
+ /* join the channel, if the user is there */
+ if(chanlist_contains(userlist_user_chanlist(user_name), (const char *)channel)) {
+ GCDEST gcd;
+ GCEVENT gce;
+ char * l_user_name = util_utf2loc(user_name);
+
+ chatroom_setup_event(&gcd, &gce, (const char *)channel, GC_EVENT_JOIN);
+ gce.pszStatus = CHATROOM_NORMAL_STATUS;
+ gce.bIsMe = FALSE;
+ gce.bAddToLog = FALSE;
+ gce.pszNick = l_user_name;
+ gce.pszUID = l_user_name;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ free(l_user_name);
+ }
+
+ return 1; /* keep enumerating */
+}
+
+/* chatroom_channel_join:
+ * joins the specified channel and updates channel list
+ * (if @connecting_to_net != 0)
+ * params:
+ * @connecting_to_net - non-0 if the chatroom_channel_join() was called
+ * due to connecting-to-the-net event
+ */
+void chatroom_channel_join(
+ const char * channel, int connecting_to_net)
+{
+ char * chanlist;
+ struct chatroom_status_entry * entry;
+
+ ASSERT_RETURNIFFAIL(!user_offline());
+
+ /* validate channel name */
+ if(!chatroom_is_valid_channel(channel))
+ return;
+
+ /* add channel entry to hashtable
+ */
+ entry = (struct chatroom_status_entry*)hashtable_search(s_channelHash, (void*)channel);
+ if(entry == NULL) {
+ entry = malloc(sizeof(struct chatroom_status_entry));
+ entry->topic = strdup("");
+ hashtable_insert(s_channelHash, strdup(channel), entry);
+ }
+
+ /* update user's channel list
+ */
+ chanlist = *user_p_chanlist();
+ if(!connecting_to_net) {
+ /* don't do anything if we've joined the channel already */
+ if(chanlist_contains(chanlist, channel))
+ return;
+
+ /* add this channel to chanlist and store it in db */
+ user_set_chanlist(*user_p_chanlist() = chanlist_add(chanlist, channel), TRUE);
+ }
+
+ /* send join msg to the network:
+ * dont do this for the MAIN channel, as it is considered
+ * "special"
+ */
+ if(strcmp(channel, VQCHAT_MAIN_CHANNEL))
+ chatroom_send_net_join(channel);
+
+ /* do stuff with Chat module */
+ if(s_fHaveChat) {
+ GCWINDOW gcw;
+ char * channel_name = chatroom_make_name(channel),
+ * l_channel_name = util_utf2loc(channel_name),
+ * l_current_topic = util_utf2loc(entry->topic);
+ free(channel_name);
+
+ /* create window */
+ gcw.cbSize = sizeof(gcw);
+ gcw.iType = GCW_CHATROOM;
+ gcw.pszModule = VQCHAT_PROTO;
+ gcw.pszName = l_channel_name;
+ gcw.pszID = channel;
+ gcw.pszStatusbarText = NULL;
+ gcw.bDisableNickList = FALSE;
+ gcw.dwItemData = 0;
+
+ if(!CallService(MS_GC_NEWCHAT, 0, (LPARAM)&gcw)) {
+ /* setup channel's window */
+ GCDEST gcd;
+ GCEVENT gce;
+ char * l_user_nickname = util_utf2loc(user_nickname());
+
+ /* register the "Normal" status */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_ADDGROUP);
+ gce.pszStatus = CHATROOM_NORMAL_STATUS;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ /* fill in the nick list */
+ userlist_enum(chatroom_channel_join_userlist_enum_fn, (void*) channel);
+
+ /* add me to the list */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_JOIN);
+ gce.pszNick = l_user_nickname;
+ gce.pszUID = l_user_nickname;
+ gce.pszStatus = CHATROOM_NORMAL_STATUS;
+ gce.bIsMe = TRUE;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ /* set current topic */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_TOPIC);
+ gce.pszText = l_current_topic;
+ gce.bAddToLog = FALSE;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_SETSBTEXT);
+ /* this is a workaround for a bug in Chat implementation
+ * where it will segfault if strlen(pszText) == "" */
+ gce.pszText = strlen(l_current_topic) ? l_current_topic: " ";
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ /* init done */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_CONTROL);
+ CallService(MS_GC_EVENT, (WPARAM)WINDOW_INITDONE, (LPARAM)&gce);
+ CallService(MS_GC_EVENT, (WPARAM)WINDOW_ONLINE, (LPARAM)&gce);
+
+ free(l_user_nickname);
+ }
+
+ /* free channel name '#chan' we've alloced */
+ free(l_channel_name);
+ free(l_current_topic);
+ }
+}
+
+void chatroom_channel_part(const char * channel, int disconnected_event)
+{
+ char * chanlist;
+ ASSERT_RETURNIFFAIL(!user_offline());
+
+ chanlist = *user_p_chanlist();
+ if(!disconnected_event) {
+ /* check if we've parted this channel already */
+ if(!chanlist_contains(chanlist, channel))
+ return;
+
+ /* remove channel from chanlist and store it in db */
+ user_set_chanlist(*user_p_chanlist() = chanlist_remove(chanlist, channel), TRUE);
+ }
+
+ /* remove channel entry from hashtable */
+ hashtable_remove(s_channelHash, (void*)channel, 1);
+
+ /* send part message to the network, except for the VQCHAT_MAIN_CHANNEL */
+ if(strcmp(channel, VQCHAT_MAIN_CHANNEL))
+ chatroom_send_net_part(channel);
+
+ /* do stuff with the chat module */
+ if(s_fHaveChat) {
+ GCDEST gcd;
+ GCEVENT gce;
+
+ /* close chat window */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_CONTROL);
+ CallService(MS_GC_EVENT, WINDOW_TERMINATE, (LPARAM)&gce);
+ }
+}
+
+void chatroom_channel_topic_change(
+ const char * channel, const char * new_topic,
+ int notify_network, int add_to_log)
+{
+ struct chatroom_status_entry * entry;
+ int topic_changed;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(channel) && VALIDPTR(new_topic));
+
+ /* get current channel topic (if there is one)
+ * or alloc new one to store channel (as this might not be open already
+ * and we will need this topic if we join the channel)
+ */
+ entry = (struct chatroom_status_entry*) hashtable_search(s_channelHash, (void*)channel);
+ if(entry == NULL) {
+ topic_changed = 1;
+
+ entry = malloc(sizeof(struct chatroom_status_entry));
+ entry->topic = strdup(new_topic);
+ hashtable_insert(s_channelHash, strdup(channel), entry);
+ } else {
+ /* we have previous topic here: check if the new one is different */
+ topic_changed = strcmp(entry->topic, new_topic);
+
+ /* store the new topic for the channel */
+ if(topic_changed) {
+ free(entry->topic);
+ entry->topic = strdup(new_topic);
+ }
+ }
+
+ /* the topic has changed and we have this channel's topic
+ * on the list: change the topic
+ */
+ if(s_fHaveChat && topic_changed
+ && chanlist_contains(*user_p_chanlist(), channel)) {
+ GCDEST gcd;
+ GCEVENT gce;
+ char * l_new_topic = util_utf2loc(new_topic);
+
+ /* update chatroom topic */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_TOPIC);
+ gce.pszText = l_new_topic;
+ gce.bAddToLog = add_to_log;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ /* set status bar to topic text */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_SETSBTEXT);
+ /* this is a workaround for a bug in Chat implementation
+ * where it will segfault if strlen(pszText) == "" */
+ gce.pszText = strlen(l_new_topic) ? l_new_topic: " ";
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ /* emit topic change message to the network */
+ if(notify_network) {
+ char * r_channel = util_utf2vqp(user_codepage(), channel),
+ * r_new_topic = util_utf2vqp(user_codepage(), new_topic);
+
+ msgloop_send(
+ vqp_msg_channel_topic_change(s_vqpLink, r_channel, r_new_topic),
+ 0);
+ free(r_channel);
+ free(r_new_topic);
+ }
+
+ free(l_new_topic);
+ }
+}
+
+/* chatroom_channel_show_settings_dlg:
+ * Shows channel settings dialog for specified channel.
+ * Focuses on current channel settings dialog, if one already active.
+ */
+void chatroom_channel_show_settings_dlg(const char * channel)
+{
+ ASSERT_RETURNIFFAIL(VALIDPTR(channel)
+ && chanlist_contains(*user_p_chanlist(), channel));
+
+ /* show channel settings dialog
+ */
+ if(!s_hwndChannelSettingsDlg) {
+ /* create and init the dialog */
+ s_hwndChannelSettingsDlg = CreateDialog(
+ g_hDllInstance, MAKEINTRESOURCE(IDD_CS),
+ NULL, chatroom_channel_settings_wndproc);
+
+ SendMessage(s_hwndChannelSettingsDlg, VQCHAT_WM_INITCS, 0, (LPARAM)channel);
+ }
+
+ /* activate window */
+ SetActiveWindow(s_hwndChannelSettingsDlg);
+}
+
+const char * chatroom_channel_get_topic(const char * channel)
+{
+ struct chatroom_status_entry * entry;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), "");
+
+ entry = (struct chatroom_status_entry*)hashtable_search(s_channelHash, (void*)channel);
+ return (entry && entry->topic) ? entry->topic: "";
+}
+
+static int chatroom_user_name_change_enum_fn(
+ const char * channel, void * enum_fn_data)
+{
+ GCDEST gcd;
+ GCEVENT gce;
+ const char * user_name = ((const char **)enum_fn_data)[0],
+ * new_user_name = ((const char **)enum_fn_data)[1];
+
+ char * l_user_name = util_utf2loc(user_name),
+ * l_new_user_name = util_utf2loc(new_user_name);
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel) && VALIDPTR(enum_fn_data), 1);
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(user_name) && VALIDPTR(new_user_name), 1);
+
+ /* update contact's nickname */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_NICK);
+ gce.pszUID = l_user_name;
+ gce.pszNick = l_user_name;
+ gce.pszText = l_new_user_name;
+ gce.bAddToLog = TRUE;
+ gce.bIsMe = TRUE;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ /* update contact's UID */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_CHID);
+ gce.pszUID = l_user_name;
+ gce.pszText = l_new_user_name;
+ gce.pszStatus = CHATROOM_NORMAL_STATUS;
+ gce.bIsMe = TRUE;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ free(l_user_name);
+ free(l_new_user_name);
+
+ return 1;
+}
+
+void chatroom_user_name_change(const char * user_name, const char * new_user_name)
+{
+ const char * enum_fn_data[2];
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(new_user_name));
+
+ enum_fn_data[0] = user_name;
+ enum_fn_data[1] = new_user_name;
+
+ chanlist_enum(
+ *user_p_chanlist(),
+ chatroom_user_name_change_enum_fn,
+ enum_fn_data);
+}
+
+static int chatroom_user_status_change_enum_fn(
+ const char * channel, void * notice_text)
+{
+ GCDEST gcd;
+ GCEVENT gce;
+ char * l_notice_text = util_utf2loc(notice_text);
+
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_INFORMATION);
+ gce.pszText = (const char *)l_notice_text;
+ gce.bAddToLog = TRUE;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ free(l_notice_text);
+
+ return 1;
+}
+
+void chatroom_user_status_change(int new_status)
+{
+ char * notice_text;
+
+ notice_text = malloc(sizeof(user_nickname()) + 64);
+ ASSERT_RETURNIFFAIL(VALIDPTR(notice_text));
+
+ sprintf(notice_text, "%s changed status to \"%s\"",
+ user_nickname(), user_status_name(new_status));
+
+ chanlist_enum(
+ *user_p_chanlist(),
+ chatroom_user_status_change_enum_fn,
+ (void*)notice_text);
+
+ free(notice_text);
+}
+
+void chatroom_channel_user_join(
+ const char * channel, const char * user_name,
+ int explicit_join)
+{
+ ASSERT_RETURNIFFAIL(VALIDPTR(channel) && VALIDPTR(user_name));
+
+ if(s_fHaveChat) {
+ GCDEST gcd;
+ GCEVENT gce;
+ char * l_user_name = util_utf2loc(user_name);
+
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_JOIN);
+ gce.pszNick = l_user_name;
+ gce.pszUID = l_user_name;
+ gce.pszStatus = CHATROOM_NORMAL_STATUS;
+ gce.bIsMe = FALSE;
+ gce.bAddToLog = explicit_join;
+
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ free(l_user_name);
+ }
+}
+
+void chatroom_channel_user_part(
+ const char * channel, const char * user_name,
+ int explicit_part)
+{
+ ASSERT_RETURNIFFAIL(VALIDPTR(channel) && VALIDPTR(user_name));
+
+ if(s_fHaveChat) {
+ GCDEST gcd;
+ GCEVENT gce;
+ char * l_nickname = util_utf2loc(user_name);
+
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_PART);
+ gce.pszUID = l_nickname;
+ gce.pszNick = l_nickname;
+ gce.pszText = explicit_part ? NULL: Translate("ping timeout");
+ gce.bAddToLog = TRUE;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ free(l_nickname);
+ }
+}
+
+static int chatroom_channel_user_name_change_enum_event_fn(
+ const char * channel, void * fn_data)
+{
+ const char * user_name = ((const char **)fn_data)[0],
+ * new_user_name = ((const char **)fn_data)[1];
+ int add_to_log = (int)((const char **)fn_data)[2];
+
+ /* send notice text to this channel, only
+ * if the user is there too
+ */
+ if(!chanlist_contains(userlist_user_chanlist(user_name), channel)) {
+ GCDEST gcd;
+ GCEVENT gce;
+ char * l_user_name = util_utf2loc(user_name),
+ * l_new_user_name = util_utf2loc(new_user_name);
+
+ /* change nickname */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_NICK);
+ gce.pszUID = l_user_name;
+ gce.pszText = l_new_user_name;
+ gce.pszNick = l_new_user_name;
+ gce.bAddToLog = add_to_log;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ /* change user UID */
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_CHID);
+ gce.pszUID = l_user_name;
+ gce.pszText = l_new_user_name;
+ gce.pszStatus = CHATROOM_NORMAL_STATUS;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ free(l_user_name);
+ free(l_new_user_name);
+ }
+
+ return 1; /* keep enumerating */
+}
+
+void chatroom_channel_user_name_change(
+ const char * user_name, const char * new_user_name,
+ int add_to_log)
+{
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(new_user_name));
+
+ if(s_fHaveChat) {
+ const char * enum_fn_data[3];
+
+ enum_fn_data[0] = user_name;
+ enum_fn_data[1] = new_user_name;
+ enum_fn_data[2] = (const char *) add_to_log;
+ chanlist_enum(
+ *user_p_chanlist(),
+ chatroom_channel_user_name_change_enum_event_fn,
+ (void*)enum_fn_data);
+ }
+}
+
+static int chatroom_channel_user_status_change_enum_event_fn(
+ const char * channel, void * enum_fn_data)
+{
+ const char * user_name = ((const char **)enum_fn_data)[0],
+ * notice_text = ((const char **)enum_fn_data)[1];
+ int add_to_log = (int)((const char **)enum_fn_data)[2];
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(notice_text) && VALIDPTR(user_name), 1);
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel) && VALIDPTR(notice_text), 1);
+
+ /* send notice text to this channel, only
+ * if the user is there too
+ */
+ if(chanlist_contains(userlist_user_chanlist(user_name), channel)) {
+ GCDEST gcd;
+ GCEVENT gce;
+ char * l_notice_text = util_utf2loc(notice_text);
+
+ chatroom_setup_event(&gcd, &gce, channel, GC_EVENT_INFORMATION);
+ gce.pszText = (const char *)l_notice_text;
+ gce.bAddToLog = add_to_log;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ free(l_notice_text);
+ }
+
+ return 1; /* keep enumerating */
+}
+
+/* chatroom_channel_user_status_change:
+ * notifies user status change to all of registered channel
+ */
+void chatroom_channel_user_status_change(
+ const char * user_name, int new_status, const char * new_status_text,
+ int add_to_log)
+{
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name));
+
+ if(s_fHaveChat) {
+ const char * enum_fn_data[3];
+ char * notice_text;
+ int status_len = strlen(new_status_text);
+
+ /* make notice text */
+ notice_text = malloc(64 + strlen(user_name) + status_len);
+ sprintf(notice_text,
+ status_len
+ ? "%s changed status to \"%s\": %s"
+ : "%s changed status to \"%s\"",
+ user_name,
+ user_status_name(new_status),
+ new_status_text
+ );
+
+ /* send notice text through all the channels */
+ enum_fn_data[0] = user_name;
+ enum_fn_data[1] = notice_text;
+ enum_fn_data[2] = (const char *)add_to_log;
+ chanlist_enum(
+ *user_p_chanlist(),
+ chatroom_channel_user_status_change_enum_event_fn,
+ (void*)enum_fn_data);
+
+ /* free notice text */
+ free(notice_text);
+ }
+}
+
+void chatroom_channel_user_text(
+ const char * channel, const char * user_name,
+ const char * text, int action_text)
+{
+ ASSERT_RETURNIFFAIL(VALIDPTR(channel) && VALIDPTR(user_name));
+ ASSERT_RETURNIFFAIL(VALIDPTR(text));
+
+ if(s_fHaveChat && chanlist_contains(*user_p_chanlist(), channel)) {
+ GCDEST gcd;
+ GCEVENT gce;
+ char * escaped_text = chatroom_escape_message(text),
+ * l_user_name = util_utf2loc(user_name),
+ * l_escaped_text = util_utf2loc(escaped_text);
+
+ chatroom_setup_event(
+ &gcd, &gce, channel,
+ action_text ? GC_EVENT_ACTION: GC_EVENT_MESSAGE);
+ gce.pszUID = l_user_name;
+ gce.pszNick = l_user_name;
+ gce.pszText = l_escaped_text;
+ gce.bAddToLog = TRUE;
+ CallService(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ free(l_user_name);
+ free(l_escaped_text);
+ free(escaped_text);
+ }
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/chatroom.h b/plugins/!NotAdopted/VypressChat/chatroom.h
new file mode 100644
index 0000000000..3ebaaf70b8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/chatroom.h
@@ -0,0 +1,63 @@
+/*
+ * 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: chatroom.h,v 1.13 2005/03/13 02:31:09 bobas Exp $
+ */
+
+
+#ifndef __CHATROOM_H
+#define __CHATROOM_H
+
+void chatroom_init();
+void chatroom_uninit();
+void chatroom_hook_modules_loaded();
+int chatroom_module_installed();
+
+void chatroom_connected(vqp_link_t link);
+void chatroom_disconnected();
+
+int chatroom_is_valid_channel(const char * channel);
+
+void chatroom_user_name_change(
+ const char * user_name, const char * new_user_name);
+void chatroom_user_status_change(int new_status);
+
+void chatroom_channel_join(const char * channel, int connected_event);
+void chatroom_channel_part(const char * channel, int disconnected_event);
+void chatroom_channel_topic_change(
+ const char * channel, const char * new_topic, int notify_network,
+ int add_to_log);
+void chatroom_channel_show_settings_dlg(const char * channel);
+
+const char * chatroom_channel_get_topic(const char * channel);
+
+void chatroom_channel_user_join(const char * channel, const char * user_name, int explicit_join);
+void chatroom_channel_user_part(const char * channel, const char * user_name, int explicit_part);
+void chatroom_channel_user_name_change(
+ const char * user_name, const char * new_user_name,
+ int add_to_log);
+void chatroom_channel_user_status_change(
+ const char * user_name, int new_status, const char * new_status_text,
+ int add_to_log);
+
+void chatroom_channel_user_text(
+ const char * channel, const char * user_name,
+ const char * text, int action_text);
+
+#endif /* #ifndef __CHATROOM_H */
+
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;
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/contacts.h b/plugins/!NotAdopted/VypressChat/contacts.h
new file mode 100644
index 0000000000..8e1dbf749d
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contacts.h
@@ -0,0 +1,61 @@
+/*
+ * 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.h,v 1.13 2005/03/15 02:03:47 bobas Exp $
+ */
+
+#ifndef __CONTACTS_H
+#define __CONTACTS_H
+
+#include <time.h>
+
+void contacts_connected(vqp_link_t link);
+void contacts_disconnected();
+void contacts_hook_modules_loaded();
+
+void contacts_set_all_offline();
+
+HANDLE contacts_add_contact(const char * nickname, int permanent);
+HANDLE contacts_find_contact(const char * nickname);
+
+void contacts_set_contact_status(HANDLE hContact, int new_status);
+void contacts_set_contact_nickname(HANDLE hContact, const char * new_nickname);
+void contacts_set_contact_addr(HANDLE hContact, vqp_addr_t addr);
+void contacts_set_contact_about(HANDLE hContact, const char * about_text);
+void contacts_set_contact_gender(HANDLE hContact, enum vqp_gender gender);
+
+enum contact_property_enum {
+ CONTACT_COMPUTER, CONTACT_USER, CONTACT_WORKGROUP,
+ CONTACT_PLATFORM, CONTACT_SOFTWARE
+};
+void contacts_set_contact_property(
+ HANDLE hContact, enum contact_property_enum property, const char * string);
+char * contacts_get_contact_property(HANDLE hContact, enum contact_property_enum property);
+
+int contacts_get_contact_info(HANDLE hContact, WPARAM flags);
+int contacts_get_contact_status(HANDLE hContact);
+char * contacts_get_nickname(HANDLE hContact);
+void contacts_input_contact_message(HANDLE hContact, const char * message_text);
+int contacts_send_contact_message(
+ HANDLE hContact, const char * message_text, WPARAM flags, int is_queued);
+void contacts_send_beep(HANDLE hContact);
+
+int contacts_is_user_contact(HANDLE hContact);
+int contacts_is_chatroom_contact(HANDLE hContact);
+
+#endif
diff --git a/plugins/!NotAdopted/VypressChat/contrib/hashtable.c b/plugins/!NotAdopted/VypressChat/contrib/hashtable.c
new file mode 100644
index 0000000000..5bc1b2f539
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/hashtable.c
@@ -0,0 +1,287 @@
+/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#include "hashtable.h"
+#include "hashtable_private.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef MEMWATCH
+# include "../contrib/memwatch.h"
+#endif
+
+/*
+Credit for primes table: Aaron Krowne
+ http://br.endernet.org/~akrowne/
+ http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
+*/
+static const unsigned int primes[] = {
+53, 97, 193, 389,
+769, 1543, 3079, 6151,
+12289, 24593, 49157, 98317,
+196613, 393241, 786433, 1572869,
+3145739, 6291469, 12582917, 25165843,
+50331653, 100663319, 201326611, 402653189,
+805306457, 1610612741
+};
+const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
+const float max_load_factor = 0.65;
+
+/*****************************************************************************/
+struct hashtable *
+create_hashtable(unsigned int minsize,
+ unsigned int (*hashf) (void*),
+ int (*eqf) (void*,void*),
+ void (*value_free_fn)(void *))
+{
+ struct hashtable *h;
+ unsigned int pindex, size = primes[0];
+ /* Check requested hashtable isn't too large */
+ if (minsize > (1u << 30)) return NULL;
+ /* Enforce size as prime */
+ for (pindex=0; pindex < prime_table_length; pindex++) {
+ if (primes[pindex] > minsize) { size = primes[pindex]; break; }
+ }
+ h = (struct hashtable *)malloc(sizeof(struct hashtable));
+ if (NULL == h) return NULL; /*oom*/
+ h->table = (struct entry **)malloc(sizeof(struct entry*) * size);
+ if (NULL == h->table) { free(h); return NULL; } /*oom*/
+ memset(h->table, 0, size * sizeof(struct entry *));
+ h->tablelength = size;
+ h->primeindex = pindex;
+ h->entrycount = 0;
+ h->hashfn = hashf;
+ h->eqfn = eqf;
+ h->value_free_fn = value_free_fn;
+ h->loadlimit = (unsigned int) ceil(size * max_load_factor);
+ return h;
+}
+
+/*****************************************************************************/
+unsigned int
+hash(struct hashtable *h, void *k)
+{
+ /* Aim to protect against poor hash functions by adding logic here
+ * - logic taken from java 1.4 hashtable source */
+ unsigned int i = h->hashfn(k);
+ i += ~(i << 9);
+ i ^= ((i >> 14) | (i << 18)); /* >>> */
+ i += (i << 4);
+ i ^= ((i >> 10) | (i << 22)); /* >>> */
+ return i;
+}
+
+/*****************************************************************************/
+static int
+hashtable_expand(struct hashtable *h)
+{
+ /* Double the size of the table to accomodate more entries */
+ struct entry **newtable;
+ struct entry *e;
+ struct entry **pE;
+ unsigned int newsize, i, index;
+ /* Check we're not hitting max capacity */
+ if (h->primeindex == (prime_table_length - 1)) return 0;
+ newsize = primes[++(h->primeindex)];
+
+ newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize);
+ if (NULL != newtable)
+ {
+ memset(newtable, 0, newsize * sizeof(struct entry *));
+ /* This algorithm is not 'stable'. ie. it reverses the list
+ * when it transfers entries between the tables */
+ for (i = 0; i < h->tablelength; i++) {
+ while (NULL != (e = h->table[i])) {
+ h->table[i] = e->next;
+ index = indexFor(newsize,e->h);
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+ free(h->table);
+ h->table = newtable;
+ }
+ /* Plan B: realloc instead */
+ else
+ {
+ newtable = (struct entry **)
+ realloc(h->table, newsize * sizeof(struct entry *));
+ if (NULL == newtable) { (h->primeindex)--; return 0; }
+ h->table = newtable;
+ memset(newtable[h->tablelength], 0, newsize - h->tablelength);
+ for (i = 0; i < h->tablelength; i++) {
+ for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
+ index = indexFor(newsize,e->h);
+ if (index == i)
+ {
+ pE = &(e->next);
+ }
+ else
+ {
+ *pE = e->next;
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+ }
+ }
+ h->tablelength = newsize;
+ h->loadlimit = (unsigned int) ceil(newsize * max_load_factor);
+ return -1;
+}
+
+/*****************************************************************************/
+unsigned int
+hashtable_count(struct hashtable *h)
+{
+ return h->entrycount;
+}
+
+/*****************************************************************************/
+int
+hashtable_insert(struct hashtable *h, void *k, void *v)
+{
+ /* This method allows duplicate keys - but they shouldn't be used */
+ unsigned int index;
+ struct entry *e;
+ if (++(h->entrycount) > h->loadlimit)
+ {
+ /* Ignore the return value. If expand fails, we should
+ * still try cramming just this value into the existing table
+ * -- we may not have memory for a larger table, but one more
+ * element may be ok. Next time we insert, we'll try expanding again.*/
+ hashtable_expand(h);
+ }
+ e = (struct entry *)malloc(sizeof(struct entry));
+ if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
+ e->h = hash(h,k);
+ index = indexFor(h->tablelength,e->h);
+ e->k = k;
+ e->v = v;
+ e->next = h->table[index];
+ h->table[index] = e;
+ return -1;
+}
+
+/*****************************************************************************/
+void * /* returns value associated with key */
+hashtable_search(struct hashtable *h, void *k)
+{
+ struct entry *e;
+ unsigned int hashvalue, index;
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hashvalue);
+ e = h->table[index];
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v;
+ e = e->next;
+ }
+ return NULL;
+}
+
+/*****************************************************************************/
+void * /* returns value associated with key */
+hashtable_remove(struct hashtable *h, void *k, int free_value)
+{
+ /* TODO: consider compacting the table when the load factor drops enough,
+ * or provide a 'compact' method. */
+
+ struct entry *e;
+ struct entry **pE;
+ void *v;
+ unsigned int hashvalue, index;
+
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hash(h,k));
+ pE = &(h->table[index]);
+ e = *pE;
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
+ {
+ *pE = e->next;
+ h->entrycount--;
+ v = e->v;
+ freekey(e->k);
+ free(e);
+
+ if(h->value_free_fn && free_value) {
+ h->value_free_fn(v);
+ v = NULL;
+ }
+
+ return v;
+ }
+ pE = &(e->next);
+ e = e->next;
+ }
+ return NULL;
+}
+
+/*****************************************************************************/
+/* destroy */
+void
+hashtable_destroy(struct hashtable *h, int free_values)
+{
+ unsigned int i;
+ struct entry *e, *f;
+ struct entry **table = h->table;
+ if (free_values)
+ {
+ for (i = 0; i < h->tablelength; i++)
+ {
+ e = table[i];
+ while (NULL != e)
+ { f = e; e = e->next; freekey(f->k);
+ if(h->value_free_fn) h->value_free_fn(f->v);
+ free(f); }
+ }
+ }
+ else
+ {
+ for (i = 0; i < h->tablelength; i++)
+ {
+ e = table[i];
+ while (NULL != e)
+ { f = e; e = e->next; freekey(f->k); free(f); }
+ }
+ }
+ free(h->table);
+ free(h);
+}
+
+void hashtable_enumerate(
+ struct hashtable * h,
+ hashtable_enum_fn enum_fn, void * user_data)
+{
+ unsigned int i;
+ struct entry * e;
+
+ for(i = 0; i < h->tablelength; i++) {
+ for(e = h->table[i]; e; e = e->next)
+ if(!enum_fn(e->k, e->v, user_data))
+ return;
+ }
+}
+
+/*
+ * Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/hashtable.h b/plugins/!NotAdopted/VypressChat/contrib/hashtable.h
new file mode 100644
index 0000000000..bee0da25e7
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/hashtable.h
@@ -0,0 +1,195 @@
+/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#ifndef __HASHTABLE_CWC22_H__
+#define __HASHTABLE_CWC22_H__
+
+struct hashtable;
+
+/* Example of use:
+ *
+ * struct hashtable *h;
+ * struct some_key *k;
+ * struct some_value *v;
+ *
+ * static unsigned int hash_from_key_fn( void *k );
+ * static int keys_equal_fn ( void *key1, void *key2 );
+ *
+ * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn, value_free_fn);
+ * k = (struct some_key *) malloc(sizeof(struct some_key));
+ * v = (struct some_value *) malloc(sizeof(struct some_value));
+ *
+ * (initialise k and v to suitable values)
+ *
+ * if (! hashtable_insert(h,k,v) )
+ * { exit(-1); }
+ *
+ * if (NULL == (found = hashtable_search(h,k) ))
+ * { printf("not found!"); }
+ *
+ * if (NULL == (found = hashtable_remove(h,k) ))
+ * { printf("Not found\n"); }
+ *
+ */
+
+/* Macros may be used to define type-safe(r) hashtable access functions, with
+ * methods specialized to take known key and value types as parameters.
+ *
+ * Example:
+ *
+ * Insert this at the start of your file:
+ *
+ * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value);
+ *
+ * This defines the functions 'insert_some', 'search_some' and 'remove_some'.
+ * These operate just like hashtable_insert etc., with the same parameters,
+ * but their function signatures have 'struct some_key *' rather than
+ * 'void *', and hence can generate compile time errors if your program is
+ * supplying incorrect data as a key (and similarly for value).
+ *
+ * Note that the hash and key equality functions passed to create_hashtable
+ * still take 'void *' parameters instead of 'some key *'. This shouldn't be
+ * a difficult issue as they're only defined and passed once, and the other
+ * functions will ensure that only valid keys are supplied to them.
+ *
+ * The cost for this checking is increased code size and runtime overhead
+ * - if performance is important, it may be worth switching back to the
+ * unsafe methods once your program has been debugged with the safe methods.
+ * This just requires switching to some simple alternative defines - eg:
+ * #define insert_some hashtable_insert
+ *
+ */
+
+/*****************************************************************************
+ * create_hashtable
+
+ * @name create_hashtable
+ * @param minsize minimum initial size of hashtable
+ * @param hashfunction function for hashing keys
+ * @param key_eq_fn function for determining key equality
+ * @param value_free_fn function to free value (on removal/destroing)
+ * @return newly created hashtable or NULL on failure
+ */
+
+struct hashtable *
+create_hashtable(unsigned int minsize,
+ unsigned int (*hashfunction) (void*),
+ int (*key_eq_fn) (void*,void*),
+ void (*value_free_fn)(void *v));
+
+/*****************************************************************************
+ * hashtable_insert
+
+ * @name hashtable_insert
+ * @param h the hashtable to insert into
+ * @param k the key - hashtable claims ownership and will free on removal
+ * @param v the value - does not claim ownership
+ * @return non-zero for successful insertion
+ *
+ * This function will cause the table to expand if the insertion would take
+ * the ratio of entries to table size over the maximum load factor.
+ *
+ * This function does not check for repeated insertions with a duplicate key.
+ * The value returned when using a duplicate key is undefined -- when
+ * the hashtable changes size, the order of retrieval of duplicate key
+ * entries is reversed.
+ * If in doubt, remove before insert.
+ */
+
+int
+hashtable_insert(struct hashtable *h, void *k, void *v);
+
+#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \
+int fnname (struct hashtable *h, keytype *k, valuetype *v) \
+{ \
+ return hashtable_insert(h,k,v); \
+}
+
+/*****************************************************************************
+ * hashtable_search
+
+ * @name hashtable_search
+ * @param h the hashtable to search
+ * @param k the key to search for - does not claim ownership
+ * @return the value associated with the key, or NULL if none found
+ */
+
+void *
+hashtable_search(struct hashtable *h, void *k);
+
+#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \
+valuetype * fnname (struct hashtable *h, keytype *k) \
+{ \
+ return (valuetype *) (hashtable_search(h,k)); \
+}
+
+/*****************************************************************************
+ * hashtable_remove
+
+ * @name hashtable_remove
+ * @param h the hashtable to remove the item from
+ * @param k the key to search for - does not claim ownership
+ * @param free_value whether to free value (thus it would return NULL)
+ * @return the value associated with the key, or NULL if none found
+ */
+
+void * /* returns value */
+hashtable_remove(struct hashtable *h, void *k, int free_value);
+
+#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \
+valuetype * fnname (struct hashtable *h, keytype *k) \
+{ \
+ return (valuetype *) (hashtable_remove(h,k)); \
+}
+
+
+/*****************************************************************************
+ * hashtable_count
+
+ * @name hashtable_count
+ * @param h the hashtable
+ * @return the number of items stored in the hashtable
+ */
+unsigned int
+hashtable_count(struct hashtable *h);
+
+
+/*****************************************************************************
+ * hashtable_destroy
+
+ * @name hashtable_destroy
+ * @param h the hashtable
+ * @param free_values whether to call 'value_free_fn' on the remaining values
+ */
+
+void
+hashtable_destroy(struct hashtable *h, int free_values);
+
+
+/* hashtable_enum_fn:
+ * should return 0 when finished and no more enumeration needed
+ */
+typedef int (*hashtable_enum_fn)(void * key, void * value, void * user_data);
+
+void hashtable_enumerate(struct hashtable * h, hashtable_enum_fn enum_fn, void * user_data);
+
+#endif /* __HASHTABLE_CWC22_H__ */
+
+/*
+ * Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h b/plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h
new file mode 100644
index 0000000000..3bc85f8c15
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#ifndef __HASHTABLE_PRIVATE_CWC22_H__
+#define __HASHTABLE_PRIVATE_CWC22_H__
+
+#include "hashtable.h"
+
+/*****************************************************************************/
+struct entry
+{
+ void *k, *v;
+ unsigned int h;
+ struct entry *next;
+};
+
+struct hashtable {
+ unsigned int tablelength;
+ struct entry **table;
+ unsigned int entrycount;
+ unsigned int loadlimit;
+ unsigned int primeindex;
+ unsigned int (*hashfn) (void *k);
+ int (*eqfn) (void *k1, void *k2);
+ void (*value_free_fn)(void *v);
+};
+
+/*****************************************************************************/
+unsigned int
+hash(struct hashtable *h, void *k);
+
+/*****************************************************************************/
+/* indexFor */
+static inline unsigned int
+indexFor(unsigned int tablelength, unsigned int hashvalue) {
+ return (hashvalue % tablelength);
+};
+
+/* Only works if tablelength == 2^N */
+/*static inline unsigned int
+indexFor(unsigned int tablelength, unsigned int hashvalue)
+{
+ return (hashvalue & (tablelength - 1u));
+}
+*/
+
+/*****************************************************************************/
+#define freekey(X) free(X)
+/*define freekey(X) ; */
+
+
+/*****************************************************************************/
+
+#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/
+
+/*
+ * Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/m_chat.h b/plugins/!NotAdopted/VypressChat/contrib/m_chat.h
new file mode 100644
index 0000000000..8caf5d0e8c
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/m_chat.h
@@ -0,0 +1,424 @@
+/*
+Chat module plugin for Miranda IM
+
+Copyright (C) 2003 Jörgen Persson
+
+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.
+*/
+
+
+#if 0
+
+/*
+ This plugin provides event driven chat rooms for protocols that wish to use it.
+ It is built for IRC, which I also develop and is naturally biased towards IRC,
+ but it should work very well with other protocols too. I will try to explain as
+ careful as possible in this document how to use chat.dll
+
+ -- General guidelines --
+
+ There is one rule a protocol MUST follow to use this:
+
+ 1. Do not touch contacts that has a byte "ChatRoom" set to ANYTHING other than 0! (Could be 1, 2, 3, ...)
+ This is because chat.dll adds contacts to the clist using the protocol name
+ supplied by the protocol. But this will naturally not work well if the
+ protocol also tampers with the contacts. The value of the BYTE indicates which type of
+ window/contact it is (see the GCW_* flags below). There is two exceptions to this rule:
+
+ * You should continue to handle the right click menu items of these
+ contacts as usual, by hooking the menu prebuild hook etc. Chat.dll can not
+ handle this in an efficient manner!
+
+ * You should also handle when the user deletes the contact/room from the
+ contact list, as the protocol will then most likely have to send some message
+ to the server that the user has left the room.
+
+ 2. The chat.dll plugin keeps its own copies of strings passed.
+
+*/
+
+
+// Example of implementing point 1:
+
+// This is a code snippet that is common in protocols:
+
+
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !lstrcmpi(szProto, PROTONAME))
+ {
+// ... do something with the hContact here;
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+
+
+// You should do this instead:
+
+
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !lstrcmpi(szProto, PROTONAME))
+ {
+ if(DBGetContactSettingByte(hContact, PROTONAME, "ChatRoom", 0) == 0)
+ {
+// ... do something with the hContact here;
+ }
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+
+// There is not more to it than that. To recapitulate: do not touch contacts where the
+// BYTE "ChatRoom" is set to 1, apart from the two exceptions mentioned!
+
+// ... now onto how to use this thing!
+
+
+#endif
+
+
+
+//------------------------- SERVICES ------------------------
+/*
+ -- Register with the chat module --
+
+ The first thing that a protocol need to do is register with chat.dll. This is best done
+ after ALL modules has loaded naturally. The registration is needed to make sure that
+ the protocol obey rule 1 mentioned above, but equally important to set protocol specific
+ settings.
+
+ wParam= NULL
+ lParam= (LPARAM)(GCREGISTER *)gcr
+ returns 0 on success or nonzero on failure
+*/
+
+#define GC_BOLD 0x0001 //enable the 'bold' button
+#define GC_ITALICS 0x0002 //enable the 'italics' button
+#define GC_UNDERLINE 0x0004 //enable the 'underline' button
+#define GC_COLOR 0x0008 //enable the 'foreground color' button
+#define GC_BKGCOLOR 0x0010 //enable the 'background color' button
+#define GC_ACKMSG 0x0020 //the protocol must ack. messages sent
+#define GC_TYPNOTIF 0x0040 //enable typing notifications
+#define GC_CHANMGR 0x0080 //enable the 'channel settings' button
+
+typedef struct {
+ int cbSize; //Set to sizeof();
+ DWORD dwFlags; //Use GC_* flags above
+ const char *pszModule; //This MUST be the protocol name as registered with Miranda IM
+ const char *pszModuleDispName; //This is the protocol name as it will be displayed to the user
+ int iMaxText; //Max message length the protocol supports. Will limit the typing area
+ int nColors; //Number of colors in the colorchooser menu for the color buttons. Max = 100
+ COLORREF* pColors; //pointer to the first static COLORREF array. ie: COLORREF crCols[nColors]; pColors = &crCols[0];
+ } GCREGISTER;
+#define MS_GC_REGISTER "GChat/Register"
+
+
+
+/*
+ -- Tell the chat module to create a new window --
+
+ Create a new chat room and set various settings related to it. This is not
+ the same as actually joining the chat room. The chat room will not be visible
+ to the user until the 'set up' phase is completed. See the MS_GC_EVENT for that.
+
+ wParam=0
+ lParam=(LPARAM)(GCWINDOW*)gcw
+
+ returns 0 on success or nonzero on failure
+*/
+#define GCW_CHATROOM 1
+#define GCW_SERVER 2
+#define GCW_PRIVMESS 3
+
+typedef struct {
+ int cbSize; //Set to sizeof();
+ int iType; //Use one of the GCW_* flags above to set the general type of usage for the window
+ const char *pszModule; //The name of the protocol owning the window (the same as pszModule when you register)
+ const char *pszName; //The name of the chat room as it will be displayed to the user
+ const char *pszID; //The unique identifier for the chat room
+ const char *pszStatusbarText; //Optional text to set in the statusbar, or NULL.
+ BOOL bDisableNickList; //Disable the nicklist
+ DWORD dwItemData; //Set user defined data for this chat room. Retrieve it by using the service below
+ } GCWINDOW;
+#define MS_GC_NEWCHAT "GChat/NewChat"
+
+
+
+/*
+ -- Show an event --
+
+ Events is what drives chat.dll! After having created the chat room with
+ MS_GC_NEWCHAT it is time to make it work for real. Start off by telling chat.dll
+ what statuses a user can have by sending GC_EVENT_ADDGROUP as many times as needed.
+ Then send join events
+ (GC_EVENT_JOIN) to populate the user list. You will need to send one event
+ per user that should be added. When you are done with filling the user list
+ it is a good time to end the 'set up' phase and make the window visible by
+ calling a GC_EVENT_VISIBILITY event. A nice tip is to make sure the bAddToLog
+ member of GCEVENT is set to FALSE during the initial filling of the user list.
+
+ The GCDEST structure and its members are always used, but what members of
+ GCEVENT to use is determined by what event is sent to chat.dll. bAddToLog and
+ time is valid (at least where it makes sense). See the description of each event
+ for more information of what members that are valid.
+
+ It is possible to send formatted text (bold, italics, underlined, foreground color
+ and background color) by using the following keywords in pszText:
+ %cRRRGGGBBB - set the foreground color
+ %C - set foreground color to default
+ %fRRRGGGBBB - set the background color
+ %F - set the background color to default
+ %b - enable bold
+ %B - disable bold
+ %u - enable underlined
+ %U - disable underlined
+ %i - enable italics
+ %I - disable italics
+ %r - reset all to default
+ %% - escape the formatting. Translates to %
+
+ wParam=0
+ lParam=(LPARAM)(GCEVENT *) gce
+
+ returns 0 on success or nonzero on failure
+*/
+
+
+// GC_EVENT_JOIN <pszNick> has joined
+// A user is joining the channel, set bIsMe to indicate who is the user
+// pszNick - Display name
+// pszUID - Unique identifier
+// pszStatus - Which group (status) to add the user to
+// bIsMe - Is this the user? Used to indicate that the user has joined the channel
+#define GC_EVENT_JOIN 0x0001
+
+// GC_EVENT_PART <pszNick> has left[: pszText]
+// A user left the chat room
+// pszUID - Unique identifier
+// pszText - part message
+#define GC_EVENT_PART 0x0002
+
+// GC_EVENT_QUIT <pszNick> disconnected[: pszText]
+// A user disconnected, use pszID = NULL (of GCDEST) to broadcast to all windows.
+// pszUID - Unique identifier
+// pszText - part message
+#define GC_EVENT_QUIT 0x0004
+
+// GC_EVENT_KICK <pszStatus> kicked <pszNick>
+// A user is kicking another user from the room
+// pszUID - Unique identifier of the one being kicked
+// pszStatus - Name of user doing the kick
+#define GC_EVENT_KICK 0x0008
+
+// GC_EVENT_NICK <pszNick> is now known as <pszText>
+// A user changed his name
+// NOTE, see GC_EVENT_CHID also
+// pszUID - Unique identifier of the one changing name
+// pszText - New name of the user
+#define GC_EVENT_NICK 0x0010
+
+// GC_EVENT_NOTICE Notice from <pszNick>: <pszText>
+// An IRC type notice, will be sent to the active window
+// pszUID - Unique identifier
+// pszText - Notice text
+#define GC_EVENT_NOTICE 0x0020
+
+// GC_EVENT_MESSAGE
+// A regular chat room message
+// is outgoing or incoming
+// pszUID - Unique identifier
+// pszText - Message text, use the formatting variables above.
+// NOTE make sure % is translated to %% to avoid accidental formatting
+#define GC_EVENT_MESSAGE 0x0040
+
+// GC_EVENT_TOPIC Topic is <pszText>
+// pszUID - Unique identifier
+// pszText - Topic text
+#define GC_EVENT_TOPIC 0x0080
+
+// GC_EVENT_INFORMATION
+// Informational style text
+// pszText - Information text
+#define GC_EVENT_INFORMATION 0x0100
+
+// GC_EVENT_ACTION
+// An IRC Style action event. Same as GC_EVENT_MESSAGE otherwise
+#define GC_EVENT_ACTION 0x0200
+
+// GC_EVENT_ADDSTATUS <pszText> enables '<pszStatus>' for <pszNick>
+// pszUID - Unique identifier
+// pszText - The one enabling the status for another user
+// pszStatus - The status given
+#define GC_EVENT_ADDSTATUS 0x0400
+
+// GC_EVENT_REMOVESTATUS <pszText> disables '<pszStatus>' for <pszNick>
+// pszUID - Unique identifier
+// pszText - The one disabling the status for another user
+// pszStatus - The status taken
+#define GC_EVENT_REMOVESTATUS 0x0800
+
+// GC_EVENT_CHID - not shown in the log
+// Change the unique identifier of a contact
+// pszUID - Unique identifier
+// pszText - The new unique identifier
+#define GC_EVENT_CHID 0x1000
+
+// GC_EVENT_CHID - not shown in the log
+// Change the name of a window
+// pszText - The new name
+#define GC_EVENT_CHWINNAME 0x1001
+
+// GC_EVENT_ADDGROUP - not shown in the log
+// Add a new status group to the user list
+// pszStatus - The new group name
+#define GC_EVENT_ADDGROUP 0x1002
+
+// GC_EVENT_SETITEMDATA GC_EVENT_SETITEMDATA - not shown in the log
+// Get or set the user defined data of a window
+// dwItemData - The itemdata to set or get
+#define GC_EVENT_SETITEMDATA 0x1003
+#define GC_EVENT_GETITEMDATA 0x1004
+
+// GC_EVENT_CONTROL - not shown in the log
+// Call WINDOW_INITDONE after the initial setup is done.
+// Also use it to control aspects of a window if needed .
+// No members of GCEVENT used, send one of the below flags in wParam instead
+#define WINDOW_INITDONE 1 //send when the window is joined and all users have ben added to the nicklist
+#define WINDOW_VISIBLE 2 //make the room visible (most likely you will never use this)
+#define WINDOW_HIDDEN 3 //make the room hidden (most likely you will never use this)
+#define WINDOW_MAXIMIZE 4 //make the room maximized (most likely you will never use this)
+#define WINDOW_MINIMIZE 5 //make the room minimized (most likely you will never use this)
+#define WINDOW_CLEARLOG 6 //clear the log of the room
+#define WINDOW_TERMINATE 7 //send to remove a window from chat.dll,
+#define WINDOW_OFFLINE 8 //send when the user leave the room
+#define WINDOW_ONLINE 9 //send when the user join the room
+
+#define GC_EVENT_CONTROL 0x1005
+
+// GC_EVENT_SETSBTEXT - not shown in the log
+// Set the text of the statusbar
+// pszText - text
+#define GC_EVENT_SETSBTEXT 0x1006
+
+// GC_EVENT_ACK - not shown in the log
+// Used to ack a outgoing message, when GC_ACKMSG is set
+// dwItemData - The itemdata
+#define GC_EVENT_ACK 0x1007
+
+// GC_EVENT_SENDMESSAGE - not shown in the log
+// Send a message from the window as if the user had typed it.
+// Used by IRC to broadcast /AME and /AMSG messages
+// pszText - The text
+#define GC_EVENT_SENDMESSAGE 0x1008
+
+typedef struct {
+ char *pszModule; //Name of the protocol (same as you registered with)
+ char *pszID; //Unique identifier of the room corresponding to the event, or NULL to broadcast to all rooms.
+ int iType; //Use GC_EVENT_* as defined above. Only one event per service call.
+} GCDEST;
+
+typedef struct {
+ int cbSize; // Set to sizeof();
+ GCDEST* pDest; // pointer to a GCDEST structure
+ const char *pszText; // Text, usage depends on type of event (see above), max 2048 characters
+ const char *pszNick; // Nick, usage depends on type of event (see above)
+ const char *pszUID; // Unique identifier, usage depends on type of event (see above)
+ const char *pszStatus; // Status, usage depends on type of event (see above)
+ const char *pszUserInfo; // Additional user information that is displayed in the log only for join, part, quit and nick
+ BOOL bIsMe; // Is this event related to the user?
+ BOOL bAddToLog; // Should this event be added to the message log
+ DWORD dwItemData; // User specified data
+ time_t time; // Time of the event
+ } GCEVENT;
+#define MS_GC_EVENT "GChat/NewEvent"
+
+
+
+
+//------------------------- HOOKS ------------------------
+/*
+ -- user interaction --
+ Hook this to receive notifications about user commands. The below flags will tell what sort of
+ user interaction is taking place and is set in iType of the GCDEST pointer member. The other
+ members of GCDEST will tell what protocol and chat room name it is.
+
+ wParam=0
+ lParam=(LPARAM)(GCEVENT *)pgch
+
+ Returning nonzero from your hook will stop other hooks from being called.
+*/
+#define GC_USER_MESSAGE 1 // user typed a message, with \n delimiting lines, valid members: pszText
+#define GC_USER_CHANMGR 2 // user clicked the chat room settings button
+#define GC_USER_LOGMENU 3 // user has chosen a message log menu item, valid members: dwData
+#define GC_USER_NICKLISTMENU 4 // user has chosen a user list menu item, valid members: dwData
+#define GC_USER_TYPNOTIFY 5 // user is typing
+#define GC_USER_PRIVMESS 6 // user wants to talk privately to user, valid members: pszText, pszUID
+#define GC_USER_TERMINATE 7 // a chat window is about to be closed, useful for freeing the Item data which is passed in dwData, valid members: dwData
+#define ME_GC_EVENT "GChat/OutgoingEvent"
+
+typedef struct {
+ GCDEST* pDest; // Same meaning as for MS_GC_EVENT
+ char * pszText; // Text
+ char * pszUID; // Unique identifier
+ DWORD dwData; // user data
+ } GCHOOK;
+
+
+/*
+ -- Build the pop up menus --
+ The user is activating a right click menu and the protocol should tell what
+ Items should be added to the menu. You should have a static array of struct gc_item's.
+ When the hook is fired the protocol should set nItems to the number of gc_item's
+ it want to add and then set Item to point to that array.
+
+ wParam=0
+ lParam=(LPARAM)(GCMENUITEM *)gcmi
+
+ Returning nonzero from your hook will stop other hooks from being called.
+
+*/
+
+#define MENU_NEWPOPUP 1 // add submenu
+#define MENU_POPUPITEM 2 // add item to current submenu
+#define MENU_POPUPSEPARATOR 3 // add separator to current submenu
+#define MENU_SEPARATOR 4 // add separator to menu
+#define MENU_ITEM 5 // add item
+struct gc_item {
+ char * pszDesc; // Textual description of the menu item to add
+ DWORD dwID; // must not be 0, must be unique. Will be returned via the above hook when the user click the item
+ int uType; // What kind of item is it?
+ BOOL bDisabled; // should the item be disabled
+ };
+
+#define MENU_ON_LOG 1 // pop up menu on the log
+#define MENU_ON_NICKLIST 2 // pop up menu on the user list
+typedef struct {
+ char * pszModule; // Set by chat.dll to the protocol name, do not change.
+ char * pszID; // The unique identifier of the window
+ char * pszUID; // The unique identifier of the user, if clicked in the user list
+ int Type; // MENU_ON_LOG or MENU_ON_USERLIST, what menu type is it?
+ int nItems; // set to number of items
+ struct gc_item* Item; // pointer to the first in the array of gc_item's
+ } GCMENUITEMS;
+#define ME_GC_BUILDMENU "GChat/BuildMenu"
+
diff --git a/plugins/!NotAdopted/VypressChat/contrib/memwatch.c b/plugins/!NotAdopted/VypressChat/contrib/memwatch.c
new file mode 100644
index 0000000000..fc59fa40a1
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/memwatch.c
@@ -0,0 +1,2664 @@
+/*
+** MEMWATCH.C
+** Nonintrusive ANSI C memory leak / overwrite detection
+** Copyright (C) 1992-2003 Johan Lindh
+** All rights reserved.
+** Version 2.71
+
+ This file is part of MEMWATCH.
+
+ MEMWATCH 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.
+
+ MEMWATCH 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 MEMWATCH; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+**
+** 920810 JLI [1.00]
+** 920830 JLI [1.10 double-free detection]
+** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit]
+** 921022 JLI [1.20 ASSERT and VERIFY]
+** 921105 JLI [1.30 C++ support and TRACE]
+** 921116 JLI [1.40 mwSetOutFunc]
+** 930215 JLI [1.50 modified ASSERT/VERIFY]
+** 930327 JLI [1.51 better auto-init & PC-lint support]
+** 930506 JLI [1.55 MemWatch class, improved C++ support]
+** 930507 JLI [1.60 mwTest & CHECK()]
+** 930809 JLI [1.65 Abort/Retry/Ignore]
+** 930820 JLI [1.70 data dump when unfreed]
+** 931016 JLI [1.72 modified C++ new/delete handling]
+** 931108 JLI [1.77 mwSetAssertAction() & some small changes]
+** 940110 JLI [1.80 no-mans-land alloc/checking]
+** 940328 JLI [2.00 version 2.0 rewrite]
+** Improved NML (no-mans-land) support.
+** Improved performance (especially for free()ing!).
+** Support for 'read-only' buffers (checksums)
+** ^^ NOTE: I never did this... maybe I should?
+** FBI (free'd block info) tagged before freed blocks
+** Exporting of the mwCounter variable
+** mwBreakOut() localizes debugger support
+** Allocation statistics (global, per-module, per-line)
+** Self-repair ability with relinking
+** 950913 JLI [2.10 improved garbage handling]
+** 951201 JLI [2.11 improved auto-free in emergencies]
+** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()]
+** 960514 JLI [2.12 undefining of existing macros]
+** 960515 JLI [2.13 possibility to use default new() & delete()]
+** 960516 JLI [2.20 suppression of file flushing on unfreed msgs]
+** 960516 JLI [2.21 better support for using MEMWATCH with DLL's]
+** 960710 JLI [X.02 multiple logs and mwFlushNow()]
+** 960801 JLI [2.22 merged X.01 version with current]
+** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's]
+** 960805 JLI [2.31 merged X.02 version with current]
+** 961002 JLI [2.32 support for realloc() + fixed STDERR bug]
+** 961222 JLI [2.40 added mwMark() & mwUnmark()]
+** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY]
+** 970113 JLI [2.42 added support for PC-Lint 7.00g]
+** 970207 JLI [2.43 added support for strdup()]
+** 970209 JLI [2.44 changed default filename to lowercase]
+** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers]
+** 970723 JLI [2.46 added MW_ARI_NULLREAD flag]
+** 970813 JLI [2.47 stabilized marker handling]
+** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway]
+** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support]
+** 980417 JLI [2.51 more checks for invalid addresses]
+** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting]
+** 990112 JLI [2.53 added check for empty heap to mwIsOwned]
+** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML]
+** 990224 JLI [2.56 changed ordering of members in structures]
+** 990303 JLI [2.57 first maybe-fixit-for-hpux test]
+** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit]
+** 990517 JLI [2.59 fixed some high-sensitivity warnings]
+** 990610 JLI [2.60 fixed some more high-sensitivity warnings]
+** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names]
+** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()]
+** 991007 JLI [2.63 first shot at a 64-bit compatible version]
+** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const]
+** 000704 JLI [2.65 added some more detection for 64-bits]
+** 010502 JLI [2.66 incorporated some user fixes]
+** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)]
+** [added array destructor for C++ (thanks rdasilva@connecttel.com)]
+** [added mutex support (thanks rdasilva@connecttel.com)]
+** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined]
+** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked]
+** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen]
+** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)]
+** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)]
+*/
+
+#define __MEMWATCH_C 1
+
+#ifdef MW_NOCPP
+#define MEMWATCH_NOCPP
+#endif
+#ifdef MW_STDIO
+#define MEMWATCH_STDIO
+#endif
+
+/***********************************************************************
+** Include files
+***********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <time.h>
+#include <limits.h>
+#include "memwatch.h"
+
+#ifndef toupper
+#include <ctype.h>
+#endif
+
+#if defined(WIN32) || defined(__WIN32__)
+#define MW_HAVE_MUTEX 1
+#include <windows.h>
+#endif
+
+#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
+#define MW_HAVE_MUTEX 1
+#include <pthread.h>
+#endif
+
+/***********************************************************************
+** Defines & other weird stuff
+***********************************************************************/
+
+/*lint -save -e767 */
+#define VERSION "2.71" /* the current version number */
+#define CHKVAL(mw) (0xFE0180L^(long)mw->count^(long)mw->size^(long)mw->line)
+#define FLUSH() mwFlush()
+#define TESTS(f,l) if(mwTestAlways) (void)mwTestNow(f,l,1)
+#define PRECHK 0x01234567L
+#define POSTCHK 0x76543210L
+#define mwBUFFER_TO_MW(p) ( (mwData*) (void*) ( ((char*)p)-mwDataSize-mwOverflowZoneSize ) )
+/*lint -restore */
+
+#define MW_NML 0x0001
+
+#ifdef _MSC_VER
+#define COMMIT "c" /* Microsoft C requires the 'c' to perform as desired */
+#else
+#define COMMIT "" /* Normal ANSI */
+#endif /* _MSC_VER */
+
+#ifdef __cplusplus
+#define CPPTEXT "++"
+#else
+#define CPPTEXT ""
+#endif /* __cplusplus */
+
+#ifdef MEMWATCH_STDIO
+#define mwSTDERR stderr
+#else
+#define mwSTDERR mwLog
+#endif
+
+#ifdef MW_HAVE_MUTEX
+#define MW_MUTEX_INIT() mwMutexInit()
+#define MW_MUTEX_TERM() mwMutexTerm()
+#define MW_MUTEX_LOCK() mwMutexLock()
+#define MW_MUTEX_UNLOCK() mwMutexUnlock()
+#else
+#define MW_MUTEX_INIT()
+#define MW_MUTEX_TERM()
+#define MW_MUTEX_LOCK()
+#define MW_MUTEX_UNLOCK()
+#endif
+
+/***********************************************************************
+** If you really, really know what you're doing,
+** you can predefine these things yourself.
+***********************************************************************/
+
+#ifndef mwBYTE_DEFINED
+# if CHAR_BIT != 8
+# error need CHAR_BIT to be 8!
+# else
+typedef unsigned char mwBYTE;
+# define mwBYTE_DEFINED 1
+# endif
+#endif
+
+#if defined(ULONGLONG_MAX) || defined(ULLONG_MAX) || defined(_UI64_MAX) || defined(ULONG_LONG_MAX)
+# define mw64BIT 1
+# define mwROUNDALLOC_DEFAULT 8
+#else
+# if UINT_MAX <= 0xFFFFUL
+# define mw16BIT 1
+# define mwROUNDALLOC_DEFAULT 2
+# else
+# if ULONG_MAX > 0xFFFFFFFFUL
+# define mw64BIT 1
+# define mwROUNDALLOC_DEFAULT 8
+# else
+# define mw32BIT 1
+# define mwROUNDALLOC_DEFAULT 4
+# endif
+# endif
+#endif
+
+/* mwROUNDALLOC is the number of bytes to */
+/* round up to, to ensure that the end of */
+/* the buffer is suitable for storage of */
+/* any kind of object */
+#ifndef mwROUNDALLOC
+# define mwROUNDALLOC mwROUNDALLOC_DEFAULT
+#endif
+
+#ifndef mwDWORD_DEFINED
+#if ULONG_MAX == 0xFFFFFFFFUL
+typedef unsigned long mwDWORD;
+#define mwDWORD_DEFINED "unsigned long"
+#endif
+#endif
+
+#ifndef mwDWORD_DEFINED
+#if UINT_MAX == 0xFFFFFFFFUL
+typedef unsigned int mwDWORD;
+#define mwDWORD_DEFINED "unsigned int"
+#endif
+#endif
+
+#ifndef mwDWORD_DEFINED
+#if USHRT_MAX == 0xFFFFFFFFUL
+typedef unsigned short mwDWORD;
+#define mwDWORD_DEFINED "unsigned short"
+#endif
+#endif
+
+#ifndef mwBYTE_DEFINED
+#error "can't find out the correct type for a 8 bit scalar"
+#endif
+
+#ifndef mwDWORD_DEFINED
+#error "can't find out the correct type for a 32 bit scalar"
+#endif
+
+/***********************************************************************
+** Typedefs & structures
+***********************************************************************/
+
+/* main data holding area, precedes actual allocation */
+typedef struct mwData_ mwData;
+struct mwData_ {
+ mwData* prev; /* previous allocation in chain */
+ mwData* next; /* next allocation in chain */
+ const char* file; /* file name where allocated */
+ long count; /* action count */
+ long check; /* integrity check value */
+#if 0
+ long crc; /* data crc value */
+#endif
+ size_t size; /* size of allocation */
+ int line; /* line number where allocated */
+ unsigned flag; /* flag word */
+ };
+
+/* statistics structure */
+typedef struct mwStat_ mwStat;
+struct mwStat_ {
+ mwStat* next; /* next statistic buffer */
+ const char* file;
+ long total; /* total bytes allocated */
+ long num; /* total number of allocations */
+ long max; /* max allocated at one time */
+ long curr; /* current allocations */
+ int line;
+ };
+
+/* grabbing structure, 1K in size */
+typedef struct mwGrabData_ mwGrabData;
+struct mwGrabData_ {
+ mwGrabData* next;
+ int type;
+ char blob[ 1024 - sizeof(mwGrabData*) - sizeof(int) ];
+ };
+
+typedef struct mwMarker_ mwMarker;
+struct mwMarker_ {
+ void *host;
+ char *text;
+ mwMarker *next;
+ int level;
+ };
+
+#if defined(WIN32) || defined(__WIN32__)
+typedef HANDLE mwMutex;
+#endif
+
+#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
+typedef pthread_mutex_t mwMutex;
+#endif
+
+/***********************************************************************
+** Static variables
+***********************************************************************/
+
+static int mwInited = 0;
+static int mwInfoWritten = 0;
+static int mwUseAtexit = 0;
+static FILE* mwLog = NULL;
+static int mwFlushing = 0;
+static int mwStatLevel = MW_STAT_DEFAULT;
+static int mwNML = MW_NML_DEFAULT;
+static int mwFBI = 0;
+static long mwAllocLimit = 0L;
+static int mwUseLimit = 0;
+
+static long mwNumCurAlloc = 0L;
+static mwData* mwHead = NULL;
+static mwData* mwTail = NULL;
+static int mwDataSize = 0;
+static unsigned char mwOverflowZoneTemplate[] = "mEmwAtch";
+static int mwOverflowZoneSize = mwROUNDALLOC;
+
+static void (*mwOutFunction)(int) = NULL;
+static int (*mwAriFunction)(const char*) = NULL;
+static int mwAriAction = MW_ARI_ABORT;
+
+static char mwPrintBuf[MW_TRACE_BUFFER+8];
+
+static unsigned long mwCounter = 0L;
+static long mwErrors = 0L;
+
+static int mwTestFlags = 0;
+static int mwTestAlways = 0;
+
+static FILE* mwLogB1 = NULL;
+static int mwFlushingB1 = 0;
+
+static mwStat* mwStatList = NULL;
+static long mwStatTotAlloc = 0L;
+static long mwStatMaxAlloc = 0L;
+static long mwStatNumAlloc = 0L;
+static long mwStatCurAlloc = 0L;
+static long mwNmlNumAlloc = 0L;
+static long mwNmlCurAlloc = 0L;
+
+static mwGrabData* mwGrabList = NULL;
+static long mwGrabSize = 0L;
+
+static void * mwLastFree[MW_FREE_LIST];
+static const char *mwLFfile[MW_FREE_LIST];
+static int mwLFline[MW_FREE_LIST];
+static int mwLFcur = 0;
+
+static mwMarker* mwFirstMark = NULL;
+
+static FILE* mwLogB2 = NULL;
+static int mwFlushingB2 = 0;
+
+#ifdef MW_HAVE_MUTEX
+static mwMutex mwGlobalMutex;
+#endif
+
+/***********************************************************************
+** Static function declarations
+***********************************************************************/
+
+static void mwAutoInit( void );
+static FILE* mwLogR( void );
+static void mwLogW( FILE* );
+static int mwFlushR( void );
+static void mwFlushW( int );
+static void mwFlush( void );
+static void mwIncErr( void );
+static void mwUnlink( mwData*, const char* file, int line );
+static int mwRelink( mwData*, const char* file, int line );
+static int mwIsHeapOK( mwData *mw );
+static int mwIsOwned( mwData* mw, const char* file, int line );
+static int mwTestBuf( mwData* mw, const char* file, int line );
+static void mwDefaultOutFunc( int );
+static void mwWrite( const char* format, ... );
+static void mwLogFile( const char* name );
+static size_t mwFreeUp( size_t, int );
+static const void *mwTestMem( const void *, unsigned, int );
+static int mwStrCmpI( const char *s1, const char *s2 );
+static int mwTestNow( const char *file, int line, int always_invoked );
+static void mwDropAll( void );
+static const char *mwGrabType( int type );
+static unsigned mwGrab_( unsigned kb, int type, int silent );
+static unsigned mwDrop_( unsigned kb, int type, int silent );
+static int mwARI( const char* text );
+static void mwStatReport( void );
+static mwStat* mwStatGet( const char*, int, int );
+static void mwStatAlloc( size_t, const char*, int );
+static void mwStatFree( size_t, const char*, int );
+static int mwCheckOF( const void * p );
+static void mwWriteOF( void * p );
+static char mwDummy( char c );
+#ifdef MW_HAVE_MUTEX
+static void mwMutexInit( void );
+static void mwMutexTerm( void );
+static void mwMutexLock( void );
+static void mwMutexUnlock( void );
+#endif
+
+/***********************************************************************
+** System functions
+***********************************************************************/
+
+void mwInit( void ) {
+ time_t tid;
+
+ if( mwInited++ > 0 ) return;
+
+ MW_MUTEX_INIT();
+
+ /* start a log if none is running */
+ if( mwLogR() == NULL ) mwLogFile( "memwatch.log" );
+ if( mwLogR() == NULL ) {
+ int i;
+ char buf[32];
+ /* oops, could not open it! */
+ /* probably because it's already open */
+ /* so we try some other names */
+ for( i=1; i<100; i++ ) {
+ sprintf( buf, "memwat%02d.log", i );
+ mwLogFile( buf );
+ if( mwLogR() != NULL ) break;
+ }
+ }
+
+ /* initialize the statistics */
+ mwStatList = NULL;
+ mwStatTotAlloc = 0L;
+ mwStatCurAlloc = 0L;
+ mwStatMaxAlloc = 0L;
+ mwStatNumAlloc = 0L;
+ mwNmlCurAlloc = 0L;
+ mwNmlNumAlloc = 0L;
+
+ /* calculate the buffer size to use for a mwData */
+ mwDataSize = sizeof(mwData);
+ while( mwDataSize % mwROUNDALLOC ) mwDataSize ++;
+
+ /* write informational header if needed */
+ if( !mwInfoWritten ) {
+ mwInfoWritten = 1;
+ (void) time( &tid );
+ mwWrite(
+ "\n============="
+ " MEMWATCH " VERSION " Copyright (C) 1992-1999 Johan Lindh "
+ "=============\n");
+ mwWrite( "\nStarted at %s\n", ctime( &tid ) );
+
+/**************************************************************** Generic */
+ mwWrite( "Modes: " );
+#ifdef mwNew
+ mwWrite( "C++ " );
+#endif /* mwNew */
+#ifdef __STDC__
+ mwWrite( "__STDC__ " );
+#endif /* __STDC__ */
+#ifdef mw16BIT
+ mwWrite( "16-bit " );
+#endif
+#ifdef mw32BIT
+ mwWrite( "32-bit " );
+#endif
+#ifdef mw64BIT
+ mwWrite( "64-bit " );
+#endif
+ mwWrite( "mwDWORD==(" mwDWORD_DEFINED ")\n" );
+ mwWrite( "mwROUNDALLOC==%d sizeof(mwData)==%d mwDataSize==%d\n",
+ mwROUNDALLOC, sizeof(mwData), mwDataSize );
+/**************************************************************** Generic */
+
+/************************************************************ Microsoft C */
+#ifdef _MSC_VER
+ mwWrite( "Compiled using Microsoft C" CPPTEXT
+ " %d.%02d\n", _MSC_VER / 100, _MSC_VER % 100 );
+#endif /* _MSC_VER */
+/************************************************************ Microsoft C */
+
+/************************************************************** Borland C */
+#ifdef __BORLANDC__
+ mwWrite( "Compiled using Borland C"
+#ifdef __cplusplus
+ "++ %d.%01d\n", __BCPLUSPLUS__/0x100, (__BCPLUSPLUS__%0x100)/0x10 );
+#else
+ " %d.%01d\n", __BORLANDC__/0x100, (__BORLANDC__%0x100)/0x10 );
+#endif /* __cplusplus */
+#endif /* __BORLANDC__ */
+/************************************************************** Borland C */
+
+/************************************************************** Watcom C */
+#ifdef __WATCOMC__
+ mwWrite( "Compiled using Watcom C %d.%02d ",
+ __WATCOMC__/100, __WATCOMC__%100 );
+#ifdef __FLAT__
+ mwWrite( "(32-bit flat model)" );
+#endif /* __FLAT__ */
+ mwWrite( "\n" );
+#endif /* __WATCOMC__ */
+/************************************************************** Watcom C */
+
+ mwWrite( "\n" );
+ FLUSH();
+ }
+
+ if( mwUseAtexit ) (void) atexit( mwAbort );
+ return;
+ }
+
+void mwAbort( void ) {
+ mwData *mw;
+ mwMarker *mrk;
+ char *data;
+ time_t tid;
+ int c, i, j;
+ int errors;
+
+ tid = time( NULL );
+ mwWrite( "\nStopped at %s\n", ctime( &tid) );
+
+ if( !mwInited )
+ mwWrite( "internal: mwAbort(): MEMWATCH not initialized!\n" );
+
+ /* release the grab list */
+ mwDropAll();
+
+ /* report mwMarked items */
+ while( mwFirstMark ) {
+ mrk = mwFirstMark->next;
+ mwWrite( "mark: %p: %s\n", mwFirstMark->host, mwFirstMark->text );
+ free( mwFirstMark->text );
+ free( mwFirstMark );
+ mwFirstMark = mrk;
+ mwErrors ++;
+ }
+
+ /* release all still allocated memory */
+ errors = 0;
+ while( mwHead != NULL && errors < 3 ) {
+ if( !mwIsOwned(mwHead, __FILE__, __LINE__ ) ) {
+ if( errors < 3 )
+ {
+ errors ++;
+ mwWrite( "internal: NML/unfreed scan restarting\n" );
+ FLUSH();
+ mwHead = mwHead;
+ continue;
+ }
+ mwWrite( "internal: NML/unfreed scan aborted, heap too damaged\n" );
+ FLUSH();
+ break;
+ }
+ mwFlushW(0);
+ if( !(mwHead->flag & MW_NML) ) {
+ mwErrors++;
+ data = ((char*)mwHead)+mwDataSize;
+ mwWrite( "unfreed: <%ld> %s(%d), %ld bytes at %p ",
+ mwHead->count, mwHead->file, mwHead->line, (long)mwHead->size, data+mwOverflowZoneSize );
+ if( mwCheckOF( data ) ) {
+ mwWrite( "[underflowed] ");
+ FLUSH();
+ }
+ if( mwCheckOF( (data+mwOverflowZoneSize+mwHead->size) ) ) {
+ mwWrite( "[overflowed] ");
+ FLUSH();
+ }
+ mwWrite( " \t{" );
+ j = 16; if( mwHead->size < 16 ) j = (int) mwHead->size;
+ for( i=0;i<16;i++ ) {
+ if( i<j ) mwWrite( "%02X ",
+ (unsigned char) *(data+mwOverflowZoneSize+i) );
+ else mwWrite( ".. " );
+ }
+ for( i=0;i<j;i++ ) {
+ c = *(data+mwOverflowZoneSize+i);
+ if( c < 32 || c > 126 ) c = '.';
+ mwWrite( "%c", c );
+ }
+ mwWrite( "}\n" );
+ mw = mwHead;
+ mwUnlink( mw, __FILE__, __LINE__ );
+ free( mw );
+ }
+ else {
+ data = ((char*)mwHead) + mwDataSize + mwOverflowZoneSize;
+ if( mwTestMem( data, mwHead->size, MW_VAL_NML ) ) {
+ mwErrors++;
+ mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
+ mwHead->count, data + mwOverflowZoneSize, mwHead->file, mwHead->line );
+ FLUSH();
+ }
+ mwNmlNumAlloc --;
+ mwNmlCurAlloc -= mwHead->size;
+ mw = mwHead;
+ mwUnlink( mw, __FILE__, __LINE__ );
+ free( mw );
+ }
+ }
+
+ if( mwNmlNumAlloc ) mwWrite("internal: NoMansLand block counter %ld, not zero\n", mwNmlNumAlloc );
+ if( mwNmlCurAlloc ) mwWrite("internal: NoMansLand byte counter %ld, not zero\n", mwNmlCurAlloc );
+
+ /* report statistics */
+ mwStatReport();
+ FLUSH();
+
+ mwInited = 0;
+ mwHead = mwTail = NULL;
+ if( mwErrors )
+ fprintf(mwSTDERR,"MEMWATCH detected %ld anomalies\n",mwErrors);
+ mwLogFile( NULL );
+ mwErrors = 0;
+
+ MW_MUTEX_TERM();
+
+ }
+
+void mwTerm( void ) {
+ if( mwInited == 1 )
+ {
+ mwAbort();
+ return;
+ }
+ if( !mwInited )
+ mwWrite("internal: mwTerm(): MEMWATCH has not been started!\n");
+ else
+ mwInited --;
+ }
+
+void mwStatistics( int level )
+{
+ mwAutoInit();
+ if( level<0 ) level=0;
+ if( mwStatLevel != level )
+ {
+ mwWrite( "statistics: now collecting on a %s basis\n",
+ level<1?"global":(level<2?"module":"line") );
+ mwStatLevel = level;
+ }
+}
+
+void mwAutoCheck( int onoff ) {
+ mwAutoInit();
+ mwTestAlways = onoff;
+ if( onoff ) mwTestFlags = MW_TEST_ALL;
+ }
+
+void mwSetOutFunc( void (*func)(int) ) {
+ mwAutoInit();
+ mwOutFunction = func;
+ }
+
+static void mwWriteOF( void *p )
+{
+ int i;
+ unsigned char *ptr;
+ ptr = (unsigned char*) p;
+ for( i=0; i<mwOverflowZoneSize; i++ )
+ {
+ *(ptr+i) = mwOverflowZoneTemplate[i%8];
+ }
+ return;
+}
+
+static int mwCheckOF( const void *p )
+{
+ int i;
+ const unsigned char *ptr;
+ ptr = (const unsigned char *) p;
+ for( i=0; i<mwOverflowZoneSize; i++ )
+ {
+ if( *(ptr+i) != mwOverflowZoneTemplate[i%8] )
+ return 1; /* errors found */
+ }
+ return 0; /* no errors */
+}
+
+int mwTest( const char *file, int line, int items ) {
+ mwAutoInit();
+ mwTestFlags = items;
+ return mwTestNow( file, line, 0 );
+ }
+
+/*
+** Returns zero if there are no errors.
+** Returns nonzero if there are errors.
+*/
+int mwTestBuffer( const char *file, int line, void *p ) {
+ mwData* mw;
+
+ mwAutoInit();
+
+ /* do the quick ownership test */
+ mw = (mwData*) mwBUFFER_TO_MW( p );
+
+ if( mwIsOwned( mw, file, line ) ) {
+ return mwTestBuf( mw, file, line );
+ }
+ return 1;
+ }
+
+void mwBreakOut( const char* cause ) {
+ fprintf(mwSTDERR, "breakout: %s\n", cause);
+ mwWrite("breakout: %s\n", cause );
+ return;
+ }
+
+/*
+** 981217 JLI: is it possible that ->next is not always set?
+*/
+void * mwMark( void *p, const char *desc, const char *file, unsigned line ) {
+ mwMarker *mrk;
+ unsigned n, isnew;
+ char *buf;
+ int tot, oflow = 0;
+ char wherebuf[128];
+
+ mwAutoInit();
+ TESTS(NULL,0);
+
+ if( desc == NULL ) desc = "unknown";
+ if( file == NULL ) file = "unknown";
+
+ tot = sprintf( wherebuf, "%.48s called from %s(%d)", desc, file, line );
+ if( tot >= (int)sizeof(wherebuf) ) { wherebuf[sizeof(wherebuf)-1] = 0; oflow = 1; }
+
+ if( p == NULL ) {
+ mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc );
+ return p;
+ }
+
+ if( mwFirstMark != NULL && !mwIsReadAddr( mwFirstMark, sizeof( mwMarker ) ) )
+ {
+ mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n",
+ file, line, mwFirstMark, desc );
+ return p;
+ }
+
+ for( mrk=mwFirstMark; mrk; mrk=mrk->next )
+ {
+ if( mrk->next != NULL && !mwIsReadAddr( mrk->next, sizeof( mwMarker ) ) )
+ {
+ mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n",
+ file, line, mrk, mrk->next, desc );
+ return p;
+ }
+ if( mrk->host == p ) break;
+ }
+
+ if( mrk == NULL ) {
+ isnew = 1;
+ mrk = (mwMarker*) malloc( sizeof( mwMarker ) );
+ if( mrk == NULL ) {
+ mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
+ return p;
+ }
+ mrk->next = NULL;
+ n = 0;
+ }
+ else {
+ isnew = 0;
+ n = strlen( mrk->text );
+ }
+
+ n += strlen( wherebuf );
+ buf = (char*) malloc( n+3 );
+ if( buf == NULL ) {
+ if( isnew ) free( mrk );
+ mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
+ return p;
+ }
+
+ if( isnew ) {
+ memcpy( buf, wherebuf, n+1 );
+ mrk->next = mwFirstMark;
+ mrk->host = p;
+ mrk->text = buf;
+ mrk->level = 1;
+ mwFirstMark = mrk;
+ }
+ else {
+ strcpy( buf, mrk->text );
+ strcat( buf, ", " );
+ strcat( buf, wherebuf );
+ free( mrk->text );
+ mrk->text = buf;
+ mrk->level ++;
+ }
+
+ if( oflow ) {
+ mwIncErr();
+ mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
+ }
+ return p;
+ }
+
+void* mwUnmark( void *p, const char *file, unsigned line ) {
+ mwMarker *mrk, *prv;
+ mrk = mwFirstMark;
+ prv = NULL;
+ while( mrk ) {
+ if( mrk->host == p ) {
+ if( mrk->level < 2 ) {
+ if( prv ) prv->next = mrk->next;
+ else mwFirstMark = mrk->next;
+ free( mrk->text );
+ free( mrk );
+ return p;
+ }
+ mrk->level --;
+ return p;
+ }
+ prv = mrk;
+ mrk = mrk->next;
+ }
+ mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p );
+ return p;
+ }
+
+
+/***********************************************************************
+** Abort/Retry/Ignore handlers
+***********************************************************************/
+
+static int mwARI( const char *estr ) {
+ char inbuf[81];
+ int c;
+ fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr);
+ (void) fgets(inbuf,sizeof(inbuf),stdin);
+ for( c=0; inbuf[c] && inbuf[c] <= ' '; c++ ) ;
+ c = inbuf[c];
+ if( c == 'R' || c == 'r' ) {
+ mwBreakOut( estr );
+ return MW_ARI_RETRY;
+ }
+ if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE;
+ return MW_ARI_ABORT;
+ }
+
+/* standard ARI handler (exported) */
+int mwAriHandler( const char *estr ) {
+ mwAutoInit();
+ return mwARI( estr );
+ }
+
+/* used to set the ARI function */
+void mwSetAriFunc( int (*func)(const char *) ) {
+ mwAutoInit();
+ mwAriFunction = func;
+ }
+
+/***********************************************************************
+** Allocation handlers
+***********************************************************************/
+
+void* mwMalloc( size_t size, const char* file, int line) {
+ size_t needed;
+ mwData *mw;
+ char *ptr;
+ void *p;
+
+ mwAutoInit();
+
+ MW_MUTEX_LOCK();
+
+ TESTS(file,line);
+
+ mwCounter ++;
+ needed = mwDataSize + mwOverflowZoneSize*2 + size;
+ if( needed < size )
+ {
+ /* theoretical case: req size + mw overhead exceeded size_t limits */
+ return NULL;
+ }
+
+ /* if this allocation would violate the limit, fail it */
+ if( mwUseLimit && ((long)size + mwStatCurAlloc > mwAllocLimit) ) {
+ mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
+ mwCounter, file, line, (long)size, mwAllocLimit - mwStatCurAlloc );
+ mwIncErr();
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+
+ mw = (mwData*) malloc( needed );
+ if( mw == NULL ) {
+ if( mwFreeUp(needed,0) >= needed ) {
+ mw = (mwData*) malloc(needed);
+ if( mw == NULL ) {
+ mwWrite( "internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed );
+ mwIncErr();
+ FLUSH();
+ }
+ }
+ if( mw == NULL ) {
+ mwWrite( "fail: <%ld> %s(%d), %ld wanted %ld allocated\n",
+ mwCounter, file, line, (long)size, mwStatCurAlloc );
+ mwIncErr();
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+ }
+
+ mw->count = mwCounter;
+ mw->prev = NULL;
+ mw->next = mwHead;
+ mw->file = file;
+ mw->size = size;
+ mw->line = line;
+ mw->flag = 0;
+ mw->check = CHKVAL(mw);
+
+ if( mwHead ) mwHead->prev = mw;
+ mwHead = mw;
+ if( mwTail == NULL ) mwTail = mw;
+
+ ptr = ((char*)mw) + mwDataSize;
+ mwWriteOF( ptr ); /* '*(long*)ptr = PRECHK;' */
+ ptr += mwOverflowZoneSize;
+ p = ptr;
+ memset( ptr, MW_VAL_NEW, size );
+ ptr += size;
+ mwWriteOF( ptr ); /* '*(long*)ptr = POSTCHK;' */
+
+ mwNumCurAlloc ++;
+ mwStatCurAlloc += (long) size;
+ mwStatTotAlloc += (long) size;
+ if( mwStatCurAlloc > mwStatMaxAlloc )
+ mwStatMaxAlloc = mwStatCurAlloc;
+ mwStatNumAlloc ++;
+
+ if( mwStatLevel ) mwStatAlloc( size, file, line );
+
+ MW_MUTEX_UNLOCK();
+ return p;
+ }
+
+void* mwRealloc( void *p, size_t size, const char* file, int line) {
+ int oldUseLimit, i;
+ mwData *mw;
+ char *ptr;
+
+ mwAutoInit();
+
+ if( p == NULL ) return mwMalloc( size, file, line );
+ if( size == 0 ) { mwFree( p, file, line ); return NULL; }
+
+ MW_MUTEX_LOCK();
+
+ /* do the quick ownership test */
+ mw = (mwData*) mwBUFFER_TO_MW( p );
+ if( mwIsOwned( mw, file, line ) ) {
+
+ /* if the buffer is an NML, treat this as a double-free */
+ if( mw->flag & MW_NML )
+ {
+ mwIncErr();
+ if( *((unsigned char*)(mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
+ {
+ mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
+ mwCounter, file, line, mw );
+ }
+ goto check_dbl_free;
+ }
+
+ /* if this allocation would violate the limit, fail it */
+ if( mwUseLimit && ((long)size + mwStatCurAlloc - (long)mw->size > mwAllocLimit) ) {
+ TESTS(file,line);
+ mwCounter ++;
+ mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
+ mwCounter, file, line, (unsigned long)size - mw->size, mwAllocLimit - mwStatCurAlloc );
+ mwIncErr();
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+
+ /* fake realloc operation */
+ oldUseLimit = mwUseLimit;
+ mwUseLimit = 0;
+ ptr = (char*) mwMalloc( size, file, line );
+ if( ptr != NULL ) {
+ if( size < mw->size )
+ memcpy( ptr, p, size );
+ else
+ memcpy( ptr, p, mw->size );
+ mwFree( p, file, line );
+ }
+ mwUseLimit = oldUseLimit;
+ MW_MUTEX_UNLOCK();
+ return (void*) ptr;
+ }
+
+ /* Unknown pointer! */
+
+ /* using free'd pointer? */
+check_dbl_free:
+ for(i=0;i<MW_FREE_LIST;i++) {
+ if( mwLastFree[i] == p ) {
+ mwIncErr();
+ mwWrite( "realloc: <%ld> %s(%d), %p was"
+ " freed from %s(%d)\n",
+ mwCounter, file, line, p,
+ mwLFfile[i], mwLFline[i] );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+ }
+
+ /* some weird pointer */
+ mwIncErr();
+ mwWrite( "realloc: <%ld> %s(%d), unknown pointer %p\n",
+ mwCounter, file, line, p );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+
+char *mwStrdup( const char* str, const char* file, int line ) {
+ size_t len;
+ char *newstring;
+
+ MW_MUTEX_LOCK();
+
+ if( str == NULL ) {
+ mwIncErr();
+ mwWrite( "strdup: <%ld> %s(%d), strdup(NULL) called\n",
+ mwCounter, file, line );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return NULL;
+ }
+
+ len = strlen( str ) + 1;
+ newstring = (char*) mwMalloc( len, file, line );
+ if( newstring != NULL ) memcpy( newstring, str, len );
+ MW_MUTEX_UNLOCK();
+ return newstring;
+ }
+
+void mwFree( void* p, const char* file, int line ) {
+ int i;
+ mwData* mw;
+ char buffer[ sizeof(mwData) + (mwROUNDALLOC*3) + 64 ];
+
+ /* this code is in support of C++ delete */
+ if( file == NULL ) {
+ mwFree_( p );
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+ mwAutoInit();
+
+ MW_MUTEX_LOCK();
+ TESTS(file,line);
+ mwCounter ++;
+
+ /* on NULL free, write a warning and return */
+ if( p == NULL ) {
+ mwWrite( "NULL free: <%ld> %s(%d), NULL pointer free'd\n",
+ mwCounter, file, line );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+ /* do the quick ownership test */
+ mw = (mwData*) mwBUFFER_TO_MW( p );
+
+ if( mwIsOwned( mw, file, line ) ) {
+ (void) mwTestBuf( mw, file, line );
+
+ /* if the buffer is an NML, treat this as a double-free */
+ if( mw->flag & MW_NML )
+ {
+ if( *(((unsigned char*)mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
+ {
+ mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
+ mwCounter, file, line, mw );
+ }
+ goto check_dbl_free;
+ }
+
+ /* update the statistics */
+ mwNumCurAlloc --;
+ mwStatCurAlloc -= (long) mw->size;
+ if( mwStatLevel ) mwStatFree( mw->size, mw->file, mw->line );
+
+ /* we should either free the allocation or keep it as NML */
+ if( mwNML ) {
+ mw->flag |= MW_NML;
+ mwNmlNumAlloc ++;
+ mwNmlCurAlloc += (long) mw->size;
+ memset( ((char*)mw)+mwDataSize+mwOverflowZoneSize, MW_VAL_NML, mw->size );
+ }
+ else {
+ /* unlink the allocation, and enter the post-free data */
+ mwUnlink( mw, file, line );
+ memset( mw, MW_VAL_DEL,
+ mw->size + mwDataSize+mwOverflowZoneSize+mwOverflowZoneSize );
+ if( mwFBI ) {
+ memset( mw, '.', mwDataSize + mwOverflowZoneSize );
+ sprintf( buffer, "FBI<%ld>%s(%d)", mwCounter, file, line );
+ strncpy( (char*)(void*)mw, buffer, mwDataSize + mwOverflowZoneSize );
+ }
+ free( mw );
+ }
+
+ /* add the pointer to the last-free track */
+ mwLFfile[ mwLFcur ] = file;
+ mwLFline[ mwLFcur ] = line;
+ mwLastFree[ mwLFcur++ ] = p;
+ if( mwLFcur == MW_FREE_LIST ) mwLFcur = 0;
+
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+ /* check for double-freeing */
+check_dbl_free:
+ for(i=0;i<MW_FREE_LIST;i++) {
+ if( mwLastFree[i] == p ) {
+ mwIncErr();
+ mwWrite( "double-free: <%ld> %s(%d), %p was"
+ " freed from %s(%d)\n",
+ mwCounter, file, line, p,
+ mwLFfile[i], mwLFline[i] );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+ }
+
+ /* some weird pointer... block the free */
+ mwIncErr();
+ mwWrite( "WILD free: <%ld> %s(%d), unknown pointer %p\n",
+ mwCounter, file, line, p );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+void* mwCalloc( size_t a, size_t b, const char *file, int line ) {
+ void *p;
+ size_t size = a * b;
+ p = mwMalloc( size, file, line );
+ if( p == NULL ) return NULL;
+ memset( p, 0, size );
+ return p;
+ }
+
+void mwFree_( void *p ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ MW_MUTEX_UNLOCK();
+ free(p);
+ }
+
+void* mwMalloc_( size_t size ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ MW_MUTEX_UNLOCK();
+ return malloc( size );
+ }
+
+void* mwRealloc_( void *p, size_t size ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ MW_MUTEX_UNLOCK();
+ return realloc( p, size );
+ }
+
+void* mwCalloc_( size_t a, size_t b ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ MW_MUTEX_UNLOCK();
+ return calloc( a, b );
+ }
+
+void mwFlushNow( void ) {
+ if( mwLogR() ) fflush( mwLogR() );
+ return;
+ }
+
+void mwDoFlush( int onoff ) {
+ mwFlushW( onoff<1?0:onoff );
+ if( onoff ) if( mwLogR() ) fflush( mwLogR() );
+ return;
+ }
+
+void mwLimit( long lim ) {
+ TESTS(NULL,0);
+ mwWrite("limit: old limit = ");
+ if( !mwAllocLimit ) mwWrite( "none" );
+ else mwWrite( "%ld bytes", mwAllocLimit );
+ mwWrite( ", new limit = ");
+ if( !lim ) {
+ mwWrite( "none\n" );
+ mwUseLimit = 0;
+ }
+ else {
+ mwWrite( "%ld bytes\n", lim );
+ mwUseLimit = 1;
+ }
+ mwAllocLimit = lim;
+ FLUSH();
+ }
+
+void mwSetAriAction( int action ) {
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ mwAriAction = action;
+ MW_MUTEX_UNLOCK();
+ return;
+ }
+
+int mwAssert( int exp, const char *exps, const char *fn, int ln ) {
+ int i;
+ char buffer[MW_TRACE_BUFFER+8];
+ if( exp ) {
+ return 0;
+ }
+ mwAutoInit();
+ MW_MUTEX_LOCK();
+ TESTS(fn,ln);
+ mwIncErr();
+ mwCounter++;
+ mwWrite( "assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
+ if( mwAriFunction != NULL ) {
+ sprintf( buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps );
+ i = (*mwAriFunction)(buffer);
+ switch( i ) {
+ case MW_ARI_IGNORE:
+ mwWrite( "assert trap: <%ld> IGNORED - execution continues\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 0;
+ case MW_ARI_RETRY:
+ mwWrite( "assert trap: <%ld> RETRY - executing again\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 1;
+ }
+ }
+ else {
+ if( mwAriAction & MW_ARI_IGNORE ) {
+ mwWrite( "assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 0;
+ }
+ fprintf(mwSTDERR,"\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps );
+ }
+
+ FLUSH();
+ (void) mwTestNow( fn, ln, 1 );
+ FLUSH();
+
+ if( mwAriAction & MW_ARI_NULLREAD ) {
+ /* This is made in an attempt to kick in */
+ /* any debuggers or OS stack traces */
+ FLUSH();
+ /*lint -save -e413 */
+ i = *((int*)NULL);
+ mwDummy( (char)i );
+ /*lint -restore */
+ }
+
+ MW_MUTEX_UNLOCK();
+ exit(255);
+ /* NOT REACHED - the return statement is in to keep */
+ /* stupid compilers from squeaking about differing return modes. */
+ /* Smart compilers instead say 'code unreachable...' */
+ /*lint -save -e527 */
+ return 0;
+ /*lint -restore */
+ }
+
+int mwVerify( int exp, const char *exps, const char *fn, int ln ) {
+ int i;
+ char buffer[MW_TRACE_BUFFER+8];
+ if( exp ) {
+ return 0;
+ }
+ mwAutoInit();
+ MW_MUTEX_LOCK();
+ TESTS(fn,ln);
+ mwIncErr();
+ mwCounter++;
+ mwWrite( "verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
+ if( mwAriFunction != NULL ) {
+ sprintf( buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps );
+ i = (*mwAriFunction)(buffer);
+ if( i == 0 ) {
+ mwWrite( "verify trap: <%ld> IGNORED - execution continues\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 0;
+ }
+ if( i == 1 ) {
+ mwWrite( "verify trap: <%ld> RETRY - executing again\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 1;
+ }
+ }
+ else {
+ if( mwAriAction & MW_ARI_NULLREAD ) {
+ /* This is made in an attempt to kick in */
+ /* any debuggers or OS stack traces */
+ FLUSH();
+ /*lint -save -e413 */
+ i = *((int*)NULL);
+ mwDummy( (char)i );
+ /*lint -restore */
+ }
+ if( mwAriAction & MW_ARI_IGNORE ) {
+ mwWrite( "verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
+ MW_MUTEX_UNLOCK();
+ return 0;
+ }
+ fprintf(mwSTDERR,"\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps );
+ }
+ FLUSH();
+ (void) mwTestNow( fn, ln, 1 );
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ exit(255);
+ /* NOT REACHED - the return statement is in to keep */
+ /* stupid compilers from squeaking about differing return modes. */
+ /* Smart compilers instead say 'code unreachable...' */
+ /*lint -save -e527 */
+ return 0;
+ /*lint -restore */
+ }
+
+void mwTrace( const char *format, ... ) {
+ int tot, oflow = 0;
+ va_list mark;
+
+ mwAutoInit();
+ MW_MUTEX_LOCK();
+ TESTS(NULL,0);
+ if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc;
+
+ va_start( mark, format );
+ tot = vsprintf( mwPrintBuf, format, mark );
+ va_end( mark );
+ if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; }
+ for(tot=0;mwPrintBuf[tot];tot++)
+ (*mwOutFunction)( mwPrintBuf[tot] );
+ if( oflow ) {
+ mwIncErr();
+ mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
+ }
+
+ FLUSH();
+ MW_MUTEX_UNLOCK();
+ }
+
+
+/***********************************************************************
+** Grab & Drop
+***********************************************************************/
+
+unsigned mwGrab( unsigned kb ) {
+ TESTS(NULL,0);
+ return mwGrab_( kb, MW_VAL_GRB, 0 );
+ }
+
+unsigned mwDrop( unsigned kb ) {
+ TESTS(NULL,0);
+ return mwDrop_( kb, MW_VAL_GRB, 0 );
+ }
+
+static void mwDropAll() {
+ TESTS(NULL,0);
+ (void) mwDrop_( 0, MW_VAL_GRB, 0 );
+ (void) mwDrop_( 0, MW_VAL_NML, 0 );
+ if( mwGrabList != NULL )
+ mwWrite( "internal: the grab list is not empty after mwDropAll()\n");
+ }
+
+static const char *mwGrabType( int type ) {
+ switch( type ) {
+ case MW_VAL_GRB:
+ return "grabbed";
+ case MW_VAL_NML:
+ return "no-mans-land";
+ default:
+ /* do nothing */
+ ;
+ }
+ return "<unknown type>";
+ }
+
+static unsigned mwGrab_( unsigned kb, int type, int silent ) {
+ unsigned i = kb;
+ mwGrabData *gd;
+ if( !kb ) i = kb = 65000U;
+
+ for(;kb;kb--) {
+ if( mwUseLimit &&
+ (mwStatCurAlloc + mwGrabSize + (long)sizeof(mwGrabData) > mwAllocLimit) ) {
+ if( !silent ) {
+ mwWrite("grabbed: all allowed memory to %s (%u kb)\n",
+ mwGrabType(type), i-kb);
+ FLUSH();
+ }
+ return i-kb;
+ }
+ gd = (mwGrabData*) malloc( sizeof(mwGrabData) );
+ if( gd == NULL ) {
+ if( !silent ) {
+ mwWrite("grabbed: all available memory to %s (%u kb)\n",
+ mwGrabType(type), i-kb);
+ FLUSH();
+ }
+ return i-kb;
+ }
+ mwGrabSize += (long) sizeof(mwGrabData);
+ gd->next = mwGrabList;
+ memset( gd->blob, type, sizeof(gd->blob) );
+ gd->type = type;
+ mwGrabList = gd;
+ }
+ if( !silent ) {
+ mwWrite("grabbed: %u kilobytes of %s memory\n", i, mwGrabType(type) );
+ FLUSH();
+ }
+ return i;
+ }
+
+static unsigned mwDrop_( unsigned kb, int type, int silent ) {
+ unsigned i = kb;
+ mwGrabData *gd,*tmp,*pr;
+ const void *p;
+
+ if( mwGrabList == NULL && kb == 0 ) return 0;
+ if( !kb ) i = kb = 60000U;
+
+ pr = NULL;
+ gd = mwGrabList;
+ for(;kb;) {
+ if( gd == NULL ) {
+ if( i-kb > 0 && !silent ) {
+ mwWrite("dropped: all %s memory (%u kb)\n", mwGrabType(type), i-kb);
+ FLUSH();
+ }
+ return i-kb;
+ }
+ if( gd->type == type ) {
+ if( pr ) pr->next = gd->next;
+ kb --;
+ tmp = gd;
+ if( mwGrabList == gd ) mwGrabList = gd->next;
+ gd = gd->next;
+ p = mwTestMem( tmp->blob, sizeof( tmp->blob ), type );
+ if( p != NULL ) {
+ mwWrite( "wild pointer: <%ld> %s memory hit at %p\n",
+ mwCounter, mwGrabType(type), p );
+ FLUSH();
+ }
+ mwGrabSize -= (long) sizeof(mwGrabData);
+ free( tmp );
+ }
+ else {
+ pr = gd;
+ gd = gd->next;
+ }
+ }
+ if( !silent ) {
+ mwWrite("dropped: %u kilobytes of %s memory\n", i, mwGrabType(type) );
+ FLUSH();
+ }
+ return i;
+ }
+
+/***********************************************************************
+** No-Mans-Land
+***********************************************************************/
+
+void mwNoMansLand( int level ) {
+ mwAutoInit();
+ TESTS(NULL,0);
+ switch( level ) {
+ case MW_NML_NONE:
+ (void) mwDrop_( 0, MW_VAL_NML, 0 );
+ break;
+ case MW_NML_FREE:
+ break;
+ case MW_NML_ALL:
+ (void) mwGrab_( 0, MW_VAL_NML, 0 );
+ break;
+ default:
+ return;
+ }
+ mwNML = level;
+ }
+
+/***********************************************************************
+** Static functions
+***********************************************************************/
+
+static void mwAutoInit( void )
+{
+ if( mwInited ) return;
+ mwUseAtexit = 1;
+ mwInit();
+ return;
+}
+
+static FILE *mwLogR() {
+ if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) return mwLog;
+ if( mwLog == mwLogB1 ) mwLogB2 = mwLog;
+ if( mwLog == mwLogB2 ) mwLogB1 = mwLog;
+ if( mwLogB1 == mwLogB2 ) mwLog = mwLogB1;
+ if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) {
+ mwWrite("internal: log file handle damaged and recovered\n");
+ FLUSH();
+ return mwLog;
+ }
+ fprintf(mwSTDERR,"\nMEMWATCH: log file handle destroyed, using mwSTDERR\n" );
+ mwLog = mwLogB1 = mwLogB2 = mwSTDERR;
+ return mwSTDERR;
+ }
+
+static void mwLogW( FILE *p ) {
+ mwLog = mwLogB1 = mwLogB2 = p;
+ }
+
+static int mwFlushR() {
+ if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) return mwFlushing;
+ if( mwFlushing == mwFlushingB1 ) mwFlushingB2 = mwFlushing;
+ if( mwFlushing == mwFlushingB2 ) mwFlushingB1 = mwFlushing;
+ if( mwFlushingB1 == mwFlushingB2 ) mwFlushing = mwFlushingB1;
+ if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) {
+ mwWrite("internal: flushing flag damaged and recovered\n");
+ FLUSH();
+ return mwFlushing;
+ }
+ mwWrite("internal: flushing flag destroyed, so set to true\n");
+ mwFlushing = mwFlushingB1 = mwFlushingB2 = 1;
+ return 1;
+ }
+
+static void mwFlushW( int n ) {
+ mwFlushing = mwFlushingB1 = mwFlushingB2 = n;
+ }
+
+static void mwIncErr() {
+ mwErrors++;
+ mwFlushW( mwFlushR()+1 );
+ FLUSH();
+ }
+
+static void mwFlush() {
+ if( mwLogR() == NULL ) return;
+#ifdef MW_FLUSH
+ fflush( mwLogR() );
+#else
+ if( mwFlushR() ) fflush( mwLogR() );
+#endif
+ return;
+ }
+
+static void mwUnlink( mwData* mw, const char* file, int line ) {
+ if( mw->prev == NULL ) {
+ if( mwHead != mw )
+ mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 NULL, but not head\n",
+ mwCounter, file, line, mw );
+ mwHead = mw->next;
+ }
+ else {
+ if( mw->prev->next != mw )
+ mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 failure\n",
+ mwCounter, file, line, mw );
+ else mw->prev->next = mw->next;
+ }
+ if( mw->next == NULL ) {
+ if( mwTail != mw )
+ mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 NULL, but not tail\n",
+ mwCounter, file, line, mw );
+ mwTail = mw->prev;
+ }
+ else {
+ if( mw->next->prev != mw )
+ mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 failure\n",
+ mwCounter, file, line, mw );
+ else mw->next->prev = mw->prev;
+ }
+ }
+
+/*
+** Relinking tries to repair a damaged mw block.
+** Returns nonzero if it thinks it successfully
+** repaired the heap chain.
+*/
+static int mwRelink( mwData* mw, const char* file, int line ) {
+ int fails;
+ mwData *mw1, *mw2;
+ long count, size;
+ mwStat *ms;
+
+ if( file == NULL ) file = "unknown";
+
+ if( mw == NULL ) {
+ mwWrite("relink: cannot repair MW at NULL\n");
+ FLUSH();
+ goto emergency;
+ }
+
+ if( !mwIsSafeAddr(mw, mwDataSize) ) {
+ mwWrite("relink: MW-%p is a garbage pointer\n", mw);
+ FLUSH();
+ goto emergency;
+ }
+
+ mwWrite("relink: <%ld> %s(%d) attempting to repair MW-%p...\n", mwCounter, file, line, mw );
+ FLUSH();
+ fails = 0;
+
+ /* Repair from head */
+ if( mwHead != mw ) {
+ if( !mwIsSafeAddr( mwHead, mwDataSize ) ) {
+ mwWrite("relink: failed for MW-%p; head pointer destroyed\n", mw );
+ FLUSH();
+ goto emergency;
+ }
+ for( mw1=mwHead; mw1; mw1=mw1->next ) {
+ if( mw1->next == mw ) {
+ mw->prev = mw1;
+ break;
+ }
+ if( mw1->next &&
+ ( !mwIsSafeAddr(mw1->next, mwDataSize ) || mw1->next->prev != mw1) ) {
+ mwWrite("relink: failed for MW-%p; forward chain fragmented at MW-%p: 'next' is %p\n", mw, mw1, mw1->next );
+ FLUSH();
+ goto emergency;
+ }
+ }
+ if( mw1 == NULL ) {
+ mwWrite("relink: MW-%p not found in forward chain search\n", mw );
+ FLUSH();
+ fails ++;
+ }
+ }
+ else
+ {
+ mwWrite( "relink: MW-%p is the head (first) allocation\n", mw );
+ if( mw->prev != NULL )
+ {
+ mwWrite( "relink: MW-%p prev pointer is non-NULL, you have a wild pointer\n", mw );
+ mw->prev = NULL;
+ }
+ }
+
+ /* Repair from tail */
+ if( mwTail != mw ) {
+ if( !mwIsSafeAddr( mwTail, mwDataSize ) ) {
+ mwWrite("relink: failed for MW-%p; tail pointer destroyed\n", mw );
+ FLUSH();
+ goto emergency;
+ }
+ for( mw1=mwTail; mw1; mw1=mw1->prev ) {
+ if( mw1->prev == mw ) {
+ mw->next = mw1;
+ break;
+ }
+ if( mw1->prev && (!mwIsSafeAddr(mw1->prev, mwDataSize ) || mw1->prev->next != mw1) ) {
+ mwWrite("relink: failed for MW-%p; reverse chain fragmented at MW-%p, 'prev' is %p\n", mw, mw1, mw1->prev );
+ FLUSH();
+ goto emergency;
+ }
+ }
+ if( mw1 == NULL ) {
+ mwWrite("relink: MW-%p not found in reverse chain search\n", mw );
+ FLUSH();
+ fails ++;
+ }
+ }
+ else
+ {
+ mwWrite( "relink: MW-%p is the tail (last) allocation\n", mw );
+ if( mw->next != NULL )
+ {
+ mwWrite( "relink: MW-%p next pointer is non-NULL, you have a wild pointer\n", mw );
+ mw->next = NULL;
+ }
+ }
+
+ if( fails > 1 ) {
+ mwWrite("relink: heap appears intact, MW-%p probably garbage pointer\n", mw );
+ FLUSH();
+ goto verifyok;
+ }
+
+ /* restore MW info where possible */
+ if( mwIsReadAddr( mw->file, 1 ) ) {
+ ms = mwStatGet( mw->file, -1, 0 );
+ if( ms == NULL ) mw->file = "<relinked>";
+ }
+ mw->check = CHKVAL(mw);
+ goto verifyok;
+
+ /* Emergency repair */
+ emergency:
+
+ if( mwHead == NULL && mwTail == NULL )
+ {
+ if( mwStatCurAlloc == 0 )
+ mwWrite("relink: <%ld> %s(%d) heap is empty, nothing to repair\n", mwCounter, file, line );
+ else
+ mwWrite("relink: <%ld> %s(%d) heap damaged beyond repair\n", mwCounter, file, line );
+ FLUSH();
+ return 0;
+ }
+
+ mwWrite("relink: <%ld> %s(%d) attempting emergency repairs...\n", mwCounter, file, line );
+ FLUSH();
+
+ if( mwHead == NULL || mwTail == NULL )
+ {
+ if( mwHead == NULL ) mwWrite("relink: mwHead is NULL, but mwTail is %p\n", mwTail );
+ else mwWrite("relink: mwTail is NULL, but mwHead is %p\n", mwHead );
+ }
+
+ mw1=NULL;
+ if( mwHead != NULL )
+ {
+ if( !mwIsReadAddr( mwHead, mwDataSize ) || mwHead->check != CHKVAL(mwHead) )
+ {
+ mwWrite("relink: mwHead (MW-%p) is damaged, skipping forward scan\n", mwHead );
+ mwHead = NULL;
+ goto scan_reverse;
+ }
+ if( mwHead->prev != NULL )
+ {
+ mwWrite("relink: the mwHead pointer's 'prev' member is %p, not NULL\n", mwHead->prev );
+ }
+ for( mw1=mwHead; mw1; mw1=mw1->next )
+ {
+ if( mw1->next )
+ {
+ if( !mwIsReadAddr(mw1->next,mwDataSize) ||
+ !mw1->next->check != CHKVAL(mw1) ||
+ mw1->next->prev != mw1 )
+ {
+ mwWrite("relink: forward chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
+ mw1, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw1->file, mw1->line );
+ if( mwIsReadAddr(mw1->next,mwDataSize ) )
+ {
+ mwWrite("relink: forward chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
+ mw1->next, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"",
+ mwIsReadAddr(mw1->file,16)?mw1->file:"<garbage-pointer>", mw1->line );
+ }
+ else
+ {
+ mwWrite("relink: the 'next' pointer of this MW points to %p, which is out-of-legal-access\n",
+ mw1->next );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+
+scan_reverse:
+ mw2=NULL;
+ if( mwTail != NULL )
+ {
+ if( !mwIsReadAddr(mwTail,mwDataSize) || mwTail->check != CHKVAL(mwTail) )
+ {
+ mwWrite("relink: mwTail (%p) is damaged, skipping reverse scan\n", mwTail );
+ mwTail = NULL;
+ goto analyze;
+ }
+ if( mwTail->next != NULL )
+ {
+ mwWrite("relink: the mwTail pointer's 'next' member is %p, not NULL\n", mwTail->next );
+ }
+ for( mw2=mwTail; mw2; mw2=mw2->prev )
+ {
+ if( mw2->prev )
+ {
+ if( !mwIsReadAddr(mw2->prev,mwDataSize) ||
+ !mw2->prev->check != CHKVAL(mw2) ||
+ mw2->prev->next != mw2 )
+ {
+ mwWrite("relink: reverse chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
+ mw2, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw2->file, mw2->line );
+ if( mwIsReadAddr(mw2->prev,mwDataSize ) )
+ {
+ mwWrite("relink: reverse chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
+ mw2->prev, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"",
+ mwIsReadAddr(mw2->file,16)?mw2->file:"<garbage-pointer>", mw2->line );
+ }
+ else
+ {
+ mwWrite("relink: the 'prev' pointer of this MW points to %p, which is out-of-legal-access\n",
+ mw2->prev );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+analyze:
+ if( mwHead == NULL && mwTail == NULL )
+ {
+ mwWrite("relink: both head and tail pointers damaged, aborting program\n");
+ mwFlushW(1);
+ FLUSH();
+ abort();
+ }
+ if( mwHead == NULL )
+ {
+ mwHead = mw2;
+ mwWrite("relink: heap truncated, MW-%p designated as new mwHead\n", mw2 );
+ mw2->prev = NULL;
+ mw1 = mw2 = NULL;
+ }
+ if( mwTail == NULL )
+ {
+ mwTail = mw1;
+ mwWrite("relink: heap truncated, MW-%p designated as new mwTail\n", mw1 );
+ mw1->next = NULL;
+ mw1 = mw2 = NULL;
+ }
+ if( mw1 == NULL && mw2 == NULL &&
+ mwHead->prev == NULL && mwTail->next == NULL ) {
+ mwWrite("relink: verifying heap integrity...\n" );
+ FLUSH();
+ goto verifyok;
+ }
+ if( mw1 && mw2 && mw1 != mw2 ) {
+ mw1->next = mw2;
+ mw2->prev = mw1;
+ mwWrite("relink: emergency repairs successful, assessing damage...\n");
+ FLUSH();
+ }
+ else {
+ mwWrite("relink: heap totally destroyed, aborting program\n");
+ mwFlushW(1);
+ FLUSH();
+ abort();
+ }
+
+ /* Verify by checking that the number of active allocations */
+ /* match the number of entries in the chain */
+verifyok:
+ if( !mwIsHeapOK( NULL ) ) {
+ mwWrite("relink: heap verification FAILS - aborting program\n");
+ mwFlushW(1);
+ FLUSH();
+ abort();
+ }
+ for( size=count=0, mw1=mwHead; mw1; mw1=mw1->next ) {
+ count ++;
+ size += (long) mw1->size;
+ }
+ if( count == mwNumCurAlloc ) {
+ mwWrite("relink: successful, ");
+ if( size == mwStatCurAlloc ) {
+ mwWrite("no allocations lost\n");
+ }
+ else {
+ if( mw != NULL ) {
+ mwWrite("size information lost for MW-%p\n", mw);
+ mw->size = 0;
+ }
+ }
+ }
+ else {
+ mwWrite("relink: partial, %ld MW-blocks of %ld bytes lost\n",
+ mwNmlNumAlloc+mwNumCurAlloc-count, mwNmlCurAlloc+mwStatCurAlloc-size );
+ return 0;
+ }
+
+ return 1;
+ }
+
+/*
+** If mwData* is NULL:
+** Returns 0 if heap chain is broken.
+** Returns 1 if heap chain is intact.
+** If mwData* is not NULL:
+** Returns 0 if mwData* is missing or if chain is broken.
+** Returns 1 if chain is intact and mwData* is found.
+*/
+static int mwIsHeapOK( mwData *includes_mw ) {
+ int found = 0;
+ mwData *mw;
+
+ for( mw = mwHead; mw; mw=mw->next ) {
+ if( includes_mw == mw ) found++;
+ if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0;
+ if( mw->prev ) {
+ if( !mwIsSafeAddr( mw->prev, mwDataSize ) ) return 0;
+ if( mw==mwHead || mw->prev->next != mw ) return 0;
+ }
+ if( mw->next ) {
+ if( !mwIsSafeAddr( mw->next, mwDataSize ) ) return 0;
+ if( mw==mwTail || mw->next->prev != mw ) return 0;
+ }
+ else if( mw!=mwTail ) return 0;
+ }
+
+ if( includes_mw != NULL && !found ) return 0;
+
+ return 1;
+ }
+
+static int mwIsOwned( mwData* mw, const char *file, int line ) {
+ int retv;
+ mwStat *ms;
+
+ /* see if the address is legal according to OS */
+ if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0;
+
+ /* make sure we have _anything_ allocated */
+ if( mwHead == NULL && mwTail == NULL && mwStatCurAlloc == 0 )
+ return 0;
+
+ /* calculate checksum */
+ if( mw->check != CHKVAL(mw) ) {
+ /* may be damaged checksum, see if block is in heap */
+ if( mwIsHeapOK( mw ) ) {
+ /* damaged checksum, repair it */
+ mwWrite( "internal: <%ld> %s(%d), checksum for MW-%p is incorrect\n",
+ mwCounter, file, line, mw );
+ mwIncErr();
+ if( mwIsReadAddr( mw->file, 1 ) ) {
+ ms = mwStatGet( mw->file, -1, 0 );
+ if( ms == NULL ) mw->file = "<relinked>";
+ }
+ else mw->file = "<unknown>";
+ mw->size = 0;
+ mw->check = CHKVAL(mw);
+ return 1;
+ }
+ /* no, it's just some garbage data */
+ return 0;
+ }
+
+ /* check that the non-NULL pointers are safe */
+ if( mw->prev && !mwIsSafeAddr( mw->prev, mwDataSize ) ) mwRelink( mw, file, line );
+ if( mw->next && !mwIsSafeAddr( mw->next, mwDataSize ) ) mwRelink( mw, file, line );
+
+ /* safe address, checksum OK, proceed with heap checks */
+
+ /* see if the block is in the heap */
+ retv = 0;
+ if( mw->prev ) { if( mw->prev->next == mw ) retv ++; }
+ else { if( mwHead == mw ) retv++; }
+ if( mw->next ) { if( mw->next->prev == mw ) retv ++; }
+ else { if( mwTail == mw ) retv++; }
+ if( mw->check == CHKVAL(mw) ) retv ++;
+ if( retv > 2 ) return 1;
+
+ /* block not in heap, check heap for corruption */
+
+ if( !mwIsHeapOK( mw ) ) {
+ if( mwRelink( mw, file, line ) )
+ return 1;
+ }
+
+ /* unable to repair */
+ mwWrite( "internal: <%ld> %s(%d), mwIsOwned fails for MW-%p\n",
+ mwCounter, file, line, mw );
+ mwIncErr();
+
+ return 0;
+ }
+
+/*
+** mwTestBuf:
+** Checks a buffers links and pre/postfixes.
+** Writes errors found to the log.
+** Returns zero if no errors found.
+*/
+static int mwTestBuf( mwData* mw, const char* file, int line ) {
+ int retv = 0;
+ char *p;
+
+ if( file == NULL ) file = "unknown";
+
+ if( !mwIsSafeAddr( mw, mwDataSize + mwOverflowZoneSize ) ) {
+ mwWrite( "internal: <%ld> %s(%d): pointer MW-%p is invalid\n",
+ mwCounter, file, line, mw );
+ mwIncErr();
+ return 2;
+ }
+
+ if( mw->check != CHKVAL(mw) ) {
+ mwWrite( "internal: <%ld> %s(%d), info trashed; relinking\n",
+ mwCounter, file, line );
+ mwIncErr();
+ if( !mwRelink( mw, file, line ) ) return 2;
+ }
+
+ if( mw->prev && mw->prev->next != mw ) {
+ mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link1 broken\n",
+ mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
+ mwIncErr();
+ if( !mwRelink( mw, file, line ) ) retv = 2;
+ }
+ if( mw->next && mw->next->prev != mw ) {
+ mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link2 broken\n",
+ mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
+ mwIncErr();
+ if( !mwRelink( mw, file, line ) ) retv = 2;
+ }
+
+ p = ((char*)mw) + mwDataSize;
+ if( mwCheckOF( p ) ) {
+ mwWrite( "underflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
+ mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
+ mwIncErr();
+ retv = 1;
+ }
+ p += mwOverflowZoneSize + mw->size;
+ if( mwIsReadAddr( p, mwOverflowZoneSize ) && mwCheckOF( p ) ) {
+ mwWrite( "overflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
+ mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
+ mwIncErr();
+ retv = 1;
+ }
+
+ return retv;
+ }
+
+static void mwDefaultOutFunc( int c ) {
+ if( mwLogR() ) fputc( c, mwLogR() );
+ }
+
+static void mwWrite( const char *format, ... ) {
+ int tot, oflow = 0;
+ va_list mark;
+ mwAutoInit();
+ if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc;
+ va_start( mark, format );
+ tot = vsprintf( mwPrintBuf, format, mark );
+ va_end( mark );
+ if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; }
+ for(tot=0;mwPrintBuf[tot];tot++)
+ (*mwOutFunction)( mwPrintBuf[tot] );
+ if( oflow ) {
+ mwWrite( "\ninternal: mwWrite(): WARNING! OUTPUT EXCEEDED %u CHARS: SYSTEM UNSTABLE\n", MW_TRACE_BUFFER-1 );
+ FLUSH();
+ }
+ return;
+ }
+
+static void mwLogFile( const char *name ) {
+ time_t tid;
+ (void) time( &tid );
+ if( mwLogR() != NULL ) {
+ fclose( mwLogR() );
+ mwLogW( NULL );
+ }
+ if( name == NULL ) return;
+ mwLogW( fopen( name, "a" COMMIT ) );
+ if( mwLogR() == NULL )
+ mwWrite( "logfile: failed to open/create file '%s'\n", name );
+ }
+
+/*
+** Try to free NML memory until a contiguous allocation of
+** 'needed' bytes can be satisfied. If this is not enough
+** and the 'urgent' parameter is nonzero, grabbed memory is
+** also freed.
+*/
+static size_t mwFreeUp( size_t needed, int urgent ) {
+ void *p;
+ mwData *mw, *mw2;
+ char *data;
+
+ /* free grabbed NML memory */
+ for(;;) {
+ if( mwDrop_( 1, MW_VAL_NML, 1 ) == 0 ) break;
+ p = malloc( needed );
+ if( p == NULL ) continue;
+ free( p );
+ return needed;
+ }
+
+ /* free normal NML memory */
+ mw = mwHead;
+ while( mw != NULL ) {
+ if( !(mw->flag & MW_NML) ) mw = mw->next;
+ else {
+ data = ((char*)mw)+mwDataSize+mwOverflowZoneSize;
+ if( mwTestMem( data, mw->size, MW_VAL_NML ) ) {
+ mwIncErr();
+ mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
+ mw->count, data + mwOverflowZoneSize, mw->file, mw->line );
+ }
+ mw2 = mw->next;
+ mwUnlink( mw, "mwFreeUp", 0 );
+ free( mw );
+ mw = mw2;
+ p = malloc( needed );
+ if( p == NULL ) continue;
+ free( p );
+ return needed;
+ }
+ }
+
+ /* if not urgent (for internal purposes), fail */
+ if( !urgent ) return 0;
+
+ /* free grabbed memory */
+ for(;;) {
+ if( mwDrop_( 1, MW_VAL_GRB, 1 ) == 0 ) break;
+ p = malloc( needed );
+ if( p == NULL ) continue;
+ free( p );
+ return needed;
+ }
+
+ return 0;
+ }
+
+static const void * mwTestMem( const void *p, unsigned len, int c ) {
+ const unsigned char *ptr;
+ ptr = (const unsigned char *) p;
+ while( len-- ) {
+ if( *ptr != (unsigned char)c ) return (const void*)ptr;
+ ptr ++;
+ }
+ return NULL;
+ }
+
+static int mwStrCmpI( const char *s1, const char *s2 ) {
+ if( s1 == NULL || s2 == NULL ) return 0;
+ while( *s1 ) {
+ if( toupper(*s2) == toupper(*s1) ) { s1++; s2++; continue; }
+ return 1;
+ }
+ return 0;
+ }
+
+#define AIPH() if( always_invoked ) { mwWrite("autocheck: <%ld> %s(%d) ", mwCounter, file, line ); always_invoked = 0; }
+
+static int mwTestNow( const char *file, int line, int always_invoked ) {
+ int retv = 0;
+ mwData *mw;
+ char *data;
+
+ if( file && !always_invoked )
+ mwWrite("check: <%ld> %s(%d), checking %s%s%s\n",
+ mwCounter, file, line,
+ (mwTestFlags & MW_TEST_CHAIN) ? "chain ": "",
+ (mwTestFlags & MW_TEST_ALLOC) ? "alloc ": "",
+ (mwTestFlags & MW_TEST_NML) ? "nomansland ": ""
+ );
+
+ if( mwTestFlags & MW_TEST_CHAIN ) {
+ for( mw = mwHead; mw; mw=mw->next ) {
+ if( !mwIsSafeAddr(mw, mwDataSize) ) {
+ AIPH();
+ mwWrite("check: heap corruption detected\n");
+ mwIncErr();
+ return retv + 1;
+ }
+ if( mw->prev ) {
+ if( !mwIsSafeAddr(mw->prev, mwDataSize) ) {
+ AIPH();
+ mwWrite("check: heap corruption detected\n");
+ mwIncErr();
+ return retv + 1;
+ }
+ if( mw==mwHead || mw->prev->next != mw ) {
+ AIPH();
+ mwWrite("check: heap chain broken, prev link incorrect\n");
+ mwIncErr();
+ retv ++;
+ }
+ }
+ if( mw->next ) {
+ if( !mwIsSafeAddr(mw->next, mwDataSize) ) {
+ AIPH();
+ mwWrite("check: heap corruption detected\n");
+ mwIncErr();
+ return retv + 1;
+ }
+ if( mw==mwTail || mw->next->prev != mw ) {
+ AIPH();
+ mwWrite("check: heap chain broken, next link incorrect\n");
+ mwIncErr();
+ retv ++;
+ }
+ }
+ else if( mw!=mwTail ) {
+ AIPH();
+ mwWrite("check: heap chain broken, tail incorrect\n");
+ mwIncErr();
+ retv ++;
+ }
+ }
+ }
+ if( mwTestFlags & MW_TEST_ALLOC ) {
+ for( mw = mwHead; mw; mw=mw->next ) {
+ if( mwTestBuf( mw, file, line ) ) retv ++;
+ }
+ }
+ if( mwTestFlags & MW_TEST_NML ) {
+ for( mw = mwHead; mw; mw=mw->next ) {
+ if( (mw->flag & MW_NML) ) {
+ data = ((char*)mw)+mwDataSize+mwOverflowZoneSize;
+ if( mwTestMem( data, mw->size, MW_VAL_NML ) ) {
+ mwIncErr();
+ mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
+ mw->count, data + mwOverflowZoneSize, mw->file, mw->line );
+ }
+ }
+ }
+ }
+
+
+ if( file && !always_invoked && !retv )
+ mwWrite("check: <%ld> %s(%d), complete; no errors\n",
+ mwCounter, file, line );
+ return retv;
+ }
+
+/**********************************************************************
+** Statistics
+**********************************************************************/
+
+static void mwStatReport()
+{
+ mwStat* ms, *ms2;
+ const char *modname;
+ int modnamelen;
+
+ /* global statistics report */
+ mwWrite( "\nMemory usage statistics (global):\n" );
+ mwWrite( " N)umber of allocations made: %ld\n", mwStatNumAlloc );
+ mwWrite( " L)argest memory usage : %ld\n", mwStatMaxAlloc );
+ mwWrite( " T)otal of all alloc() calls: %ld\n", mwStatTotAlloc );
+ mwWrite( " U)nfreed bytes totals : %ld\n", mwStatCurAlloc );
+ FLUSH();
+
+ if( mwStatLevel < 1 ) return;
+
+ /* on a per-module basis */
+ mwWrite( "\nMemory usage statistics (detailed):\n");
+ mwWrite( " Module/Line Number Largest Total Unfreed \n");
+ for( ms=mwStatList; ms; ms=ms->next )
+ {
+ if( ms->line == -1 )
+ {
+ if( ms->file == NULL || !mwIsReadAddr(ms->file,22) ) modname = "<unknown>";
+ else modname = ms->file;
+ modnamelen = strlen(modname);
+ if( modnamelen > 42 )
+ {
+ modname = modname + modnamelen - 42;
+ }
+
+ mwWrite(" %-42s %-8ld %-8ld %-8ld %-8ld\n",
+ modname, ms->num, ms->max, ms->total, ms->curr );
+ if( ms->file && mwStatLevel > 1 )
+ {
+ for( ms2=mwStatList; ms2; ms2=ms2->next )
+ {
+ if( ms2->line!=-1 && ms2->file!=NULL && !mwStrCmpI( ms2->file, ms->file ) )
+ {
+ mwWrite( " %-8d %-8ld %-8ld %-8ld %-8ld\n",
+ ms2->line, ms2->num, ms2->max, ms2->total, ms2->curr );
+ }
+ }
+ }
+ }
+ }
+}
+
+static mwStat* mwStatGet( const char *file, int line, int makenew ) {
+ mwStat* ms;
+
+ if( mwStatLevel < 2 ) line = -1;
+
+ for( ms=mwStatList; ms!=NULL; ms=ms->next ) {
+ if( line != ms->line ) continue;
+ if( file==NULL ) {
+ if( ms->file == NULL ) break;
+ continue;
+ }
+ if( ms->file == NULL ) continue;
+ if( !strcmp( ms->file, file ) ) break;
+ }
+
+ if( ms != NULL ) return ms;
+
+ if( !makenew ) return NULL;
+
+ ms = (mwStat*) malloc( sizeof(mwStat) );
+ if( ms == NULL ) {
+ if( mwFreeUp( sizeof(mwStat), 0 ) < sizeof(mwStat) ||
+ (ms=(mwStat*)malloc(sizeof(mwStat))) == NULL ) {
+ mwWrite("internal: memory low, statistics incomplete for '%s'\n", file );
+ return NULL;
+ }
+ }
+ ms->file = file;
+ ms->line = line;
+ ms->total = 0L;
+ ms->max = 0L;
+ ms->num = 0L;
+ ms->curr = 0L;
+ ms->next = mwStatList;
+ mwStatList = ms;
+ return ms;
+ }
+
+static void mwStatAlloc( size_t size, const char* file, int line ) {
+ mwStat* ms;
+
+ /* update the module statistics */
+ ms = mwStatGet( file, -1, 1 );
+ if( ms != NULL ) {
+ ms->total += (long) size;
+ ms->curr += (long) size;
+ ms->num ++;
+ if( ms->curr > ms->max ) ms->max = ms->curr;
+ }
+
+ /* update the line statistics */
+ if( mwStatLevel > 1 && line != -1 && file ) {
+ ms = mwStatGet( file, line, 1 );
+ if( ms != NULL ) {
+ ms->total += (long) size;
+ ms->curr += (long) size;
+ ms->num ++;
+ if( ms->curr > ms->max ) ms->max = ms->curr;
+ }
+ }
+
+ }
+
+static void mwStatFree( size_t size, const char* file, int line ) {
+ mwStat* ms;
+
+ /* update the module statistics */
+ ms = mwStatGet( file, -1, 1 );
+ if( ms != NULL ) ms->curr -= (long) size;
+
+ /* update the line statistics */
+ if( mwStatLevel > 1 && line != -1 && file ) {
+ ms = mwStatGet( file, line, 1 );
+ if( ms != NULL ) ms->curr -= (long) size;
+ }
+ }
+
+/***********************************************************************
+** Safe memory checkers
+**
+** Using ifdefs, implement the operating-system specific mechanism
+** of identifying a piece of memory as legal to access with read
+** and write priviliges. Default: return nonzero for non-NULL pointers.
+***********************************************************************/
+
+static char mwDummy( char c )
+{
+ return c;
+}
+
+#ifndef MW_SAFEADDR
+#ifdef WIN32
+#define MW_SAFEADDR
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+int mwIsReadAddr( const void *p, unsigned len )
+{
+ if( p == NULL ) return 0;
+ if( IsBadReadPtr(p,len) ) return 0;
+ return 1;
+}
+int mwIsSafeAddr( void *p, unsigned len )
+{
+ /* NOTE: For some reason, under Win95 the IsBad... */
+ /* can return false for invalid pointers. */
+ if( p == NULL ) return 0;
+ if( IsBadReadPtr(p,len) || IsBadWritePtr(p,len) ) return 0;
+ return 1;
+}
+#endif /* WIN32 */
+#endif /* MW_SAFEADDR */
+
+#ifndef MW_SAFEADDR
+#ifdef SIGSEGV
+#define MW_SAFEADDR
+
+typedef void (*mwSignalHandlerPtr)( int );
+mwSignalHandlerPtr mwOldSIGSEGV = (mwSignalHandlerPtr) 0;
+jmp_buf mwSIGSEGVjump;
+static void mwSIGSEGV( int n );
+
+static void mwSIGSEGV( int n )
+{
+ n = n;
+ longjmp( mwSIGSEGVjump, 1 );
+}
+
+int mwIsReadAddr( const void *p, unsigned len )
+{
+ const char *ptr;
+
+ if( p == NULL ) return 0;
+ if( !len ) return 1;
+
+ /* set up to catch the SIGSEGV signal */
+ mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV );
+
+ if( setjmp( mwSIGSEGVjump ) )
+ {
+ signal( SIGSEGV, mwOldSIGSEGV );
+ return 0;
+ }
+
+ /* read all the bytes in the range */
+ ptr = (const char *)p;
+ ptr += len;
+
+ /* the reason for this rather strange construct is that */
+ /* we want to keep the number of used parameters and locals */
+ /* to a minimum. if we use len for a counter gcc will complain */
+ /* it may get clobbered by longjmp() at high warning levels. */
+ /* it's a harmless warning, but this way we don't have to see it. */
+ do
+ {
+ ptr --;
+ if( *ptr == 0x7C ) (void) mwDummy( (char)0 );
+ } while( (const void*) ptr != p );
+
+ /* remove the handler */
+ signal( SIGSEGV, mwOldSIGSEGV );
+
+ return 1;
+}
+int mwIsSafeAddr( void *p, unsigned len )
+{
+ char *ptr;
+
+ if( p == NULL ) return 0;
+ if( !len ) return 1;
+
+ /* set up to catch the SIGSEGV signal */
+ mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV );
+
+ if( setjmp( mwSIGSEGVjump ) )
+ {
+ signal( SIGSEGV, mwOldSIGSEGV );
+ return 0;
+ }
+
+ /* read and write-back all the bytes in the range */
+ ptr = (char *)p;
+ ptr += len;
+
+ /* the reason for this rather strange construct is that */
+ /* we want to keep the number of used parameters and locals */
+ /* to a minimum. if we use len for a counter gcc will complain */
+ /* it may get clobbered by longjmp() at high warning levels. */
+ /* it's a harmless warning, but this way we don't have to see it. */
+ do
+ {
+ ptr --;
+ *ptr = mwDummy( *ptr );
+ } while( (void*) ptr != p );
+
+ /* remove the handler */
+ signal( SIGSEGV, mwOldSIGSEGV );
+
+ return 1;
+}
+#endif /* SIGSEGV */
+#endif /* MW_SAFEADDR */
+
+#ifndef MW_SAFEADDR
+int mwIsReadAddr( const void *p, unsigned len )
+{
+ if( p == NULL ) return 0;
+ if( len == 0 ) return 1;
+ return 1;
+}
+int mwIsSafeAddr( void *p, unsigned len )
+{
+ if( p == NULL ) return 0;
+ if( len == 0 ) return 1;
+ return 1;
+}
+#endif
+
+/**********************************************************************
+** Mutex handling
+**********************************************************************/
+
+#if defined(WIN32) || defined(__WIN32__)
+
+static void mwMutexInit( void )
+{
+ mwGlobalMutex = CreateMutex( NULL, FALSE, NULL);
+ return;
+}
+
+static void mwMutexTerm( void )
+{
+ CloseHandle( mwGlobalMutex );
+ return;
+}
+
+static void mwMutexLock( void )
+{
+ if( WaitForSingleObject(mwGlobalMutex, 1000 ) == WAIT_TIMEOUT )
+ {
+ mwWrite( "mwMutexLock: timed out, possible deadlock\n" );
+ }
+ return;
+}
+
+static void mwMutexUnlock( void )
+{
+ ReleaseMutex( mwGlobalMutex );
+ return;
+}
+
+#endif
+
+#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
+
+static void mwMutexInit( void )
+{
+ pthread_mutex_init( &mwGlobalMutex, NULL );
+ return;
+}
+
+static void mwMutexTerm( void )
+{
+ pthread_mutex_destroy( &mwGlobalMutex );
+ return;
+}
+
+static void mwMutexLock( void )
+{
+ pthread_mutex_lock(&mwGlobalMutex);
+ return;
+}
+
+static void mwMutexUnlock( void )
+{
+ pthread_mutex_unlock(&mwGlobalMutex);
+ return;
+}
+
+#endif
+
+/**********************************************************************
+** C++ new & delete
+**********************************************************************/
+
+#if 0 /* 980317: disabled C++ */
+
+#ifdef __cplusplus
+#ifndef MEMWATCH_NOCPP
+
+int mwNCur = 0;
+const char *mwNFile = NULL;
+int mwNLine = 0;
+
+class MemWatch {
+public:
+ MemWatch();
+ ~MemWatch();
+ };
+
+MemWatch::MemWatch() {
+ if( mwInited ) return;
+ mwUseAtexit = 0;
+ mwInit();
+ }
+
+MemWatch::~MemWatch() {
+ if( mwUseAtexit ) return;
+ mwTerm();
+ }
+
+/*
+** This global new will catch all 'new' calls where MEMWATCH is
+** not active.
+*/
+void* operator new( unsigned size ) {
+ mwNCur = 0;
+ return mwMalloc( size, "<unknown>", 0 );
+ }
+
+/*
+** This is the new operator that's called when a module uses mwNew.
+*/
+void* operator new( unsigned size, const char *file, int line ) {
+ mwNCur = 0;
+ return mwMalloc( size, file, line );
+ }
+
+/*
+** This is the new operator that's called when a module uses mwNew[].
+** -- hjc 07/16/02
+*/
+void* operator new[] ( unsigned size, const char *file, int line ) {
+ mwNCur = 0;
+ return mwMalloc( size, file, line );
+ }
+
+/*
+** Since this delete operator will recieve ALL delete's
+** even those from within libraries, we must accept
+** delete's before we've been initialized. Nor can we
+** reliably check for wild free's if the mwNCur variable
+** is not set.
+*/
+void operator delete( void *p ) {
+ if( p == NULL ) return;
+ if( !mwInited ) {
+ free( p );
+ return;
+ }
+ if( mwNCur ) {
+ mwFree( p, mwNFile, mwNLine );
+ mwNCur = 0;
+ return;
+ }
+ mwFree_( p );
+ }
+
+void operator delete[]( void *p ) {
+ if( p == NULL ) return;
+ if( !mwInited ) {
+ free( p );
+ return;
+ }
+ if( mwNCur ) {
+ mwFree( p, mwNFile, mwNLine );
+ mwNCur = 0;
+ return;
+ }
+ mwFree_( p );
+ }
+
+#endif /* MEMWATCH_NOCPP */
+#endif /* __cplusplus */
+
+#endif /* 980317: disabled C++ */
+
+/* MEMWATCH.C */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/memwatch.h b/plugins/!NotAdopted/VypressChat/contrib/memwatch.h
new file mode 100644
index 0000000000..c60d31b7e8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/memwatch.h
@@ -0,0 +1,707 @@
+/*
+** MEMWATCH.H
+** Nonintrusive ANSI C memory leak / overwrite detection
+** Copyright (C) 1992-2002 Johan Lindh
+** All rights reserved.
+** Version 2.71
+**
+************************************************************************
+**
+** PURPOSE:
+**
+** MEMWATCH has been written to allow guys and gals that like to
+** program in C a public-domain memory error control product.
+** I hope you'll find it's as advanced as most commercial packages.
+** The idea is that you use it during the development phase and
+** then remove the MEMWATCH define to produce your final product.
+** MEMWATCH is distributed in source code form in order to allow
+** you to compile it for your platform with your own compiler.
+** It's aim is to be 100% ANSI C, but some compilers are more stingy
+** than others. If it doesn't compile without warnings, please mail
+** me the configuration of operating system and compiler you are using
+** along with a description of how to modify the source, and the version
+** number of MEMWATCH that you are using.
+**
+************************************************************************
+
+ This file is part of MEMWATCH.
+
+ MEMWATCH 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.
+
+ MEMWATCH 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 MEMWATCH; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+************************************************************************
+**
+** REVISION HISTORY:
+**
+** 920810 JLI [1.00]
+** 920830 JLI [1.10 double-free detection]
+** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit]
+** 921022 JLI [1.20 ASSERT and VERIFY]
+** 921105 JLI [1.30 C++ support and TRACE]
+** 921116 JLI [1.40 mwSetOutFunc]
+** 930215 JLI [1.50 modified ASSERT/VERIFY]
+** 930327 JLI [1.51 better auto-init & PC-lint support]
+** 930506 JLI [1.55 MemWatch class, improved C++ support]
+** 930507 JLI [1.60 mwTest & CHECK()]
+** 930809 JLI [1.65 Abort/Retry/Ignore]
+** 930820 JLI [1.70 data dump when unfreed]
+** 931016 JLI [1.72 modified C++ new/delete handling]
+** 931108 JLI [1.77 mwSetAssertAction() & some small changes]
+** 940110 JLI [1.80 no-mans-land alloc/checking]
+** 940328 JLI [2.00 version 2.0 rewrite]
+** Improved NML (no-mans-land) support.
+** Improved performance (especially for free()ing!).
+** Support for 'read-only' buffers (checksums)
+** ^^ NOTE: I never did this... maybe I should?
+** FBI (free'd block info) tagged before freed blocks
+** Exporting of the mwCounter variable
+** mwBreakOut() localizes debugger support
+** Allocation statistics (global, per-module, per-line)
+** Self-repair ability with relinking
+** 950913 JLI [2.10 improved garbage handling]
+** 951201 JLI [2.11 improved auto-free in emergencies]
+** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()]
+** 960514 JLI [2.12 undefining of existing macros]
+** 960515 JLI [2.13 possibility to use default new() & delete()]
+** 960516 JLI [2.20 suppression of file flushing on unfreed msgs]
+** 960516 JLI [2.21 better support for using MEMWATCH with DLL's]
+** 960710 JLI [X.02 multiple logs and mwFlushNow()]
+** 960801 JLI [2.22 merged X.01 version with current]
+** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's]
+** 960805 JLI [2.31 merged X.02 version with current]
+** 961002 JLI [2.32 support for realloc() + fixed STDERR bug]
+** 961222 JLI [2.40 added mwMark() & mwUnmark()]
+** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY]
+** 970113 JLI [2.42 added support for PC-Lint 7.00g]
+** 970207 JLI [2.43 added support for strdup()]
+** 970209 JLI [2.44 changed default filename to lowercase]
+** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers]
+** 970723 JLI [2.46 added MW_ARI_NULLREAD flag]
+** 970813 JLI [2.47 stabilized marker handling]
+** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway]
+** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support]
+** 980417 JLI [2.51 more checks for invalid addresses]
+** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting]
+** 990112 JLI [2.53 added check for empty heap to mwIsOwned]
+** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML]
+** 990224 JLI [2.56 changed ordering of members in structures]
+** 990303 JLI [2.57 first maybe-fixit-for-hpux test]
+** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit]
+** 990517 JLI [2.59 fixed some high-sensitivity warnings]
+** 990610 JLI [2.60 fixed some more high-sensitivity warnings]
+** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names]
+** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()]
+** 991007 JLI [2.63 first shot at a 64-bit compatible version]
+** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const]
+** 000704 JLI [2.65 added some more detection for 64-bits]
+** 010502 JLI [2.66 incorporated some user fixes]
+** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)]
+** [added array destructor for C++ (thanks rdasilva@connecttel.com)]
+** [added mutex support (thanks rdasilva@connecttel.com)]
+** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined]
+** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked]
+** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen]
+** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)]
+** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)]
+**
+** To use, simply include 'MEMWATCH.H' as a header file,
+** and add MEMWATCH.C to your list of files, and define the macro
+** 'MEMWATCH'. If this is not defined, MEMWATCH will disable itself.
+**
+** To call the standard C malloc / realloc / calloc / free; use mwMalloc_(),
+** mwCalloc_() and mwFree_(). Note that mwFree_() will correctly
+** free both malloc()'d memory as well as mwMalloc()'d.
+**
+** 980317: C++ support has been disabled.
+** The code remains, but is not compiled.
+**
+** For use with C++, which allows use of inlining in header files
+** and class specific new/delete, you must also define 'new' as
+** 'mwNew' and 'delete' as 'mwDelete'. Do this *after* you include
+** C++ header files from libraries, otherwise you can mess up their
+** class definitions. If you don't define these, the C++ allocations
+** will not have source file and line number information. Also note,
+** most C++ class libraries implement their own C++ memory management,
+** and don't allow anyone to override them. MFC belongs to this crew.
+** In these cases, the only thing to do is to use MEMWATCH_NOCPP.
+**
+** You can capture output from MEMWATCH using mwSetOutFunc().
+** Just give it the adress of a "void myOutFunc(int c)" function,
+** and all characters to be output will be redirected there.
+**
+** A failing ASSERT() or VERIFY() will normally always abort your
+** program. This can be changed using mwSetAriFunc(). Give it a
+** pointer to a "int myAriFunc(const char *)" function. Your function
+** must ask the user whether to Abort, Retry or Ignore the trap.
+** Return 2 to Abort, 1 to Retry or 0 to Ignore. Beware retry; it
+** causes the expression to be evaluated again! MEMWATCH has a
+** default ARI handler. It's disabled by default, but you can enable
+** it by calling 'mwDefaultAri()'. Note that this will STILL abort
+** your program unless you define MEMWATCH_STDIO to allow MEMWATCH
+** to use the standard C I/O streams. Also, setting the ARI function
+** will cause MEMWATCH *NOT* to write the ARI error to stderr. The
+** error string is passed to the ARI function instead, as the
+** 'const char *' parameter.
+**
+** You can disable MEMWATCH's ASSERT/VERIFY and/or TRACE implementations.
+** This can be useful if you're using a debug terminal or smart debugger.
+** Disable them by defining MW_NOASSERT, MW_NOVERIFY or MW_NOTRACE.
+**
+** MEMWATCH fills all allocated memory with the byte 0xFE, so if
+** you're looking at erroneous data which are all 0xFE:s, the
+** data probably was not initialized by you. The exception is
+** calloc(), which will fill with zero's. All freed buffers are
+** zapped with 0xFD. If this is what you look at, you're using
+** data that has been freed. If this is the case, be aware that
+** MEMWATCH places a 'free'd block info' structure immediately
+** before the freed data. This block contains info about where
+** the block was freed. The information is in readable text,
+** in the format "FBI<counter>filename(line)", for example:
+** "FBI<267>test.c(12)". Using FBI's slows down free(), so it's
+** disabled by default. Use mwFreeBufferInfo(1) to enable it.
+**
+** To aid in tracking down wild pointer writes, MEMWATCH can perform
+** no-mans-land allocations. No-mans-land will contain the byte 0xFC.
+** MEMWATCH will, when this is enabled, convert recently free'd memory
+** into NML allocations.
+**
+** MEMWATCH protects it's own data buffers with checksums. If you
+** get an internal error, it means you're overwriting wildly,
+** or using an uninitialized pointer.
+**
+************************************************************************
+**
+** Note when compiling with Microsoft C:
+** - MSC ignores fflush() by default. This is overridden, so that
+** the disk log will always be current.
+**
+** This utility has been tested with:
+** PC-lint 7.0k, passed as 100% ANSI C compatible
+** Microsoft Visual C++ on Win16 and Win32
+** Microsoft C on DOS
+** SAS C on an Amiga 500
+** Gnu C on a PC running Red Hat Linux
+** ...and using an (to me) unknown compiler on an Atari machine.
+**
+************************************************************************
+**
+** Format of error messages in MEMWATCH.LOG:
+** message: <sequence-number> filename(linenumber), information
+**
+** Errors caught by MemWatch, when they are detected, and any
+** actions taken besides writing to the log file MEMWATCH.LOG:
+**
+** Double-freeing:
+** A pointer that was recently freed and has not since been
+** reused was freed again. The place where the previous free()
+** was executed is displayed.
+** Detect: delete or free() using the offending pointer.
+** Action: The delete or free() is cancelled, execution continues.
+** Underflow:
+** You have written just ahead of the allocated memory.
+** The size and place of the allocation is displayed.
+** Detect: delete or free() of the damaged buffer.
+** Action: The buffer is freed, but there may be secondary damage.
+** Overflow:
+** Like underflow, but you've written after the end of the buffer.
+** Detect: see Underflow.
+** Action: see Underflow.
+** WILD free:
+** An unrecognized pointer was passed to delete or free().
+** The pointer may have been returned from a library function;
+** in that case, use mwFree_() to force free() of it.
+** Also, this may be a double-free, but the previous free was
+** too long ago, causing MEMWATCH to 'forget' it.
+** Detect: delete or free() of the offending pointer.
+** Action: The delete or free() is cancelled, execution continues.
+** NULL free:
+** It's unclear to me whether or not freeing of NULL pointers
+** is legal in ANSI C, therefore a warning is written to the log file,
+** but the error counter remains the same. This is legal using C++,
+** so the warning does not appear with delete.
+** Detect: When you free(NULL).
+** Action: The free() is cancelled.
+** Failed:
+** A request to allocate memory failed. If the allocation is
+** small, this may be due to memory depletion, but is more likely
+** to be memory fragmentation problems. The amount of memory
+** allocated so far is displayed also.
+** Detect: When you new, malloc(), realloc() or calloc() memory.
+** Action: NULL is returned.
+** Realloc:
+** A request to re-allocate a memory buffer failed for reasons
+** other than out-of-memory. The specific reason is shown.
+** Detect: When you realloc()
+** Action: realloc() is cancelled, NULL is returned
+** Limit fail:
+** A request to allocate memory failed since it would violate
+** the limit set using mwLimit(). mwLimit() is used to stress-test
+** your code under simulated low memory conditions.
+** Detect: At new, malloc(), realloc() or calloc().
+** Action: NULL is returned.
+** Assert trap:
+** An ASSERT() failed. The ASSERT() macro works like C's assert()
+** macro/function, except that it's interactive. See your C manual.
+** Detect: On the ASSERT().
+** Action: Program ends with an advisory message to stderr, OR
+** Program writes the ASSERT to the log and continues, OR
+** Program asks Abort/Retry/Ignore? and takes that action.
+** Verify trap:
+** A VERIFY() failed. The VERIFY() macro works like ASSERT(),
+** but if MEMWATCH is not defined, it still evaluates the
+** expression, but it does not act upon the result.
+** Detect: On the VERIFY().
+** Action: Program ends with an advisory message to stderr, OR
+** Program writes the VERIFY to the log and continues, OR
+** Program asks Abort/Retry/Ignore? and takes that action.
+** Wild pointer:
+** A no-mans-land buffer has been written into. MEMWATCH can
+** allocate and distribute chunks of memory solely for the
+** purpose of trying to catch random writes into memory.
+** Detect: Always on CHECK(), but can be detected in several places.
+** Action: The error is logged, and if an ARI handler is installed,
+** it is executed, otherwise, execution continues.
+** Unfreed:
+** A memory buffer you allocated has not been freed.
+** You are informed where it was allocated, and whether any
+** over or underflow has occured. MemWatch also displays up to
+** 16 bytes of the data, as much as it can, in hex and text.
+** Detect: When MemWatch terminates.
+** Action: The buffer is freed.
+** Check:
+** An error was detected during a CHECK() operation.
+** The associated pointer is displayed along with
+** the file and line where the CHECK() was executed.
+** Followed immediately by a normal error message.
+** Detect: When you CHECK()
+** Action: Depends on the error
+** Relink:
+** After a MEMWATCH internal control block has been trashed,
+** MEMWATCH tries to repair the damage. If successful, program
+** execution will continue instead of aborting. Some information
+** about the block may be gone permanently, though.
+** Detect: N/A
+** Action: Relink successful: program continues.
+** Relink fails: program aborts.
+** Internal:
+** An internal error is flagged by MEMWATCH when it's control
+** structures have been damaged. You are likely using an uninitialized
+** pointer somewhere in your program, or are zapping memory all over.
+** The message may give you additional diagnostic information.
+** If possible, MEMWATCH will recover and continue execution.
+** Detect: Various actions.
+** Action: Whatever is needed
+** Mark:
+** The program terminated without umarking all marked pointers. Marking
+** can be used to track resources other than memory. mwMark(pointer,text,...)
+** when the resource is allocated, and mwUnmark(pointer) when it's freed.
+** The 'text' is displayed for still marked pointers when the program
+** ends.
+** Detect: When MemWatch terminates.
+** Action: The error is logged.
+**
+**
+************************************************************************
+**
+** The author may be reached by e-mail at the address below. If you
+** mail me about source code changes in MEMWATCH, remember to include
+** MW's version number.
+**
+** Johan Lindh
+** johan@linkdata.se
+**
+** The latest version of MEMWATCH may be downloaded from
+** http://www.linkdata.se/
+*/
+
+#ifndef __MEMWATCH_H
+#define __MEMWATCH_H
+
+/* Make sure that malloc(), realloc(), calloc() and free() are declared. */
+/*lint -save -e537 */
+#include <stdlib.h>
+/*lint -restore */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+** Constants used
+** All MEMWATCH constants start with the prefix MW_, followed by
+** a short mnemonic which indicates where the constant is used,
+** followed by a descriptive text about it.
+*/
+
+#define MW_ARI_NULLREAD 0x10 /* Null read (to start debugger) */
+#define MW_ARI_ABORT 0x04 /* ARI handler says: abort program! */
+#define MW_ARI_RETRY 0x02 /* ARI handler says: retry action! */
+#define MW_ARI_IGNORE 0x01 /* ARI handler says: ignore error! */
+
+#define MW_VAL_NEW 0xFE /* value in newly allocated memory */
+#define MW_VAL_DEL 0xFD /* value in newly deleted memory */
+#define MW_VAL_NML 0xFC /* value in no-mans-land */
+#define MW_VAL_GRB 0xFB /* value in grabbed memory */
+
+#define MW_TEST_ALL 0xFFFF /* perform all tests */
+#define MW_TEST_CHAIN 0x0001 /* walk the heap chain */
+#define MW_TEST_ALLOC 0x0002 /* test allocations & NML guards */
+#define MW_TEST_NML 0x0004 /* test all-NML areas for modifications */
+
+#define MW_NML_NONE 0 /* no NML */
+#define MW_NML_FREE 1 /* turn FREE'd memory into NML */
+#define MW_NML_ALL 2 /* all unused memory is NML */
+#define MW_NML_DEFAULT 0 /* the default NML setting */
+
+#define MW_STAT_GLOBAL 0 /* only global statistics collected */
+#define MW_STAT_MODULE 1 /* collect statistics on a module basis */
+#define MW_STAT_LINE 2 /* collect statistics on a line basis */
+#define MW_STAT_DEFAULT 0 /* the default statistics setting */
+
+/*
+** MemWatch internal constants
+** You may change these and recompile MemWatch to change the limits
+** of some parameters. Respect the recommended minimums!
+*/
+#define MW_TRACE_BUFFER 2048 /* (min 160) size of TRACE()'s output buffer */
+#define MW_FREE_LIST 64 /* (min 4) number of free()'s to track */
+
+/*
+** Exported variables
+** In case you have to remove the 'const' keyword because your compiler
+** doesn't support it, be aware that changing the values may cause
+** unpredictable behaviour.
+** - mwCounter contains the current action count. You can use this to
+** place breakpoints using a debugger, if you want.
+*/
+#ifndef __MEMWATCH_C
+extern const unsigned long mwCounter;
+#endif
+
+/*
+** System functions
+** Normally, it is not nessecary to call any of these. MEMWATCH will
+** automatically initialize itself on the first MEMWATCH function call,
+** and set up a call to mwAbort() using atexit(). Some C++ implementations
+** run the atexit() chain before the program has terminated, so you
+** may have to use mwInit() or the MemWatch C++ class to get good
+** behaviour.
+** - mwInit() can be called to disable the atexit() usage. If mwInit()
+** is called directly, you must call mwTerm() to end MemWatch, or
+** mwAbort().
+** - mwTerm() is usually not nessecary to call; but if called, it will
+** call mwAbort() if it finds that it is cancelling the 'topmost'
+** mwInit() call.
+** - mwAbort() cleans up after MEMWATCH, reports unfreed buffers, etc.
+*/
+void mwInit( void );
+void mwTerm( void );
+void mwAbort( void );
+
+/*
+** Setup functions
+** These functions control the operation of MEMWATCH's protective features.
+** - mwFlushNow() causes MEMWATCH to flush it's buffers.
+** - mwDoFlush() controls whether MEMWATCH flushes the disk buffers after
+** writes. The default is smart flushing: MEMWATCH will not flush buffers
+** explicitly until memory errors are detected. Then, all writes are
+** flushed until program end or mwDoFlush(0) is called.
+** - mwLimit() sets the allocation limit, an arbitrary limit on how much
+** memory your program may allocate in bytes. Used to stress-test app.
+** Also, in virtual-memory or multitasking environs, puts a limit on
+** how much MW_NML_ALL can eat up.
+** - mwGrab() grabs up X kilobytes of memory. Allocates actual memory,
+** can be used to stress test app & OS both.
+** - mwDrop() drops X kilobytes of grabbed memory.
+** - mwNoMansLand() sets the behaviour of the NML logic. See the
+** MW_NML_xxx for more information. The default is MW_NML_DEFAULT.
+** - mwStatistics() sets the behaviour of the statistics collector. See
+** the MW_STAT_xxx defines for more information. Default MW_STAT_DEFAULT.
+** - mwFreeBufferInfo() enables or disables the tagging of free'd buffers
+** with freeing information. This information is written in text form,
+** using sprintf(), so it's pretty slow. Disabled by default.
+** - mwAutoCheck() performs a CHECK() operation whenever a MemWatch function
+** is used. Slows down performance, of course.
+** - mwCalcCheck() calculates checksums for all data buffers. Slow!
+** - mwDumpCheck() logs buffers where stored & calc'd checksums differ. Slow!!
+** - mwMark() sets a generic marker. Returns the pointer given.
+** - mwUnmark() removes a generic marker. If, at the end of execution, some
+** markers are still in existence, these will be reported as leakage.
+** returns the pointer given.
+*/
+void mwFlushNow( void );
+void mwDoFlush( int onoff );
+void mwLimit( long bytes );
+unsigned mwGrab( unsigned kilobytes );
+unsigned mwDrop( unsigned kilobytes );
+void mwNoMansLand( int mw_nml_level );
+void mwStatistics( int level );
+void mwFreeBufferInfo( int onoff );
+void mwAutoCheck( int onoff );
+void mwCalcCheck( void );
+void mwDumpCheck( void );
+void * mwMark( void *p, const char *description, const char *file, unsigned line );
+void * mwUnmark( void *p, const char *file, unsigned line );
+
+/*
+** Testing/verification/tracing
+** All of these macros except VERIFY() evaluates to a null statement
+** if MEMWATCH is not defined during compilation.
+** - mwIsReadAddr() checks a memory area for read privilige.
+** - mwIsSafeAddr() checks a memory area for both read & write privilige.
+** This function and mwIsReadAddr() is highly system-specific and
+** may not be implemented. If this is the case, they will default
+** to returning nonzero for any non-NULL pointer.
+** - CHECK() does a complete memory integrity test. Slow!
+** - CHECK_THIS() checks only selected components.
+** - CHECK_BUFFER() checks the indicated buffer for errors.
+** - mwASSERT() or ASSERT() If the expression evaluates to nonzero, execution continues.
+** Otherwise, the ARI handler is called, if present. If not present,
+** the default ARI action is taken (set with mwSetAriAction()).
+** ASSERT() can be disabled by defining MW_NOASSERT.
+** - mwVERIFY() or VERIFY() works just like ASSERT(), but when compiling without
+** MEMWATCH the macro evaluates to the expression.
+** VERIFY() can be disabled by defining MW_NOVERIFY.
+** - mwTRACE() or TRACE() writes some text and data to the log. Use like printf().
+** TRACE() can be disabled by defining MW_NOTRACE.
+*/
+int mwIsReadAddr( const void *p, unsigned len );
+int mwIsSafeAddr( void *p, unsigned len );
+int mwTest( const char *file, int line, int mw_test_flags );
+int mwTestBuffer( const char *file, int line, void *p );
+int mwAssert( int, const char*, const char*, int );
+int mwVerify( int, const char*, const char*, int );
+
+/*
+** User I/O functions
+** - mwTrace() works like printf(), but dumps output either to the
+** function specified with mwSetOutFunc(), or the log file.
+** - mwPuts() works like puts(), dumps output like mwTrace().
+** - mwSetOutFunc() allows you to give the adress of a function
+** where all user output will go. (exeption: see mwSetAriFunc)
+** Specifying NULL will direct output to the log file.
+** - mwSetAriFunc() gives MEMWATCH the adress of a function to call
+** when an 'Abort, Retry, Ignore' question is called for. The
+** actual error message is NOT printed when you've set this adress,
+** but instead it is passed as an argument. If you call with NULL
+** for an argument, the ARI handler is disabled again. When the
+** handler is disabled, MEMWATCH will automatically take the
+** action specified by mwSetAriAction().
+** - mwSetAriAction() sets the default ARI return value MEMWATCH should
+** use if no ARI handler is specified. Defaults to MW_ARI_ABORT.
+** - mwAriHandler() is an ANSI ARI handler you can use if you like. It
+** dumps output to stderr, and expects input from stdin.
+** - mwBreakOut() is called in certain cases when MEMWATCH feels it would
+** be nice to break into a debugger. If you feel like MEMWATCH, place
+** an execution breakpoint on this function.
+*/
+void mwTrace( const char* format_string, ... );
+void mwPuts( const char* text );
+void mwSetOutFunc( void (*func)(int) );
+void mwSetAriFunc( int (*func)(const char*) );
+void mwSetAriAction( int mw_ari_value );
+int mwAriHandler( const char* cause );
+void mwBreakOut( const char* cause );
+
+/*
+** Allocation/deallocation functions
+** These functions are the ones actually to perform allocations
+** when running MEMWATCH, for both C and C++ calls.
+** - mwMalloc() debugging allocator
+** - mwMalloc_() always resolves to a clean call of malloc()
+** - mwRealloc() debugging re-allocator
+** - mwRealloc_() always resolves to a clean call of realloc()
+** - mwCalloc() debugging allocator, fills with zeros
+** - mwCalloc_() always resolves to a clean call of calloc()
+** - mwFree() debugging free. Can only free memory which has
+** been allocated by MEMWATCH.
+** - mwFree_() resolves to a) normal free() or b) debugging free.
+** Can free memory allocated by MEMWATCH and malloc() both.
+** Does not generate any runtime errors.
+*/
+void* mwMalloc( size_t, const char*, int );
+void* mwMalloc_( size_t );
+void* mwRealloc( void *, size_t, const char*, int );
+void* mwRealloc_( void *, size_t );
+void* mwCalloc( size_t, size_t, const char*, int );
+void* mwCalloc_( size_t, size_t );
+void mwFree( void*, const char*, int );
+void mwFree_( void* );
+char* mwStrdup( const char *, const char*, int );
+
+/*
+** Enable/disable precompiler block
+** This block of defines and if(n)defs make sure that references
+** to MEMWATCH is completely removed from the code if the MEMWATCH
+** manifest constant is not defined.
+*/
+#ifndef __MEMWATCH_C
+#ifdef MEMWATCH
+
+#define mwASSERT(exp) while(mwAssert((int)(exp),#exp,__FILE__,__LINE__))
+#ifndef MW_NOASSERT
+#ifndef ASSERT
+#define ASSERT mwASSERT
+#endif /* !ASSERT */
+#endif /* !MW_NOASSERT */
+#define mwVERIFY(exp) while(mwVerify((int)(exp),#exp,__FILE__,__LINE__))
+#ifndef MW_NOVERIFY
+#ifndef VERIFY
+#define VERIFY mwVERIFY
+#endif /* !VERIFY */
+#endif /* !MW_NOVERIFY */
+#define mwTRACE mwTrace
+#ifndef MW_NOTRACE
+#ifndef TRACE
+#define TRACE mwTRACE
+#endif /* !TRACE */
+#endif /* !MW_NOTRACE */
+
+/* some compilers use a define and not a function */
+/* for strdup(). */
+#ifdef strdup
+#undef strdup
+#endif
+
+#define malloc(n) mwMalloc(n,__FILE__,__LINE__)
+#define strdup(p) mwStrdup(p,__FILE__,__LINE__)
+#define realloc(p,n) mwRealloc(p,n,__FILE__,__LINE__)
+#define calloc(n,m) mwCalloc(n,m,__FILE__,__LINE__)
+#define free(p) mwFree(p,__FILE__,__LINE__)
+#define CHECK() mwTest(__FILE__,__LINE__,MW_TEST_ALL)
+#define CHECK_THIS(n) mwTest(__FILE__,__LINE__,n)
+#define CHECK_BUFFER(b) mwTestBuffer(__FILE__,__LINE__,b)
+#define MARK(p) mwMark(p,#p,__FILE__,__LINE__)
+#define UNMARK(p) mwUnmark(p,__FILE__,__LINE__)
+
+#else /* MEMWATCH */
+
+#define mwASSERT(exp)
+#ifndef MW_NOASSERT
+#ifndef ASSERT
+#define ASSERT mwASSERT
+#endif /* !ASSERT */
+#endif /* !MW_NOASSERT */
+
+#define mwVERIFY(exp) exp
+#ifndef MW_NOVERIFY
+#ifndef VERIFY
+#define VERIFY mwVERIFY
+#endif /* !VERIFY */
+#endif /* !MW_NOVERIFY */
+
+/*lint -esym(773,mwTRACE) */
+#define mwTRACE /*lint -save -e506 */ 1?(void)0:mwDummyTraceFunction /*lint -restore */
+#ifndef MW_NOTRACE
+#ifndef TRACE
+/*lint -esym(773,TRACE) */
+#define TRACE mwTRACE
+#endif /* !TRACE */
+#endif /* !MW_NOTRACE */
+
+extern void mwDummyTraceFunction(const char *,...);
+/*lint -save -e652 */
+#define mwDoFlush(n)
+#define mwPuts(s)
+#define mwInit()
+#define mwGrab(n)
+#define mwDrop(n)
+#define mwLimit(n)
+#define mwTest(f,l)
+#define mwSetOutFunc(f)
+#define mwSetAriFunc(f)
+#define mwDefaultAri()
+#define mwNomansland()
+#define mwStatistics(f)
+#define mwMark(p,t,f,n) (p)
+#define mwUnmark(p,f,n) (p)
+#define mwMalloc(n,f,l) malloc(n)
+#define mwStrdup(p,f,l) strdup(p)
+#define mwRealloc(p,n,f,l) realloc(p,n)
+#define mwCalloc(n,m,f,l) calloc(n,m)
+#define mwFree(p) free(p)
+#define mwMalloc_(n) malloc(n)
+#define mwRealloc_(p,n) realloc(p,n)
+#define mwCalloc_(n,m) calloc(n,m)
+#define mwFree_(p) free(p)
+#define mwAssert(e,es,f,l)
+#define mwVerify(e,es,f,l) (e)
+#define mwTrace mwDummyTrace
+#define mwTestBuffer(f,l,b) (0)
+#define CHECK()
+#define CHECK_THIS(n)
+#define CHECK_BUFFER(b)
+#define MARK(p) (p)
+#define UNMARK(p) (p)
+/*lint -restore */
+
+#endif /* MEMWATCH */
+#endif /* !__MEMWATCH_C */
+
+#ifdef __cplusplus
+ }
+#endif
+
+#if 0 /* 980317: disabled C++ */
+
+/*
+** C++ support section
+** Implements the C++ support. Please note that in order to avoid
+** messing up library classes, C++ support is disabled by default.
+** You must NOT enable it until AFTER the inclusion of all header
+** files belonging to code that are not compiled with MEMWATCH, and
+** possibly for some that are! The reason for this is that a C++
+** class may implement it's own new() function, and the preprocessor
+** would substitute this crucial declaration for MEMWATCH new().
+** You can forcibly deny C++ support by defining MEMWATCH_NOCPP.
+** To enble C++ support, you must be compiling C++, MEMWATCH must
+** be defined, MEMWATCH_NOCPP must not be defined, and finally,
+** you must define 'new' to be 'mwNew', and 'delete' to be 'mwDelete'.
+** Unlike C, C++ code can begin executing *way* before main(), for
+** example if a global variable is created. For this reason, you can
+** declare a global variable of the class 'MemWatch'. If this is
+** is the first variable created, it will then check ALL C++ allocations
+** and deallocations. Unfortunately, this evaluation order is not
+** guaranteed by C++, though the compilers I've tried evaluates them
+** in the order encountered.
+*/
+#ifdef __cplusplus
+#ifndef __MEMWATCH_C
+#ifdef MEMWATCH
+#ifndef MEMWATCH_NOCPP
+extern int mwNCur;
+extern const char *mwNFile;
+extern int mwNLine;
+class MemWatch {
+public:
+ MemWatch();
+ ~MemWatch();
+ };
+void * operator new(size_t);
+void * operator new(size_t,const char *,int);
+void * operator new[] (size_t,const char *,int); // hjc 07/16/02
+void operator delete(void *);
+#define mwNew new(__FILE__,__LINE__)
+#define mwDelete (mwNCur=1,mwNFile=__FILE__,mwNLine=__LINE__),delete
+#endif /* MEMWATCH_NOCPP */
+#endif /* MEMWATCH */
+#endif /* !__MEMWATCH_C */
+#endif /* __cplusplus */
+
+#endif /* 980317: disabled C++ */
+
+#endif /* __MEMWATCH_H */
+
+/* EOF MEMWATCH.H */
diff --git a/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c b/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c
new file mode 100644
index 0000000000..54bd87f6d9
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c
@@ -0,0 +1,21 @@
+#include <string.h>
+
+#include "strhashfunc.h"
+
+unsigned int hashtable_strhashfunc(void * p)
+{
+ const char * str = (const char *)p;
+ unsigned int hash = 5381;
+ int c;
+
+ while ((c = *str++) != '\0')
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return hash;
+}
+
+int hashtable_strequalfunc(void * str1, void * str2)
+{
+ return !strcmp(str1, str2);
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h b/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h
new file mode 100644
index 0000000000..6f02120a05
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h
@@ -0,0 +1,8 @@
+#ifndef __STRHASHFUNC_H
+#define __STRHASHFUNC_H
+
+unsigned int hashtable_strhashfunc(void *str);
+int hashtable_strequalfunc(void * str1, void * str2);
+
+#endif /* #ifndef __STRHASHFUNC_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/contrib/unicows.dll b/plugins/!NotAdopted/VypressChat/contrib/unicows.dll
new file mode 100644
index 0000000000..d9928088d6
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/contrib/unicows.dll
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/docs/AUTHORS b/plugins/!NotAdopted/VypressChat/docs/AUTHORS
new file mode 100644
index 0000000000..cc54d7b96d
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/docs/AUTHORS
@@ -0,0 +1 @@
+Saulius Menkevicius (bob at nulis dot lt)
diff --git a/plugins/!NotAdopted/VypressChat/docs/COPYING b/plugins/!NotAdopted/VypressChat/docs/COPYING
new file mode 100644
index 0000000000..fbdd65f6f8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/docs/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/plugins/!NotAdopted/VypressChat/docs/CREDITS b/plugins/!NotAdopted/VypressChat/docs/CREDITS
new file mode 100644
index 0000000000..7dd09a2258
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/docs/CREDITS
@@ -0,0 +1,10 @@
+Thanks goes to:
+
+- m3x for his IRC protocol source, Chat module SDK and help which
+ made vqchat plugins possible;
+
+- Christopher Clark, for his hashtable routines.
+
+- Miranda-IM developer crew on miranda-im.org for their support.
+
+- All the testers.
diff --git a/plugins/!NotAdopted/VypressChat/docs/ChangeLog b/plugins/!NotAdopted/VypressChat/docs/ChangeLog
new file mode 100644
index 0000000000..5f5b07f2c1
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/docs/ChangeLog
@@ -0,0 +1,50 @@
+$Id: ChangeLog,v 1.27 2005/04/12 19:58:54 bobas Exp $
+
+- 0.9.3.0 (2005-04-12)
+ * Bumped version number a bit;
+ * More broadcast masks can be specified (practically unlimited);
+ * Fixed non-major bug with not handling nickname changes;
+ * Check if there is another contact with the same name before changing
+ it's nickname. This should help avoid duplicate nicknames on DB.
+ * An option to lock contact nickname so the contact will not change
+ the nickname if user changes it, and the contact will become offline
+ instead.
+ * An option can be set for a user if he prefers private messages or
+ private chats for communication (not for vypress chat 2.0+ clients);
+
+- 0.0.9.2 (2005-03-15)
+ * Fix Vypress Chat 2.0 compatibility, message send/recv should work;
+ * Messages can be sent even if the contact is offline; It will be
+ delivered when the contact becomes online;
+ * Add another user info page, where contact's nickname can be set;
+ * Make 'Join Channel' dialog drop down list appear correctly on win98;
+ * Add option whether to append user's nickname when topic is set.
+
+- 0.0.9.1 (2005-03-13)
+ * Always show join/leave messages, even if the user left the net
+ "quietly" (i.e. because his PC rebooted or similar cause);
+ * Reply with who-here only on channels != "#Main";
+ * Fix localized nicknames on chatrooms user lists;
+ * Sound can be specified to be emitted when Alert Beep is received;
+ * Fix some issues with very large user lists;
+ * Show user gender in the generic info page;
+ * Added user info page which shows computer name, user name,
+ IP/IPX address, workgroup, platform, software version and user's
+ channel list;
+ * Put IP address edit controls back (thanks Alexander);
+ * Fix several memory leaks;
+ * Hash first outgoing message to seed rng correctly and avoid
+ collisions if someone else on the network startups miranda exactly
+ at the same second.
+
+- 0.0.9.0 (2005-03-07)
+ * Multiple broadcast masks can be specified (up to 3);
+ * IPX network connection capability;
+ * Support for UTF-8 and Vypress Chat 1.9x has been added;
+ * "<Nickname> has renamed in <Nickname>" is no longer shown
+ when nickname is not changed in options dialog after 'Apply';
+ * 'Join a channel' menu entry is now visible with modified clist_*s;
+ * Lots of various fixes and improvements.
+
+- 0.0.1.0 (2005-02-17)
+ * first release;
diff --git a/plugins/!NotAdopted/VypressChat/docs/NEWS b/plugins/!NotAdopted/VypressChat/docs/NEWS
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/docs/NEWS
diff --git a/plugins/!NotAdopted/VypressChat/docs/README b/plugins/!NotAdopted/VypressChat/docs/README
new file mode 100644
index 0000000000..0a0cf965ee
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/docs/README
@@ -0,0 +1,79 @@
+Readme skeleton (to be written)
+$Id: README,v 1.1 2005/03/02 18:58:34 bobas Exp $
+
+CONTENTS
+*********************************
+1. WHAT IS IT
+2. USAGE
+3. COMPILING FROM SOURCE
+4. CODING
+5. FAQ
+
+
+
+1. WHAT IS IT
+=================================
+1.1. Features
+ TODO
+
+1.2. Requirements
+ TODO
+
+1.3. Limitations
+ TODO
+
+1.4. Authors
+ TODO
+
+2. USAGE
+=================================
+2.1. Settings
+ TODO
+
+2.2. Contacts
+ TODO
+
+2.3. Updating nickname or guid(unique identifier) for a contact
+ TODO
+
+3. COMPILING FROM SOURCE
+=================================
+3.1. Requirements
+ You will need a mingw/cygwin instalation with basic unix
+ tools (like ls, rm) and gcc, make, binutils. Maybe something else.
+ Just try to compile and watch for errors :)
+
+3.2. Gow to build it
+ TODO
+
+3.3. Anonymous access to CVS repository
+ TODO
+
+4. CODING
+=================================
+4.1. Code layout
+ Tabs are 8 chars and max code line length is 100 chars.
+
+4.2. Charset stuff
+ Everything inside plugins' structs and between the calls is done
+ in utf-8, sans some exceptions (strings from/to network and Windows).
+
+ Strings that are sent to network should first be reprocessed
+ by util_utf2vqp(), to translate them to fitting encoding for the
+ receiving host. The same is with incoming network strings
+ - use util_vqp2utf().
+
+ Use 'W Win32 calls to send/recv unicode strings within Win32 API.
+ util_utf2ucs() and util_ucs2uni() routines are provided for
+ conversion from/to wchar_t * to char * and back.
+
+4.3. Basic source overview
+ TODO
+
+4.4. Vypress Chat/quickChat protocol
+ TODO
+
+5. FAQ
+ TODO
+
+EOF
diff --git a/plugins/!NotAdopted/VypressChat/docs/TODO b/plugins/!NotAdopted/VypressChat/docs/TODO
new file mode 100644
index 0000000000..014cbbedcc
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/docs/TODO
@@ -0,0 +1,36 @@
+$Id: TODO,v 1.27 2005/04/11 20:24:01 bobas Exp $
+
+next version
+=====================
+- bug: sometimes topic is shown as ... in vypress chat.
+- bug: (QC: 3. If netcard has more the one ip-addresses (e.g. for EmLan
+ protocol on terminal server), incoming message is duplicated so much
+ time, how many additional ip-addresses are determined :).)
+ ; can this be fixed by filtering incoming packets by registered broadcasts???
+ ; i guess, no, we'll have to provide an adapter list in configuration dialog
+ for the user to select which interface he would like to bind to...
+- check if message was received by handling msgack;
+- check guid(uuids) to identify vypresschat users on net join events;
+- file transfers;
+- doesn't work over vpn;
+- implement the extended miranda's plugin unload mechanism;
+- error messages in configuration page, network error notifies in balloon;
+- VYPRESSCHAT 2.x RSA_FULL-encoded chats;
+- set max nickname, channel names;
+- track if the user is idle (time of last activity);
+- bans by ip/nickname;
+- antiflood measures;
+- check nickname collisions;
+- chatrooms: disable 'send message' on chatroom contact's menu;
+- add localisation file;
+- user details in chatroom info;
+- settings: 'ignore mass messages';
+- report netgroup name;
+- submit default sound for beep;
+- installer script;
+- bug: multicast doesn't work properly.., (hopefully this happens with vmware only);
+- settings: add netcard selection list (this will fix the next bug);
+
+even later:
+===========
+- look at sechat;
diff --git a/plugins/!NotAdopted/VypressChat/docs/dbformat.txt b/plugins/!NotAdopted/VypressChat/docs/dbformat.txt
new file mode 100644
index 0000000000..5225470e59
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/docs/dbformat.txt
@@ -0,0 +1,38 @@
+NULL contact:
+ "Nick" (asciiz) - user nickname (default)
+ "Uuid" (asciiz) - asciiz representation of user's uuid (not used on quickchat)
+ "Port" (word) - port to connect to
+ "ProtoConn" (byte) - connection type: 0: UDP/IP; 1: IPX;
+ "ProtoOpt" (dword) - custom protocol options (from `enum vqp_proto_opt`)
+ "BroadcastMasks" (multiple dword blob)
+ - with all specified network masks;
+ "Multicast" (dword) - multicast address
+ "MulticastScope" (byte) - multicast scope
+ "UserlistTimeout"(byte) - userlist timeout value (in seconds)
+ "Chanlist" (asciiz) - user's list of channels (in chanlist format - see chanlist.c)
+ "Codepage" (byte) - UTF-8, if 1; locale, if 0
+ "Gender" (byte) - gender 'M'/'F'/'?'
+ "MsgOnAlertBeep" (byte) - whether to emit message '*** ALERT BEEP ***' on alert beep;
+ "NicknameOnTopic" (byte) - whether to append user's nickname when topic is set;
+ "ContactsPreferMsg" (byte) - value new contacts will have "PreferMsg" set to
+ "Broadcast#0" (dword) - network broadcast #0 (DEPRECIATED)
+ "Broadcast#1" (dword) - network broadcast #1 (DEPRECIATED)
+ "Broadcast#2" (dword) - network broadcast #2 (DEPRECIATED)
+
+Buddy contact:
+ -- standard ones --
+ "Nick" (assciz) - buddy nickname
+ "Status" (word) - buddy status
+ "Gender" (byte) - gender 'M'/'F'/'?'
+
+ -- non standard settings --
+ "Computer" (utf8 asciiz)- computer name
+ "ComputerUser" (utf8 asciiz) - computer user name
+ "Workgroup" (utf8 asciiz) - computer workgroup
+ "Platform" (utf8 asciiz)- platform name
+ "Software" (utf8 asciiz)- software name
+ "QueuedMsgs" (blob) - see contacts.c:contacts_add_queued_message()
+ "PreferMsg" (byte) - whether user prefers to get private message, instead of
+ using private chats.
+ "LockNick" (byte) - whether to not let the nickname of the contact change
+ automatically when user renames.
diff --git a/plugins/!NotAdopted/VypressChat/iconpack.rc b/plugins/!NotAdopted/VypressChat/iconpack.rc
new file mode 100644
index 0000000000..9f7e35de19
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/iconpack.rc
@@ -0,0 +1,40 @@
+/*
+ * 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: iconpack.rc,v 1.2 2005/02/24 20:06:39 bobas Exp $
+ */
+
+#include "miranda.h"
+
+#ifdef VYPRESSCHAT
+102 ICON FIXED IMPURE "icons/vypresschat.ico"
+104 ICON FIXED IMPURE "icons/vypresschat_online.ico"
+105 ICON FIXED IMPURE "icons/vypresschat_offline.ico"
+128 ICON FIXED IMPURE "icons/vypresschat_away.ico"
+131 ICON FIXED IMPURE "icons/vypresschat_na.ico"
+158 ICON FIXED IMPURE "icons/vypresschat_dnd.ico"
+#endif
+
+#ifdef QUICKCHAT
+102 ICON FIXED IMPURE "icons/quickchat.ico"
+104 ICON FIXED IMPURE "icons/quickchat_online.ico"
+105 ICON FIXED IMPURE "icons/quickchat_offline.ico"
+128 ICON FIXED IMPURE "icons/quickchat_away.ico"
+131 ICON FIXED IMPURE "icons/quickchat_na.ico"
+158 ICON FIXED IMPURE "icons/quickchat_dnd.ico"
+#endif
diff --git a/plugins/!NotAdopted/VypressChat/icons/contact_bell.ico b/plugins/!NotAdopted/VypressChat/icons/contact_bell.ico
new file mode 100644
index 0000000000..ed2c3cf0a0
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/contact_bell.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat.ico
new file mode 100644
index 0000000000..8337ca00d0
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/quickchat.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat_away.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat_away.ico
new file mode 100644
index 0000000000..75ca2da0db
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/quickchat_away.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat_channel.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat_channel.ico
new file mode 100644
index 0000000000..7143ae8181
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/quickchat_channel.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat_dnd.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat_dnd.ico
new file mode 100644
index 0000000000..403a285ada
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/quickchat_dnd.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat_large.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat_large.ico
new file mode 100644
index 0000000000..de95f46e7b
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/quickchat_large.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat_na.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat_na.ico
new file mode 100644
index 0000000000..0420e5682e
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/quickchat_na.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat_offline.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat_offline.ico
new file mode 100644
index 0000000000..0e7f7ab113
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/quickchat_offline.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat_online.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat_online.ico
new file mode 100644
index 0000000000..8337ca00d0
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/quickchat_online.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat.ico
new file mode 100644
index 0000000000..0cd8ed0fb0
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/vypresschat.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat_away.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat_away.ico
new file mode 100644
index 0000000000..4a2ca6ee99
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/vypresschat_away.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat_channel.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat_channel.ico
new file mode 100644
index 0000000000..5e9da74e4e
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/vypresschat_channel.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat_dnd.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat_dnd.ico
new file mode 100644
index 0000000000..d461c6031f
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/vypresschat_dnd.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat_large.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat_large.ico
new file mode 100644
index 0000000000..7ffeaa6897
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/vypresschat_large.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat_na.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat_na.ico
new file mode 100644
index 0000000000..2b331a92e8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/vypresschat_na.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat_offline.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat_offline.ico
new file mode 100644
index 0000000000..2b331a92e8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/vypresschat_offline.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat_online.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat_online.ico
new file mode 100644
index 0000000000..0cd8ed0fb0
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/icons/vypresschat_online.ico
Binary files differ
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/COPYING b/plugins/!NotAdopted/VypressChat/libvqproto/COPYING
new file mode 100644
index 0000000000..fbdd65f6f8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/PROTOCOL b/plugins/!NotAdopted/VypressChat/libvqproto/PROTOCOL
new file mode 100644
index 0000000000..352ccd6057
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/PROTOCOL
@@ -0,0 +1,348 @@
+$Id: PROTOCOL,v 1.1 2005/02/23 16:10:14 bobas Exp $
+
+Vypress Chat protocol description
+revision 1.0, 2005-01-23
+---------------------------------
+
+I've recieved requests for VypressChat protocol specs, so here it is. This is
+based on information I got from Vasily Krysin (echat) and Konstantin Vyatkin
+(Vypress Research). Please note that information concerning 1.9.x is
+incomplete; Vypress Research stopped answering my emails, It look like
+they don't want to support vyqchat anymore. If you know anything more
+than below, please contact me, so I could make this manual as complete
+as possible. Especially RSA usage and bulletin-board support deatils are
+welcome!
+
+I. Vypress Chat 1.5.x protocol
+------------------------------
+
+Warning! Information provided below may contain subtle errors (like
+sender exchanged with reciever). If in doubt, please compare it with
+vcprotocol.cpp from VyQChat (see VCProtocol::vc_* functions). These
+mistakes will be corrected in future revisions of this document.
+
+Each Vypress Chat packet begins with 'X' and 9 random characters from
+'0'..'9' range (unique packet ID). Then comes packet type id (single
+character). For example:
+
+X1234567892#Main\0Nick\0text of string\0
+
+^^ ^^ ^ ^ ^ ^ ^----- 0x00 == string splitter
+|| || | | | |------------------- plain text to display on chat
+|| || | | |--------------------- 0x00 == string splitter
+|| || | |------------------------- nick
+|| || |--------------------------- 0x00 == string splitter
+|| ||-------------------------------- channel
+|| |--------------------------------- 0x32 == type of packet - chat
+||------------------------------------------ packet id
+|------------------------------------------- 0x58 == Vypress Chat
+
+==============================================================================
+Available packets:
+
+**********************************************************************************************
+*** Packet from list updater (Who here)
+
+ '0' + <Name of updater> + #0
+ ^^^ - its a single byte 0x00,
+ showing end of a line
+
+
+**********************************************************************************************
+*** Answer to the previous packet ("Im here")
+
+ '1' + <Name of updater> + #0 + <Name of responder> + #0 + Status + RemoteActive
+ ^^^^^
+ status of responder (byte)
+ '0' - Normal
+ '1' - DND
+ '2' - Away
+ '3' - Offline
+ ^^^^^
+ window state (byte)
+ '0' - active
+ '1' - not active
+
+**********************************************************************************************
+*** Simple chat string on the channel
+
+ '2' + <Name of channel> + #0 + <Name of author> + #0 + <Text> + #0
+
+
+**********************************************************************************************
+*** Nick change
+
+ '3' + <From who> + #0 + <To who> + #0 + Gender
+ ^^^^ - gender of the user
+ '0' - male
+ '1' - female
+
+**********************************************************************************************
+*** Join to channel
+
+ '4' + <Who joined> + #0 + <Name of channel> + #0 + Status + Gender
+
+**********************************************************************************************
+*** Leave from channel
+
+ '5' + <Who leaved> + #0 + <Name of channel> + #0 + Gender
+
+**********************************************************************************************
+*** Message
+
+ '6' + <From who> + #0 + <To who> + #0 + <Text> + #0
+
+**********************************************************************************************
+*** Multiaddress message
+
+ 'E' + <From who> + #0 + <To who> + #0 + <Text> + #0
+
+**********************************************************************************************
+*** Message ack
+
+ '7' + Status + <Who received> + #0 + <To who ack> + #0 + Gender + CurrentAA + #0
+ ^^^^^^^^^
+ current autoanswer (may be empty, for ex in Normal mode)
+
+**********************************************************************************************
+*** Remote execution
+
+ '8' + <Who execute> + #0 + <To who> + #0 + <Command line> + #0 + <Password> + #0
+ ^^^^^^^^
+ may be empty
+
+**********************************************************************************************
+*** Execution ack
+
+ '9' + <To who> + #0 + <From who> + #0 + <Execution text> + #0
+
+**********************************************************************************************
+*** /ME on the channels
+
+ 'A' + <Name of channel> + #0 + <Who said> + #0 + <Text> + #0
+
+**********************************************************************************************
+*** New topic
+
+ 'B' + <Name of channel> + #0 + <Text of topic> + ' (' + <Name of author> + ') ' + #0
+
+**********************************************************************************************
+*** Current topic (sending to each who join channel)
+
+ 'C' + <To who> + #0 + <Name of channel> + #0 + <Text of topic> + #0
+
+**********************************************************************************************
+*** Change of status
+
+ 'D' + <Who changed> + #0 + Status + Gender + CurrentAA + #0
+
+**********************************************************************************************
+*** INFO request
+
+ 'F' + <To who> + #0 + <From who> + #0
+
+**********************************************************************************************
+*** INFO request ack
+
+ 'G' + <To who> + #0 + <From who> + #0 + <Computer name> + #0 + <User name> + #0 + <IP addresses> + #0 + <List of channels - one string without splitting> + '#' + #0 + CurrentAA + #0
+
+**********************************************************************************************
+*** BEEP
+
+ signal
+ 'H' + '0' + <To who> + #0 + <From who> + #0
+
+ ack
+ 'H' + '1' + <To who> + #0 + <From who> + #0 + Gender
+
+**********************************************************************************************
+*** Sound request
+
+ 'I' + <From who> + #0 + <File name> + #0 + <Name of channel> + #0
+
+**********************************************************************************************
+*** Private
+
+ join to chat
+ 'J' + '0' + <From who> + #0 + <To who> + #0 + Gender
+
+ leave chat
+ 'J' + '1' + <From who> + #0 + <To who> + #0 + Gender
+
+ Simple string
+ 'J' + '2' + <From who> + #0 + <To who> + #0 + <Text> + #0
+
+ "/me" string
+ 'J' + '3' + <From who> + #0 + <To who> + #0 + <Text> + #0
+
+**********************************************************************************************
+*** /HERE request
+
+ 'L' + <From who> + #0 + <Name of channel> + #0
+
+**********************************************************************************************
+*** /HERE ack
+
+ 'K' + <To who> + #0 + <Name of channel> + #0 + <From who> + #0 + RemoteActive
+
+**********************************************************************************************
+*** Activity change
+
+ 'M' + <From who> + #0 + RemoteActive
+
+**********************************************************************************************
+*** Request for channel list
+
+ 'N' + <From who> + #0
+
+**********************************************************************************************
+*** Request for channel list ack
+
+ 'O' + <To who> + #0 + <List of channels - one string without splitting> + '#' + #0
+
+**********************************************************************************************
+*** Ping (not implemented - SeaD)
+
+ ping
+ 'P' + '0' + <To who> + #0 + <From who> + #0 + <Time hh:mm:ss - 8 symbols> + #0
+
+ pong
+ 'P' + '1' + <To who> + #0 + <From who> + #0 + <Received time sending back hh:mm:ss - 8 symbols> + #0)
+
+**********************************************************************************************
+
+II. Vypress Chat 1.9.x protocol changes
+---------------------------------------
+
+Vypress Chat 1.9.x uses version advertising, so that newer VC versions
+know what functions are supported by remote side of conversation and can
+preserve compatibility with older software.
+
+Second important change is unique user ID based on the UUID. As far as I
+know UUIDs are supported in Windows only for now. UUID is a 128-bit
+number that should be globally unique and persistent (should not change
+after reset). Vypress Chat 1.9 uses UUID when handling a nickname
+conflict; Vypress Chat 1.9.1 also uses it during file transfer. UUID is
+widely used at 1.9.5 and 2.0 to store RSA public key in association with
+user. (see
+http://msdn.microsoft.com/library/en-us/dnnetcomp/html/PPCGuidGen.asp
+for info on how to create uuids). In vyqchat UUIDs are created using
+OpenSSL random numbers generator, and than saved in config file so it
+doesn't change (until you delete this file); UUID is however not really
+used yet for any purpose, except including in packets.
+
+//**************************************************************************
+//*** Join to the #Main (MAIN_CHANNEL_NAME)
+//
+// '4' + <Who's joined> + #0 + <Channel name> + #0 + Status + Gender
+//
+// Ver 1.9:
+// '4' + <Nickname> + #0 + "#Main" + #0 + (char)Status + (char)Gender + #0 +
+(dword)VERSION + UUID + (char)cCodePage + (DWORD)dwColor + #0
+
+PACKERSION(1,9) indicates the protocol version. Version 1,92 means that
+your client is able to receive files via TCP. Version 1,93 means you
+are able to respond to the TCP pings. Version 1,9 should be used unless
+you implement these featues. Version should be encoded using 4 bytes, so
+for example, version 1.91 looks like this: 5B,00,01,00
+
+cCodePage should be '1' for UTF8 encoding, '0' otherwise.
+
+It is suggested to always set it to '1' (to indicate that you are using
+UTF-8 encodings for all strings) to avoid charset incompatibility. When you
+are joining the network with such flag, Vypress Chat 1.9 or later will send
+you all text string as UTF-8 encoded (including nicknames and channel names
+in all packets).
+
+dwColor is a RGB encoded color value that chat may use to display your
+strings at common chat. This is optional feature in Vypress Chat that allows
+using custom color and if a user has this feature switched on then Chat will
+use this value to display your text. You may use RGB(0,0,0) (black).
+
+Vypress Chat 1.9 relies on the PING-PONG for maintaining users list, so
+you should respond to the following packets:
+
+//**************************************************************************
+//*** PING
+//
+// PING
+// 'P' + '0' + <ToWhom> + #0 + <FromNickname> + #0 + <Time in the format
+hh:mm:ss, 8 charactes> + #0
+
+Response:
+//**************************************************************************
+//*** PONG
+//
+// PONG
+// 'P' + '1' + <ToWhom> + #0 + <MyNicknmane> + #0 + <received time should
+be sent back> + #0)
+
+//**************************************************************************
+//*** I'm here!
+//
+// '1' + <NickFrom> + #0 + <YourNick> + #0 + Status + RemoteActive
+// ^^^^^
+// your current status
+// '0' - Normal
+// '1' - DND
+// '2' - Away
+// '3' - Offline
+// ^^^^^
+// windows state
+// '0' - active
+// '1' - inactive
+//
+// Ver 1.9:
+// '1' + <NickFrom> + #0 + <YourNick> + #0 + (char)Status + RemoteActive +
+#0 + (DWORD)PACKVERSION(1.9) + (char)Gender + UUID + #0 + (DWORD)0
+(reserved) + (char)cCodePage + (DWORD)dwColor#0
+//
+// CodePage: 0 - Current ANSI, 1 - UTF-8
+
+//**************************************************************************
+//*** Who is here?
+//
+// '0' + <FromNickname> + #0 + (char)cCodePage + #0
+//
+
+//**************************************************************************
+//*** Join a channel #Channel
+//
+// '4' + <NicknameFrom> + #0 + <ChannelName> + #0 + Status + Gender
+//
+// Ver 1.9:
+// '4' + <NicknameFrom> + #0 + < ChannelName > + #0 + (char)Status +
+(char)Gender + #0 + (char)cCodePage + #0
+
+
+//**************************************************************************
+//*** Message delivery confirmation / AutoAnswer
+//
+// '7' + Status + <ToNickname> + #0 + <FromNickname> + #0 + Gender
+// + CurrentAA + #0
+//
+^^^^^^^^
+// current auto answer (may be empty)
+// Ver 1.9
+//
+// '7' + Status + <ToNickname> + #0 + <FromNickname> + #0 + Gender +
+CurrentAA + #0 + PacketSignature + #0
+//
+
+PacketSignature - first random bytes from the packet header with message
+
+//**************************************************************************
+//*** INFO request reply
+//
+// 'G' + <ToNickname> + #0 + <FromNickname> + #0 + <Computer name> + #0 +
+<Current user name> + #0 + <IP addresses> + #0 + <Channels list, without
+separators in one line> + '#' + #0 + CurrentAA + #0 + <Platform (OS)
+version> +#0 + <Chat software> + #0
+
+
+//**************************************************************************
+//*** Flood blocking notification (your packets was blocked by other user
+// for flood)
+//
+// 'Z' + <ToNickname> + #0 + <FromNickname> + #0 + <seconds count> + #0
+
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/README b/plugins/!NotAdopted/VypressChat/libvqproto/README
new file mode 100644
index 0000000000..49842d7176
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/README
@@ -0,0 +1,7 @@
+$Id: README,v 1.1 2005/02/23 16:10:14 bobas Exp $
+
+I. What it is
+===============================
+This library implements support for QuickChat and Vypress Chat (tm) networks.
+
+To be written
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/link.c b/plugins/!NotAdopted/VypressChat/libvqproto/link.c
new file mode 100644
index 0000000000..876af36d03
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/link.c
@@ -0,0 +1,611 @@
+/*
+ * libvqproto: Vypress/QChat protocol interface library
+ * (c) Saulius Menkevicius 2005
+ *
+ * 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: link.c,v 1.14 2005/03/08 17:21:23 bobas Exp $
+ */
+
+#include <time.h>
+#include <stdlib.h>
+
+#include "vqproto.h"
+#include "link.h"
+#include "message.h"
+
+/* global data
+ */
+#ifndef MEMWATCH
+void * (* vqp_mmi_malloc)(size_t) = NULL;
+void (* vqp_mmi_free)(void *) = NULL;
+#endif
+
+/* static routines
+ */
+static __inline unsigned short
+ushort2bcd(unsigned short ushort)
+{
+ return (ushort % 10) | (((ushort / 10) % 10) << 4)
+ | (((ushort / 100) % 10) << 8) | (((ushort / 1000) % 10) << 12);
+}
+
+static void
+vqp_make_msg_sig(struct vqp_message_struct * msg)
+{
+ static unsigned rand_num = 0;
+ int i;
+
+ /* hash message contents to get some initial random num */
+ if(!rand_num) {
+ for(i=0; i < msg->content_len; i++)
+ rand_num += msg->content[i];
+ rand_num %= 211;
+ }
+
+ /* make sure the rng is seeded correctly */
+ srand((unsigned) time(NULL) + rand_num++);
+
+ /* generate packet signature */
+ for(i=0; i < VQP_LINK_SIG_LEN; i++)
+ msg->sig[i] = (unsigned char)('0' + rand() % ('9' - '0' + 1));
+
+ /* add '\0' at the end (not truly necessary, but it helps to have
+ * asciiz string instead of an unbounded char[]
+ */
+ msg->sig[VQP_LINK_SIG_LEN] = '\0';
+}
+
+static int
+vqp_is_seen_msg_sig(struct vqp_link_struct * link, char * sig)
+{
+ int i;
+ for(i = 0; i < VQP_LINK_SEEN_SIGS_NUM; i++)
+ if(!memcmp(link->seen_sigs[i], sig, VQP_LINK_SIG_LEN))
+ return 1;
+ return 0;
+}
+
+static void
+vqp_add_seen_msg_sig(struct vqp_message_struct * msg)
+{
+ memcpy(msg->link->seen_sigs[msg->link->seen_sig_inc],
+ msg->sig, VQP_LINK_SIG_LEN);
+ msg->link->seen_sig_inc = (msg->link->seen_sig_inc + 1)
+ % VQP_LINK_SEEN_SIGS_NUM;
+}
+
+static int
+vqp_link_open_setup_udp_multicast(struct vqp_link_struct * link)
+{
+ struct ip_mreq mreq;
+ const unsigned char opt_loop = 1;
+ const unsigned char ttl = 32;
+
+ /* set IP_MULTICAST_LOOP to 1, so our host receives
+ * the messages we send
+ */
+ if(setsockopt( link->tx_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (void*)&opt_loop, sizeof(opt_loop)
+ ) != 0)
+ return -1;
+
+ /* set IP_MULTICAST_TTL to 32, that is the packets
+ * will go through 32 routers before getting scrapped
+ */
+ if(setsockopt( link->tx_socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ (void *)&ttl, sizeof(ttl)
+ ) != 0)
+ return -1;
+
+
+ /* set our group membership */
+ mreq.imr_multiaddr.s_addr = htonl(link->conndata.udp.multicast_address);
+ mreq.imr_interface.s_addr = INADDR_ANY;
+ if(setsockopt( link->rx_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (void *)&mreq, sizeof(mreq)
+ ) != 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+vqp_link_open_setup_tx_broadcast(struct vqp_link_struct * link)
+{
+ const int sock_opt = 1;
+ return setsockopt(
+ link->tx_socket, SOL_SOCKET, SO_BROADCAST,
+ (void *)&sock_opt, sizeof(sock_opt));
+}
+
+static int
+vqp_link_open_setup_udp(struct vqp_link_struct * link, int * p_error)
+{
+ int setup_result;
+ const int sockopt_true = 1;
+ struct sockaddr_in sin;
+
+ /* setup tx socket */
+ link->tx_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if(link->tx_socket < 0) {
+ if(p_error)
+ *p_error = errno;
+ return 1;
+ }
+
+ link->rx_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if(link->rx_socket < 0) {
+ if(p_error)
+ *p_error = errno;
+
+ closesocket(link->tx_socket);
+ return 1;
+ }
+
+ /* on win32 we can setup the socket so we can
+ * use multiple clients on the same pc
+ */
+#ifdef _WIN32
+ setsockopt(
+ link->rx_socket, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&sockopt_true, sizeof(sockopt_true));
+#endif
+
+ /* bind rx socket */
+ sin.sin_family = PF_INET;
+ sin.sin_addr.s_addr = htonl(link->conndata.udp.local_address
+ ? link->conndata.udp.local_address
+ : INADDR_ANY);
+ sin.sin_port = htons(link->port);
+ if(bind(link->rx_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ if(p_error)
+ *p_error = errno;
+
+ closesocket(link->rx_socket);
+ closesocket(link->tx_socket);
+ return 1;
+ }
+
+ link->tx_socket = link->rx_socket;
+
+ /* setup sockets for multicast or broadcast service
+ */
+ setup_result = (link->options & VQP_PROTOCOL_OPT_MULTICAST)
+ ? vqp_link_open_setup_udp_multicast(link)
+ : vqp_link_open_setup_tx_broadcast(link);
+
+ if(setup_result < 0) {
+ if(p_error)
+ *p_error = errno;
+
+ closesocket(link->rx_socket);
+ closesocket(link->tx_socket);
+ return 1;
+ }
+
+ /* success */
+ return 0;
+}
+
+static int vqp_link_open_setup_ipx(struct vqp_link_struct * link, int * p_error)
+{
+ struct sockaddr_ipx six;
+
+ /* setup tx socket */
+ link->tx_socket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
+ if(link->tx_socket < 0) {
+ if(p_error)
+ *p_error = errno;
+ return 1;
+ }
+
+ if(vqp_link_open_setup_tx_broadcast(link)) {
+ if(p_error)
+ *p_error = errno;
+
+ closesocket(link->tx_socket);
+ return 1;
+ }
+
+ /* setup rx socket */
+ link->rx_socket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
+ if(link->rx_socket < 0) {
+ if(p_error)
+ *p_error = errno;
+ closesocket(link->rx_socket);
+ return 1;
+ }
+
+ /* bind rx socket */
+ memset(&six, 0, sizeof(six));
+ six.sa_family = AF_IPX;
+ six.sa_socket = htons(ushort2bcd(link->port));
+
+ if(bind(link->rx_socket, (struct sockaddr *)&six, sizeof(six))) {
+ if(p_error)
+ *p_error = errno;
+
+ closesocket(link->rx_socket);
+ closesocket(link->tx_socket);
+ return 1;
+ }
+
+ /* save node and network number */
+ memcpy(link->conndata.ipx.netnum, six.sa_netnum, 4);
+ memcpy(link->conndata.ipx.nodenum, six.sa_nodenum, 6);
+
+ /* success */
+ return 0;
+}
+
+/* exported routines
+ */
+void vqp_init(
+ void * (* mmi_malloc_func)(size_t),
+ void (* mmi_free_func)(void *))
+{
+#ifndef MEMWATCH
+ if(mmi_malloc_func)
+ vqp_mmi_malloc = mmi_malloc_func;
+ else vqp_mmi_malloc = malloc;
+
+ if(mmi_free_func)
+ vqp_mmi_free = mmi_free_func;
+ else vqp_mmi_free = free;
+#endif
+}
+
+void vqp_uninit()
+{
+}
+
+vqp_link_t vqp_link_open(
+ enum vqp_protocol_type protocol,
+ enum vqp_protocol_options options,
+ enum vqp_protocol_connection connection,
+ unsigned long local_address,
+ unsigned long * p_broadcast_addresses, /* 0UL terminated list */
+ unsigned long multicast_address,
+ unsigned short port,
+ int * p_error)
+{
+ struct vqp_link_struct * link;
+
+ /* alloc and init link struct */
+ link = vqp_mmi_malloc(sizeof(struct vqp_link_struct));
+ if(!link) {
+ if(p_error) *p_error = ENOMEM;
+ return NULL;
+ }
+
+ link->rx_socket = 0;
+ link->tx_socket = 0;
+ link->protocol = protocol;
+ link->options = options;
+ link->connection = connection;
+ link->port = port ? port: 8167;
+ link->seen_sig_inc = 0;
+ memset(link->seen_sigs, 0, VQP_LINK_SEEN_SIGS_NUM * VQP_LINK_SIG_LEN);
+
+ if(connection == VQP_PROTOCOL_CONN_UDP) {
+ /* UDP
+ */
+ link->conndata.udp.local_address = local_address;
+ link->conndata.udp.multicast_address = multicast_address;
+ link->conndata.udp.p_broadcast_addresses = NULL;
+
+ if(vqp_link_open_setup_udp(link, p_error)) {
+ vqp_mmi_free(link);
+ return NULL;
+ }
+
+ /* setup broadcast masks lists
+ */
+ if(!(link->options & VQP_PROTOCOL_OPT_MULTICAST)) {
+ /* standard broadcast
+ */
+ int i;
+ for(i = 0; p_broadcast_addresses[i]; i++) /* nothing */ ;
+
+ if(!i) {
+ /* no broadcast addresses defined:
+ * use 255.255.255.255 */
+ link->conndata.udp.p_broadcast_addresses
+ = vqp_mmi_malloc(sizeof(unsigned long) * 2);
+
+ if(!link->conndata.udp.p_broadcast_addresses) {
+ vqp_mmi_free(link);
+ if(p_error)
+ *p_error = ENOMEM;
+
+ closesocket(link->rx_socket);
+ closesocket(link->tx_socket);
+ return NULL;
+ }
+
+ link->conndata.udp.p_broadcast_addresses[0] = 0xffffffffUL;
+ link->conndata.udp.p_broadcast_addresses[1] = 0UL;
+ } else {
+ /* copy broadcast addresses */
+ size_t listsz = sizeof(unsigned long) * (i + 1);
+ link->conndata.udp.p_broadcast_addresses = vqp_mmi_malloc(listsz);
+
+ if(link->conndata.udp.p_broadcast_addresses == NULL) {
+ vqp_mmi_free(link);
+ if(p_error)
+ *p_error = ENOMEM;
+
+ closesocket(link->rx_socket);
+ closesocket(link->tx_socket);
+ return NULL;
+ }
+ memcpy(link->conndata.udp.p_broadcast_addresses,
+ p_broadcast_addresses, listsz);
+ }
+ }
+ }
+ else {
+ /* IPX
+ */
+ if(vqp_link_open_setup_ipx(link, p_error)) {
+ vqp_mmi_free(link);
+ return NULL;
+ }
+ }
+
+ return (vqp_link_t)link;
+}
+
+int vqp_link_close(vqp_link_t vqlink)
+{
+ struct vqp_link_struct * link = P_VQP_LINK_STRUCT(vqlink);
+
+ closesocket(link->tx_socket);
+ closesocket(link->rx_socket);
+
+ if(link->connection == VQP_PROTOCOL_CONN_UDP && link->conndata.udp.p_broadcast_addresses)
+ vqp_mmi_free(link->conndata.udp.p_broadcast_addresses);
+
+ vqp_mmi_free(link);
+
+ return 0;
+}
+
+int vqp_link_rx_socket(vqp_link_t link)
+{
+ return P_VQP_LINK_STRUCT(link)->rx_socket;
+}
+
+enum vqp_protocol_type
+vqp_link_protocol(vqp_link_t link)
+{
+ return P_VQP_LINK_STRUCT(link)->protocol;
+}
+
+int vqp_link_send(vqp_msg_t vqmsg)
+{
+ struct vqp_message_struct * msg = P_VQP_MESSAGE_STRUCT(vqmsg);
+ char * packet;
+ size_t packet_len;
+
+ /* check that the message contains something */
+ if(msg->content_len == 0)
+ return EINVAL;
+
+ /* check that we have the correct msg dst addr (if specified)
+ */
+
+ if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT) {
+ /* assign & register unique packet id for the message */
+ vqp_make_msg_sig(msg);
+ vqp_add_seen_msg_sig(msg);
+
+ /* alloc real packet contents with signature */
+ packet_len = 1 + VQP_LINK_SIG_LEN + msg->content_len;
+ packet = vqp_mmi_malloc(packet_len);
+ if(!packet)
+ return ENOMEM;
+
+ /* fill packet contents in */
+ packet[0] = 'X'; /* vypress chat packet */
+ memcpy(packet + 1, msg->sig, VQP_LINK_SIG_LEN);
+ memcpy(packet + 1 + VQP_LINK_SIG_LEN,
+ msg->content, msg->content_len);
+ } else {
+ /* there's no packet sig to add for quickchat packets */
+ packet = msg->content;
+ packet_len = msg->content_len;
+ }
+
+ if(msg->link->connection == VQP_PROTOCOL_CONN_UDP) {
+ /* IP/UDP transport
+ */
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = PF_INET;
+ sin.sin_port = htons(msg->link->port);
+
+ /* send message to all the netmasks/multicasts specified */
+ if(!vqp_addr_is_nil(&msg->dst_addr)) {
+ /* send packet directly to specified address
+ */
+ sin.sin_addr.s_addr = htonl(msg->dst_addr.node.ip);
+ sendto(msg->link->tx_socket, packet, packet_len, 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+ }
+ else if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT
+ && (msg->link->options & VQP_PROTOCOL_OPT_MULTICAST))
+ {
+ /* send packet to multicast group
+ */
+ sin.sin_addr.s_addr = htonl(msg->link->conndata.udp.multicast_address);
+ sendto(msg->link->tx_socket, packet, packet_len, 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+ }
+ else {
+ /* send packet to multiple broadcast addresses
+ */
+ int n;
+ for(n = 0; msg->link->conndata.udp.p_broadcast_addresses[n] != 0; n++) {
+ sin.sin_addr.s_addr = htonl(
+ msg->link->conndata.udp.p_broadcast_addresses[n]);
+
+ sendto(msg->link->tx_socket, packet, packet_len, 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+ }
+ }
+ }
+ else if(msg->link->connection == VQP_PROTOCOL_CONN_IPX) {
+ /* IPX transport
+ */
+ struct sockaddr_ipx six;
+
+ memset(&six, 0, sizeof(six));
+ six.sa_family = AF_IPX;
+ six.sa_socket = htons(ushort2bcd(msg->link->port));
+
+ if(!vqp_addr_is_nil(&msg->dst_addr)) {
+ /* send packet to specified address
+ */
+ memcpy(six.sa_netnum, msg->link->conndata.ipx.netnum, 4);
+ memcpy(six.sa_nodenum, msg->dst_addr.node.ipx, 6);
+ }
+ else {
+ /* send packet to broadcast
+ */
+ memset(six.sa_netnum, 0, 4);
+ memset(six.sa_nodenum, 0xff, 6);
+ }
+
+ sendto( msg->link->tx_socket, packet, packet_len, 0,
+ (struct sockaddr *)&six, sizeof(six));
+ }
+
+ /* free packet data */
+ if(packet != msg->content)
+ vqp_mmi_free(packet);
+
+ return 0; /* packet sent ok */
+}
+
+int vqp_link_recv(vqp_link_t vqlink, vqp_msg_t * p_in_msg)
+{
+ struct vqp_link_struct * link = P_VQP_LINK_STRUCT(vqlink);
+ struct vqp_message_struct * msg;
+ struct sockaddr_in sin;
+ struct sockaddr_ipx six;
+ socklen_t sa_len;
+ char * buf;
+ ssize_t buf_data_len;
+
+ /* receive the msg */
+ buf = vqp_mmi_malloc(VQP_MAX_PACKET_SIZE);
+
+ if(link->connection == VQP_PROTOCOL_CONN_UDP) {
+ sa_len = sizeof(sin);
+ buf_data_len = recvfrom(
+ link->rx_socket, (void*)buf, VQP_MAX_PACKET_SIZE, 0,
+ (struct sockaddr *) &sin, &sa_len);
+ } else {
+ sa_len = sizeof(six);
+ buf_data_len = recvfrom(
+ link->rx_socket, (void *)buf, VQP_MAX_PACKET_SIZE, 0,
+ (struct sockaddr *) &six, &sa_len);
+ }
+
+ if(buf_data_len <= 1) {
+ vqp_mmi_free(buf);
+ return errno;
+ }
+
+ if(link->protocol == VQP_PROTOCOL_VYPRESSCHAT) {
+ /* check that the packets begins with 'X' and contains
+ * a signature */
+ if(buf[0] != 'X' || buf_data_len < (1 + VQP_LINK_SIG_LEN + 1)) {
+ vqp_mmi_free(buf);
+ return 1;
+ }
+
+ /* check that the signature is not already seen */
+ if(vqp_is_seen_msg_sig(link, buf + 1)) {
+ vqp_mmi_free(buf);
+ return 1;
+ }
+ }
+
+ /* alloc message */
+ msg = vqp_mmi_malloc(sizeof(struct vqp_message_struct));
+ if(!msg) return 1;
+
+ msg->link = link;
+ msg->src_addr.conn = link->connection;
+ if(link->connection == VQP_PROTOCOL_CONN_UDP) {
+ msg->src_addr.node.ip = ntohl(sin.sin_addr.s_addr);
+ } else {
+ memcpy(msg->src_addr.node.ipx, six.sa_nodenum, 6);
+ }
+
+ /* copy contents */
+ msg->content_len = (link->protocol == VQP_PROTOCOL_VYPRESSCHAT)
+ ? buf_data_len - 1 - VQP_LINK_SIG_LEN
+ : buf_data_len;
+
+ msg->content = vqp_mmi_malloc(msg->content_len);
+ if(!msg->content) {
+ vqp_mmi_free(buf);
+ vqp_mmi_free(msg);
+ return 1;
+ }
+
+ if(link->protocol == VQP_PROTOCOL_VYPRESSCHAT) {
+ /* copy signature */
+ memcpy(msg->sig, buf + 1, VQP_LINK_SIG_LEN);
+ msg->sig[VQP_LINK_SIG_LEN] = '\0';
+
+ /* copy contents */
+ memcpy(msg->content, buf + 1 + VQP_LINK_SIG_LEN, msg->content_len);
+ } else {
+ memcpy(msg->content, buf, msg->content_len);
+ }
+
+ /* free packet buffer */
+ vqp_mmi_free(buf);
+
+ /* return the msg */
+ *p_in_msg = msg;
+ return 0;
+}
+
+void vqp_addr_nil(vqp_link_t link, vqp_addr_t * p_addr)
+{
+ p_addr->conn = P_VQP_LINK_STRUCT(link)->connection;
+ memset(&p_addr->node, 0, sizeof(union vqp_addr_node_union));
+}
+
+int vqp_addr_is_nil(vqp_addr_t * p_addr)
+{
+ int is_nil;
+
+ if(p_addr->conn == VQP_PROTOCOL_CONN_UDP) {
+ is_nil = (p_addr->node.ip == 0);
+ } else {
+ char nil_ipx[6] = { 0, 0, 0, 0, 0, 0 };
+ is_nil = memcmp(nil_ipx, p_addr->node.ipx, 6) == 0;
+ }
+ return is_nil;
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/link.h b/plugins/!NotAdopted/VypressChat/libvqproto/link.h
new file mode 100644
index 0000000000..d7ee7bcbed
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/link.h
@@ -0,0 +1,106 @@
+/*
+ * libvqproto: Vypress/QChat protocol interface library
+ * (c) Saulius Menkevicius 2005
+ *
+ * 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: link.h,v 1.11 2005/03/08 17:21:36 bobas Exp $
+ */
+
+#ifndef __LINK_H
+#define __LINK_H
+
+/* header stuff */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef _WIN32
+/* Win32 system */
+# include <windows.h>
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <wsipx.h>
+#else
+/* UNIX system */
+# include <sys/types.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+# include <sys/time.h>
+# include <unistd.h>
+# include <netdb.h>
+#endif
+
+/* windows compatibility hacks
+ */
+#ifdef WIN32
+#define ENETUNREACH ENOENT
+#define ENOMSG ENOENT
+#else
+/* unix system */
+#define closesocket close
+#endif
+
+/* link structure
+ */
+#define VQP_LINK_SEEN_SIGS_NUM 20
+#define VQP_LINK_SIG_LEN 9
+
+struct vqp_link_struct {
+ /* link info */
+ enum vqp_protocol_type protocol;
+ enum vqp_protocol_options options;
+ enum vqp_protocol_connection connection;
+ unsigned short port;
+
+ /* connection-specific data */
+ union vqp_link_conndata_union {
+ struct vqp_link_conndata_udp_struct {
+ unsigned long local_address;
+ unsigned long * p_broadcast_addresses;
+ unsigned long multicast_address;
+ } udp;
+
+ struct vqp_link_conndata_ipx_struct {
+ unsigned char netnum[4];
+ unsigned char nodenum[6];
+ } ipx;
+ } conndata;
+
+
+ /* link sockets */
+ int rx_socket, tx_socket;
+
+ /* keeps track packet sigs already seen */
+ unsigned int seen_sig_inc;
+ char seen_sigs[VQP_LINK_SEEN_SIGS_NUM][VQP_LINK_SIG_LEN];
+};
+#define P_VQP_LINK_STRUCT(p) ((struct vqp_link_struct *) p)
+
+/* global data */
+
+#ifdef MEMWATCH
+# include "../contrib/memwatch.h"
+# define vqp_mmi_malloc(s) malloc(s)
+# define vqp_mmi_free(p) free(p)
+#else
+extern void * (* vqp_mmi_malloc)(size_t);
+extern void (* vqp_mmi_free)(void *);
+#endif
+
+#endif /* __LINK_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/makefile b/plugins/!NotAdopted/VypressChat/libvqproto/makefile
new file mode 100644
index 0000000000..dc76a8eb0c
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/makefile
@@ -0,0 +1,21 @@
+# $Id: makefile,v 1.5 2005/03/14 22:35:18 bobas Exp $
+
+TARGET_STATIC = libvqproto.a
+
+O_FILES = link.o message.o uuid.o
+HEADERS = vqproto.h link.h message.h uuid.h
+
+$(TARGET_STATIC): $(O_FILES)
+ $(AR) r $(TARGET_STATIC) $(O_FILES)
+
+link.o: link.c $(HEADERS)
+ $(CC) -c link.c -o link.o
+
+message.o: message.c $(HEADERS)
+ $(CC) -c message.c -o message.o
+
+uuid.o: uuid.c $(HEADERS)
+ $(CC) -c uuid.c -o uuid.o
+
+clean:
+ rm -f *.o $(TARGET_STATIC)
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/message.c b/plugins/!NotAdopted/VypressChat/libvqproto/message.c
new file mode 100644
index 0000000000..1ea1233c68
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/message.c
@@ -0,0 +1,1218 @@
+/*
+ * libvqproto: Vypress/QChat protocol interface library
+ * (c) Saulius Menkevicius 2005
+ *
+ * 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: message.c,v 1.18 2005/03/08 16:53:22 bobas Exp $
+ */
+
+#include <stdarg.h>
+#include <sys/types.h>
+
+#include "vqproto.h"
+#include "link.h"
+#include "message.h"
+
+#ifdef _WIN32
+typedef unsigned int uint32_t;
+#endif
+
+/* static data
+ */
+
+/* static routines
+ */
+static char *
+vqp_msg_printf(size_t * p_res_sz, char * fmt, ...)
+{
+ char * buf, * str;
+ size_t buf_sz, buf_alloc, len;
+ va_list vargs;
+ int ival;
+
+#define BUF_ALLOC_CHUNK 256
+#define BUF_ENSURE_FREE_SZ(size) \
+ if(buf_sz + (size) > buf_alloc) { \
+ char * new_buf; \
+ buf_alloc = ((buf_sz + size + BUF_ALLOC_CHUNK - 1) / BUF_ALLOC_CHUNK) \
+ * BUF_ALLOC_CHUNK; \
+ new_buf = vqp_mmi_malloc(buf_alloc); \
+ if(!new_buf) { \
+ vqp_mmi_free(buf); \
+ va_end(vargs); \
+ return NULL; \
+ } \
+ memcpy(new_buf, buf, buf_sz); \
+ vqp_mmi_free(buf); \
+ buf = new_buf; \
+ }
+
+ /* initialize vars */
+ buf_sz = 0;
+ buf_alloc = BUF_ALLOC_CHUNK;
+ buf = vqp_mmi_malloc(buf_alloc);
+ if(!buf) return NULL;
+
+ /* append args */
+ va_start(vargs, fmt);
+
+ for(; *fmt; fmt ++) {
+ if(*fmt == '%') {
+ switch( *(++ fmt)) {
+ case 'c':
+ /* char */
+ BUF_ENSURE_FREE_SZ(1);
+ buf[buf_sz ++] = (char)va_arg(vargs, int);
+ break;
+
+ case 'l':
+ /* long */
+ BUF_ENSURE_FREE_SZ(sizeof(uint32_t));
+ *((uint32_t *)(buf + buf_sz)) = (uint32_t)va_arg(vargs, int);
+ buf_sz += sizeof(uint32_t);
+ break;
+
+ case 's': /* asciiz */
+ len = strlen(str = va_arg(vargs, char *)) + 1;
+ BUF_ENSURE_FREE_SZ(len);
+ memcpy(buf + buf_sz, (str), len);
+ buf_sz += len;
+ break;
+
+ case 'h':
+ /* channel ('#" + asciiz) */
+ len = strlen(str = va_arg(vargs, char *)) + 2;
+ BUF_ENSURE_FREE_SZ(len);
+ buf[buf_sz] = '#';
+ memcpy(buf + buf_sz + 1, str, len - 1);
+ buf_sz += len;
+ break;
+
+ case 'u':
+ /* uuid */
+ BUF_ENSURE_FREE_SZ(sizeof(vqp_uuid_t));
+ memcpy(buf + buf_sz, (char*)va_arg(vargs, vqp_uuid_t *), sizeof(vqp_uuid_t));
+ buf_sz += sizeof(vqp_uuid_t);
+ break;
+
+ case 'S': /* enum vqp_status */
+ BUF_ENSURE_FREE_SZ(1);
+ ival = va_arg(vargs, int);
+ buf[buf_sz++] = ((enum vqp_status)ival == VQP_STATUS_NOTAVAILABLE)
+ ? '3'
+ : ((enum vqp_status)ival == VQP_STATUS_AWAY)
+ ? '2'
+ : ((enum vqp_status)ival == VQP_STATUS_DND)
+ ? '1': '0';
+ break;
+
+ case 'G': /* enum vqp_gender */
+ BUF_ENSURE_FREE_SZ(1);
+ buf[buf_sz ++] = ((enum vqp_gender)va_arg(vargs, int)
+ == VQP_GENDER_MALE) ? '0': '1';
+ break;
+
+ case 'C': /* enum vqp_codepage */
+ BUF_ENSURE_FREE_SZ(1);
+ buf[buf_sz ++] = ((enum vqp_codepage)va_arg(vargs, int)
+ == VQP_CODEPAGE_LOCALE) ? '0': '1';
+ break;
+
+ case 'A': /* enum vqp_active */
+ BUF_ENSURE_FREE_SZ(1);
+ buf[buf_sz ++] = ((enum vqp_active)va_arg(vargs, int)
+ == VQP_ACTIVE_INACTIVE) ? '0': '1';
+ break;
+
+ case '0': /* '\0' */
+ BUF_ENSURE_FREE_SZ(1);
+ buf[buf_sz ++] = '\0';
+ break;
+
+ /* case '%': */
+ default:
+ /* '%' and the rest */
+ BUF_ENSURE_FREE_SZ(1);
+ buf[buf_sz ++] = *fmt;
+ break;
+ }
+ } else {
+ BUF_ENSURE_FREE_SZ(1);
+ buf[buf_sz ++] = *fmt;
+ }
+ }
+
+ va_end(vargs);
+
+ *p_res_sz = buf_sz;
+ return buf;
+}
+
+/* vqp_msg_scanf:
+ * reads message format (same as vqp_msg_printf)
+ * returns:
+ * non-0 on failure,
+ * 0, on success
+ */
+static int
+vqp_msg_scanf(const struct vqp_message_struct * msg, char * fmt, ...)
+{
+ va_list vargs;
+ char ** str_args[8], * str;
+ const char * buf = msg->content, * scan;
+ ssize_t buf_sz = msg->content_len;
+ int n_str_args = 0, i;
+
+ va_start(vargs, fmt);
+
+ for(; *fmt; fmt++) {
+ /* no more data */
+ if(buf_sz <= 0)
+ goto error_cleanup;
+
+ if(*fmt == '%') {
+ switch(* (++fmt)) {
+ case 'c':
+ *(va_arg(vargs, char *)) = *(buf++);
+ buf_sz -= 1;
+ break;
+
+ case 'l':
+ if(buf_sz < sizeof(uint32_t))
+ goto error_cleanup;
+
+ *(va_arg(vargs, uint32_t *)) = *(uint32_t *)buf;
+ buf += sizeof(uint32_t);
+ buf_sz -= sizeof(uint32_t);
+ break;
+
+ case 'u':
+ if(buf_sz < sizeof(vqp_uuid_t))
+ goto error_cleanup;
+
+ memcpy((char*)va_arg(vargs, vqp_uuid_t *), buf, sizeof(vqp_uuid_t));
+ buf += sizeof(vqp_uuid_t);
+ buf_sz -= sizeof(vqp_uuid_t);
+ break;
+
+ case 's':
+ /* get the length of this string */
+ i = 0;
+ for(scan = buf; *scan != '\0'; scan ++)
+ if(++i == buf_sz) break;
+
+ /* alloc & copy the string */
+ str = vqp_mmi_malloc(i + 1);
+ if(!str) goto error_cleanup;
+
+ memcpy(str, buf, i);
+ str[i] = '\0';
+ buf += i + 1;
+ buf_sz -= i + 1;
+
+ /* store arg */
+ str_args[n_str_args] = va_arg(vargs, char **);
+ *str_args[n_str_args++] = str;
+ break;
+
+ case 'h':
+ /* channel must begin with a '#' */
+ if(*buf != '#')
+ goto error_cleanup;
+
+ /* get the length of channel name (minus the '#' char) */
+ i = 0;
+ for(scan = buf + 1; *scan != '\0'; scan ++)
+ if(++i == buf_sz) break;
+
+ /* the channel name must not be an empty string */
+ if(!i) goto error_cleanup;
+
+ /* alloc & copy the string */
+ str = vqp_mmi_malloc(i + 1);
+ if(!str) goto error_cleanup;
+
+ /* copy the channel name and '\0' (minus the '#') */
+ memcpy(str, buf + 1, i);
+ str[i] = '\0';
+ buf += i + 2;
+ buf_sz -= i + 2;
+
+ /* store arg */
+ str_args[n_str_args] = va_arg(vargs, char **);
+ *str_args[n_str_args++] = str;
+ break;
+
+ case 'S':
+ *(va_arg(vargs, enum vqp_status *)) =
+ (*buf == '3')
+ ? VQP_STATUS_NOTAVAILABLE
+ : (*buf == '2')
+ ? VQP_STATUS_AWAY
+ : (*buf == '1')
+ ? VQP_STATUS_DND
+ : VQP_STATUS_AVAILABLE;
+ buf ++;
+ buf_sz --;
+ break;
+
+ case 'G':
+ *(va_arg(vargs, enum vqp_gender *)) =
+ (*buf == '0') ? VQP_GENDER_MALE: VQP_GENDER_FEMALE;
+ buf ++;
+ buf_sz --;
+ break;
+
+ case 'C':
+ *(va_arg(vargs, enum vqp_codepage *)) =
+ (*buf == '0') ? VQP_CODEPAGE_LOCALE: VQP_CODEPAGE_UTF8;
+ buf ++;
+ buf_sz --;
+ break;
+
+ case 'A':
+ *(va_arg(vargs, enum vqp_active *)) =
+ (*buf == '0') ? VQP_ACTIVE_INACTIVE: VQP_ACTIVE_ACTIVE;
+ buf ++;
+ buf_sz --;
+ break;
+
+ case '0':
+ default:
+ if(*buf != (*fmt == '0' ? '\0': *fmt))
+ goto error_cleanup;
+ buf ++;
+ buf_sz --;
+ break;
+ }
+ } else if(*fmt == '\0') {
+ /* ignore the rest of buf string */
+ goto success;
+ } else {
+ if(*fmt != *buf)
+ goto error_cleanup;
+ buf ++;
+ buf_sz --;
+ }
+ }
+
+success:
+ /* everything was parsed ok */
+ return 0;
+
+error_cleanup:
+ /* free the strings we've alloced so far
+ * and nullify their argument pointers
+ */
+ for(i = 0; i < n_str_args; i++) {
+ free(*str_args[i]);
+ *str_args[i] = NULL;
+ }
+
+ va_end(vargs);
+ return 1;
+}
+
+/* exported routines
+ */
+
+const char *
+vqp_msg_signature(const vqp_msg_t msg)
+{
+ return P_VQP_MESSAGE_STRUCT(msg)->sig;
+}
+
+vqp_link_t
+vqp_msg_link(const vqp_msg_t msg)
+{
+ return (vqp_link_t)P_VQP_MESSAGE_STRUCT(msg)->link;
+}
+
+vqp_addr_t
+vqp_msg_src_addr(const vqp_msg_t msg)
+{
+ return P_VQP_MESSAGE_STRUCT(msg)->src_addr;
+}
+
+/* vqp_msg_parse:
+ * parses the specified message and invokes the corresponding
+ * callback for the message type specified in `parse_func' struct
+ * returns:
+ * non-0 on error
+ * 0 on success
+ */
+void vqp_msg_parse(
+ const struct vqp_parse_func_struct * parsers,
+ const vqp_msg_t vqmsg,
+ void * user_data)
+{
+ const struct vqp_message_struct * msg = P_VQP_MESSAGE_STRUCT(vqmsg);
+ char * s_src = NULL, * s_dst = NULL, * s_text = NULL,
+ * s_timestamp = NULL, * s_floodsecs = NULL, * s_password = NULL,
+ * s_cmdline = NULL, * s_channel = NULL, * s_packetsig = NULL,
+ * s_workgroup = NULL;
+ enum vqp_codepage codepage;
+ enum vqp_status status;
+ enum vqp_gender gender;
+ enum vqp_active active;
+ vqp_uuid_t uuid;
+ unsigned int i_swversion, i_pref_color, i_reserved;
+
+ if(msg->content_len < 2)
+ return;
+
+ switch(msg->content[0])
+ {
+ case '0': /* refresh req */
+ if(!parsers->func_refresh_req)
+ break;
+
+ if(!vqp_msg_scanf(msg, "0%s%C", &s_src, &codepage)) {
+ parsers->func_refresh_req(vqmsg, user_data, s_src, codepage);
+ }
+ else if(!vqp_msg_scanf(msg, "0%s", &s_src)) {
+ parsers->func_refresh_req(vqmsg, user_data, s_src, VQP_CODEPAGE_LOCALE);
+ }
+ break;
+
+ case '1': /* refresh ack */
+ if(!parsers->func_refresh_ack)
+ break;
+
+ /* try to parse as a vypresschat 1.9+ version of ack */
+ if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT
+ && !vqp_msg_scanf(
+ msg, "1%s%s%S%A%0" "%l%G%u" "%0%l%C%l",
+ &s_dst, &s_src, &status, &active,
+ &i_swversion, &gender, &uuid,
+ &i_reserved, &codepage, &i_pref_color))
+ {
+ parsers->func_refresh_ack(
+ vqmsg, user_data,
+ s_src, s_dst, status, active, gender, i_swversion, &uuid,
+ codepage, i_pref_color);
+ }
+ else if(!vqp_msg_scanf(msg, "1%s%s%S%A", &s_dst, &s_src, &status, &active))
+ {
+ vqp_uuid_t empty_uuid;
+ vqp_uuid_create_nil(&empty_uuid);
+
+ parsers->func_refresh_ack(
+ vqmsg, user_data,
+ s_src, s_dst, status, active,
+ VQP_COMPAT_GENDER, VQP_COMPAT_SWVERSION, &empty_uuid,
+ VQP_COMPAT_CODEPAGE, VQP_COMPAT_PREF_COLOR);
+ }
+ else if(msg->link->protocol == VQP_PROTOCOL_QUICKCHAT
+ && !vqp_msg_scanf(msg, "1%s%s%S", &s_dst, &s_src, &status))
+ {
+ vqp_uuid_t empty_uuid;
+ vqp_uuid_create_nil(&empty_uuid);
+
+ parsers->func_refresh_ack(
+ vqmsg, user_data,
+ s_src, s_dst, status,
+ VQP_COMPAT_ACTIVE, VQP_COMPAT_GENDER, VQP_COMPAT_SWVERSION,
+ &empty_uuid, VQP_COMPAT_CODEPAGE, VQP_COMPAT_PREF_COLOR);
+ }
+
+ break;
+
+ case 'P': /* ping / pong */
+ if(msg->link->protocol != VQP_PROTOCOL_VYPRESSCHAT)
+ break;
+
+ if(!vqp_msg_scanf(msg, "P0%s%s%s", &s_dst, &s_src, &s_timestamp)) {
+ if(parsers->func_ping)
+ parsers->func_ping(vqmsg, user_data, s_src, s_dst, s_timestamp);
+ }
+ else if(!vqp_msg_scanf(msg, "P1%s%s%s", &s_dst, &s_src, &s_timestamp)) {
+ if(parsers->func_ping)
+ parsers->func_ping(vqmsg, user_data, s_src, s_dst, s_timestamp);
+ }
+
+ break;
+
+ case 'Z': /* flood notification */
+ if(msg->link->protocol != VQP_PROTOCOL_VYPRESSCHAT
+ || !parsers->func_flood_notification)
+ break;
+
+ if(!vqp_msg_scanf(msg, "Z%s%s%s", &s_dst, &s_src, &s_floodsecs)) {
+ parsers->func_flood_notification(
+ vqmsg, user_data, s_src, s_dst, s_floodsecs);
+ }
+
+ break;
+
+ case '8': /* remote execution */
+ if(!parsers->func_remote_exec)
+ break;
+
+ if(!vqp_msg_scanf(msg, "8%s%s%s%s", &s_src, &s_dst, &s_cmdline, s_password)) {
+ parsers->func_remote_exec(
+ vqmsg, user_data,
+ s_src, s_dst, s_cmdline, s_password);
+ }
+
+ break;
+
+ case '9': /* remote exec ack */
+ if(!parsers->func_remote_exec_ack)
+ break;
+
+ if(!vqp_msg_scanf(msg, "9%s%s%s", &s_dst, &s_src, &s_text)) {
+ parsers->func_remote_exec_ack(
+ vqmsg, user_data,
+ s_src, s_dst, s_text);
+ }
+ break;
+
+ case '3': /* nick change */
+ if(!parsers->func_nick_change)
+ break;
+
+ if(!vqp_msg_scanf(msg, "3%s%s%G", &s_src, &s_text, &gender)) {
+ parsers->func_nick_change(vqmsg, user_data, s_src, s_text, gender);
+ }
+ else if(!vqp_msg_scanf(msg, "3%s%s", &s_src, &s_text)) {
+ parsers->func_nick_change(
+ vqmsg, user_data, s_src, s_text, VQP_COMPAT_GENDER);
+ }
+
+ break;
+
+ case 'D': /* status change */
+ if(!parsers->func_status_change)
+ break;
+
+ if(!vqp_msg_scanf(msg, "D%s%S%G%s", &s_src, &status, &gender, &s_text)) {
+ parsers->func_status_change(
+ vqmsg, user_data, s_src, status, gender, s_text);
+ }
+
+ break;
+
+ case 'M': /* active change */
+ if(!parsers->func_active_change)
+ break;
+
+ if(!vqp_msg_scanf(msg, "M%s%A", &s_src, &active)) {
+ parsers->func_active_change(vqmsg, user_data, s_src, active);
+ }
+
+ break;
+
+ case '4': /* channel join */
+ if(!parsers->func_channel_join)
+ break;
+
+ if(msg->link->protocol==VQP_PROTOCOL_VYPRESSCHAT
+ && !vqp_msg_scanf(
+ msg, "4%s#" VQP_MAIN_CHANNEL "%0%S%G%0%l%u" "%0%C%l%0",
+ &s_src, &status, &gender, &i_swversion, &uuid,
+ &codepage, &i_pref_color))
+ {
+ parsers->func_channel_join(
+ vqmsg, user_data,
+ VQP_MAIN_CHANNEL, s_src, status, gender, i_swversion, &uuid,
+ codepage, i_pref_color);
+ }
+ else if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT
+ && !vqp_msg_scanf(
+ msg, "4%s%h%S%G%0%C%0",
+ &s_src, &s_channel, &status, &gender, &codepage))
+ {
+ vqp_uuid_t nil_uuid;
+ vqp_uuid_create_nil(&nil_uuid);
+
+ parsers->func_channel_join(
+ vqmsg, user_data,
+ s_channel, s_src, status, gender, VQP_COMPAT_SWVERSION, &nil_uuid,
+ codepage, VQP_COMPAT_PREF_COLOR);
+ }
+ else if(!vqp_msg_scanf(msg, "4%s%h%S%G", &s_src, &s_channel, &status, &gender)) {
+ vqp_uuid_t nil_uuid;
+ vqp_uuid_create_nil(&nil_uuid);
+
+ parsers->func_channel_join(
+ vqmsg, user_data,
+ s_channel, s_src, status, gender, VQP_COMPAT_SWVERSION,
+ &nil_uuid, VQP_COMPAT_CODEPAGE, VQP_COMPAT_PREF_COLOR);
+ }
+
+ break;
+
+ case '5': /* channel leave */
+ if(!parsers->func_channel_leave)
+ break;
+
+ if(!vqp_msg_scanf(msg, "5%s%h%G", &s_src, &s_channel, &gender)) {
+ parsers->func_channel_leave(vqmsg, user_data, s_channel, s_src, gender);
+ }
+
+ break;
+
+ case '2': /* channel text */
+ if(!parsers->func_channel_text)
+ break;
+
+ if(!vqp_msg_scanf(msg, "2%h%s%s", &s_channel, &s_src, &s_text)) {
+ parsers->func_channel_text(
+ vqmsg, user_data, s_channel, s_src, s_text, 0);
+ }
+
+ break;
+
+ case 'A': /* channel /me text */
+ if(!parsers->func_channel_text)
+ break;
+
+ if(!vqp_msg_scanf(msg, "A%h%s%s", &s_channel, &s_src, &s_text)) {
+ parsers->func_channel_text(
+ vqmsg, user_data, s_channel, s_src, s_text, 1);
+ }
+
+ break;
+
+ case 'I': /* channel sound request */
+ if(!parsers->func_channel_sound)
+ break;
+
+ if(!vqp_msg_scanf(msg, "I%s%s%h", &s_src, &s_text, &s_channel)) {
+ parsers->func_channel_sound(
+ vqmsg, user_data, s_channel, s_src, s_text);
+ }
+
+ break;
+
+ case 'B': /* topic change */
+ if(!parsers->func_channel_topic_change)
+ break;
+
+ if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT
+ && !vqp_msg_scanf(msg, "B%h%s", &s_channel, &s_text))
+ {
+ parsers->func_channel_topic_change(
+ vqmsg, user_data, s_channel, s_text);
+ }
+ else if(msg->link->protocol == VQP_PROTOCOL_QUICKCHAT
+ && !vqp_msg_scanf(msg, "B%s", &s_text))
+ {
+ parsers->func_channel_topic_change(
+ vqmsg, user_data, VQP_MAIN_CHANNEL, s_text);
+ }
+
+ break;
+
+ case 'C': /* current topic */
+ if(!parsers->func_channel_current_topic)
+ break;
+
+ if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT
+ && !vqp_msg_scanf(msg, "C%s%h%s", &s_dst, &s_channel, &s_text))
+ {
+ parsers->func_channel_current_topic(
+ vqmsg, user_data, s_dst, s_channel, s_text);
+ }
+ else if(msg->link->protocol == VQP_PROTOCOL_QUICKCHAT
+ && !vqp_msg_scanf(msg, "C%s%s", &s_dst, &s_text))
+ {
+ parsers->func_channel_current_topic(
+ vqmsg, user_data, s_dst, VQP_MAIN_CHANNEL, s_text);
+ }
+
+ break;
+
+ case 'L': /* whois is on this channel req */
+ if(!parsers->func_channel_whohere_req)
+ break;
+
+ if(!vqp_msg_scanf(msg, "L%s%h", &s_src, &s_channel)) {
+ parsers->func_channel_whohere_req(
+ vqmsg, user_data, s_channel, s_src);
+ }
+
+ break;
+
+ case 'K': /* whois is on this channel ack */
+ if(!parsers->func_channel_whohere_ack)
+ break;
+
+ if(!vqp_msg_scanf(msg, "K%s%h%s%A", &s_dst, &s_channel, &s_src, &active)) {
+ parsers->func_channel_whohere_ack(
+ vqmsg, user_data, s_channel, s_src, s_dst, active);
+ }
+
+ break;
+
+ case 'N': /* channel list req */
+ if(!parsers->func_channel_list_req)
+ break;
+
+ if(!vqp_msg_scanf(msg, "N%s", &s_src)) {
+ parsers->func_channel_list_req(vqmsg, user_data, s_src);
+ }
+
+ break;
+
+ case 'O': /* channel list ack */
+ if(!parsers->func_channel_list_ack)
+ break;
+
+ if(!vqp_msg_scanf(msg, "O%s%s", &s_dst, &s_text)) {
+ parsers->func_channel_list_ack(vqmsg, user_data, s_dst, s_text);
+ }
+
+ break;
+
+ case '6': /* message */
+ if(!parsers->func_message)
+ break;
+
+ if(!vqp_msg_scanf(msg, "6%s%s%s", &s_src, &s_dst, &s_text)) {
+ parsers->func_message(vqmsg, user_data, s_src, s_dst, s_text, 0);
+ }
+
+ break;
+
+ case 'E': /* multiaddress message */
+ if(!parsers->func_message)
+ break;
+
+ if(!vqp_msg_scanf(msg, "E%s%s%s", &s_src, &s_dst, &s_text)) {
+ parsers->func_message(vqmsg, user_data, s_src, s_dst, s_text, 1);
+ }
+
+ break;
+
+ case '7': /* message ack */
+ if(!parsers->func_message_ack)
+ break;
+
+ if(msg->link->protocol==VQP_PROTOCOL_VYPRESSCHAT
+ && !vqp_msg_scanf(
+ msg, "7%S%s%s%G%s%s",
+ &status, &s_dst, &s_src, &gender, &s_text, &s_packetsig))
+ {
+ parsers->func_message_ack(
+ vqmsg, user_data,
+ s_src, s_dst, status, gender, s_text, s_packetsig);
+ }
+ else if(!vqp_msg_scanf(
+ msg, "7%S%s%s%G%s",
+ &status, &s_dst, &s_src, &gender, &s_text))
+ {
+ parsers->func_message_ack(
+ vqmsg, user_data,
+ s_src, s_dst, status, gender, s_text, VQP_COMPAT_PACKET_SIG);
+ }
+
+ break;
+
+ case 'F': /* info req */
+ if(!parsers->func_info_req)
+ break;
+
+ if(!vqp_msg_scanf(msg, "F%s%s", &s_dst, &s_src)) {
+ parsers->func_info_req(vqmsg, user_data, s_src, s_dst);
+ }
+
+ break;
+
+ case 'G': /* info ack */
+ if(!parsers->func_info_ack)
+ break;
+
+ if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT
+ && !vqp_msg_scanf(msg, "G%s%s%s%s" "%s%s%s" "%s%s%s",
+ &s_dst, &s_src, &s_text, &s_packetsig,
+ &s_timestamp, &s_channel, &s_password,
+ &s_workgroup, &s_cmdline, &s_floodsecs))
+ {
+ parsers->func_info_ack(
+ vqmsg, user_data,
+ s_src, s_dst, s_text, s_packetsig,
+ s_timestamp, s_channel, s_password, s_workgroup,
+ s_cmdline, s_floodsecs);
+ }
+ else if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT
+ && !vqp_msg_scanf(msg, "G%s%s%s%s" "%s%s%s",
+ &s_dst, &s_src, &s_text, &s_packetsig,
+ &s_timestamp, &s_channel, &s_cmdline))
+ {
+ parsers->func_info_ack(
+ vqmsg, user_data,
+ s_src, s_dst, s_text, s_packetsig,
+ s_timestamp, s_channel, s_cmdline, VQP_COMPAT_NETGROUP,
+ VQP_COMPAT_PLATFORM, VQP_COMPAT_SWNAME_VC
+ );
+ }
+ else if(msg->link->protocol == VQP_PROTOCOL_QUICKCHAT
+ && !vqp_msg_scanf(msg, "G%s%s%s%s" "%s%s%s%s",
+ &s_dst, &s_src, &s_text, &s_packetsig,
+ &s_timestamp, &s_password, &s_channel, &s_cmdline))
+ {
+ char * address_str = vqp_mmi_malloc(64);
+
+ if(address_str) {
+ if(msg->link->connection == VQP_PROTOCOL_CONN_UDP) {
+ sprintf(address_str, "%u.%u.%u.%u:%u",
+ (unsigned)((msg->src_addr.node.ip >> 24) & 0xff),
+ (unsigned)((msg->src_addr.node.ip >> 16) & 0xff),
+ (unsigned)((msg->src_addr.node.ip >> 8) & 0xff),
+ (unsigned)(msg->src_addr.node.ip & 0xff),
+ (unsigned)msg->link->port
+ );
+ } else {
+ sprintf(address_str, "%x-%x-%x-%x-%x-%x:%u",
+ (unsigned)(msg->src_addr.node.ipx[0]),
+ (unsigned)(msg->src_addr.node.ipx[1]),
+ (unsigned)(msg->src_addr.node.ipx[2]),
+ (unsigned)(msg->src_addr.node.ipx[3]),
+ (unsigned)(msg->src_addr.node.ipx[4]),
+ (unsigned)(msg->src_addr.node.ipx[5]),
+ (unsigned)msg->link->port
+ );
+ }
+
+ parsers->func_info_ack(
+ vqmsg, user_data,
+ s_src, s_dst, s_text, s_packetsig,
+ address_str, s_channel, s_cmdline, VQP_COMPAT_NETGROUP,
+ VQP_COMPAT_PLATFORM, VQP_COMPAT_SWNAME_QC
+ );
+ vqp_mmi_free(address_str);
+ }
+ }
+
+ break;
+
+ case 'H': /* beep / beep-ack */
+ if(msg->content[1] == '0' && parsers->func_beep_signal) {
+ if(!vqp_msg_scanf(msg, "H0%s%s", &s_dst, &s_src)) {
+ parsers->func_beep_signal(vqmsg, user_data, s_src, s_dst);
+ }
+ }
+ else if(msg->content[1] == '1' && parsers->func_beep_ack) {
+ if(!vqp_msg_scanf(msg, "H1%s%s%G", &s_dst, &s_src, &gender)) {
+ parsers->func_beep_ack(vqmsg, user_data, s_src, s_dst, gender);
+ }
+ }
+
+ break;
+
+ case 'J': /* private open/close/text */
+ if(msg->content[1] == '0' && parsers->func_private_open) {
+ if(!vqp_msg_scanf(msg, "J0%s%s%G", &s_src, &s_dst, &gender)) {
+ parsers->func_private_open(vqmsg, user_data, s_src, s_dst, gender);
+ }
+ }
+ else if(msg->content[1] == '1' && parsers->func_private_close) {
+ if(!vqp_msg_scanf(msg, "J1%s%s%G", &s_src, &s_dst, &gender)) {
+ parsers->func_private_close(vqmsg, user_data, s_src, s_dst, gender);
+ }
+ }
+ else if(msg->content[1] == '2' && parsers->func_private_text) {
+ if(!vqp_msg_scanf(msg, "J2%s%s%s", &s_src, &s_dst, &s_text)) {
+ parsers->func_private_text(
+ vqmsg, user_data, s_src, s_dst, s_text, 0);
+ }
+ }
+ else if(msg->content[1] == '3' && parsers->func_private_text) {
+ if(!vqp_msg_scanf(msg, "J3%s%s%s", &s_src, &s_dst, &s_text)) {
+ parsers->func_private_text(
+ vqmsg, user_data, s_src, s_dst, s_text, 1);
+ }
+ }
+
+ break;
+
+ default:
+ /* unknown message */
+ break;
+ }
+
+ /* free strings we've parsed in */
+ if(s_src) vqp_mmi_free(s_src);
+ if(s_dst) vqp_mmi_free(s_dst);
+ if(s_text) vqp_mmi_free(s_text);
+ if(s_timestamp) vqp_mmi_free(s_timestamp);
+ if(s_floodsecs) vqp_mmi_free(s_floodsecs);
+ if(s_password) vqp_mmi_free(s_password);
+ if(s_cmdline) vqp_mmi_free(s_cmdline);
+ if(s_channel) vqp_mmi_free(s_channel);
+ if(s_packetsig) vqp_mmi_free(s_packetsig);
+ if(s_workgroup) vqp_mmi_free(s_workgroup);
+}
+
+void vqp_msg_free(vqp_msg_t msg)
+{
+ if(P_VQP_MESSAGE_STRUCT(msg)->content)
+ vqp_mmi_free(P_VQP_MESSAGE_STRUCT(msg)->content);
+
+ vqp_mmi_free(msg);
+}
+
+void vqp_msg_set_dst_addr(vqp_msg_t msg, vqp_addr_t dst_addr)
+{
+ P_VQP_MESSAGE_STRUCT(msg)->dst_addr = dst_addr;
+}
+
+/* message contructor functions
+ */
+
+#define NEWMSG_INIT(vqlink) \
+ struct vqp_message_struct * msg; \
+ msg = vqp_mmi_malloc(sizeof(struct vqp_message_struct)); \
+ if(!msg) return NULL; \
+ msg->link = (vqlink); \
+ vqp_addr_nil(vqlink, &msg->dst_addr);
+
+#define NEWMSG_PRINTF(fmt, ...) \
+ msg->content = vqp_msg_printf(&msg->content_len, fmt, __VA_ARGS__); \
+ if(!msg->content) { vqp_mmi_free(msg); return NULL; }
+
+#define NEWMSG_RETURN() \
+ return (vqp_msg_t)msg;
+
+/* user detection messages
+ */
+vqp_msg_t vqp_msg_refresh_req(
+ vqp_link_t link,
+ const char * src, enum vqp_codepage src_codepage)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("0%s%C%0", src, src_codepage);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_refresh_ack(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ enum vqp_status src_status, enum vqp_active src_active, enum vqp_gender src_gender,
+ unsigned int src_swversion, const vqp_uuid_t * src_uuid,
+ enum vqp_codepage src_codepage, unsigned int src_pref_color)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF(
+ "1%s%s%S%A%0" "%l%G%u%0%l" "%C%l%0",
+ dst, src, src_status, src_active,
+ (unsigned int)src_swversion, src_gender, src_uuid, (unsigned int)0,
+ src_codepage, (unsigned int)src_pref_color);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_ping(
+ vqp_link_t link,
+ const char * src, const char * dst, const char * time_stamp)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("P0%s%s%s", dst, src, time_stamp);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_pong(
+ vqp_link_t link,
+ const char * src, const char * dst, const char * orig_time_stamp)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("P1%s%s%s", dst, src, orig_time_stamp);
+ NEWMSG_RETURN();
+}
+
+/* flood notification */
+vqp_msg_t vqp_msg_flood_notification(
+ vqp_link_t link,
+ const char * src, const char * dst, const char * secs_blocked)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("Z%s%s%s", dst, src, secs_blocked);
+ NEWMSG_RETURN();
+}
+
+/* remote execution */
+vqp_msg_t vqp_msg_remote_exec(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * cmdline, const char * password)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("8%s%s%s%s", dst, src, cmdline, password);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_remote_exec_ack(
+ vqp_link_t link,
+ const char * src, const char * dst, const char * exec_text)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("9%s%s%s", dst, src, exec_text);
+ NEWMSG_RETURN();
+}
+
+/* user status */
+vqp_msg_t vqp_msg_nick_change(
+ vqp_link_t link,
+ const char * src, const char * src_new_nick, enum vqp_gender src_gender)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("3%s%s%G", src, src_new_nick, src_gender);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_status_change(
+ vqp_link_t link,
+ const char * src, enum vqp_status src_status,
+ enum vqp_gender src_gender, const char * src_autoanswer)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("D%s%S%G%s", src, src_status, src_gender, src_autoanswer);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_active_change(
+ vqp_link_t link,
+ const char * src, enum vqp_active src_is_active)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("M%s%A", src, src_is_active);
+ NEWMSG_RETURN();
+}
+
+/* channels */
+vqp_msg_t vqp_msg_channel_join(
+ vqp_link_t link,
+ const char * channel, const char * src,
+ enum vqp_status src_status, enum vqp_gender src_gender,
+ unsigned int src_swversion, const vqp_uuid_t * src_uuid,
+ enum vqp_codepage src_codepage, unsigned int src_pref_color)
+{
+ NEWMSG_INIT(link);
+
+ if(!strcmp(channel, VQP_MAIN_CHANNEL)) {
+ NEWMSG_PRINTF(
+ "4%s%h%S%G%0" "%l%u%0%C%l%0%0",
+ src, channel, src_status, src_gender,
+ src_swversion, src_uuid, src_codepage, src_pref_color);
+ } else {
+ NEWMSG_PRINTF(
+ "4%s%h%S%G%C%0",
+ src, channel, src_status, src_gender, src_codepage);
+ }
+
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_leave(
+ vqp_link_t link,
+ const char * channel, const char * src,
+ enum vqp_gender src_gender)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("5%s%h%G", src, channel, src_gender);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_text(
+ vqp_link_t link,
+ const char * channel, const char * src,
+ const char * text, int is_action_text)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF(is_action_text ? "A%h%s%s": "2%h%s%s", channel, src, text);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_sound(
+ vqp_link_t link,
+ const char * channel, const char * src,
+ const char * sound_filename)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("I%s%s%h", src, sound_filename, channel);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_topic_change(
+ vqp_link_t link,
+ const char * channel, const char * topic_text)
+{
+ NEWMSG_INIT(link);
+ if(P_VQP_LINK_STRUCT(link)->protocol == VQP_PROTOCOL_VYPRESSCHAT) {
+ NEWMSG_PRINTF("B%h%s", channel, topic_text);
+ } else {
+ NEWMSG_PRINTF("B%s", topic_text);
+ }
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_current_topic(
+ vqp_link_t link,
+ const char * channel, const char * dst, const char * topic_text)
+{
+ NEWMSG_INIT(link);
+ if(P_VQP_LINK_STRUCT(link)->protocol == VQP_PROTOCOL_VYPRESSCHAT) {
+ NEWMSG_PRINTF("C%s%h%s", dst, channel, topic_text);
+ } else {
+ NEWMSG_PRINTF("C%s%s", dst, topic_text);
+ }
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_whohere_req(
+ vqp_link_t link,
+ const char * channel, const char * src)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("L%s%h", src, channel);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_whohere_ack(
+ vqp_link_t link,
+ const char * channel, const char * src, const char * dst,
+ enum vqp_active src_is_active)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("K%s%h%s%A", dst, channel, src, src_is_active);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_list_req(
+ vqp_link_t link,
+ const char * src)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("N%s", src);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_channel_list_ack(
+ vqp_link_t link,
+ const char * dst, const char * channel_list)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("O%s%s", dst, channel_list);
+ NEWMSG_RETURN();
+}
+
+/* message delivery/ack */
+vqp_msg_t vqp_msg_message(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * text, int is_multiaddress_msg)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF(is_multiaddress_msg ? "E%s%s%s": "6%s%s%s", src, dst, text);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_message_ack(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ enum vqp_status src_status, enum vqp_gender src_gender,
+ const char * src_autoanswer, const char * orig_packetsig)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("7%S%s%s%G%s%s",
+ src_status, dst, src, src_gender, src_autoanswer, orig_packetsig);
+ NEWMSG_RETURN();
+}
+
+/* info req-ack */
+vqp_msg_t vqp_msg_info_req(
+ vqp_link_t link,
+ const char * src, const char * dst)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("F%s%s", dst, src);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_info_ack(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * src_computer_name, const char * src_user_name,
+ const char * src_ip_address, const char * src_channel_list,
+ const char * src_autoanswer, const char * src_workgroup,
+ const char * src_platform, const char * src_software)
+{
+ NEWMSG_INIT(link);
+
+ if(P_VQP_LINK_STRUCT(link)->protocol == VQP_PROTOCOL_VYPRESSCHAT) {
+ NEWMSG_PRINTF("G%s%s%s%s" "%s%s%s" "%s%s%s%0",
+ dst, src, src_computer_name, src_user_name,
+ src_ip_address, src_channel_list, src_autoanswer,
+ src_workgroup, src_platform, src_software);
+ } else {
+ NEWMSG_PRINTF("G%s%s%s%s0 %%%00 Kb%0%s%s",
+ dst, src, src_computer_name, src_user_name,
+ src_channel_list, src_autoanswer);
+ }
+ NEWMSG_RETURN();
+}
+
+/* beep's */
+vqp_msg_t vqp_msg_beep_signal(
+ vqp_link_t link,
+ const char * src, const char * dst)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("H0%s%s", dst, src);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_beep_ack(
+ vqp_link_t link,
+ const char * src, const char * dst, enum vqp_gender src_gender)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("H1%s%s%G", dst, src, src_gender);
+ NEWMSG_RETURN();
+}
+
+/* privates */
+vqp_msg_t vqp_msg_private_open(
+ vqp_link_t link,
+ const char * src, const char * dst, enum vqp_gender src_gender)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("J0%s%s%G", src, dst, src_gender);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_private_close(
+ vqp_link_t link,
+ const char * src, const char * dst, enum vqp_gender src_gender)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF("J1%s%s%G", src, dst, src_gender);
+ NEWMSG_RETURN();
+}
+
+vqp_msg_t vqp_msg_private_text(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * text, int is_action_text)
+{
+ NEWMSG_INIT(link);
+ NEWMSG_PRINTF(
+ is_action_text ? "J3%s%s%s": "J2%s%s%s",
+ src, dst, text);
+ NEWMSG_RETURN();
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/message.h b/plugins/!NotAdopted/VypressChat/libvqproto/message.h
new file mode 100644
index 0000000000..7f39081f27
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/message.h
@@ -0,0 +1,57 @@
+/*
+ * libvqproto: Vypress/QChat protocol interface library
+ * (c) Saulius Menkevicius 2005
+ *
+ * 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: message.h,v 1.12 2005/03/08 16:53:22 bobas Exp $
+ */
+
+#ifndef __MESSAGE_H
+#define __MESSAGE_H
+
+#include "link.h"
+
+/* message defaults */
+#define VQP_COMPAT_ACTIVE VQP_ACTIVE_ACTIVE
+#define VQP_COMPAT_GENDER VQP_GENDER_MALE
+#define VQP_COMPAT_CODEPAGE VQP_CODEPAGE_LOCALE
+#define VQP_COMPAT_SWVERSION 0
+#define VQP_COMPAT_PREF_COLOR 0
+#define VQP_COMPAT_PACKET_SIG ""
+#define VQP_COMPAT_SWNAME_QC "QuickChat 1.5 (or compatible)"
+#define VQP_COMPAT_SWNAME_VC "VypressChat 1.5 (or compatible)"
+#define VQP_COMPAT_NETGROUP ""
+#ifdef _WIN32
+# define VQP_COMPAT_PLATFORM "Windows"
+#else
+# define VQP_COMPAT_PLATFORM "Unix"
+#endif
+
+/* message structs */
+
+struct vqp_message_struct {
+ struct vqp_link_struct * link;
+
+ char sig[VQP_LINK_SIG_LEN + 1]; /* packet signature */
+ size_t content_len; /* message length */
+ char * content; /* message contents */
+ vqp_addr_t src_addr; /* packet source address */
+ vqp_addr_t dst_addr; /* packet destination address (can be not specified) */
+};
+#define P_VQP_MESSAGE_STRUCT(p) ((struct vqp_message_struct *)p)
+
+#endif /* __MESSAGE_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/protocol-direct-connections b/plugins/!NotAdopted/VypressChat/libvqproto/protocol-direct-connections
new file mode 100644
index 0000000000..9de272f17f
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/protocol-direct-connections
@@ -0,0 +1,97 @@
+$Id: protocol-direct-connections,v 1.1 2005/03/17 11:02:44 bobas Exp $
+
+Vypress Chat 1.9x+ direct connection descriptions
+(used for file transfer and private messages)
+2005-03-14
+---------------------------------
+
+You should have TCP listening socket at the same port as you have
+UDP message socket. After accepting and getting the socket you
+should recv an packet header, with 2 newlines at the end. newlines
+are (char)0x0a.
+
+[File transfer request]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: file-transfer<0x0a>
+ content-type: binary<0x0a>
+ content-length: 213<0x0a>
+ file-name: abc.txt<0x0a>
+ file-desc: my text file description<0x0a>
+ files-left: 0<0x0a>
+ desc-files-name:<0x20>
+ desc-files-size: 0<0x0a>
+ user-uuid: my-uuid(MS UUID string representation)<0x0a>
+ <0x0a>
+ <0x0a>
+ <0x00>
+
+ [<file-content> if we get <file-transfer-ack>]
+
+[File transfer ack]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: file-transfer-ready<0x0a>
+ <0x0a>
+
+[File transfer deny]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: file-transfer-deny<0x0a>
+ start-from: 0<0x0a>
+ <0x0a>
+
+[File transfer done]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: file-transfer-done<0x0a>
+ ?????????????????????????????????
+
+[Chat request]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: chat-request<0x0a>
+ user-uuid: <my-uuid(MS UUID string representation)><0x0a>
+ user-name: <my-nickname><0x0a>
+ ckey: <public key for this communication><0x0a>
+ content-length: 0<0x0a>
+ <0x0a>
+
+[Chat request OK]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: chat-request-ok<0x0a>
+ user-uuid: <my-uuid(MS UUID string representation)><0x0a>
+ user-name: HIGHLAND<0x0a>
+ ckey: <0x0a>
+ content-length: 0<0x0a>
+ <0x0a>
+
+[Chat msg: typing]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: chat-msg-typing<0x0a>
+ user-uuid: <my-uuid(MS UUID string representation)><0x0a>
+ user-name: HIGHLAND<0x0a>
+ typing-mode: <'0' or '1'><0x0a>
+ content-length: 0<0x0a>
+ <0x0a>
+
+[Chat msg]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: chat-msg<0x0a>
+ user-uuid: <my-uuid(MS UUID string representation)><0x0a>
+ user-name: HIGHLAND<0x0a>
+ content-length: <content-length><0x0a>
+ <0x0a>
+ <content>
+
+[Chat request FIN]
+ protocol: vychat<0x0a>
+ version: 1<0x0a>
+ message-type: chat-request-fin
+ user-uuid: <my-uuid><0x0a>
+ user-name: <my-nickname><0x0a>
+ content-length: 0<0x0a>
+ <0x0a>
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/uuid.c b/plugins/!NotAdopted/VypressChat/libvqproto/uuid.c
new file mode 100644
index 0000000000..9d4073af07
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/uuid.c
@@ -0,0 +1,108 @@
+/*
+ * libvqproto: Vypress/QChat protocol interface library
+ * (c) Saulius Menkevicius 2005
+ *
+ * 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: uuid.c,v 1.2 2005/03/02 18:29:09 bobas Exp $
+ */
+
+#include "vqproto.h"
+#include "link.h"
+#include "uuid.h"
+
+#ifdef _WIN32
+# include <rpc.h>
+#endif
+
+/* static data
+ */
+
+/* static routines
+ */
+
+/* exported routines
+ */
+
+void vqp_uuid_create(vqp_uuid_t * p_uuid)
+{
+#ifdef _WIN32
+ UuidCreate((UUID *) p_uuid);
+#else
+ /* XXX */
+#endif
+}
+
+void vqp_uuid_create_nil(vqp_uuid_t * p_uuid)
+{
+#ifdef _WIN32
+ UuidCreateNil((UUID *) p_uuid);
+#else
+ /* XXX */
+#endif
+}
+
+int vqp_uuid_is_nil(const vqp_uuid_t * p_uuid)
+{
+#ifdef _WIN32
+ RPC_STATUS rpc_status;
+ return UuidIsNil((UUID *) p_uuid, &rpc_status);
+#else
+ /* XXX */
+ return 0;
+#endif
+}
+
+int vqp_uuid_is_equal(const vqp_uuid_t * p_uuid_a, const vqp_uuid_t * p_uuid_b)
+{
+#ifdef _WIN32
+ RPC_STATUS rpc_status;
+ return UuidCompare((UUID *) p_uuid_a, (UUID *) p_uuid_b, &rpc_status) == 0;
+#else
+ /* XXX */
+ return 0;
+#endif
+}
+
+char * vqp_uuid_to_string(const vqp_uuid_t * p_uuid)
+{
+#ifdef _WIN32
+ unsigned char * rpc_str;
+ char * str;
+ int len;
+
+ UuidToString((UUID *) p_uuid, &rpc_str);
+ len = strlen(rpc_str) + 1;
+ str = vqp_mmi_malloc(len);
+ memcpy(str, rpc_str, len);
+ RpcStringFree(&rpc_str);
+
+ return str;
+#else
+ /* XXX */
+ return NULL;
+#endif
+}
+
+int vqp_uuid_from_string(vqp_uuid_t * p_uuid, const char * str)
+{
+#ifdef _WIN32
+ return UuidFromString((unsigned char *)str, (UUID *) p_uuid) != RPC_S_OK;
+#else
+ /* XXX */
+ return 1;
+#endif
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/uuid.h b/plugins/!NotAdopted/VypressChat/libvqproto/uuid.h
new file mode 100644
index 0000000000..485ef5ce48
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/uuid.h
@@ -0,0 +1,27 @@
+/*
+ * libvqproto: Vypress/QChat protocol interface library
+ * (c) Saulius Menkevicius 2005
+ *
+ * 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: uuid.h,v 1.1 2005/02/28 19:55:23 bobas Exp $
+ */
+
+#ifndef __UUID_H
+#define __UUID_H
+
+
+#endif /* __UUID_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/vqproto.h b/plugins/!NotAdopted/VypressChat/libvqproto/vqproto.h
new file mode 100644
index 0000000000..a631936c26
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/libvqproto/vqproto.h
@@ -0,0 +1,595 @@
+/*
+ * libvqproto: Vypress/QChat protocol interface library
+ * (c) Saulius Menkevicius 2005
+ *
+ * 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: vqproto.h,v 1.14 2005/03/17 11:02:44 bobas Exp $
+ */
+
+#ifndef __VQPROTO_H
+#define __VQPROTO_H
+
+/* required headers */
+#include <sys/types.h>
+
+/* notes
+ *-----------------------------------
+
+I. All the functions (except some of them) return 0 on success, and non-0
+ as unix-style error constant (ENOMEM, EACCESS, etc).
+
+*/
+
+/* API constants & macros
+ *-----------------------------------*/
+#define VQP_MAX_PACKET_SIZE 0x2000
+#define VQP_MAIN_CHANNEL "Main"
+#define VQP_MAKE_SWVERSION(hi, lo) \
+ ((((hi) & 0xffff) << 16) | ((lo) & 0xffff))
+
+/* type definitions
+ *-----------------------------------*/
+typedef void * vqp_link_t;
+typedef void * vqp_msg_t;
+typedef void * vqp_dconn_t;
+
+typedef struct vqp_uuid_struct {
+ unsigned long data1;
+ unsigned short data2, data3;
+ unsigned char data4[8];
+} vqp_uuid_t;
+
+enum vqp_protocol_type {
+ VQP_PROTOCOL_QUICKCHAT,
+ VQP_PROTOCOL_VYPRESSCHAT
+}
+;
+enum vqp_protocol_options {
+ VQP_PROTOCOL_OPT_MULTICAST = 1,
+ VQP_PROTOCOL_OPT_FILETRANSFER = 2
+};
+
+enum vqp_protocol_connection {
+ VQP_PROTOCOL_CONN_UDP,
+ VQP_PROTOCOL_CONN_IPX
+};
+
+enum vqp_status {
+ VQP_STATUS_AVAILABLE,
+ VQP_STATUS_AWAY,
+ VQP_STATUS_DND,
+ VQP_STATUS_NOTAVAILABLE
+};
+
+enum vqp_active {
+ VQP_ACTIVE_ACTIVE,
+ VQP_ACTIVE_INACTIVE
+};
+
+enum vqp_gender {
+ VQP_GENDER_MALE,
+ VQP_GENDER_FEMALE
+};
+
+enum vqp_codepage {
+ VQP_CODEPAGE_LOCALE,
+ VQP_CODEPAGE_UTF8
+};
+
+typedef struct vqp_addr_struct {
+ enum vqp_protocol_connection conn;
+
+ union vqp_addr_node_union {
+ unsigned long ip;
+ unsigned char ipx[6];
+ } node;
+} vqp_addr_t;
+
+struct vqp_parse_func_struct {
+ unsigned int struct_size;
+
+ /* user detection */
+ void (* func_refresh_req)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, enum vqp_codepage src_codepage);
+
+ void (* func_refresh_ack)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst,
+ enum vqp_status src_mode, enum vqp_active src_active, enum vqp_gender src_gender,
+ unsigned int src_swversion, const vqp_uuid_t * src_uuid,
+ enum vqp_codepage src_codepage, unsigned int src_pref_color);
+
+ void (* func_ping)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst, const char * time_stamp);
+
+ void (* func_pong)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst, const char * orig_time_stamp);
+
+ /* flood notification */
+ void (* func_flood_notification)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst, const char * secs_blocked);
+
+ /* remote execution */
+ void (* func_remote_exec)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst,
+ const char * cmdline, const char * password);
+
+ void (* func_remote_exec_ack)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst, const char * exec_text);
+
+ /* user status */
+ void (* func_nick_change)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * src_new_nick, enum vqp_gender src_gender);
+
+ void (* func_status_change)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, enum vqp_status src_status,
+ enum vqp_gender src_gender, const char * src_autoanswer);
+
+ void (* func_active_change)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, enum vqp_active src_is_active);
+
+ /* channels */
+ void (* func_channel_join)(
+ const vqp_msg_t msg, void * user_data,
+ const char * channel, const char * src,
+ enum vqp_status src_status, enum vqp_gender src_gender,
+ unsigned int src_swversion, const vqp_uuid_t * src_uuid,
+ enum vqp_codepage src_codepage, unsigned int src_pref_color);
+
+ void (* func_channel_leave)(
+ const vqp_msg_t msg, void * user_data,
+ const char * channel, const char * src,
+ enum vqp_gender src_gender);
+
+ void (* func_channel_text)(
+ const vqp_msg_t msg, void * user_data,
+ const char * channel, const char * src,
+ const char * text, int is_action_text);
+
+ void (* func_channel_sound)(
+ const vqp_msg_t msg, void * user_data,
+ const char * channel, const char * src,
+ const char * sound_filename);
+
+ void (* func_channel_topic_change)(
+ const vqp_msg_t msg, void * user_data,
+ const char * channel, const char * topic_text);
+
+ void (* func_channel_current_topic)(
+ const vqp_msg_t msg, void * user_data,
+ const char * channel, const char * dst,
+ const char * topic_text);
+
+ void (* func_channel_whohere_req)(
+ const vqp_msg_t msg, void * user_data,
+ const char * channel, const char * src);
+
+ void (* func_channel_whohere_ack)(
+ const vqp_msg_t msg, void * user_data,
+ const char * channel, const char * src, const char * dst,
+ enum vqp_active src_is_active);
+
+ void (* func_channel_list_req)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src);
+
+ void (* func_channel_list_ack)(
+ const vqp_msg_t msg, void * user_data,
+ const char * dst, const char * channel_list);
+
+ /* message delivery/ack */
+ void (* func_message)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst,
+ const char * text, int is_multiaddress_msg);
+
+ void (* func_message_ack)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst,
+ enum vqp_status src_status, enum vqp_gender src_gender,
+ const char * src_autoanswer, const char * orig_packetsig);
+
+ /* info req-ack */
+ void (* func_info_req)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst);
+
+ void (* func_info_ack)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst,
+ const char * src_computer_name, const char * src_user_name,
+ const char * src_ip_addresses, const char * src_channel_list,
+ const char * src_autoanswer, const char * src_workgroup,
+ const char * src_platform, const char * src_software);
+
+ /* beep's */
+ void (* func_beep_signal)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst);
+
+ void (* func_beep_ack)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst, enum vqp_gender src_gender);
+
+ /* privates */
+ void (* func_private_open)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst, enum vqp_gender src_gender);
+
+ void (* func_private_close)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst, enum vqp_gender src_gender);
+
+ void (* func_private_text)(
+ const vqp_msg_t msg, void * user_data,
+ const char * src, const char * dst,
+ const char * text, int is_action_text);
+};
+
+struct vqp_dconn_parse_funcs {
+ unsigned int struct_size;
+
+ /* user detection */
+ void (* func_file_transfer) (
+ const vqp_dconn_t dconn, void * user_data,
+ size_t content_len, const char * file_name,
+ const char * file_desc, int files_left, vqp_uuid_t * p_uuid);
+
+ void (* func_file_transfer_ready) (
+ const vqp_dconn_t dconn, void * user_data);
+
+ void (* func_data)(
+ const vqp_dconn_t dconn, void * user_data,
+ void * data, size_t data_sz);
+};
+
+/* link functions
+ *-----------------------------------*/
+
+/* vqp_init:
+ * initializes the library.
+ *
+ * you can override the default memory interface (malloc/free) with user-supplied
+ * routines by specifying mmi_malloc and mmi_free parameters
+ */
+void vqp_init(
+ void * (* mmi_malloc)(size_t),
+ void (* mmi_free)(void *));
+
+/* vqp_uninit:
+ * deinitializes the library.
+ *
+ * currently does nothing, but you should call it anyway
+ */
+void vqp_uninit();
+
+/* vqp_link_open:
+ * allocates new link to the network
+ *
+ * returns:
+ * link identifier (vqp_link_t), or NULL on error
+ * unix error num in *p_error
+ */
+vqp_link_t vqp_link_open(
+ enum vqp_protocol_type protocol,
+ enum vqp_protocol_options options,
+ enum vqp_protocol_connection connection,
+ unsigned long local_address, /* address to bind to (can be 0) (with IP only) */
+ unsigned long * p_broadcast_addresses, /* 0UL terminated list, (with IP only) */
+ unsigned long multicast_address, /* (with IP only) */
+ unsigned short port,
+ int * p_error);
+
+/* vqp_link_close:
+ * closes the link
+ */
+int vqp_link_close(vqp_link_t link);
+
+/* vqp_link_rx_socket:
+ * returns RX (receiving) socket of the link
+ * which can be used to select() the link message socket
+ */
+int vqp_link_rx_socket(vqp_link_t link);
+
+/* vqp_link_file_rx:
+ * returns server socket of the link that you can select() on and accept()
+ * (this works with VQP_PROTOCOL_CONN_UDP only)
+ */
+int vqp_link_file_rx(vqp_link_t link);
+
+/* vqp_link_protocol:
+ * returns links's protocol
+ */
+enum vqp_protocol_type vqp_link_protocol(vqp_link_t);
+
+/* vqp_link_send:
+ * sends specified message
+ */
+int vqp_link_send(vqp_msg_t msg);
+
+/* vqp_link_recv:
+ * receives message from the link
+ *
+ * returns:
+ * 0, on success, *p_in_msg is set to new message
+ * unix-style errno (Exxx), on error
+ */
+int vqp_link_recv(vqp_link_t link, vqp_msg_t * p_in_msg);
+
+/* address funcs
+ *-----------------------------------*/
+/* vqp_addr_nil:
+ * returns a 'nil' address for specified link
+ */
+void vqp_addr_nil(vqp_link_t link, vqp_addr_t * p_addr);
+
+/* vqp_addr_is_nil:
+ * returns if address is a nil address
+ */
+int vqp_addr_is_nil(vqp_addr_t * p_addr);
+
+/* uuid (unique user identifier) funcs
+ *-----------------------------------*/
+
+/* vqp_uuid_create:
+ * creates unique uuid,
+ */
+void vqp_uuid_create(vqp_uuid_t * uuid);
+
+/* vqp_uuid_create_nil:
+ * creates a nil uuid,
+ */
+void vqp_uuid_create_nil(vqp_uuid_t * uuid);
+
+/* vqp_uuid_is_nil:
+ * returns non-0, if the uuid is nil
+ */
+int vqp_uuid_is_nil(const vqp_uuid_t * uuid);
+
+/* vqp_uuid_is_equal:
+ * returns non-0 if the uuids are equal
+ */
+int vqp_uuid_is_equal(const vqp_uuid_t * uuid_a, const vqp_uuid_t * uuid_b);
+
+/* vqp_uuid_to_string:
+ * returns a malloc'ed string representation of the uuid
+ */
+char * vqp_uuid_to_string(const vqp_uuid_t * uuid);
+
+/* vqp_uuid_from_string:
+ * tries to parse the uuid string and copy its contents
+ * to @uuid;
+ * returns:
+ * 0 on success,
+ * non-o on failure
+ */
+int vqp_uuid_from_string(vqp_uuid_t * uuid, const char * str);
+
+/* message handling
+ *-----------------------------------*/
+const char * vqp_msg_signature(const vqp_msg_t msg);
+vqp_link_t vqp_msg_link(const vqp_msg_t msg);
+vqp_addr_t vqp_msg_src_addr(const vqp_msg_t msg);
+
+void vqp_msg_parse(
+ const struct vqp_parse_func_struct * parse_func,
+ const vqp_msg_t msg,
+ void * user_data);
+
+void vqp_msg_free(vqp_msg_t msg);
+
+void vqp_msg_set_dst_addr(vqp_msg_t msg, vqp_addr_t dst_addr);
+
+/* user detection */
+vqp_msg_t vqp_msg_refresh_req(
+ vqp_link_t link,
+ const char * src, enum vqp_codepage src_codepage);
+
+vqp_msg_t vqp_msg_refresh_ack(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ enum vqp_status src_mode, enum vqp_active src_active, enum vqp_gender src_gender,
+ unsigned int src_swversion, const vqp_uuid_t * src_uuid,
+ enum vqp_codepage src_codepage, unsigned int src_pref_color);
+
+vqp_msg_t vqp_msg_ping(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * time_stamp);
+
+vqp_msg_t vqp_msg_pong(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * orig_time_stamp);
+
+/* flood notification */
+vqp_msg_t vqp_msg_flood_notification(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * secs_blocked);
+
+/* remote execution */
+vqp_msg_t vqp_msg_remote_exec(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * cmdline, const char * password);
+
+vqp_msg_t vqp_msg_remote_exec_ack(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * exec_text);
+
+/* user status */
+vqp_msg_t vqp_msg_nick_change(
+ vqp_link_t link,
+ const char * src, const char * src_new_nick, enum vqp_gender src_gender);
+
+vqp_msg_t vqp_msg_status_change(
+ vqp_link_t link,
+ const char * src, enum vqp_status src_status,
+ enum vqp_gender src_gender, const char * src_autoanswer);
+
+vqp_msg_t vqp_msg_active_change(
+ vqp_link_t link,
+ const char * src, enum vqp_active src_is_active);
+
+/* channels */
+vqp_msg_t vqp_msg_channel_join(
+ vqp_link_t link,
+ const char * src, const char * channel,
+ enum vqp_status src_status, enum vqp_gender src_gender,
+ unsigned int src_swversion, const vqp_uuid_t * src_uuid,
+ enum vqp_codepage src_codepage, unsigned int src_pref_color);
+
+vqp_msg_t vqp_msg_channel_leave(
+ vqp_link_t link,
+ const char * channel, const char * src,
+ enum vqp_gender src_gender);
+
+vqp_msg_t vqp_msg_channel_text(
+ vqp_link_t link,
+ const char * channel, const char * src,
+ const char * text, int is_action_text);
+
+vqp_msg_t vqp_msg_channel_sound(
+ vqp_link_t link,
+ const char * channel, const char * src,
+ const char * sound_filename);
+
+vqp_msg_t vqp_msg_channel_topic_change(
+ vqp_link_t link,
+ const char * channel, const char * topic_text);
+
+vqp_msg_t vqp_msg_channel_current_topic(
+ vqp_link_t link,
+ const char * channel, const char * dst,
+ const char * topic_text);
+
+vqp_msg_t vqp_msg_channel_whohere_req(
+ vqp_link_t link,
+ const char * channel, const char * src);
+
+vqp_msg_t vqp_msg_channel_whohere_ack(
+ vqp_link_t link,
+ const char * channel, const char * src, const char * dst,
+ enum vqp_active src_is_active);
+
+vqp_msg_t vqp_msg_channel_list_req(
+ vqp_link_t link,
+ const char * src);
+
+vqp_msg_t vqp_msg_channel_list_ack(
+ vqp_link_t link,
+ const char * dst, const char * channel_list);
+
+/* message delivery/ack */
+vqp_msg_t vqp_msg_message(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * text, int is_multiaddress_msg);
+
+vqp_msg_t vqp_msg_message_ack(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ enum vqp_status src_status, enum vqp_gender src_gender,
+ const char * src_autoanswer, const char * orig_packetsig);
+
+/* info req-ack */
+vqp_msg_t vqp_msg_info_req(
+ vqp_link_t link,
+ const char * src, const char * dst);
+
+vqp_msg_t vqp_msg_info_ack(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * src_computer_name, const char * src_user_name,
+ const char * src_ip_addresses, const char * src_channel_list,
+ const char * src_autoanswer, const char * src_workgroup,
+ const char * src_platform, const char * src_software);
+
+/* beep's */
+vqp_msg_t vqp_msg_beep_signal(
+ vqp_link_t link,
+ const char * src, const char * dst);
+
+vqp_msg_t vqp_msg_beep_ack(
+ vqp_link_t link,
+ const char * src, const char * dst, enum vqp_gender src_gender);
+
+/* privates */
+vqp_msg_t vqp_msg_private_open(
+ vqp_link_t link,
+ const char * src, const char * dst, enum vqp_gender src_gender);
+
+vqp_msg_t vqp_msg_private_close(
+ vqp_link_t link,
+ const char * src, const char * dst, enum vqp_gender src_gender);
+
+vqp_msg_t vqp_msg_private_text(
+ vqp_link_t link,
+ const char * src, const char * dst,
+ const char * text, int is_action_text);
+
+/* direct connection message funcs
+ * (used for file transfers and private messages on vypress chat 2.0)
+ *--------------------------------------------------------------------*/
+
+vqp_dconn_t vqp_dconn_accept(vqp_link_t link);
+
+vqp_dconn_t vqp_dconn_open(vqp_link_t link, vqp_addr_t remote_addr);
+void vqp_dconn_close(vqp_dconn_t dconn);
+
+int vqp_dconn_socket(vqp_dconn_t dconn);
+
+int vqp_dconn_read(
+ vqp_dconn_t dconn, const struct vqp_dconn_parse_funcs * parse_funcs,
+ void * user_data);
+
+int vqp_dconn_send_file_transfer_request(
+ vqp_dconn_t dconn, size_t content_len, const char * file_name,
+ const char * file_desc, int files_left, const vqp_uuid_t * p_uuid);
+
+int vqp_dconn_send_file_transfer_ready(vqp_dconn_t dconn);
+int vqp_dconn_send_file_transfer_deny(vqp_dconn_t dconn);
+int vqp_dconn_send_file_transfer_done(vqp_dconn_t dconn);
+
+int vqp_dconn_send_chat_request(
+ vqp_dconn_t dconn, const vqp_uuid_t * p_uuid, const char * nickname,
+ const char * public_key);
+int vqp_dconn_send_chat_request_ok(
+ vqp_dconn_t dconn, const vqp_uuid_t * p_uuid, const char * nickname);
+int vqp_dconn_send_chat_typing(
+ vqp_dconn_t dconn, const vqp_uuid_t * p_uuid, const char * nickname,
+ int is_typing);
+int vqp_dconn_send_chat_msg(
+ vqp_dconn_t dconn, const vqp_uuid_t * p_uuid, const char * nickname,
+ size_t content_length);
+int vqp_dconn_send_chat_fin(
+ vqp_dconn_t dconn, const vqp_uuid_t * p_uuid, const char * nickname);
+
+int vqp_dconn_send(vqp_dconn_t dconn, const void * data, size_t data_sz);
+
+#endif /* #ifdef __VQPROTO_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/main.c b/plugins/!NotAdopted/VypressChat/main.c
new file mode 100644
index 0000000000..ccf295017d
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/main.c
@@ -0,0 +1,132 @@
+/*
+ * 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: main.c,v 1.11 2005/03/09 14:44:22 bobas Exp $
+ */
+
+#include "miranda.h"
+#include "main.h"
+#include "service.h"
+#include "user.h"
+#include "userlist.h"
+#include "chatroom.h"
+#include "msghandler.h"
+#include "options.h"
+#include "skin.h"
+
+/* forward references
+ */
+
+/* global data
+ */
+HINSTANCE g_hDllInstance; /* plugin dll instance */
+HANDLE g_hMainThread;
+
+PLUGINLINK * pluginLink;
+
+/* exported routines
+ */
+BOOL WINAPI
+DllMain(HINSTANCE hInstanceDLL, DWORD fwReason, LPVOID lpvReserved)
+{
+ g_hDllInstance = hInstanceDLL;
+ return TRUE;
+}
+
+PLUGININFO pluginInfo = {
+ sizeof(PLUGININFO),
+ VQCHAT_PROTO_NAME " Protocol",
+ PLUGIN_MAKE_VERSION(VQCHAT_VER_MAJ0, VQCHAT_VER_MAJ1, VQCHAT_VER_MIN0, VQCHAT_VER_MIN1),
+ "Adds support for " VQCHAT_PROTO_NAME " networks.",
+ "Saulius Menkevicius",
+ "bobas@sourceforge.net",
+ "(C) 2005 Saulius Menkevicius",
+ "http://sourceforge.net/projects/miranda-vqchat/",
+ 0, // NOT transient
+ 0 // doesn't replace anything builtin
+};
+
+__declspec(dllexport) PLUGININFO *
+MirandaPluginInfo(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+__declspec(dllexport) int
+Load(PLUGINLINK * link)
+{
+ PROTOCOLDESCRIPTOR pd;
+ INITCOMMONCONTROLSEX iccs;
+
+ /* setup static data */
+ pluginLink = link;
+
+ /* get main thread handle */
+ DuplicateHandle(
+ GetCurrentProcess(), GetCurrentThread(),
+ GetCurrentProcess(), &g_hMainThread,
+ THREAD_SET_CONTEXT, FALSE, 0);
+
+ /* init common controls library (for the IP adress entries to work)
+ */
+ memset(&iccs, 0, sizeof(iccs));
+ iccs.dwSize = sizeof(iccs);
+ iccs.dwICC = ICC_INTERNET_CLASSES;
+ InitCommonControlsEx(&iccs);
+
+ /* init vqp_link module */
+ vqp_init(NULL, NULL);
+
+ /* register this module with miranda */
+ memset(&pd, 0, sizeof(pd));
+ pd.cbSize = sizeof(pd);
+ pd.szName = VQCHAT_PROTO;
+ pd.type = PROTOTYPE_PROTOCOL;
+ CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM)&pd);
+
+ /* init our modules */
+ options_init();
+ msghandler_init();
+ skin_init();
+ user_init();
+ userlist_init();
+ chatroom_init();
+
+ /* register protocol services */
+ service_register_services();
+ service_hook_all();
+
+ return 0;
+}
+
+__declspec(dllexport) int
+Unload(void)
+{
+ service_uninit();
+ user_uninit();
+ userlist_uninit();
+ chatroom_uninit();
+ msghandler_uninit();
+ options_uninit();
+ skin_uninit();
+
+ vqp_uninit();
+
+ return 0;
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/main.h b/plugins/!NotAdopted/VypressChat/main.h
new file mode 100644
index 0000000000..953ae8c22e
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/main.h
@@ -0,0 +1,108 @@
+/*
+ * 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: main.h,v 1.19 2005/03/08 16:53:20 bobas Exp $
+ */
+
+#include "miranda.h"
+#include "version.h"
+
+#ifdef VYPRESSCHAT
+# define VQCHAT_PROTO "VYPRESSCHAT"
+# define VQCHAT_PROTO_NAME "Vypress Chat"
+# define VQCHAT_PROTO_DLL "vypresschat.dll"
+#endif
+
+#ifdef QUICKCHAT
+# define VQCHAT_PROTO "QUICKCHAT"
+# define VQCHAT_PROTO_NAME "QuickChat"
+# define VQCHAT_PROTO_DLL "quickchat.dll"
+#endif
+
+#ifdef VYPRESSCHAT
+# define VQCHAT_VQP_PROTO VQP_PROTOCOL_VYPRESSCHAT
+# define VQCHAT_VQP_DEF_PORT 8167
+# define VQCHAT_VQP_DEF_PROTO_OPT 0
+# define VQCHAT_VQP_SWVERSION VQP_MAKE_SWVERSION(1, 93)
+# define VQCHAT_VQP_DEF_COLOR 0
+# define VQCHAT_VQP_COMPAT_CODEPAGE VQP_CODEPAGE_UTF8
+# define VQCHAT_UNDEF_SWVERSION VQP_MAKE_SWVERSION(1, 50)
+#endif
+
+#ifdef QUICKCHAT
+# define VQCHAT_VQP_PROTO VQP_PROTOCOL_QUICKCHAT
+# define VQCHAT_VQP_DEF_PORT 8167
+# define VQCHAT_VQP_DEF_PROTO_OPT 0
+# define VQCHAT_VQP_SWVERSION VQP_MAKE_SWVERSION(1, 5)
+# define VQCHAT_VQP_DEF_COLOR 0
+# define VQCHAT_VQP_COMPAT_CODEPAGE VQP_CODEPAGE_LOCALE
+# define VQCHAT_UNDEF_SWVERSION VQP_MAKE_SWVERSION(1, 5)
+#endif
+
+#define VQCHAT_VQP_DEF_MULTICAST 0xe3000002
+#define VQCHAT_VQP_DEF_SCOPE 7
+#define VQCHAT_VQP_SWPLATFORM "Windows"
+#define VQCHAT_VQP_SWNAME "Miranda IM, http://www.miranda-im.org/"
+#define VQCHAT_MAIN_CHANNEL "Main"
+#define VQCHAT_UNDEF_ACTIVE VQP_ACTIVE_ACTIVE
+
+#define VQCHAT_MIN_REFRESH_TIMEOUT 1
+#define VQCHAT_MAX_REFRESH_TIMEOUT 254
+#define VQCHAT_DEF_REFRESH_TIMEOUT 15
+
+extern HINSTANCE g_hDllInstance;
+extern HANDLE g_hMainThread;
+extern PLUGINLINK * pluginLink;
+
+/* debuging allocator
+ * (watch for "memwatch.log" in miranda's dir !!)
+ */
+#ifdef MEMWATCH
+# include "contrib/memwatch.h"
+#endif
+
+/* debug traps, messages, asserts
+ */
+#ifndef NDEBUG
+# define DEBUG_TRAP() asm volatile ("int $3");
+# define DEBUG_MSG(...) \
+ do { char message[256], formatted[192]; \
+ sprintf(formatted, __VA_ARGS__); \
+ sprintf(message, "(%s:%s():%d): %s", \
+ __FILE__, __FUNCTION__, __LINE__, formatted); \
+ PUShowMessage(message, SM_NOTIFY); \
+ } while(0);
+
+#else
+# define DEBUG_TRAP()
+# define DEBUG_MSG(...)
+#endif
+
+#define VALIDPTR(p) ((unsigned long)(p) >= 0x1000)
+
+#define ASSERT_RETURNIFFAIL(assert) \
+ if(! (assert)) { \
+ DEBUG_TRAP(); \
+ return; \
+ }
+#define ASSERT_RETURNVALIFFAIL(assert, val) \
+ if(! (assert)) { \
+ DEBUG_TRAP(); \
+ return (val); \
+ }
+
diff --git a/plugins/!NotAdopted/VypressChat/makefile b/plugins/!NotAdopted/VypressChat/makefile
new file mode 100644
index 0000000000..2d0b64d628
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/makefile
@@ -0,0 +1,243 @@
+# target type
+#------------------
+TARGET ?= quickchat
+#TARGET ?= vypresschat
+TARGET_DEBUG ?= 1
+#TARGET_INSTALL_LOCATION = d:\\programs\\miranda-testing
+TARGET_INSTALL_LOCATION = d:\\devel\\miranda\\miranda
+
+# version stuff
+#-----------------------
+TARGET_VER_COPYRIGHT = (c) 2005 Saulius Menkevicius
+TARGET_VER_MAJ0 = 0
+TARGET_VER_MAJ1 = 9
+TARGET_VER_MIN0 = 3
+TARGET_VER_MIN1 = 0
+#TARGET_CVS = 1
+
+###########################################################
+###########################################################
+
+# target proto
+#------------------------------
+ifeq ($(TARGET), quickchat)
+TARGET_PROTO=QUICKCHAT
+endif
+
+ifeq ($(TARGET), vypresschat)
+TARGET_PROTO=VYPRESSCHAT
+endif
+
+# debug
+#------------------------------
+ifeq ($(TARGET_DEBUG), 1)
+TARGET_DEBUG_OPT = -D_DEBUG -DMEMWATCH
+TARGET_CC_OPTIMISE = -g -ggdb
+else
+TARGET_DEBUG_OPT = -DNDEBUG
+TARGET_CC_OPTIMISE = -Os -pipe -march=pentium
+endif
+
+ifeq ($(TARGET_CVS), 1)
+TARGET_VER_CVS = -cvs$(shell date +%y%m%d)
+endif
+
+ifeq ($(TARGET_DEBUG), 1)
+TARGET_VER_NAME = $(TARGET_VER_CVS)-dbg
+else
+TARGET_VER_NAME = $(TARGET_VER_CVS)
+endif
+
+# version stuff
+#-------------------------------
+TARGET_DIST_NAME = $(TARGET)-$(TARGET_VER_MAJ0)-$(TARGET_VER_MAJ1)-$(TARGET_VER_MIN0)-$(TARGET_VER_MIN1)$(TARGET_VER_NAME)
+
+# compiler defines
+#-------------------------------
+CC_OPT = $(TARGET_CC_OPTIMISE) $(TARGET_DEBUG_OPT) \
+ -Wall -mms-bitfields -mwindows -mno-cygwin
+
+TARGET_VER_OPT = -D$(TARGET_PROTO)
+CC = gcc $(CC_OPT) $(TARGET_VER_OPT)
+CC_OPT_DLL_LINK = $(CC_OPT) -shared -Wl,--enable-auto-image-base -lws2_32 -lrpcrt4 \
+ -Lcontrib/ -lunicows -lcomctl32
+WINDRES_OPT = $(TARGET_VER_OPT) $(TARGET_DEBUG_OPT)
+
+# defines
+#-------------------------------
+OBJEXT = obj
+TARGET_DIR = build/$(TARGET)
+
+OBJFILES_MAIN = $(TARGET_DIR)/main.$(OBJEXT) $(TARGET_DIR)/service.$(OBJEXT) $(TARGET_DIR)/util.$(OBJEXT) \
+ $(TARGET_DIR)/msghandler.$(OBJEXT) $(TARGET_DIR)/msgloop.$(OBJEXT) $(TARGET_DIR)/pthread.$(OBJEXT) \
+ $(TARGET_DIR)/user.$(OBJEXT) $(TARGET_DIR)/contacts.$(OBJEXT) $(TARGET_DIR)/options.$(OBJEXT) \
+ $(TARGET_DIR)/chanlist.$(OBJEXT) $(TARGET_DIR)/chatroom.$(OBJEXT) $(TARGET_DIR)/userlist.$(OBJEXT) \
+ $(TARGET_DIR)/strhashfunc.$(OBJEXT) $(TARGET_DIR)/hashtable.$(OBJEXT) $(TARGET_DIR)/skin.$(OBJEXT) \
+ $(TARGET_DIR)/resource.$(OBJEXT) \
+ libvqproto/libvqproto.a
+
+ifeq ($(TARGET_DEBUG), 1)
+OBJFILES = $(OBJFILES_MAIN) $(TARGET_DIR)/memwatch.$(OBJEXT)
+else
+OBJFILES = $(OBJFILES_MAIN)
+endif
+
+HEADERS = main.h service.h resource.h miranda.h options.h \
+ main.h msgloop.h msghandler.h user.h contacts.h userlist.h \
+ chanlist.h chatroom.h util.h \
+ libvqproto/vqproto.h contrib/memwatch.h \
+ version.h # this file is generated dynamically
+
+ICONPACK_ICONS = icons/$(TARGET).ico \
+ icons/$(TARGET)_online.ico \
+ icons/$(TARGET)_offline.ico \
+ icons/$(TARGET)_away.ico \
+ icons/$(TARGET)_dnd.ico \
+ icons/$(TARGET)_na.ico
+
+TARGET_ICONPACK = proto_$(TARGET_PROTO)
+
+# dependencies
+#----------------------------
+
+all: $(TARGET_DIR)/$(TARGET).dll $(TARGET_DIR)/$(TARGET_ICONPACK).dll
+
+$(TARGET_DIR)/$(TARGET).dll: $(OBJFILES) $(TARGET_DIR)
+ $(CC) $(OBJFILES) -o $(TARGET_DIR)/$(TARGET).dll $(CC_OPT_DLL_LINK)
+
+$(TARGET_DIR)/$(TARGET_ICONPACK).dll: $(TARGET_DIR)/iconpack.$(OBJEXT)
+ $(CC) $(TARGET_DIR)/iconpack.$(OBJEXT) -o $(TARGET_DIR)/$(TARGET_ICONPACK).dll $(CC_OPT_DLL_LINK)
+
+$(TARGET_DIR)/iconpack.$(OBJEXT): iconpack.rc $(ICONPACK_ICONS) $(TARGET_DIR)
+ windres $(WINDRES_OPT) -i iconpack.rc -o $(TARGET_DIR)/iconpack.$(OBJEXT)
+
+$(TARGET_DIR)/main.obj: main.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c main.c -o $(TARGET_DIR)/main.$(OBJEXT)
+
+$(TARGET_DIR)/service.$(OBJEXT): service.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c service.c -o $(TARGET_DIR)/service.$(OBJEXT)
+
+$(TARGET_DIR)/msghandler.$(OBJEXT): msghandler.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c msghandler.c -o $(TARGET_DIR)/msghandler.$(OBJEXT)
+
+$(TARGET_DIR)/msgloop.$(OBJEXT): msgloop.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c msgloop.c -o $(TARGET_DIR)/msgloop.$(OBJEXT)
+
+$(TARGET_DIR)/pthread.$(OBJEXT): pthread.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c pthread.c -o $(TARGET_DIR)/pthread.$(OBJEXT)
+
+$(TARGET_DIR)/user.$(OBJEXT): user.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c user.c -o $(TARGET_DIR)/user.$(OBJEXT)
+
+$(TARGET_DIR)/contacts.$(OBJEXT): contacts.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c contacts.c -o $(TARGET_DIR)/contacts.$(OBJEXT)
+
+$(TARGET_DIR)/userlist.$(OBJEXT): userlist.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c userlist.c -o $(TARGET_DIR)/userlist.$(OBJEXT)
+
+$(TARGET_DIR)/chatroom.$(OBJEXT): chatroom.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c chatroom.c -o $(TARGET_DIR)/chatroom.$(OBJEXT)
+
+$(TARGET_DIR)/options.$(OBJEXT): options.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c options.c -o $(TARGET_DIR)/options.$(OBJEXT)
+
+$(TARGET_DIR)/chanlist.$(OBJEXT): chanlist.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c chanlist.c -o $(TARGET_DIR)/chanlist.$(OBJEXT)
+
+$(TARGET_DIR)/util.$(OBJEXT): util.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c util.c -o $(TARGET_DIR)/util.$(OBJEXT)
+
+$(TARGET_DIR)/skin.$(OBJEXT): skin.c $(HEADERS) $(TARGET_DIR)
+ $(CC) -c skin.c -o $(TARGET_DIR)/skin.$(OBJEXT)
+
+$(TARGET_DIR)/resource.$(OBJEXT): resource.rc $(HEADERS) $(TARGET_DIR)
+ windres $(WINDRES_OPT) -i resource.rc -o $(TARGET_DIR)/resource.$(OBJEXT)
+
+$(TARGET_DIR)/memwatch.$(OBJEXT): contrib/memwatch.c contrib/memwatch.h $(TARGET_DIR)
+ $(CC) -c contrib/memwatch.c -o $(TARGET_DIR)/memwatch.$(OBJEXT)
+
+$(TARGET_DIR)/strhashfunc.$(OBJEXT): contrib/strhashfunc.c contrib/strhashfunc.h $(TARGET_DIR)
+ $(CC) -c contrib/strhashfunc.c -o $(TARGET_DIR)/strhashfunc.$(OBJEXT)
+
+$(TARGET_DIR)/hashtable.$(OBJEXT): contrib/hashtable.c contrib/hashtable.h $(TARGET_DIR)
+ $(CC) -c contrib/hashtable.c -o $(TARGET_DIR)/hashtable.$(OBJEXT)
+
+libvqproto/libvqproto.a:
+ make -C libvqproto CC='gcc $(CC_OPT)'
+
+version.h:
+ echo "/*" > version.h
+ echo " * This file is generated by 'make version.h'." >> version.h
+ echo " * Any changes you make here will be lost, please" >> version.h
+ echo " * modify the Makefile instead and run 'make version'." >> version.h
+ echo " */" >> version.h
+ echo "" >> version.h
+ echo "#ifndef __VERSION_H" >> version.h
+ echo "#define __VERSION_H" >> version.h
+ echo "#define VQCHAT_VER_MAJ0 $(TARGET_VER_MAJ0)" >> version.h
+ echo "#define VQCHAT_VER_MAJ1 $(TARGET_VER_MAJ1)" >> version.h
+ echo "#define VQCHAT_VER_MIN0 $(TARGET_VER_MIN0)" >> version.h
+ echo "#define VQCHAT_VER_MIN1 $(TARGET_VER_MIN1)" >> version.h
+ echo -n "#define VQCHAT_VER_FILEVERSION " >> version.h
+ echo -n "\"$(TARGET_VER_MAJ0), $(TARGET_VER_MAJ1), " >> version.h
+ echo "$(TARGET_VER_MIN0), $(TARGET_VER_MIN1) $(TARGET_VER_NAME)\\0\"" >> version.h
+ echo "#define VQCHAT_VER_COPYRIGHT \"$(TARGET_VER_COPYRIGHT)\"" >> version.h
+ echo "#endif /* __VERSION_H */" >> version.h
+
+version:
+ rm -f version.h
+ make version.h
+
+clean: $(TARGET_DIR)
+ make -C libvqproto clean
+ rm -f version.h
+ rm -f $(TARGET_DIR)/*.$(OBJEXT) $(TARGET_DIR)/*.dll
+ rm -rf build/$(TARGET_DIST_NAME) build/$(TARGET_DIST_NAME).zip
+
+install: all
+ cp $(TARGET_DIR)/$(TARGET).dll $(TARGET_INSTALL_LOCATION)/Plugins/
+ cp $(TARGET_DIR)/$(TARGET_ICONPACK).dll $(TARGET_INSTALL_LOCATION)/Icons/
+
+deinstall:
+ rm -f $(TARGET_INSTALL_LOCATION)/Plugins/$(TARGET).dll
+ rm -f $(TARGET_INSTALL_LOCATION)/Icons/$(TARGET_ICONPACK).dll
+
+debug:
+ make install
+ gdb $(TARGET_INSTALL_LOCATION)/miranda32.exe
+
+dist: all
+ rm -rf build/$(TARGET_DIST_NAME) build/$(TARGET_DIST_NAME).zip
+ mkdir build/$(TARGET_DIST_NAME)
+ mkdir build/$(TARGET_DIST_NAME)/Plugins build/$(TARGET_DIST_NAME)/Icons \
+ build/$(TARGET_DIST_NAME)/docs build/$(TARGET_DIST_NAME)/for-win95-98-me-only
+ cp contrib/unicows.dll build/$(TARGET_DIST_NAME)/for-win95-98-me-only
+ echo "Win95/98/ME users: place unicows.dll in the same dir as miranda32.exe" \
+ > build/$(TARGET_DIST_NAME)/for-win95-98-me-only/README
+ cp $(TARGET_DIR)/$(TARGET).dll build/$(TARGET_DIST_NAME)/Plugins
+ cp $(TARGET_DIR)/$(TARGET_ICONPACK).dll build/$(TARGET_DIST_NAME)/Icons
+ifneq ($(TARGET_DEBUG), 1)
+ strip build/$(TARGET_DIST_NAME)/Plugins/$(TARGET).dll
+ strip build/$(TARGET_DIST_NAME)/Icons/$(TARGET_ICONPACK).dll
+endif
+ cp docs/TODO docs/README docs/ChangeLog docs/AUTHORS \
+ docs/CREDITS docs/Copying docs/NEWS build/$(TARGET_DIST_NAME)\docs
+ cd build; pwd; zip -9 -r $(TARGET_DIST_NAME).zip $(TARGET_DIST_NAME)
+ rm -rf build/$(TARGET_DIST_NAME)
+
+rebuild:
+ make clean
+ make
+
+redist:
+ make TARGET_DEBUG=0 TARGET=vypresschat clean
+ make TARGET_DEBUG=0 TARGET=vypresschat dist
+ make TARGET_DEBUG=0 TARGET=quickchat clean
+ make TARGET_DEBUG=0 TARGET=quickchat dist
+ make clean
+
+$(TARGET_DIR): build
+ mkdir $(TARGET_DIR)
+
+build:
+ mkdir build
diff --git a/plugins/!NotAdopted/VypressChat/miranda.h b/plugins/!NotAdopted/VypressChat/miranda.h
new file mode 100644
index 0000000000..2a310bcd5b
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/miranda.h
@@ -0,0 +1,56 @@
+/*
+ * 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: miranda.h,v 1.12 2005/04/11 18:49:44 bobas Exp $
+ */
+
+#ifndef __MIRANDA_H
+#define __MIRANDA_H
+
+#include <windows.h>
+#define _WIN32_IE 0x3000
+#include <commctrl.h>
+#include <stdio.h>
+#include <string.h>
+#include <winsock.h>
+#include <winbase.h>
+#include <lmcons.h>
+
+#include "libvqproto/vqproto.h"
+
+#include "contrib/m_chat.h"
+#include "../miranda/SDK/headers_c/newpluginapi.h"
+#include "../miranda/SDK/headers_c/m_clist.h"
+#include "../miranda/SDK/headers_c/m_clui.h"
+#include "../miranda/SDK/headers_c/m_skin.h"
+#include "../miranda/SDK/headers_c/m_langpack.h"
+#include "../miranda/SDK/headers_c/m_protomod.h"
+#include "../miranda/SDK/headers_c/m_database.h"
+#include "../miranda/SDK/headers_c/m_system.h"
+#include "../miranda/SDK/headers_c/m_protocols.h"
+#include "../miranda/SDK/headers_c/m_userinfo.h"
+#include "../miranda/SDK/headers_c/m_options.h"
+#include "../miranda/SDK/headers_c/m_protosvc.h"
+#include "../miranda/SDK/headers_c/m_utils.h"
+#include "../miranda/SDK/headers_c/m_clc.h"
+#include "../miranda/SDK/headers_c/m_popup.h"
+#include "../miranda/SDK/headers_c/m_message.h"
+#include "../miranda/SDK/headers_c/m_addcontact.h"
+
+#endif /* #ifndef __MIRANDA_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/msghandler.c b/plugins/!NotAdopted/VypressChat/msghandler.c
new file mode 100644
index 0000000000..7123577b33
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/msghandler.c
@@ -0,0 +1,839 @@
+/*
+ * 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: msghandler.c,v 1.41 2005/03/22 16:12:29 bobas Exp $
+ */
+
+#include <time.h>
+
+#include "miranda.h"
+#include <lm.h>
+
+#include "libvqproto/vqproto.h"
+
+#include "main.h"
+#include "msghandler.h"
+#include "user.h"
+#include "userlist.h"
+#include "contacts.h"
+#include "chatroom.h"
+#include "chanlist.h"
+#include "util.h"
+#include "service.h"
+#include "skin.h"
+
+/* static data
+ */
+static struct vqp_parse_func_struct s_parseFuncs;
+
+/* static routines
+ */
+
+static void msghandler_refresh_req(
+ const vqp_msg_t msg, void * user_data,
+ const char * r_src, enum vqp_codepage src_codepage)
+{
+ char * src = util_vqp2utf(src_codepage, r_src);
+
+ if(!user_is_my_nickname(src)) {
+ char * r_nickname = util_utf2vqp(src_codepage, user_nickname());
+
+ /* send refresh ack, without delaying */
+ msgloop_send_to(
+ vqp_msg_refresh_ack(
+ vqp_msg_link(msg),
+ r_nickname, r_src, user_vqp_status(), VQP_ACTIVE_ACTIVE,
+ user_gender(), VQCHAT_VQP_SWVERSION, user_p_uuid(),
+ user_codepage(), 0),
+ 1, vqp_msg_src_addr(msg)
+ );
+ free(r_nickname);
+ }
+ free(src);
+}
+
+static void msghandler_refresh_ack(
+ const vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst,
+ enum vqp_status src_status, enum vqp_active src_active, enum vqp_gender src_gender,
+ unsigned int src_swversion, const vqp_uuid_t * src_uuid,
+ enum vqp_codepage src_codepage, unsigned int src_pref_color)
+{
+ char * src = util_vqp2utf(src_codepage, r_src),
+ * dst = util_vqp2utf(src_codepage, r_dst);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ userlist_add(
+ src, src_status, src_gender, src_uuid, src_codepage, src_swversion,
+ vqp_msg_src_addr(msg), 0);
+ }
+ free(dst);
+ free(src);
+}
+
+#ifdef VYPRESSCHAT
+static void msghandler_ping(
+ const vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst, const char * r_timestamp)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * dst = util_vqp2utf(cp, r_dst),
+ * src = util_vqp2utf(cp, r_src);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ /* send pong, without waiting */
+ msgloop_send_to(
+ vqp_msg_pong(vqp_msg_link(msg), r_dst, r_src, r_timestamp),
+ 1, vqp_msg_src_addr(msg));
+ }
+
+ free(src);
+ free(dst);
+}
+
+static void msghandler_pong(
+ const vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst, const char * r_timestamp)
+{
+ /* we don't use or need ping/pong mechanism,
+ * at least for now
+ */
+}
+
+static void msghandler_flood_notification(
+ const vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst, const char * r_secs_blocked)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ char * secs_blocked = util_vqp2utf(cp, r_secs_blocked);
+
+ HANDLE hContact = contacts_find_contact(src);
+ if(hContact) {
+ int len = strlen(secs_blocked);
+ char * fmt = malloc(len + 64);
+ sprintf(fmt, "*** YOU'RE BLOCKED FOR %s SECONDS FOR FLOOD ***",
+ secs_blocked);
+
+ contacts_input_contact_message(hContact, fmt);
+ free(fmt);
+ }
+ free(secs_blocked);
+ }
+ free(src);
+ free(dst);
+}
+#endif
+
+static void msghandler_nick_change(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_src_new_nick, enum vqp_gender src_gender)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * src_new_nick = util_vqp2utf(cp, r_src_new_nick);
+
+ if(!user_is_my_nickname(src)) {
+ userlist_name_change(src, src_new_nick);
+ }
+
+ free(src);
+ free(src_new_nick);
+}
+
+static void msghandler_status_change(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, enum vqp_status src_status,
+ enum vqp_gender src_gender, const char * r_src_autoanswer)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * src_autoanswer = util_vqp2utf(cp, r_src_autoanswer);
+
+
+ if(!user_is_my_nickname(src))
+ userlist_status_change(src, src_status, src_gender, src_autoanswer, 1);
+
+ free(src);
+ free(src_autoanswer);
+}
+
+static void msghandler_active_change(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, enum vqp_active src_is_active)
+{
+ /* XXX: we'll ignore 'active change' for now */
+}
+
+static void msghandler_channel_join(
+ vqp_msg_t msg, void * user_data,
+ const char * r_channel, const char * r_src,
+ enum vqp_status src_status, enum vqp_gender src_gender,
+ unsigned int src_swversion, const vqp_uuid_t * src_uuid,
+ enum vqp_codepage src_codepage, unsigned int src_pref_color)
+{
+ char * channel = util_vqp2utf(src_codepage, r_channel),
+ * src = util_vqp2utf(src_codepage, r_src);
+
+ if(!user_is_my_nickname(src)) {
+ const char * topic;
+ char * my_chanlist;
+
+ /* add user to userlist and update it's channel list */
+ userlist_add(
+ src, src_status, src_gender, src_uuid,
+ src_codepage, src_swversion, vqp_msg_src_addr(msg),
+ 1);
+ userlist_user_channel_join(src, channel, 1);
+
+ /* reply with refresh ack (if the channel is #Main), who-here ack,
+ * and topic ack if we're in this channel
+ */
+ my_chanlist = chanlist_copy(*user_p_chanlist());
+ my_chanlist = chanlist_add(my_chanlist, VQP_MAIN_CHANNEL);
+
+ if(chanlist_contains(my_chanlist, channel)) {
+ char * r_nickname = util_utf2vqp(src_codepage, user_nickname());
+
+ if(!strcmp(channel, VQP_MAIN_CHANNEL)) {
+ /* reply with refresh ack (without waiting) */
+ msgloop_send_to(
+ vqp_msg_refresh_ack(
+ vqp_msg_link(msg),
+ r_nickname, r_src, user_vqp_status(),
+ VQP_ACTIVE_ACTIVE, user_gender(),
+ VQCHAT_VQP_SWVERSION, user_p_uuid(),
+ user_codepage(), 0),
+ 1, vqp_msg_src_addr(msg)
+ );
+ } else {
+ /* on channels not #Main, reply with who-here ack */
+ msgloop_send_to(
+ vqp_msg_channel_whohere_ack(
+ vqp_msg_link(msg),
+ r_channel, r_nickname, r_src, VQCHAT_UNDEF_ACTIVE),
+ 0, vqp_msg_src_addr(msg)
+ );
+ }
+
+ /* reply the user with channel's topic */
+ topic = chatroom_channel_get_topic(channel);
+ if(topic) {
+ char * r_topic_text = util_utf2vqp(src_codepage, topic);
+
+ msgloop_send_to(
+ vqp_msg_channel_current_topic(
+ vqp_msg_link(msg), r_src, r_channel, r_topic_text),
+ 0, vqp_msg_src_addr(msg)
+ );
+ free(r_topic_text);
+ }
+
+ /* free strings */
+ free(r_nickname);
+ }
+
+ chanlist_free(my_chanlist);
+ }
+
+ free(channel);
+ free(src);
+}
+static void msghandler_channel_leave(
+ const vqp_msg_t msg, void * user_data,
+ const char * r_channel, const char * r_src, enum vqp_gender src_gender)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * channel = util_vqp2utf(cp, r_channel),
+ * src = util_vqp2utf(cp, r_src);
+
+ if(!user_is_my_nickname(src)) {
+ if(!strcmp(channel, VQCHAT_MAIN_CHANNEL)) {
+ /* user has left the network */
+ userlist_remove(src, 1);
+ } else {
+ /* user has left the specified channel */
+ userlist_user_channel_part(src, channel, 1);
+ }
+ }
+
+ free(channel);
+ free(src);
+}
+
+static void msghandler_channel_text(
+ vqp_msg_t msg, void * user_data,
+ const char * r_channel, const char * r_src,
+ const char * r_text, int is_action_text)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src);
+
+ if(!user_is_my_nickname(src)) {
+ char * channel = util_vqp2utf(cp, r_channel),
+ * text = util_vqp2utf(cp, r_text);
+
+ userlist_user_channel_text(
+ src, channel, text, vqp_msg_src_addr(msg), is_action_text);
+ free(channel);
+ free(text);
+ }
+ free(src);
+}
+
+#ifdef QUICKCHAT
+static int msghandler_channel_topic_change_chan_enum_fn(
+ const char * channel, void * fn_data)
+{
+ const char * topic_text = ((const char **)fn_data)[0],
+ * notify_user = ((const char **)fn_data)[1];
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel) && VALIDPTR(topic_text), 0);
+
+ chatroom_channel_topic_change(channel, (const char *)topic_text, 0, (int)notify_user);
+ return 1;
+}
+#endif
+
+static void msghandler_channel_topic_change(
+ vqp_msg_t msg, void * user_data,
+ const char * r_channel, const char * r_topic_text)
+{
+ char * channel = util_vqp2utf(user_codepage(), r_channel),
+ * topic_text = util_vqp2utf(user_codepage(), r_topic_text);
+
+#ifdef VYPRESSCHAT
+ chatroom_channel_topic_change(channel, topic_text, 0, 1);
+#endif
+
+#ifdef QUICKCHAT
+ /* there's a single topic for all the channels in quickChat:
+ * enumerate all of them and set their topics
+ */
+ const char * fn_data[2];
+ fn_data[0] = topic_text;
+ fn_data[1] = (const char *)1;
+ chanlist_enum(
+ *user_p_chanlist(),
+ msghandler_channel_topic_change_chan_enum_fn,
+ (void *) fn_data);
+#endif
+
+ free(channel);
+ free(topic_text);
+}
+
+static void msghandler_channel_current_topic(
+ vqp_msg_t msg, void * user_data,
+ const char * r_dst, const char * r_channel, const char * r_topic_text)
+{
+ enum vqp_codepage cp = user_codepage();
+ char * dst = util_vqp2utf(cp, r_dst);
+
+ if(user_is_my_nickname(dst)) {
+ char * channel = util_vqp2utf(cp, r_channel),
+ * topic_text = util_vqp2utf(cp, r_topic_text);
+
+#ifdef VYPRESSCHAT
+ chatroom_channel_topic_change(channel, topic_text, 0, 0);
+#endif
+
+#ifdef QUICKCHAT
+ /* there's a single topic for all the channels in quickChat:
+ * enumerate all of them and set their topics
+ */
+ const char * fn_data[2];
+ fn_data[0] = topic_text;
+ fn_data[1] = (const char *)0;
+ chanlist_enum(
+ *user_p_chanlist(),
+ msghandler_channel_topic_change_chan_enum_fn,
+ (void *) fn_data);
+#endif
+ free(channel);
+ free(topic_text);
+ }
+ free(dst);
+}
+static void msghandler_channel_whohere_req(
+ vqp_msg_t msg, void * user_data,
+ const char * r_channel, const char * r_src)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src);
+
+ if(!user_is_my_nickname(src)) {
+ char * channel = util_vqp2utf(cp, r_channel);
+
+ if(chanlist_contains(*user_p_chanlist(), channel)) {
+ char * r_user_nickname = util_utf2vqp(cp, user_nickname());
+
+ /* reply, that we're on this channel */
+ msgloop_send_to(
+ vqp_msg_channel_whohere_ack(
+ vqp_msg_link(msg), r_channel, r_user_nickname, r_src,
+ VQCHAT_UNDEF_ACTIVE),
+ 0, vqp_msg_src_addr(msg)
+ );
+ free(r_user_nickname);
+ }
+ free(channel);
+ }
+ free(src);
+}
+
+static void msghandler_channel_whohere_ack(
+ vqp_msg_t msg, void * user_data,
+ const char * r_channel, const char * r_src, const char * r_dst,
+ enum vqp_active src_is_active)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ char * channel = util_vqp2utf(cp, r_channel);
+
+ /* add channel to user's chanlist */
+ userlist_user_channel_join(src, channel, 0);
+ free(channel);
+ }
+ free(src);
+ free(dst);
+}
+
+static void msghandler_channel_list_req(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src);
+
+ if(!user_is_my_nickname(src)) {
+ /* make sure our chanlist contains the #Main channel */
+ char * chanlist, * vqp_chanlist, * r_chanlist;
+
+ chanlist = chanlist_copy(*user_p_chanlist());
+ chanlist = chanlist_add(chanlist, VQCHAT_MAIN_CHANNEL);
+ vqp_chanlist = chanlist_make_vqp_chanlist(chanlist);
+ free(chanlist);
+ r_chanlist = util_utf2vqp(cp, vqp_chanlist);
+ free(vqp_chanlist);
+
+ /* reply with ack */
+ msgloop_send(vqp_msg_channel_list_ack(vqp_msg_link(msg), r_src, r_chanlist), 0);
+ free(r_chanlist);
+ }
+ free(src);
+}
+
+static void msghandler_channel_list_ack(
+ vqp_msg_t msg, void * user_data,
+ const char * r_dst, const char * r_channel_list)
+{
+ enum vqp_codepage cp = user_codepage();
+ char * dst = util_vqp2utf(cp, r_dst),
+ * channel_list = util_vqp2utf(cp, r_channel_list);
+
+ if(user_is_my_nickname(dst)) {
+ char * chanlist = chanlist_parse_vqp_chanlist(channel_list);
+ service_join_channel_merge_chanlist(chanlist);
+ free(chanlist);
+ }
+ free(dst);
+ free(channel_list);
+}
+
+static void msghandler_message(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst, const char * r_text,
+ int is_multiaddress_msg)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ char * text = util_vqp2utf(cp, r_text);
+ HANDLE hContact;
+
+ hContact = contacts_find_contact(src);
+ if(!hContact)
+ hContact = contacts_add_contact(src, 0);
+
+ if(hContact) {
+ char * r_autoanswer = util_utf2vqp(cp, user_awaymsg());
+
+ /* write message event to contact window/history */
+ contacts_input_contact_message(hContact, text);
+
+ /* send message ack */
+ msgloop_send_to(
+ vqp_msg_message_ack(
+ vqp_msg_link(msg), dst, src,
+ user_vqp_status(), user_gender(),
+ r_autoanswer, vqp_msg_signature(msg)),
+ 0, vqp_msg_src_addr(msg)
+ );
+ free(r_autoanswer);
+ }
+
+ free(text);
+ }
+ free(src);
+ free(dst);
+}
+
+static void msghandler_message_ack(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst,
+ enum vqp_status src_status, enum vqp_gender src_gender,
+ const char * r_src_autoanswer, const char * orig_packetsig)
+{
+ /* we don't send any messages for now */
+}
+
+static void msghandler_info_req(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(r_src) && user_is_my_nickname(r_dst)) {
+ wchar_t w_computer_name[MAX_COMPUTERNAME_LENGTH + 1],
+ w_user_name[UNLEN + 1];
+ char * computer_name, * r_computer_name;
+ char * user_name, * r_user_name;
+ char * netgroup_name, * r_netgroup_name;
+ char * chanlist, * vqp_chanlist, * r_chanlist;
+ char * r_autoanswer;
+ DWORD computer_name_len, user_name_len;
+/* LPWKSTA_INFO_100 wksta_info100; */
+
+ /* get computer name */
+ computer_name_len = MAX_COMPUTERNAME_LENGTH;
+ w_computer_name[0] = '\0'; /* in case the call fails */
+ GetComputerNameW(w_computer_name, &computer_name_len);
+ computer_name = util_uni2utf(w_computer_name);
+ r_computer_name = util_utf2vqp(cp, computer_name);
+ free(computer_name);
+
+ /* get user name */
+ user_name_len = UNLEN;
+ w_user_name[0] = '\0'; /* in case the call fails */
+ GetUserNameW(w_user_name, &user_name_len);
+ user_name = util_uni2utf(w_user_name);
+ r_user_name = util_utf2vqp(cp, user_name);
+ free(user_name);
+
+ /* get netgroup name */
+/*
+ if(NetWkstaGetInfo(NULL, 100, (LPBYTE *) &wksta_info100) == NERR_Success) {
+ r_netgroup_name = util_uni2utf(wksta_info100->wki100_langroup);
+ NetApiBufferFree(wksta_info100);
+ } else {
+ r_netgroup_name = strdup("");
+ }
+*/
+ r_netgroup_name = strdup("");
+
+ /* make vqp chanlist */
+ chanlist = chanlist_copy(*user_p_chanlist());
+ chanlist = chanlist_add(chanlist, VQP_MAIN_CHANNEL);
+ vqp_chanlist = chanlist_make_vqp_chanlist(chanlist);
+ free(chanlist);
+ r_chanlist = util_utf2vqp(cp, vqp_chanlist);
+ free(vqp_chanlist);
+
+ /* make autoanswer */
+ r_autoanswer = util_utf2vqp(cp, user_awaymsg());
+
+ /* return info request reply */
+ /* XXX: add real ip/port address string */
+ msgloop_send_to(
+ vqp_msg_info_ack(
+ vqp_msg_link(msg), r_dst, r_src,
+ r_computer_name, r_user_name,
+ "0.0.0.0:0", r_chanlist, r_autoanswer,
+ r_netgroup_name, VQCHAT_VQP_SWPLATFORM, VQCHAT_VQP_SWNAME
+ ),
+ 0, vqp_msg_src_addr(msg)
+ );
+
+ free(r_autoanswer);
+ free(r_chanlist);
+ free(r_user_name);
+ free(r_computer_name);
+ free(r_netgroup_name);
+ }
+ free(src);
+ free(dst);
+}
+
+static void msghandler_info_ack(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst,
+ const char * r_src_computer_name, const char * r_src_user_name,
+ const char * r_src_node_address, const char * r_src_channel_list,
+ const char * r_src_autoanswer, const char * r_src_netgroup,
+ const char * r_src_platform, const char * r_src_software)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(r_src) && user_is_my_nickname(r_dst)) {
+ char * computer_name = util_vqp2utf(cp, r_src_computer_name),
+ * user_name = util_vqp2utf(cp, r_src_user_name),
+ * awaymsg = util_vqp2utf(cp, r_src_autoanswer),
+ * netgroup = util_vqp2utf(cp, r_src_netgroup),
+ * platform = util_vqp2utf(cp, r_src_platform),
+ * software = util_vqp2utf(cp, r_src_software),
+ * vqp_chanlist = util_vqp2utf(cp, r_src_channel_list);
+
+ HANDLE hContact = contacts_find_contact(src);
+ if(hContact) {
+ char * chanlist;
+
+ /* store contact info
+ */
+ contacts_set_contact_addr(hContact, vqp_msg_src_addr(msg));
+ contacts_set_contact_about(hContact, awaymsg);
+ contacts_set_contact_property(hContact, CONTACT_COMPUTER, computer_name);
+ contacts_set_contact_property(hContact, CONTACT_USER, user_name);
+ contacts_set_contact_property(hContact, CONTACT_WORKGROUP, netgroup);
+ contacts_set_contact_property(hContact, CONTACT_PLATFORM, platform);
+ contacts_set_contact_property(hContact, CONTACT_SOFTWARE, software);
+ contacts_set_contact_gender(hContact, userlist_user_gender(src));
+
+ /* update user's channel list */
+ chanlist = chanlist_parse_vqp_chanlist(vqp_chanlist);
+ userlist_user_chanlist_update(src, chanlist);
+ chanlist_free(chanlist);
+
+ /* confirm we've received the info correctly */
+ ProtoBroadcastAck(
+ VQCHAT_PROTO, hContact,
+ ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)2, (LPARAM)1);
+ }
+
+ free(vqp_chanlist);
+ free(computer_name);
+ free(user_name);
+ free(awaymsg);
+ free(netgroup);
+ free(platform);
+ free(software);
+ }
+
+ free(src);
+ free(dst);
+}
+
+static void msghandler_beep_signal(/*{{{*/
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ HANDLE hContact = contacts_find_contact(src);
+ if(hContact) {
+ /* play beep sound and emit msg, if configured */
+ SkinPlaySound(SKIN_SOUND_ALERT_BEEP);
+ if(db_byte_get(NULL, VQCHAT_PROTO, "MsgOnAlertBeep", 0))
+ contacts_input_contact_message(hContact, "*** ALERT BEEP ***");
+
+ /* send beep ack */
+ msgloop_send_to(
+ vqp_msg_beep_ack(vqp_msg_link(msg), r_dst, r_src, user_gender()),
+ 0, vqp_msg_src_addr(msg));
+ }
+ }
+ free(src);
+ free(dst);
+}
+
+static void msghandler_beep_ack(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst, enum vqp_gender src_gender)
+{
+ /* we don't make any use of this ack, at least for now */
+}
+
+static void msghandler_private_open(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst, enum vqp_gender src_gender)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ /* open chat */
+ if(!userlist_user_is_chat_open(src)) {
+ /* send chat open reply */
+ msgloop_send_to(
+ vqp_msg_private_open(
+ vqp_msg_link(msg), r_dst, r_src, user_gender()),
+ 0, vqp_msg_src_addr(msg));
+
+ /* mark the chat as open */
+ userlist_user_set_chat_open(src, 1);
+ }
+ }
+ free(src);
+ free(dst);
+}
+
+static void msghandler_private_close(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst, enum vqp_gender src_gender)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ userlist_user_set_chat_open(src, 0);
+ }
+ free(src);
+ free(dst);
+}
+
+static void msghandler_private_text(
+ vqp_msg_t msg, void * user_data,
+ const char * r_src, const char * r_dst, const char * r_text,
+ int is_action_text)
+{
+ enum vqp_codepage cp = userlist_user_codepage_vqp(r_src);
+ char * src = util_vqp2utf(cp, r_src),
+ * dst = util_vqp2utf(cp, r_dst);
+
+ if(!user_is_my_nickname(src) && user_is_my_nickname(dst)) {
+ HANDLE hContact = contacts_find_contact(src);
+ if(!hContact)
+ hContact = contacts_add_contact(src, 0);
+
+ if(hContact) {
+ char * text = util_vqp2utf(cp, r_text);
+
+ /* check that we have chat open with the user */
+ if(!userlist_user_is_chat_open(src)) {
+ msgloop_send_to(
+ vqp_msg_private_open(
+ vqp_msg_link(msg), r_dst, r_src, user_gender()),
+ 0, vqp_msg_src_addr(msg));
+
+ /* mark the chat as open */
+ userlist_user_set_chat_open(src, 1);
+ }
+
+ if(is_action_text) {
+ /* make the '<nickname> <text>' - style action text */
+ int text_len = strlen(text);
+ int src_len = strlen(src);
+ char * action_text = malloc(src_len + text_len + 10);
+
+ memcpy(action_text, src, src_len);
+ action_text[src_len] = ' ';
+ memcpy(action_text + src_len + 1, text, text_len + 1);
+
+ contacts_input_contact_message(hContact, action_text);
+ free(action_text);
+ } else {
+ contacts_input_contact_message(hContact, text);
+ }
+ free(text);
+ }
+ }
+ free(src);
+ free(dst);
+}
+
+/* exported routines
+ */
+
+/* msghandler_init:
+ * setups msghandler module
+ */
+void msghandler_init()
+{
+ memset(&s_parseFuncs, 0, sizeof(s_parseFuncs));
+ s_parseFuncs.struct_size = sizeof(s_parseFuncs);
+
+ /* add handler funcs */
+ s_parseFuncs.func_refresh_req = msghandler_refresh_req;/*{{{*/
+ s_parseFuncs.func_refresh_ack = msghandler_refresh_ack;
+
+#ifdef VYPRESSCHAT
+ s_parseFuncs.func_ping = msghandler_ping;
+ s_parseFuncs.func_pong = msghandler_pong;
+ s_parseFuncs.func_flood_notification = msghandler_flood_notification;
+#endif
+/* s_parseFuncs.func_remote_exec = msghandler_remote_exec;
+ s_parseFuncs.func_remote_exec_ack = msghandler_remote_exec_ack;
+*/
+ s_parseFuncs.func_nick_change = msghandler_nick_change;
+ s_parseFuncs.func_status_change = msghandler_status_change;
+ s_parseFuncs.func_active_change = msghandler_active_change;
+ s_parseFuncs.func_channel_join = msghandler_channel_join;
+ s_parseFuncs.func_channel_leave = msghandler_channel_leave;
+ s_parseFuncs.func_channel_text = msghandler_channel_text;
+ s_parseFuncs.func_channel_topic_change = msghandler_channel_topic_change;
+ s_parseFuncs.func_channel_current_topic = msghandler_channel_current_topic;
+ s_parseFuncs.func_channel_whohere_req = msghandler_channel_whohere_req;
+ s_parseFuncs.func_channel_whohere_ack = msghandler_channel_whohere_ack;
+ s_parseFuncs.func_channel_list_req = msghandler_channel_list_req;
+ s_parseFuncs.func_channel_list_ack = msghandler_channel_list_ack;
+ s_parseFuncs.func_message = msghandler_message;
+ s_parseFuncs.func_message_ack = msghandler_message_ack;
+ s_parseFuncs.func_info_req = msghandler_info_req;
+ s_parseFuncs.func_info_ack = msghandler_info_ack;
+ s_parseFuncs.func_beep_signal = msghandler_beep_signal;
+ s_parseFuncs.func_beep_ack = msghandler_beep_ack;
+ s_parseFuncs.func_private_open = msghandler_private_open;
+ s_parseFuncs.func_private_close = msghandler_private_close;
+ s_parseFuncs.func_private_text = msghandler_private_text;/*}}}*/
+}
+
+/* msghandler_uninit:
+ * cleansup user status module
+ */
+void msghandler_uninit()
+{
+ /* nothing here */
+}
+
+/* msghandler_apc:
+ * invoked from msgloop thread with QueueUserAPC,
+ * the param is vqp_msg_t, which gets free'd after use
+ */
+void CALLBACK msghandler_apc(ULONG_PTR vqmsg)
+{
+ vqp_msg_parse(&s_parseFuncs, (vqp_msg_t) vqmsg, 0);
+ vqp_msg_free((vqp_msg_t)vqmsg);
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/msghandler.h b/plugins/!NotAdopted/VypressChat/msghandler.h
new file mode 100644
index 0000000000..cd5ce88e15
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/msghandler.h
@@ -0,0 +1,33 @@
+/*
+ * 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: msghandler.h,v 1.3 2005/02/23 16:10:09 bobas Exp $
+ */
+
+
+#ifndef __MSGHANDLER_H
+#define __MSGHANDLER_H
+
+#include "msgloop.h"
+
+void msghandler_init();
+void msghandler_uninit();
+
+void CALLBACK msghandler_apc(ULONG_PTR msg);
+
+#endif
diff --git a/plugins/!NotAdopted/VypressChat/msgloop.c b/plugins/!NotAdopted/VypressChat/msgloop.c
new file mode 100644
index 0000000000..6815394c07
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/msgloop.c
@@ -0,0 +1,248 @@
+/*
+ * 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: msgloop.c,v 1.13 2005/03/08 17:42:25 bobas Exp $
+ */
+
+#include "main.h"
+#include "msgloop.h"
+#include "pthread.h"
+
+/* constants and struct defs
+ */
+
+/* max packet output rate is 2 packets per sec */
+#define PACKET_OUTPUT_RATE 500
+
+struct msgloop_message_list_entry {
+ struct msgloop_message_list_entry * prev, * next;
+ vqp_msg_t msg;
+};
+
+/* static data
+ */
+static PAPCFUNC s_lpfnMsgHandleApc;
+static HANDLE s_hMainThread, s_hLoopThread;
+static BOOL s_fStopLoop = TRUE,
+ s_fLoopStarted;
+static HANDLE s_hStopWaitEvent;
+
+static UINT_PTR s_outputTimer;
+static struct msgloop_message_list_entry
+ * s_outputListHead, * s_outputListTail;
+static BOOL s_outputSentThisTick = FALSE, s_outputSentThisTickInTimer;
+
+/* static routines
+ */
+
+static void CALLBACK msgloop_apc_stop(ULONG_PTR unused)
+{
+ s_fStopLoop = TRUE;
+}
+
+static void CALLBACK msgloop_apc_send_msg(ULONG_PTR vqmsg)
+{
+ vqp_link_send((vqp_msg_t)vqmsg);
+ vqp_msg_free((vqp_msg_t)vqmsg);
+}
+
+static void msgloop_loop(void * vqlink)
+{
+ WSAEVENT hReadEvent;
+ SOCKET rx_socket = vqp_link_rx_socket((vqp_link_t) vqlink);
+
+ s_fLoopStarted = TRUE;
+
+ hReadEvent = WSACreateEvent();
+ WSAEventSelect(rx_socket, hReadEvent, FD_READ);
+
+ while(!Miranda_Terminated() && !s_fStopLoop) {
+ DWORD nEvent = WSAWaitForMultipleEvents(
+ 1, &hReadEvent, FALSE, WSA_INFINITE, TRUE);
+
+ if(nEvent==WSA_WAIT_EVENT_0) {
+ vqp_msg_t msg;
+
+ WSAResetEvent(hReadEvent);
+
+ if(!vqp_link_recv((vqp_link_t) vqlink, &msg)) {
+ QueueUserAPC(
+ s_lpfnMsgHandleApc,
+ s_hMainThread, (ULONG_PTR)msg);
+ }
+ }
+ }
+
+ WSACloseEvent(hReadEvent);
+
+ /* wait for pending user APC */
+ while(SleepEx(10, TRUE) == WAIT_IO_COMPLETION) /* nothing */;
+
+ /* signal that we've finished */
+ SetEvent(s_hStopWaitEvent);
+}
+
+static void CALLBACK msgloop_message_output_timer_cb(
+ HWND hwnd, UINT nmsg, UINT_PTR idevent, DWORD dwtime)
+{
+ if(s_outputListTail && (!s_outputSentThisTick
+ || (s_outputSentThisTick && s_outputSentThisTickInTimer)))
+ {
+ struct msgloop_message_list_entry * entry = s_outputListTail;
+ QueueUserAPC(msgloop_apc_send_msg, s_hLoopThread, (ULONG_PTR)entry->msg);
+
+ s_outputListTail = entry->next;
+ if(s_outputListTail) {
+ s_outputListTail->prev = NULL;
+ } else {
+ s_outputListHead = NULL;
+ }
+
+ free(entry);
+
+ s_outputSentThisTick = TRUE;
+ s_outputSentThisTickInTimer = TRUE;
+ } else {
+ s_outputSentThisTick = FALSE;
+ }
+}
+
+/* exported routines
+ */
+
+/* msgloop_start:
+ * starts msg loop
+ */
+void msgloop_start(vqp_link_t vqlink, PAPCFUNC lpfMsgHandlerApc)
+{
+ ASSERT_RETURNIFFAIL(s_fStopLoop);
+
+ s_lpfnMsgHandleApc = lpfMsgHandlerApc;
+ DuplicateHandle(
+ GetCurrentProcess(), GetCurrentThread(),
+ GetCurrentProcess(), &s_hMainThread,
+ THREAD_SET_CONTEXT, FALSE, 0);
+
+ s_fStopLoop = FALSE;
+ s_fLoopStarted = FALSE; /* this will be set in msgloop_loop() */
+ s_hLoopThread = (HANDLE)pthread_create(msgloop_loop, (void*)vqlink);
+
+ /* start scheduler timer:
+ * this will make packets to be send no faster than specific rate
+ */
+ s_outputTimer = SetTimer(NULL, 0, PACKET_OUTPUT_RATE, msgloop_message_output_timer_cb);
+ s_outputSentThisTick = FALSE;
+}
+
+/* msgloop_stop:
+ * msgloop_stop should be called from miranda's gui thread
+ * to stop the qcs_msg loop.
+ */
+void msgloop_stop()
+{
+ struct msgloop_message_list_entry * entry;
+
+ ASSERT_RETURNIFFAIL(!s_fStopLoop);
+
+ /* stop packet scheduler timer
+ */
+ KillTimer(NULL, s_outputTimer);
+ s_outputTimer = 0;
+
+ /* signal the thread to stop */
+ s_hStopWaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ QueueUserAPC(msgloop_apc_stop, s_hLoopThread, (ULONG_PTR)NULL);
+
+ /* wait for the thread to stop
+ * and handle all the queued APCs
+ */
+ while(WaitForSingleObjectEx(s_hStopWaitEvent, 10, TRUE) == WAIT_IO_COMPLETION)
+ /* do nothing */ ;
+
+ CloseHandle(s_hStopWaitEvent);
+ CloseHandle(s_hMainThread);
+
+ /* do cleanup */
+ s_fLoopStarted = FALSE;
+
+ /* send all the queued packets that didn't get sent
+ * by packet scheduler in msgloop_packet_output_timer_c()
+ */
+ entry = s_outputListTail;
+ while(entry) {
+ struct msgloop_message_list_entry * next = entry->next;
+ vqp_link_send(entry->msg);
+ vqp_msg_free(entry->msg);
+ free(entry);
+
+ entry = next;
+ }
+ s_outputListTail = NULL;
+ s_outputListHead = NULL;
+}
+
+/* msgloop_send:
+ * msgloop_send should be used to send a message from miranda's
+ * gui thread to send a message with qcs_msg loop.
+ */
+void msgloop_send(vqp_msg_t msg, int never_wait)
+{
+ ASSERT_RETURNIFFAIL(VALIDPTR(msg));
+
+ /* send this message */
+ if(s_fLoopStarted && s_outputTimer) {
+ /* check if we've sent a packet this tick,
+ * and if we did, we'll have to wait for the next tick
+ */
+ if(s_outputSentThisTick && !never_wait) {
+ /* add msg to message queue for scheduler timer
+ */
+ struct msgloop_message_list_entry * entry;
+ entry = malloc(sizeof(struct msgloop_message_list_entry));
+ entry->msg = msg;
+ entry->next = NULL;
+ entry->prev = s_outputListHead;
+ if(entry->prev) {
+ entry->prev->next = entry;
+ } else {
+ s_outputListTail = entry;
+ }
+ s_outputListHead = entry;
+ } else {
+ QueueUserAPC(msgloop_apc_send_msg, s_hLoopThread, (ULONG_PTR)msg);
+ s_outputSentThisTick = TRUE;
+ }
+ } else {
+ if(s_outputSentThisTick && !never_wait) {
+ Sleep(PACKET_OUTPUT_RATE);
+ }
+ vqp_link_send(msg);
+ vqp_msg_free(msg);
+ s_outputSentThisTick = TRUE;
+ s_outputSentThisTickInTimer = FALSE;
+ }
+}
+
+/* msgloop_send_to:
+ * sends message to the specified address only
+ */
+void msgloop_send_to(vqp_msg_t msg, int never_wait, vqp_addr_t addr) {
+ vqp_msg_set_dst_addr(msg, addr);
+ msgloop_send(msg, never_wait);
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/msgloop.h b/plugins/!NotAdopted/VypressChat/msgloop.h
new file mode 100644
index 0000000000..efd3f6883f
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/msgloop.h
@@ -0,0 +1,35 @@
+/*
+ * 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: msgloop.h,v 1.10 2005/03/08 17:42:26 bobas Exp $
+ */
+
+
+#ifndef __MSGLOOP_H
+#define __MSGLOOP_H
+
+#include "miranda.h"
+#include "libvqproto/vqproto.h"
+
+void msgloop_start(vqp_link_t link, PAPCFUNC lpfMsgHandlerApc);
+void msgloop_stop();
+void msgloop_send(vqp_msg_t msg, int never_wait);
+void msgloop_send_to(vqp_msg_t msg, int never_wait, vqp_addr_t addr);
+
+#endif /* #ifndef __MSGLOOP_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/options.c b/plugins/!NotAdopted/VypressChat/options.c
new file mode 100644
index 0000000000..d159b06b7d
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/options.c
@@ -0,0 +1,947 @@
+/*
+ * 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: options.c,v 1.21 2005/04/11 21:44:15 bobas Exp $
+ */
+
+#include "miranda.h"
+
+#include "main.h"
+#include "user.h"
+#include "util.h"
+#include "options.h"
+#include "resource.h"
+#include "contacts.h"
+#include "userlist.h"
+#include "chanlist.h"
+#include "chatroom.h"
+
+/* static data
+ */
+static HANDLE s_hook_opt_initialise;
+static HANDLE s_hook_userinfo_initialise;
+
+/* static routines
+ */
+
+static BOOL CALLBACK
+options_user_dlgproc(HWND hdlg, UINT msg, WPARAM wp, LPARAM lp)
+{
+ static BOOL dlg_initalizing;
+
+ HWND dlgitem;
+ char * str;
+
+ switch(msg) {
+ case WM_INITDIALOG:
+ /* controls may not send PSM_CHANGED for now:
+ * we're initalizing the dialog
+ */
+ dlg_initalizing = TRUE;
+
+ /* set nickname entry */
+ util_SetDlgItemTextUtf(hdlg, IDC_USER_EDIT_NICKNAME, user_nickname());
+
+ /* setup gender combo box */
+ dlgitem = GetDlgItem(hdlg, IDC_USER_COMBO_GENDER);
+ SendMessage(dlgitem, CB_RESETCONTENT, 0, 0);
+ SendMessage(dlgitem, CB_ADDSTRING, 0, (LPARAM)"Male");
+ SendMessage(dlgitem, CB_ADDSTRING, 0, (LPARAM)"Female");
+ SendMessage(dlgitem, CB_SETCURSEL,
+ user_gender()==VQP_GENDER_MALE ? 0: 1, (LPARAM)0);
+
+ /* set UUID entry */
+#ifdef VYPRESSCHAT
+ str = vqp_uuid_to_string(user_p_uuid());
+ SetDlgItemTextA(hdlg, IDC_USER_EDIT_UUID, str);
+ free(str);
+#else
+ EnableWindow(GetDlgItem(hdlg, IDC_USER_EDIT_UUID), FALSE);
+#endif
+ /* set other options */
+ CheckDlgButton(hdlg, IDC_USER_MSGONALERTBEEP,
+ db_byte_get(NULL, VQCHAT_PROTO, "MsgOnAlertBeep", 0)
+ ? BST_CHECKED: BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_USER_NICKNAMEONTOPIC,
+ db_byte_get(NULL, VQCHAT_PROTO, "NicknameOnTopic", 0)
+ ? BST_CHECKED: BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_USER_NEWPREFERMSG,
+ db_byte_get(NULL, VQCHAT_PROTO, "ContactsPreferMsg", 0)
+ ? BST_CHECKED: BST_UNCHECKED);
+
+ /* controls can sen send PSM_CHANGED now */
+ dlg_initalizing = FALSE;
+ break;
+
+ case WM_COMMAND:
+ switch(HIWORD(wp)) {
+ case BN_CLICKED:
+ if(!dlg_initalizing)
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case EN_CHANGE:
+ switch(LOWORD(wp)) {
+ case IDC_USER_EDIT_NICKNAME:
+ case IDC_USER_EDIT_UUID:
+ if(!dlg_initalizing)
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+
+ case CBN_SELCHANGE:
+ switch(LOWORD(wp)) {
+ case IDC_USER_COMBO_GENDER:
+ if(!dlg_initalizing)
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ if(((LPNMHDR)lp)->code == PSN_APPLY) {
+ /* apply changes
+ */
+#ifdef VYPRESSCHAT
+ char * str;
+ vqp_uuid_t uuid;
+#endif
+
+ /* apply nickname */
+ str = util_GetDlgItemTextUtf(hdlg, IDC_USER_EDIT_NICKNAME);
+ if(user_set_nickname(str, TRUE)) {
+ /* XXX: show error message */
+ free(str);
+ SetFocus(GetDlgItem(hdlg, IDC_USER_EDIT_NICKNAME));
+ return TRUE;
+ }
+ free(str);
+
+ /* apply gender */
+ user_set_gender(
+ SendMessage(
+ GetDlgItem(hdlg, IDC_USER_COMBO_GENDER),
+ CB_GETCURSEL, 0, 0
+ ) == 0
+ ? VQP_GENDER_MALE: VQP_GENDER_FEMALE,
+ TRUE
+ );
+
+#ifdef VYPRESSCHAT
+ /* store uuid */
+ str = util_GetDlgItemTextUtf(hdlg, IDC_USER_EDIT_UUID);
+ if(vqp_uuid_from_string(&uuid, str)) {
+ /* restore old uuid */
+ char * old_str = vqp_uuid_to_string(user_p_uuid());
+ util_SetDlgItemTextUtf(hdlg, IDC_USER_EDIT_UUID, old_str);
+ free(old_str);
+
+ /* XXX: show error message */
+ free(str);
+ SetFocus(GetDlgItem(hdlg, IDC_USER_EDIT_UUID));
+ return TRUE;
+ }
+ free(str);
+#endif
+ db_byte_set(NULL, VQCHAT_PROTO, "MsgOnAlertBeep",
+ IsDlgButtonChecked(hdlg, IDC_USER_MSGONALERTBEEP) ? 1: 0);
+ db_byte_set(NULL, VQCHAT_PROTO, "NicknameOnTopic",
+ IsDlgButtonChecked(hdlg, IDC_USER_NICKNAMEONTOPIC) ? 1: 0);
+ db_byte_set(NULL, VQCHAT_PROTO, "ContactsPreferMsg",
+ IsDlgButtonChecked(hdlg, IDC_USER_NEWPREFERMSG) ? 1: 0);
+
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static BOOL
+options_network_dlgproc_bcast_add(HANDLE hdlg)
+{
+ DWORD ip;
+ HWND ip_cntl = GetDlgItem(hdlg, IDC_CONN_BCAST_INPUT),
+ list_cntl = GetDlgItem(hdlg, IDC_CONN_BCAST_LIST);
+ size_t list_sz, pos;
+ char * ip_string;
+
+ /* check that we've a valid broadcast mask entered */
+ if(SendMessage(ip_cntl, IPM_GETADDRESS, 0, (LPARAM)&ip) != 4)
+ return FALSE;
+
+ if(!ip) return FALSE;
+
+ /* check that there are no such ips entered into the list already */
+ list_sz = SendMessage(list_cntl, LB_GETCOUNT, 0, 0);
+ for(pos = 0; pos < list_sz; pos++) {
+ DWORD another_ip = SendMessage(list_cntl, LB_GETITEMDATA, pos, 0);
+ if(another_ip == ip)
+ return FALSE;
+ }
+
+ /* add new item into the list */
+ ip_string = malloc(16);
+ sprintf(ip_string, "%u.%u.%u.%u",
+ (unsigned)(ip >> 24) & 0xff, (unsigned)(ip >> 16) & 0xff,
+ (unsigned)(ip >> 8) & 0xff, (unsigned)(ip >> 0) & 0xff);
+
+ pos = SendMessage(list_cntl, LB_ADDSTRING, 0, (LPARAM)ip_string);
+ SendMessage(list_cntl, LB_SETITEMDATA, (WPARAM)pos, (LPARAM)ip);
+
+ free(ip_string);
+ return TRUE;
+}
+
+static BOOL CALLBACK
+options_network_dlgproc(HWND hdlg, UINT msg, WPARAM wp, LPARAM lp)
+{
+ static BOOL dlg_initalizing;
+
+ BOOL ival_read;
+ int ival;
+ DWORD ip, * ip_list;
+ size_t ip_list_sz, pos;
+
+ switch(msg) {
+ case WM_INITDIALOG:
+ /* may not send PSM_CHANGED for now */
+ dlg_initalizing = TRUE;
+
+ /*
+ * "Connection Type" frame
+ */
+
+ /* set port */
+ SetDlgItemInt(
+ hdlg, IDC_CONN_EDIT_PORT,
+ db_word_get(NULL, VQCHAT_PROTO, "Port", VQCHAT_VQP_DEF_PORT),
+ FALSE);
+
+ /* set active connection type radio
+ */
+ if(db_byte_get(NULL, VQCHAT_PROTO, "ProtoConn", 2) == 1) {
+ CheckRadioButton(
+ hdlg, IDC_CONN_RADIO_MULTICAST, IDC_CONN_RADIO_IPX,
+ IDC_CONN_RADIO_IPX);
+ } else {
+ if(db_dword_get(NULL, VQCHAT_PROTO, "ProtoOpt", VQCHAT_VQP_DEF_PROTO_OPT)
+ & VQP_PROTOCOL_OPT_MULTICAST) {
+ CheckRadioButton(
+ hdlg, IDC_CONN_RADIO_MULTICAST, IDC_CONN_RADIO_IPX,
+ IDC_CONN_RADIO_MULTICAST);
+ } else {
+ CheckRadioButton(
+ hdlg, IDC_CONN_RADIO_MULTICAST, IDC_CONN_RADIO_IPX,
+ IDC_CONN_RADIO_BROADCAST);
+ }
+ }
+
+ /* set multicast scope */
+ SetDlgItemInt(
+ hdlg, IDC_CONN_EDIT_SCOPE,
+ db_byte_get(NULL, VQCHAT_PROTO, "MulticastScope", VQCHAT_VQP_DEF_SCOPE),
+ FALSE);
+
+ /* set multicast address */
+ SendMessage(
+ GetDlgItem(hdlg, IDC_CONN_IP_MULTICAST),
+ IPM_SETADDRESS, 0, (LPARAM)db_dword_get(NULL, VQCHAT_PROTO,
+ "Multicast", VQCHAT_VQP_DEF_MULTICAST));
+
+ /* fill in broadcast masks list */
+ ip_list = db_dword_list(NULL, VQCHAT_PROTO, "BroadcastMasks", &ip_list_sz);
+ if(ip_list) {
+ char * ip_string = malloc(16);
+ size_t i;
+ HWND item = GetDlgItem(hdlg, IDC_CONN_BCAST_LIST);
+ SendMessageW(item, LB_RESETCONTENT, 0, 0);
+
+ for(i = 0; i < ip_list_sz; i++) {
+ LRESULT pos;
+
+ ip = ip_list[i];
+ sprintf(ip_string, "%u.%u.%u.%u",
+ (unsigned)(ip >> 24) & 0xff, (unsigned)(ip >> 16) & 0xff,
+ (unsigned)(ip >> 8) & 0xff, (unsigned)(ip >> 0) & 0xff);
+
+ pos = SendMessage(item, LB_ADDSTRING, 0, (LPARAM)ip_string);
+ SendMessage(item, LB_SETITEMDATA, (WPARAM)pos, (LPARAM)ip);
+ }
+ free(ip_string);
+ free(ip_list);
+ }
+ EnableWindow(GetDlgItem(hdlg, IDC_CONN_BCAST_REMOVE), FALSE);
+
+ /*
+ * "Miscellaneous Options" frame
+ */
+
+ /* set user list update period */
+ SetDlgItemInt(
+ hdlg, IDC_CONN_EDIT_REFRESH,
+ db_byte_get(NULL, VQCHAT_PROTO, "UserlistTimeout",
+ VQCHAT_DEF_REFRESH_TIMEOUT),
+ FALSE);
+
+#ifdef VYPRESSCHAT
+ /* set use/do not use utf-8 per default */
+ CheckDlgButton(
+ hdlg, IDC_CONN_CHECK_UTF8,
+ user_codepage()==VQP_CODEPAGE_UTF8 ? BST_CHECKED: BST_UNCHECKED);
+#else
+ EnableWindow(GetDlgItem(hdlg, IDC_CONN_CHECK_UTF8), FALSE);
+ EnableWindow(GetDlgItem(hdlg, IDC_CONN_LABEL_UTF8), FALSE);
+#endif
+
+ /* can send PSM_CHANGED now */
+ dlg_initalizing = FALSE;
+ break;
+
+ case WM_COMMAND:
+ switch(HIWORD(wp)) {
+ case BN_CLICKED:
+ switch(LOWORD(wp)) {
+ case IDC_CONN_BTN_DEF_MULTICAST:
+ SetDlgItemInt(hdlg, IDC_CONN_EDIT_PORT, VQCHAT_VQP_DEF_PORT, FALSE);
+ SendMessage(GetDlgItem(hdlg, IDC_CONN_IP_MULTICAST),
+ IPM_SETADDRESS, 0, (LPARAM)VQCHAT_VQP_DEF_MULTICAST);
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_CONN_BTN_DEF_PORT:
+ SetDlgItemInt(hdlg, IDC_CONN_EDIT_PORT, VQCHAT_VQP_DEF_PORT, FALSE);
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_CONN_BCAST_ADD:
+ if(options_network_dlgproc_bcast_add(hdlg))
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_CONN_BCAST_REMOVE:
+ pos = SendMessage(GetDlgItem(hdlg, IDC_CONN_BCAST_LIST),
+ LB_GETCURSEL, 0, 0);
+ if(pos != LB_ERR) {
+ SendMessage(GetDlgItem(hdlg, IDC_CONN_BCAST_LIST),
+ LB_DELETESTRING, pos, 0);
+
+ EnableWindow(
+ GetDlgItem(hdlg, IDC_CONN_BCAST_REMOVE),
+ SendMessage(
+ GetDlgItem(hdlg, IDC_CONN_BCAST_LIST),
+ LB_GETCURSEL, 0, 0
+ ) != LB_ERR
+ );
+
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+
+ case IDC_CONN_CHECK_UTF8:
+ case IDC_CONN_RADIO_MULTICAST:
+ case IDC_CONN_RADIO_BROADCAST:
+ case IDC_CONN_RADIO_IPX:
+ if(!dlg_initalizing)
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+
+ case EN_CHANGE:
+ if(!dlg_initalizing)
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case LBN_SELCHANGE:
+ switch(LOWORD(wp)) {
+ case IDC_CONN_BCAST_LIST:
+ EnableWindow(
+ GetDlgItem(hdlg, IDC_CONN_BCAST_REMOVE),
+ SendMessage(
+ GetDlgItem(hdlg, IDC_CONN_BCAST_LIST), LB_GETCURSEL,
+ 0, 0
+ ) != LB_ERR);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ if(((LPNMHDR)lp)->code == PSN_APPLY) {
+ DWORD * list;
+ size_t list_sz, i;
+ HWND item;
+
+ /* set port value */
+ ival = GetDlgItemInt(hdlg, IDC_CONN_EDIT_PORT, &ival_read, FALSE);
+ if(!ival_read) {
+ /* XXX: error message */
+ SetFocus(GetDlgItem(hdlg, IDC_CONN_EDIT_PORT));
+ return TRUE;
+ }
+ db_word_set(NULL, VQCHAT_PROTO, "Port", ival);
+
+ /* set active connection type */
+ if(IsDlgButtonChecked(hdlg, IDC_CONN_RADIO_IPX)) {
+ /* IPX */
+ db_byte_set(NULL, VQCHAT_PROTO, "ProtoConn", 1);
+ } else {
+ /* UDP (broadcast or multicast) */
+ db_byte_set(NULL, VQCHAT_PROTO, "ProtoConn", 0);
+ db_dword_set(
+ NULL, VQCHAT_PROTO, "ProtoOpt",
+ (db_dword_get(NULL, VQCHAT_PROTO,
+ "ProtoOpt", VQCHAT_VQP_DEF_PROTO_OPT)
+ & ~VQP_PROTOCOL_OPT_MULTICAST)
+ | (IsDlgButtonChecked(hdlg, IDC_CONN_RADIO_MULTICAST)
+ ? VQP_PROTOCOL_OPT_MULTICAST: 0)
+ );
+ }
+
+ /* set multicast scope value */
+ ival = GetDlgItemInt(hdlg, IDC_CONN_EDIT_SCOPE, &ival_read, FALSE);
+ if(!ival_read) {
+ /* XXX: error message */
+ SetFocus(GetDlgItem(hdlg, IDC_CONN_EDIT_SCOPE));
+ return TRUE;
+ }
+ db_byte_set(NULL, VQCHAT_PROTO, "MulticastScope", ival);
+
+ /* set multicast address */
+ ip = 0;
+ SendMessage(GetDlgItem(hdlg, IDC_CONN_IP_MULTICAST),
+ IPM_GETADDRESS, 0, (LPARAM)&ip);
+ if(ip == 0) {
+ /* XXX: error message */
+ SetFocus(GetDlgItem(hdlg, IDC_CONN_IP_MULTICAST));
+ return TRUE;
+ }
+ db_dword_set(NULL, VQCHAT_PROTO, "Multicast", ip);
+
+ /* store broadcast masks */
+ item = GetDlgItem(hdlg, IDC_CONN_BCAST_LIST);
+ list_sz = SendMessage(item, LB_GETCOUNT, 0, 0);
+ if(list_sz) {
+ list = malloc(sizeof(DWORD) * list_sz);
+ for(i = 0; i < list_sz; i++)
+ list[i] = SendMessage(item, LB_GETITEMDATA, i, 0);
+
+ db_blob_set(NULL, VQCHAT_PROTO, "BroadcastMasks",
+ list, sizeof(DWORD) * list_sz);
+ free(list);
+ } else {
+ db_unset(NULL, VQCHAT_PROTO, "BroadcastMasks");
+ }
+
+ /* "Miscellaneous Options" frame
+ */
+
+ /* set user list refresh period value */
+ ival = GetDlgItemInt(hdlg, IDC_CONN_EDIT_REFRESH, &ival_read, FALSE);
+ if(!ival_read) {
+ /* XXX: error message */
+ SetFocus(GetDlgItem(hdlg, IDC_CONN_EDIT_REFRESH));
+ return TRUE;
+ }
+ db_byte_set(NULL, VQCHAT_PROTO, "UserlistTimeout", ival);
+
+#ifdef VYPRESSCHAT
+ /* set default encoding value */
+ user_set_codepage(
+ IsDlgButtonChecked(hdlg, IDC_CONN_CHECK_UTF8)
+ ? VQP_CODEPAGE_UTF8: VQP_CODEPAGE_LOCALE,
+ TRUE
+ );
+#endif
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static int options_hook_opt_initialise(
+ WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp;
+
+ memset(&odp, 0, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = -800000000;
+ odp.hInstance = g_hDllInstance;
+
+ /* add user configuration page
+ */
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_USER);
+ odp.pszGroup = Translate("Network");
+ odp.pszTitle = Translate(VQCHAT_PROTO_NAME);
+ odp.pfnDlgProc = options_user_dlgproc;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+
+ /* add network configuration page
+ */
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_CONN);
+ odp.pszGroup = Translate("Network");
+ odp.pszTitle = Translate(VQCHAT_PROTO_NAME " Network");
+ odp.pfnDlgProc = options_network_dlgproc;
+ odp.flags = ODPF_BOLDGROUPS;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+
+ return 0; /* success */
+}
+
+static __inline void
+options_userinfo_set_dlgitem(
+ HWND hwnd, UINT item,
+ HANDLE hContact, enum contact_property_enum property)
+{
+ char * str = contacts_get_contact_property(hContact, property);
+ wchar_t * u_str = util_utf2uni(str);
+ free(str);
+ SetDlgItemTextW(hwnd, item, u_str);
+ free(u_str);
+}
+
+static void options_userinfo_set_chanlist(
+ HWND hdlg, UINT item, const char * c_chanlist)
+{
+ char * channel, * chanlist = chanlist_copy(c_chanlist);
+
+ SendMessage(GetDlgItem(hdlg, item), LB_RESETCONTENT, 0, 0);
+
+ while((channel = chanlist_shift(&chanlist)) != NULL) {
+ char * mod_channel = malloc(strlen(channel) + 2);
+ wchar_t * w_channel;
+
+ /* prepend '#' to the channel name */
+ mod_channel[0] = '#';
+ strcpy(mod_channel + 1, channel);
+ w_channel = util_utf2uni(mod_channel);
+ free(mod_channel);
+ free(channel);
+
+ SendMessageW(GetDlgItem(hdlg, item), LB_ADDSTRING, 0, (LPARAM)w_channel);
+ free(w_channel);
+ }
+}
+
+static CALLBACK BOOL
+options_userinfo_dlgproc(HWND hdlg, UINT nmsg, WPARAM wp, LPARAM lp)
+{
+ switch(nmsg) {
+ case WM_COMMAND:
+ if(HIWORD(wp)==LBN_DBLCLK && LOWORD(wp)==IDC_USERINFO_CHANNELS) {
+ HWND hlist = GetDlgItem(hdlg, IDC_USERINFO_CHANNELS);
+
+ /* join the channel user has clicked on
+ */
+ int cursel = SendMessage(hlist, LB_GETCURSEL, 0, 0);
+ if(cursel != LB_ERR) {
+ int t_len = SendMessage(hlist, LB_GETTEXTLEN, cursel, 0);
+ wchar_t * strbuf = malloc(sizeof(wchar_t) * (t_len + 1));
+ char * channel;
+
+ SendMessageW(hlist, LB_GETTEXT, cursel, (LPARAM)strbuf);
+ channel = util_uni2utf(strbuf + 1); /* skip '#' */
+ free(strbuf);
+
+ /* join the channel permanently */
+ chatroom_channel_join(channel, 0);
+ free(channel);
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if(((LPNMHDR)lp)->code == PSN_INFOCHANGED) {
+ char * nick;
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lp)->lParam;
+ ASSERT_RETURNVALIFFAIL(contacts_is_user_contact(hContact), FALSE);
+
+ /* get contact nickname */
+ nick = contacts_get_nickname(hContact);
+
+ /* set node address */
+ if(userlist_user_exists(nick)) {
+ char * node_str = util_vqpaddr2str(userlist_user_addr(nick));
+ SetDlgItemTextA(hdlg, IDC_USERINFO_NODE, node_str);
+ free(node_str);
+ } else {
+ SetDlgItemTextA(hdlg, IDC_USERINFO_NODE, "(unknown)");
+ }
+
+ /* set string properties */
+ options_userinfo_set_dlgitem(
+ hdlg, IDC_USERINFO_COMPUTER, hContact, CONTACT_COMPUTER);
+ options_userinfo_set_dlgitem(
+ hdlg, IDC_USERINFO_USER, hContact, CONTACT_USER);
+ options_userinfo_set_dlgitem(
+ hdlg, IDC_USERINFO_PLATFORM, hContact, CONTACT_PLATFORM);
+ options_userinfo_set_dlgitem(
+ hdlg, IDC_USERINFO_WORKGROUP, hContact, CONTACT_WORKGROUP);
+ options_userinfo_set_dlgitem(
+ hdlg, IDC_USERINFO_SOFTWARE, hContact, CONTACT_SOFTWARE);
+
+ /* fill in channel list */
+ options_userinfo_set_chanlist(
+ hdlg, IDC_USERINFO_CHANNELS, userlist_user_chanlist(nick));
+
+ /* free nickname */
+ free(nick);
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+static CALLBACK BOOL
+options_useropt_dlgproc(HWND hdlg, UINT nmsg, WPARAM wp, LPARAM lp)
+{
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(hdlg, GWLP_USERDATA);
+ char * nick;
+
+ switch(nmsg) {
+ case WM_INITDIALOG:
+ SendMessage(GetDlgItem(hdlg, IDC_USEROPT_PREFERS), CB_ADDSTRING,
+ 0, (LPARAM)"Private chats");
+ SendMessage(GetDlgItem(hdlg, IDC_USEROPT_PREFERS), CB_ADDSTRING,
+ 0, (LPARAM)"Messages");
+ break;
+
+ case WM_COMMAND:
+ switch(HIWORD(wp)) {
+ case BN_CLICKED:
+ switch(LOWORD(wp)) {
+ case IDC_USEROPT_SET:
+ nick = util_GetDlgItemTextUtf(hdlg, IDC_USEROPT_NICKNAME);
+ if(strlen(nick))
+ contacts_set_contact_nickname(hContact, nick);
+
+ free(nick);
+ break;
+
+ case IDC_USEROPT_LOCKNICK:
+ db_byte_set(hContact, VQCHAT_PROTO, "LockNick",
+ IsDlgButtonChecked(hdlg, IDC_USEROPT_LOCKNICK) ? 1: 0);
+ break;
+ }
+ break;
+
+ case CBN_SELCHANGE:
+ switch(LOWORD(wp)) {
+ case IDC_USEROPT_PREFERS:
+ nick = util_GetDlgItemTextUtf(hdlg, IDC_USEROPT_NICKNAME);
+ if(userlist_user_swversion(nick) < VQP_MAKE_SWVERSION(2, 0)) {
+ db_byte_set(
+ hContact, VQCHAT_PROTO, "PreferMsg",
+ SendMessage(
+ GetDlgItem(hdlg, IDC_USEROPT_PREFERS),
+ CB_GETCURSEL, 0, 0
+ ) == 1
+ );
+ }
+ free(nick);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ if(((LPNMHDR)lp)->code == PSN_INFOCHANGED) {
+ char * nick;
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lp)->lParam;
+
+ ASSERT_RETURNVALIFFAIL(contacts_is_user_contact(hContact), FALSE);
+
+ /* save current contact handle */
+ SetWindowLongPtr(hdlg, GWLP_USERDATA, (UINT_PTR)hContact);
+
+ /* set current contact nickname */
+ nick = contacts_get_nickname(hContact);
+ util_SetDlgItemTextUtf(hdlg, IDC_USEROPT_NICKNAME, nick);
+
+ /* set if contact prefers msg */
+ if(userlist_user_swversion(nick) < VQP_MAKE_SWVERSION(2, 0)) {
+ EnableWindow(GetDlgItem(hdlg, IDC_USEROPT_PREFERS), TRUE);
+ SendMessage(
+ GetDlgItem(hdlg, IDC_USEROPT_PREFERS),
+ CB_SETCURSEL,
+ db_byte_get(hContact, VQCHAT_PROTO, "PreferMsg", 0) ? 1: 0,
+ 0);
+ } else {
+ /* vypress chat 2.0+ doesn't have private chats */
+ EnableWindow(GetDlgItem(hdlg, IDC_USEROPT_PREFERS), FALSE);
+ SendMessage(GetDlgItem(hdlg, IDC_USEROPT_PREFERS),
+ CB_SETCURSEL, 1, 0);
+ }
+
+ free(nick);
+
+ /* set if we want the contact nickname to not change */
+ CheckDlgButton(hdlg, IDC_USEROPT_LOCKNICK,
+ db_byte_get(hContact, VQCHAT_PROTO, "LockNick", 0)
+ ? BST_CHECKED: BST_UNCHECKED);
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+static int options_hook_userinfo_initialise(WPARAM wp, LPARAM lp)
+{
+ OPTIONSDIALOGPAGE odp;
+ HANDLE hContact = (HANDLE)lp;
+
+ if(hContact) {
+ if(contacts_is_user_contact(hContact)) {
+ /* we show additional property sheets for user contacts only
+ */
+
+ /* basic user info page */
+ memset(&odp, 0, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = -1900000000;
+ odp.pszTitle = VQCHAT_PROTO_NAME;
+ odp.hInstance = g_hDllInstance;
+ odp.pfnDlgProc = options_userinfo_dlgproc;
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_USERINFO);
+ CallService(MS_USERINFO_ADDPAGE, wp, (LPARAM)&odp);
+
+ /* user ID page */
+ odp.pszTitle = VQCHAT_PROTO_NAME " options";
+ odp.pfnDlgProc = options_useropt_dlgproc;
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_USEROPT);
+ CallService(MS_USERINFO_ADDPAGE, wp, (LPARAM)&odp);
+ }
+ } else {
+ /* XXX: add tabs for options of 'myself' */
+ }
+
+ return 0;
+}
+
+/* options_load_settings:
+ * loads settings from database
+ * (and presets some stuff if not already present in db)
+ */
+static void options_load_settings()
+{
+ DBVARIANT dbv;
+ int rc;
+
+ /* get user name */
+ rc = db_get(NULL, VQCHAT_PROTO, "Nick", &dbv);
+ if(!rc && dbv.type==DBVT_ASCIIZ) {
+ if(strlen(dbv.pszVal) != 0) {
+ /* ok, got name from db */
+ user_set_nickname(dbv.pszVal, FALSE);
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ /* get user gender */
+ rc = db_byte_get(NULL, VQCHAT_PROTO, "Gender", 0);
+ if(rc != 'M' && rc != 'F') {
+ user_set_gender(VQP_GENDER_MALE, TRUE);
+ } else {
+ user_set_gender(rc == 'F' ? VQP_GENDER_FEMALE: VQP_GENDER_MALE, FALSE);
+ }
+
+ /* emit msg by default on alert beep */
+ rc = db_byte_get(NULL, VQCHAT_PROTO, "MsgOnAlertBeep", 10);
+ if(rc != 0 && rc != 1)
+ db_byte_set(NULL, VQCHAT_PROTO, "MsgOnAlertBeep", 1);
+
+ /* append nickname after topic */
+ rc = db_byte_get(NULL, VQCHAT_PROTO, "NicknameOnTopic", 10);
+ if(rc != 0 && rc != 1)
+ db_byte_set(NULL, VQCHAT_PROTO, "NicknameOnTopic", 1);
+
+ /* new contacts prefer private messages */
+ rc = db_byte_get(NULL, VQCHAT_PROTO, "ContactsPreferMsg", 10);
+ if(rc != 0 && rc != 1)
+ db_byte_set(NULL, VQCHAT_PROTO, "ContactsPreferMsg", 0);
+
+ /* check if port is set */
+ if(db_word_get(NULL, VQCHAT_PROTO, "Port", 0) == 0)
+ db_word_set(NULL, VQCHAT_PROTO, "Port", VQCHAT_VQP_DEF_PORT);
+
+ /* check that protocol options are set */
+ if(db_dword_get(NULL, VQCHAT_PROTO, "ProtoOpt", (DWORD)-1) == (DWORD)-1)
+ db_dword_set(NULL, VQCHAT_PROTO, "ProtoOpt", VQCHAT_VQP_DEF_PROTO_OPT);
+
+ /* check connection type (the default is IP) */
+ rc = db_byte_get(NULL, VQCHAT_PROTO, "ProtoConn", 10);
+ if(rc != 0 && rc != 1)
+ db_byte_set(NULL, VQCHAT_PROTO, "ProtoConn", 0);
+
+ /* load (or generate a new) user uuid */
+ rc = 1; /* will get reset to 0, if we already have valid uuid set */
+ if(!db_get(NULL, VQCHAT_PROTO, "Uuid", &dbv)) {
+ if(dbv.type == DBVT_ASCIIZ) {
+ /* parse uuid from string */
+ vqp_uuid_t uuid;
+ if(!vqp_uuid_from_string(&uuid, dbv.pszVal)) {
+ /* set uuid */
+ user_set_uuid(&uuid, FALSE);
+ rc = 0;
+ }
+ }
+ }
+ if(rc) {
+ /* generate new uuid and store it in database */
+ vqp_uuid_t uuid;
+ vqp_uuid_create(&uuid);
+ user_set_uuid(&uuid, TRUE);
+ }
+
+ /* load multicast settings */
+ if(db_dword_get(NULL, VQCHAT_PROTO, "Multicast", 0) == 0)
+ db_dword_set(NULL, VQCHAT_PROTO, "Multicast", VQCHAT_VQP_DEF_MULTICAST);
+
+ if(db_byte_get(NULL, VQCHAT_PROTO, "MulticastScope", 0) == 0)
+ db_byte_set(NULL, VQCHAT_PROTO, "MulticastScope", VQCHAT_VQP_DEF_SCOPE);
+
+ /* check user list timeout value */
+ if(db_get(NULL, VQCHAT_PROTO, "UserlistTimeout", &dbv) || dbv.type!=DBVT_BYTE) {
+ /* setting not set, use default */
+ db_byte_set(NULL, VQCHAT_PROTO,
+ "UserlistTimeout", VQCHAT_DEF_REFRESH_TIMEOUT);
+ } else {
+ /* check if timeout value is within valid range */
+ if(dbv.type!=DBVT_BYTE) {
+ db_unset(NULL, VQCHAT_PROTO, "UserlistTimeout");
+ db_byte_set(NULL, VQCHAT_PROTO,
+ "UserlistTimeout", VQCHAT_DEF_REFRESH_TIMEOUT);
+ } else if((unsigned char)dbv.cVal < VQCHAT_MIN_REFRESH_TIMEOUT) {
+ db_byte_set(NULL, VQCHAT_PROTO,
+ "UserlistTimeout", VQCHAT_MIN_REFRESH_TIMEOUT);
+ } else if((unsigned char)dbv.cVal > VQCHAT_MAX_REFRESH_TIMEOUT) {
+ db_byte_set(NULL, VQCHAT_PROTO,
+ "UserlistTimeout", VQCHAT_MAX_REFRESH_TIMEOUT);
+ }
+ }
+
+ /* get default codepage */
+#ifdef VYPRESSCHAT
+ rc = db_byte_get(NULL, VQCHAT_PROTO, "Codepage", 2);
+ if(rc != 0 && rc != 1) {
+ /* default to utf8 encoding on vypress chat networks */
+ user_set_codepage(VQP_CODEPAGE_UTF8, TRUE);
+ } else {
+ /* codepage setting was read successfully */
+ user_set_codepage(rc==0 ? VQP_CODEPAGE_LOCALE: VQP_CODEPAGE_UTF8, 0);
+ }
+#else
+ /* we always default to locale encoding on quickchat networks */
+ user_set_codepage(VQP_CODEPAGE_LOCALE, FALSE);
+#endif
+
+ /* load user's channel list */
+ rc = db_get(NULL, VQCHAT_PROTO, "Chanlist", &dbv);
+
+ if(!rc && !strlen(dbv.pszVal)) {
+ /* chanlist is valid, but empty */
+ user_set_chanlist(NULL, 0);
+ } else {
+ if(rc || (!rc && dbv.type!=DBVT_ASCIIZ)
+ || (!rc && !chanlist_is_valid(dbv.pszVal, 1)))
+ {
+ /* user list is unset or invalid - set to default:
+ * "#Main" channel */
+ char * default_chanlist = chanlist_add(NULL, VQCHAT_MAIN_CHANNEL);
+ user_set_chanlist(default_chanlist, TRUE);
+ chanlist_free(default_chanlist);
+ } else {
+ /* chanlist is valid: set it */
+ user_set_chanlist(dbv.pszVal, FALSE);
+ }
+ }
+ if(!rc) DBFreeVariant(&dbv);
+
+ /* convert broadcasts masks from old-style
+ */
+ if(!db_get(NULL, VQCHAT_PROTO, "Broadcast#0", &dbv) && dbv.type==DBVT_DWORD) {
+ if(dbv.dVal)
+ db_dword_list_add(NULL, VQCHAT_PROTO, "BroadcastMasks", dbv.dVal, TRUE);
+ db_unset(NULL, VQCHAT_PROTO, "Broadcast#0");
+ }
+ if(!db_get(NULL, VQCHAT_PROTO, "Broadcast#1", &dbv) && dbv.type==DBVT_DWORD) {
+ if(dbv.dVal)
+ db_dword_list_add(NULL, VQCHAT_PROTO, "BroadcastMasks", dbv.dVal, TRUE);
+ db_unset(NULL, VQCHAT_PROTO, "Broadcast#1");
+ }
+ if(!db_get(NULL, VQCHAT_PROTO, "Broadcast#2", &dbv) && dbv.type==DBVT_DWORD) {
+ if(dbv.dVal)
+ db_dword_list_add(NULL, VQCHAT_PROTO, "BroadcastMasks", dbv.dVal, TRUE);
+ db_unset(NULL, VQCHAT_PROTO, "Broadcast#2");
+ }
+}
+
+
+/* exported routines
+ */
+
+void options_init()
+{
+}
+
+void options_hook_modules_loaded()
+{
+ options_load_settings();
+
+ s_hook_opt_initialise = HookEvent(ME_OPT_INITIALISE, options_hook_opt_initialise);
+ s_hook_userinfo_initialise
+ = HookEvent(ME_USERINFO_INITIALISE, options_hook_userinfo_initialise);
+}
+
+void options_uninit()
+{
+ UnhookEvent(s_hook_opt_initialise);
+ UnhookEvent(s_hook_userinfo_initialise);
+}
+
+void options_show_network_options()
+{
+ OPENOPTIONSDIALOG ood;
+ memset(&ood, 0, sizeof(ood));
+ ood.cbSize = sizeof(ood);
+ ood.pszGroup = Translate("Network");
+ ood.pszPage = Translate(VQCHAT_PROTO_NAME " Network");
+
+ CallService(MS_OPT_OPENOPTIONS, 0, (LPARAM)&ood);
+}
+
+void options_show_user_options()
+{
+ OPENOPTIONSDIALOG ood;
+ memset(&ood, 0, sizeof(ood));
+ ood.cbSize = sizeof(ood);
+ ood.pszGroup = Translate("Network");
+ ood.pszPage = Translate(VQCHAT_PROTO_NAME);
+
+ CallService(MS_OPT_OPENOPTIONS, 0, (LPARAM)&ood);
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/options.h b/plugins/!NotAdopted/VypressChat/options.h
new file mode 100644
index 0000000000..dec0b09532
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/options.h
@@ -0,0 +1,33 @@
+/*
+ * 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: options.h,v 1.6 2005/04/11 21:44:16 bobas Exp $
+ */
+
+#ifndef __OPTIONS_H
+#define __OPTIONS_H
+
+void options_init();
+void options_uninit();
+void options_hook_modules_loaded();
+
+void options_show_user_options();
+void options_show_network_options();
+
+#endif /* #ifndef __OPTIONS_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/pthread.c b/plugins/!NotAdopted/VypressChat/pthread.c
new file mode 100644
index 0000000000..062ec1769b
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/pthread.c
@@ -0,0 +1,58 @@
+/*
+AOL Instant Messenger Plugin for Miranda IM
+
+Copyright © 2003 Robert Rainwater
+
+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.
+*/
+
+#include <process.h>
+#include "miranda.h"
+
+struct pthread_arg
+{
+ HANDLE hEvent;
+ void (*threadcode) (void *);
+ void *arg;
+};
+
+void pthread_r(struct pthread_arg *fa)
+{
+ void (*callercode) (void *) = fa->threadcode;
+ void *arg = fa->arg;
+ CallService(MS_SYSTEM_THREAD_PUSH, 0, 0);
+ SetEvent(fa->hEvent);
+/* __try { */
+ callercode(arg);
+/* } */
+/* __finally { */
+ CallService(MS_SYSTEM_THREAD_POP, 0, 0);
+/* } */
+}
+
+unsigned long pthread_create(void (*threadcode) (void *), void *arg)
+{
+ unsigned long rc;
+ struct pthread_arg fa;
+ fa.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ fa.threadcode = threadcode;
+ fa.arg = arg;
+ rc = _beginthread((void *) (void *) pthread_r, 0, &fa);
+ if ((unsigned long) -1L != rc) {
+ WaitForSingleObject(fa.hEvent, INFINITE);
+ }
+ CloseHandle(fa.hEvent);
+ return rc;
+}
diff --git a/plugins/!NotAdopted/VypressChat/pthread.h b/plugins/!NotAdopted/VypressChat/pthread.h
new file mode 100644
index 0000000000..a6f518dd6e
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/pthread.h
@@ -0,0 +1,30 @@
+/*
+AOL Instant Messenger Plugin for Miranda IM
+
+Copyright © 2003 Robert Rainwater
+
+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.
+*/
+#ifndef PTHREAD_H
+#define PTHREAD_H
+
+unsigned long pthread_create(void (*threadcode) (void *), void *arg);
+typedef CRITICAL_SECTION pthread_mutex_t;
+#define pthread_mutex_init(pmutex) InitializeCriticalSection(pmutex)
+#define pthread_mutex_destroy(pmutex) DeleteCriticalSection(pmutex)
+#define pthread_mutex_lock(pmutex) EnterCriticalSection(pmutex)
+#define pthread_mutex_unlock(pmutex) LeaveCriticalSection(pmutex)
+
+#endif
diff --git a/plugins/!NotAdopted/VypressChat/resource.rc b/plugins/!NotAdopted/VypressChat/resource.rc
new file mode 100644
index 0000000000..43f131c5f8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/resource.rc
@@ -0,0 +1,258 @@
+/*
+ * 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: resource.rc,v 1.20 2005/04/11 20:23:58 bobas Exp $
+ */
+
+#include "windows.h"
+
+#include "main.h"
+#include "resource.h"
+
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif
+
+/* protocol icons
+ */
+#ifdef VYPRESSCHAT
+IDI_VQCHAT_PROTO ICON "icons/vypresschat.ico"
+IDI_VQCHAT_PROTO_LARGE ICON "icons/vypresschat_large.ico"
+IDI_VQCHAT_PROTO_ONLINE ICON "icons/vypresschat_online.ico"
+IDI_VQCHAT_PROTO_OFFLINE ICON "icons/vypresschat_offline.ico"
+#endif
+
+#ifdef QUICKCHAT
+IDI_VQCHAT_PROTO ICON "icons/quickchat.ico"
+IDI_VQCHAT_PROTO_LARGE ICON "icons/quickchat_large.ico"
+IDI_VQCHAT_PROTO_ONLINE ICON "icons/quickchat_online.ico"
+IDI_VQCHAT_PROTO_OFFLINE ICON "icons/quickchat_offline.ico"
+#endif
+
+/* contact icons
+ */
+IDI_CONTACT_BELL ICON "icons/contact_bell.ico"
+
+/* chatroom icons
+ */
+#ifdef VYPRESSCHAT
+IDI_CHATROOM ICON "icons/vypresschat_channel.ico"
+#endif
+
+#ifdef QUICKCHAT
+IDI_CHATROOM ICON "icons/quickchat_channel.ico"
+#endif
+
+/* user options dialog
+ */
+IDD_USER DIALOGEX 0, 0, 222, 141
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "User Information",IDC_STATIC,0,0,222,84
+ LTEXT "User nickname:",IDC_STATIC,6,14,50,8
+ EDITTEXT IDC_USER_EDIT_NICKNAME,60,13,96,12,ES_AUTOHSCROLL
+ LTEXT "Gender:",IDC_STATIC,6,32,30,8
+ COMBOBOX IDC_USER_COMBO_GENDER,60,30,96,32,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Unique ID:",IDC_STATIC,6,50,42,8
+ EDITTEXT IDC_USER_EDIT_UUID,60,48,156,12,ES_AUTOHSCROLL
+ LTEXT "Universally unique ID (UUID) number identifies you on the network. Keep this number intact.",
+ IDC_STATIC,60,62,156,18
+ GROUPBOX "Other options",IDC_STATIC,0,90,222,51
+ CONTROL "Write message to contact history when alert beep is received.",
+ IDC_USER_MSGONALERTBEEP,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,6,102,210,8
+ CONTROL "Append user nickname when topic is set.",
+ IDC_USER_NICKNAMEONTOPIC,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,6,114,210,10
+ CONTROL "New contacts will prefer messages instead of private chats.",
+ IDC_USER_NEWPREFERMSG,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,6,126,210,8
+END
+
+/* network connection options dialog
+ */
+IDD_CONN DIALOGEX 0, 0, 248, 238
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "User list refresh period:",IDC_STATIC,12,188,76,8
+ EDITTEXT IDC_CONN_EDIT_REFRESH,90,186,42,12,ES_AUTOHSCROLL
+ LTEXT " seconds",IDC_STATIC,134,188,29,8
+ GROUPBOX "Connection Type",IDC_STATIC,0,0,246,168
+ LTEXT "Network port:",IDC_STATIC,12,14,46,9
+ EDITTEXT IDC_CONN_EDIT_PORT,66,12,60,12,ES_AUTOHSCROLL
+ PUSHBUTTON "Default",IDC_CONN_BTN_DEF_PORT,132,12,36,12
+ LTEXT "Multicast scope:",IDC_STATIC,24,42,52,8
+ EDITTEXT IDC_CONN_EDIT_SCOPE,84,41,60,12,ES_AUTOHSCROLL
+ PUSHBUTTON "Default",IDC_CONN_BTN_DEF_MULTICAST,150,41,36,11
+ LTEXT "Multicast address:",IDC_STATIC,24,61,60,8
+ CONTROL "Use unicode (UTF-8) encoding per default",
+ IDC_CONN_CHECK_UTF8,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,12,203,150,12
+ CONTROL "Use multicast networking",IDC_CONN_RADIO_MULTICAST,
+ "Button",BS_AUTORADIOBUTTON,12,30,174,10
+ CONTROL "Use broadcast networking",IDC_CONN_RADIO_BROADCAST,
+ "Button",BS_AUTORADIOBUTTON,12,78,174,8
+ CONTROL "Use IPX networking",IDC_CONN_RADIO_IPX,"Button",
+ BS_AUTORADIOBUTTON,12,156,174,8
+ GROUPBOX "Miscellaneous Options",IDC_STATIC,0,174,246,62
+ LTEXT "This encoding will be used when communicating over channels and with clients that do not report the encoding they use.",
+ IDC_CONN_LABEL_UTF8,24,216,216,18
+ CONTROL "",IDC_CONN_IP_MULTICAST,"SysIPAddress32",WS_TABSTOP,84,
+ 60,102,12
+ GROUPBOX "Broadcast masks",IDC_STATIC,24,87,192,66
+ LISTBOX IDC_CONN_BCAST_LIST,30,96,87,50,LBS_SORT |
+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "",IDC_CONN_BCAST_INPUT,"SysIPAddress32",WS_TABSTOP,122,
+ 107,88,12
+ PUSHBUTTON "&Add",IDC_CONN_BCAST_ADD,122,123,42,13
+ PUSHBUTTON "&Remove",IDC_CONN_BCAST_REMOVE,168,123,42,13
+END
+
+/* channel settings dialog
+ */
+IDD_CS DIALOGEX 0, 0, 264, 66
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_SYSMENU
+ | WS_VISIBLE | DS_CENTER | WS_CLIPSIBLINGS | WS_CAPTION
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Channel topic",IDC_STATIC,0,0,264,46
+ LTEXT "Channel name:",IDC_STATIC,5,11,60,8
+ EDITTEXT IDC_CS_EDIT_NAME,54,9,90,12,ES_AUTOHSCROLL | WS_DISABLED
+ LTEXT "Current topic:",IDC_STATIC,8,30,48,8
+ EDITTEXT IDC_CS_EDIT_TOPIC,54,28,162,12,ES_AUTOHSCROLL
+ PUSHBUTTON "Set",IDC_CS_BTN_SET,222,28,36,12
+ PUSHBUTTON "OK",IDOK,204,49,57,14
+END
+
+/* join channel dialog
+ */
+IDD_JC DIALOGEX 0, 0, 174, 42
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP |
+ WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
+CAPTION "Join channel"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "&Join",IDOK,118,24,52,14
+ LTEXT "Channel:",IDC_STATIC,6,8,30,8
+ COMBOBOX IDC_JC_COMBO_CHANNEL,36,6,134,62,CBS_DROPDOWN | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+END
+
+/* set nickname dialog
+ */
+IDD_SN DIALOGEX 0, 0, 186, 42
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_SYSMENU
+ | WS_VISIBLE | DS_CENTER | WS_CLIPSIBLINGS | WS_CAPTION
+CAPTION "Set nickname"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,78,24,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,132,24,50,14
+ LTEXT "Your nickname:",IDC_STATIC,6,8,54,8
+ EDITTEXT IDC_SN_EDIT_NICKNAME,56,6,126,12,ES_AUTOHSCROLL
+END
+
+/* "user details: basic info" property page
+ */
+IDD_USERINFO DIALOGEX 0, 0, 222, 132
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "Computer name:",IDC_STATIC,6,6,54,8
+ LTEXT "User name:",IDC_STATIC,6,18,54,8
+ LTEXT "Workgroup:",IDC_STATIC,6,42,54,8
+ LTEXT "Platform:",IDC_STATIC,6,54,54,8
+ LTEXT "Software:",IDC_STATIC,6,66,54,8
+ LTEXT "Channels:",IDC_STATIC,6,78,48,8
+ LISTBOX IDC_USERINFO_CHANNELS,66,78,96,48,LBS_SORT | LBS_NOTIFY |
+ LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "",IDC_USERINFO_COMPUTER,66,6,150,8
+ LTEXT "",IDC_USERINFO_USER,66,18,150,8
+ LTEXT "",IDC_USERINFO_NODE,66,30,150,8
+ LTEXT "",IDC_USERINFO_WORKGROUP,66,42,150,8
+ LTEXT "",IDC_USERINFO_PLATFORM,66,54,150,8
+ LTEXT "",IDC_USERINFO_SOFTWARE,66,66,150,8
+ LTEXT "Node address:",IDC_STATIC,6,30,54,8
+END
+
+/* "user details: <protocol> options" property page
+ */
+IDD_USEROPT DIALOGEX 0, 0, 222, 132
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "Nickname:",IDC_STATIC,6,8,34,8
+ EDITTEXT IDC_USEROPT_NICKNAME,42,6,120,12,ES_AUTOHSCROLL
+ PUSHBUTTON "&Set",IDC_USEROPT_SET,168,6,48,12
+ LTEXT "You can override nickname for the contact. This can be useful when user changes his nickname while you are offline and you want to retain contact's history.",
+ IDC_STATIC,12,20,204,24
+ GROUPBOX "Contact options",IDC_STATIC,6,54,210,72
+ CONTROL "Do not let the nickname change when user renames.",
+ IDC_USEROPT_LOCKNICK,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,12,82,180,8
+ COMBOBOX IDC_USEROPT_PREFERS,67,66,76,46,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Contact prefers:",IDC_STATIC,12,68,54,8
+END
+
+/* dll/pe version info
+ */
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VQCHAT_VER_MAJ0,VQCHAT_VER_MAJ1,VQCHAT_VER_MIN0,VQCHAT_VER_MIN1
+ PRODUCTVERSION VQCHAT_VER_MAJ0,VQCHAT_VER_MAJ1,VQCHAT_VER_MIN0,VQCHAT_VER_MIN1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004e4"
+ BEGIN
+ VALUE "Comments", "Licensed under the terms of the GNU General Public License\0"
+ VALUE "CompanyName", "\0"
+ VALUE "FileDescription", VQCHAT_PROTO_NAME " protocol plugin for Miranda IM\0"
+ VALUE "FileVersion", VQCHAT_VER_FILEVERSION
+ VALUE "InternalName", VQCHAT_PROTO_NAME " protocol plugin for Miranda IM\0"
+ VALUE "LegalCopyright", VQCHAT_VER_COPYRIGHT "\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", VQCHAT_PROTO_DLL "\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "\0"
+ VALUE "ProductVersion", VQCHAT_VER_FILEVERSION
+#ifndef NDEBUG
+ VALUE "SpecialBuild", "Debug build\0"
+#else
+ VALUE "SpecialBuild", "\0"
+#endif
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1252
+ END
+END
diff --git a/plugins/!NotAdopted/VypressChat/service.c b/plugins/!NotAdopted/VypressChat/service.c
new file mode 100644
index 0000000000..72fa7ea3b8
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/service.c
@@ -0,0 +1,730 @@
+/*
+ * 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: service.c,v 1.30 2005/03/15 02:03:47 bobas Exp $
+ */
+
+#include "miranda.h"
+#include "libvqproto/vqproto.h"
+
+#include "main.h"
+#include "service.h"
+#include "resource.h"
+#include "user.h"
+#include "userlist.h"
+#include "contacts.h"
+#include "options.h"
+#include "chatroom.h"
+#include "chanlist.h"
+#include "util.h"
+#include "msgloop.h"
+#include "skin.h"
+
+/* defines */
+#define SVC_CONTACT_CHANSETTINGS "/ChanSettings"
+#define SVC_CONTACT_BEEP "/SendBeep"
+#define SVC_MENU_SET_NICKNAME "/SetNickname"
+#define SVC_MENU_JOIN_CHANNEL "/JoinChannel"
+
+/* static data
+ */
+static HANDLE s_hook_ModulesLoaded;
+static HANDLE s_hook_menuPreBuild;
+static HANDLE s_hook_contactDeleted;
+
+static HANDLE s_hContactMenuBeep, s_hContactChanSettings;
+static HANDLE s_hMenuJoinChannel, s_hMenuSetNickname;
+static HWND s_hwndJoinChannel, s_hwndSetNickname;
+static char * s_joinChannelChanlist = NULL;
+
+static vqp_link_t s_vqpLink;
+
+/* static routines
+ */
+static int
+service_GetCaps(WPARAM wParam, LPARAM lParam)
+{
+ switch(wParam) {
+ case PFLAGNUM_1:
+ return PF1_IM | PF1_MODEMSG | PF1_BASICSEARCH;
+
+ case PFLAGNUM_2:
+ return PF2_ONLINE|PF2_SHORTAWAY|PF2_HEAVYDND|PF2_LONGAWAY;
+
+ case PFLAGNUM_3:
+ /* status modes where away can be set */
+ return PF2_HEAVYDND|PF2_LONGAWAY;
+
+ case PFLAG_UNIQUEIDTEXT:
+ return (int)"Nickname";
+
+ case PFLAG_UNIQUEIDSETTING:
+ return (int)"Nick";
+
+ case PFLAG_MAXLENOFMESSAGE:
+ return VQP_MAX_PACKET_SIZE; /* an approximation */
+ }
+
+ return 0;
+}
+
+static int
+service_GetName(WPARAM wParam, LPARAM lParam)
+{
+ strncpy((char*)lParam, VQCHAT_PROTO_NAME, wParam);
+ return 0;
+}
+
+static int
+service_LoadIcon(WPARAM wParam, LPARAM lParam)
+{
+ UINT id;
+
+ switch(wParam) {
+ case PLI_PROTOCOL|PLIF_LARGE:
+ id = IDI_VQCHAT_PROTO_LARGE;
+ break;
+
+ case PLI_PROTOCOL|PLIF_SMALL:
+ id = IDI_VQCHAT_PROTO;
+ break;
+
+ case PLI_ONLINE|PLIF_SMALL:
+ id = IDI_VQCHAT_PROTO_ONLINE;
+ break;
+
+ case PLI_OFFLINE|PLIF_SMALL:
+ id = IDI_VQCHAT_PROTO_OFFLINE;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return (int)LoadImage(
+ g_hDllInstance, MAKEINTRESOURCE(id), IMAGE_ICON,
+ GetSystemMetrics((wParam & PLIF_SMALL) ? SM_CXSMICON: SM_CXICON),
+ GetSystemMetrics((wParam & PLIF_SMALL) ? SM_CYSMICON: SM_CYICON),
+ 0);
+}
+
+static int
+service_SetStatus(WPARAM wParam, LPARAM lParam)
+{
+ int oldStatus = user_status();
+
+ if(!user_set_status(wParam)) {
+ ProtoBroadcastAck(
+ VQCHAT_PROTO, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS,
+ (HANDLE)oldStatus, (LPARAM)user_status());
+ }
+
+ return 0;
+}
+
+static int
+service_GetStatus(WPARAM wParam, LPARAM lParam)
+{
+ return user_status();
+}
+
+static int
+service_SetAwayMsg(WPARAM wParam, LPARAM lParam)
+{
+ /*int status_mode = wParam;*/
+ LPCSTR szMessage = (LPCSTR)lParam; /* note that szMessage can be NULL! */
+ char * message;
+
+ if(user_offline())
+ return 1;
+
+ /* convert to utf and set */
+ message = util_loc2utf(szMessage ? szMessage: "");
+ user_set_awaymsg(message);
+ free(message);
+
+ return 0; /* success */
+}
+
+static void CALLBACK
+service_BasicSearch_ResultAPC(ULONG_PTR dwParam)
+{
+ if(dwParam) {
+ /* user found */
+ PROTOSEARCHRESULT psr;
+
+ memset(&psr, 0, sizeof(psr));
+ psr.cbSize = sizeof(psr);
+ psr.nick = (char*)dwParam;
+
+ ProtoBroadcastAck(VQCHAT_PROTO, NULL, ACKTYPE_SEARCH,
+ ACKRESULT_DATA, (HANDLE)1, (LPARAM)&psr);
+
+ free((char*)dwParam);
+ }
+
+ /* search finished */
+ ProtoBroadcastAck(VQCHAT_PROTO, NULL, ACKTYPE_SEARCH,
+ ACKRESULT_SUCCESS, (HANDLE)1, 0);
+}
+
+/* service_BasicSearch:
+ * implement case insensitive search
+ */
+static int
+service_BasicSearch(WPARAM wParam, LPARAM lParam)
+{
+ char * nickname;
+
+ if(user_offline())
+ return 0;
+
+ nickname = util_loc2utf((char*)lParam);
+ QueueUserAPC(
+ service_BasicSearch_ResultAPC, g_hMainThread,
+ (ULONG_PTR)(userlist_user_exists(nickname) ? strdup(nickname): 0));
+ free(nickname);
+
+ return 1; /* search started (imaginary handle 1) */
+}
+
+static int
+service_AddToList(WPARAM wParam, LPARAM lParam)
+{
+ int added;
+ char * nickname;
+
+ /* wParam = flags */
+ PROTOSEARCHRESULT * psresult = (PROTOSEARCHRESULT*)lParam;
+
+ if(user_offline())
+ return 0;
+
+ nickname = util_loc2utf(psresult->nick);
+ added = (int)contacts_add_contact(nickname, 1);
+ free(nickname);
+
+ return added;
+}
+
+static int
+service_GetContactInfo(WPARAM wParam, LPARAM lParam)
+{
+ CCSDATA * ccs = (CCSDATA*)lParam;
+
+ if(user_offline())
+ return 1;
+
+ return contacts_get_contact_info(ccs->hContact, ccs->wParam);
+}
+
+static int
+service_contact_send_message(WPARAM wParam, LPARAM lParam)
+{
+ CCSDATA * ccs = (CCSDATA*)lParam;
+ char * message;
+ int result;
+
+ message = util_loc2utf((const char*)ccs->lParam);
+ result = contacts_send_contact_message(ccs->hContact, message, wParam, 0);
+ free(message);
+
+ return result;
+}
+
+static int
+service_contact_send_messageW(WPARAM wParam, LPARAM lParam)
+{
+ CCSDATA * ccs = (CCSDATA*)lParam;
+ int result, len;
+ char * message;
+
+ len = strlen((const char *) ccs->lParam);
+ message = util_uni2utf((wchar_t *)((const char *)ccs->lParam + len + 1));
+
+ result = contacts_send_contact_message(ccs->hContact, message, wParam, 0);
+ free(message);
+
+ return result;
+}
+
+/* service_contact_received_message:
+ * invoked after our PSR_MESSAGE from contacts.c:contacts_input_contact_message()
+ * gets routed through all protocol plugins, and gets back to us
+ */
+static int
+service_contact_received_message(WPARAM wParam, LPARAM lParam)
+{
+ CCSDATA * ccs = (CCSDATA *)lParam;
+ PROTORECVEVENT * pre = (PROTORECVEVENT *)ccs->lParam;
+ DBEVENTINFO dbei;
+
+ memset(&dbei, 0, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.szModule = VQCHAT_PROTO;
+ dbei.timestamp = pre->timestamp;
+ dbei.flags = (pre->flags & PREF_CREATEREAD) ? DBEF_READ: 0;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.cbBlob = strlen(pre->szMessage) + 1;
+ if(pre->flags & PREF_UNICODE)
+ dbei.cbBlob *= (sizeof(wchar_t) + 1);
+ dbei.pBlob = (PBYTE) pre->szMessage;
+
+ CallService(MS_DB_EVENT_ADD, (WPARAM)ccs->hContact, (LPARAM)&dbei);
+
+ return 0; /* success */
+}
+
+static int
+service_contact_beep(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ if(hContact && contacts_is_user_contact(hContact))
+ contacts_send_beep(hContact);
+
+ return 0;
+}
+
+static int
+service_contact_channel_settings(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ if(hContact && contacts_is_chatroom_contact(hContact)) {
+ DBVARIANT dbv;
+ if(!db_get(hContact, VQCHAT_PROTO, "Nick", &dbv)) {
+ /* show channel settings (skip '#' at the beginning) */
+ char * channel = util_loc2utf(dbv.pszVal + 1);
+ chatroom_channel_show_settings_dlg(channel);
+
+ free(channel);
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ return 0;
+}
+
+static int
+service_prebuild_contact_menu(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+
+ if(hContact) {
+ CLISTMENUITEM clmi;
+ memset(&clmi, 0, sizeof(clmi));
+ clmi.cbSize = sizeof(clmi);
+
+ /* beep */
+ if(contacts_is_user_contact(hContact)) {
+ clmi.flags = CMIM_FLAGS
+ | (contacts_get_contact_status(hContact)
+ ==ID_STATUS_OFFLINE
+ ? CMIF_GRAYED: 0);
+ } else {
+ clmi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+ }
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)s_hContactMenuBeep, (LPARAM)&clmi);
+
+ /* channel settings */
+ clmi.flags = CMIM_FLAGS
+ | (!contacts_is_chatroom_contact(hContact)
+ ? CMIF_HIDDEN: 0);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)s_hContactChanSettings, (LPARAM)&clmi);
+ }
+
+ return 0;
+}
+
+static int
+service_contact_deleted(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+
+ /* leave the channel */
+ if(hContact!=NULL && contacts_is_chatroom_contact(hContact)) {
+ DBVARIANT dbv;
+
+ if(!db_get(hContact, VQCHAT_PROTO, "Nick", &dbv)) {
+ /* channel name must begin with '#' */
+ if(dbv.pszVal[0]=='#') {
+ char * channel = util_loc2utf(dbv.pszVal + 1);
+ chatroom_channel_part(channel, 0);
+ free(channel);
+ }
+
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ return 0;
+}
+
+void service_join_channel_merge_chanlist(const char * user_chanlist)
+{
+ if(s_hwndJoinChannel) {
+ char * chanlist = chanlist_copy(user_chanlist),
+ * channel;
+
+ while((channel = chanlist_shift(&chanlist)) != NULL) {
+ if(!chanlist_contains(*user_p_chanlist(), channel)
+ && !chanlist_contains(s_joinChannelChanlist, channel)) {
+ /* add '#'+channel to combo box list */
+ char * mod_channel = malloc(strlen(channel) + 2);
+ wchar_t * u_channel;
+ mod_channel[0] = '#';
+ strcpy(mod_channel + 1, channel);
+ u_channel = util_utf2uni(mod_channel);
+ free(mod_channel);
+
+ SendMessageW(
+ GetDlgItem(s_hwndJoinChannel, IDC_JC_COMBO_CHANNEL),
+ CB_ADDSTRING, 0, (LPARAM)u_channel);
+ free(u_channel);
+
+ /* add channel to the list of already visible channels
+ * in the combo list
+ */
+ s_joinChannelChanlist = chanlist_add(
+ s_joinChannelChanlist, channel);
+ }
+ free(channel);
+ }
+ }
+}
+
+static BOOL CALLBACK service_join_channel_dlg_wndproc(
+ HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
+{
+ int len;
+ char * channel;
+ char * r_user_nickname;
+
+ switch(nMsg) {
+ case WM_INITDIALOG:
+ /* set dialog icon */
+ SendMessage(
+ hwnd, WM_SETICON, ICON_SMALL,
+ (LPARAM)LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_CHATROOM)));
+
+ /* clear channel list and send channel list req*/
+ SendMessage(GetDlgItem(hwnd, IDC_JC_COMBO_CHANNEL), CB_RESETCONTENT, 0, 0);
+
+ r_user_nickname = util_utf2vqp(user_codepage(), user_nickname());
+ msgloop_send(vqp_msg_channel_list_req(s_vqpLink, r_user_nickname), 0);
+ free(r_user_nickname);
+
+ chanlist_free(s_joinChannelChanlist);
+ s_joinChannelChanlist = NULL;
+
+ /* set focus on current combo box control */
+ SetFocus(GetDlgItem(hwnd, IDC_JC_COMBO_CHANNEL));
+
+ /* disable ok button, as there is no text in combo box */
+ EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
+ break;
+
+ case WM_COMMAND:
+ switch(HIWORD(wParam)) {
+ case BN_CLICKED:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ /* join the specified channel */
+ channel = util_GetDlgItemTextUtf(hwnd, IDC_JC_COMBO_CHANNEL);
+ chatroom_channel_join(channel[0]=='#' ? channel + 1: channel, 0);
+ free(channel);
+
+ /* close the dialog */
+ SendMessage(hwnd, WM_CLOSE, 0, 0);
+ break;
+ }
+ break;
+
+ case CBN_EDITCHANGE:
+ switch(LOWORD(wParam)) {
+ case IDC_JC_COMBO_CHANNEL:
+ len = GetWindowTextLengthW(GetDlgItem(hwnd, IDC_JC_COMBO_CHANNEL));
+ EnableWindow(GetDlgItem(hwnd, IDOK), len!=0);
+ break;
+ }
+ break;
+
+ case CBN_SELCHANGE:
+ switch(LOWORD(wParam)) {
+ case IDC_JC_COMBO_CHANNEL:
+ /* enable the 'join' button after
+ * the user selects a channel from the list
+ */
+ EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ s_hwndJoinChannel = NULL;
+
+ chanlist_free(s_joinChannelChanlist);
+ s_joinChannelChanlist = NULL;
+ break;
+ }
+ return FALSE;
+}
+
+static int service_menu_join_channel(WPARAM wParam, LPARAM lParam)
+{
+ if(!s_hwndJoinChannel) {
+ s_hwndJoinChannel = CreateDialog(
+ g_hDllInstance, MAKEINTRESOURCE(IDD_JC), NULL,
+ service_join_channel_dlg_wndproc
+ );
+ ShowWindow(s_hwndJoinChannel, SW_SHOW);
+ }
+ SetActiveWindow(s_hwndJoinChannel);
+
+ return 0;
+}
+
+static BOOL CALLBACK
+service_set_nickname_dlg_wndproc(
+ HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
+{
+ int len;
+ char * nickname;
+
+ switch(nMsg) {
+ case WM_INITDIALOG:
+ /* set dialog icon */
+ SendMessage(
+ hwnd, WM_SETICON, ICON_BIG,
+ (LPARAM)(HICON)LoadImage(
+ g_hDllInstance, MAKEINTRESOURCE(IDI_VQCHAT_PROTO_LARGE),
+ IMAGE_ICON, GetSystemMetrics(SM_CXICON),
+ GetSystemMetrics(SM_CYICON), LR_SHARED)
+ );
+
+ /* set current nickname */
+ util_SetDlgItemTextUtf(hwnd, IDC_SN_EDIT_NICKNAME, user_nickname());
+
+ /* select the entire nickname */
+ SendMessage(
+ GetDlgItem(hwnd, IDC_SN_EDIT_NICKNAME),
+ EM_SETSEL, (WPARAM)0, (LPARAM)-1);
+ SetFocus(GetDlgItem(hwnd, IDC_SN_EDIT_NICKNAME));
+ break;
+
+ case WM_COMMAND:
+ switch(HIWORD(wParam)) {
+ case BN_CLICKED:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ /* set nickname */
+ nickname = util_GetDlgItemTextUtf(hwnd, IDC_SN_EDIT_NICKNAME);
+ user_set_nickname(nickname, 1);
+ free(nickname);
+
+ /* FALLTHROUGH */
+
+ case IDCANCEL:
+ SendMessage(hwnd, WM_CLOSE, 0, 0);
+ break;
+ }
+ break;
+
+ case EN_CHANGE:
+ switch(LOWORD(wParam)) {
+ case IDC_SN_EDIT_NICKNAME:
+ /* disable the ok button, if we have
+ * an invalid nickname entered
+ */
+ len = GetWindowTextLengthW(GetDlgItem(hwnd, IDC_SN_EDIT_NICKNAME));
+ EnableWindow(GetDlgItem(hwnd, IDOK), len!=0);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ s_hwndSetNickname = NULL;
+ break;
+ }
+ return FALSE;
+}
+
+static int
+service_menu_set_nickname(WPARAM wParam, LPARAM lParam)
+{
+ if(!s_hwndSetNickname) {
+ s_hwndSetNickname = CreateDialog(
+ g_hDllInstance, MAKEINTRESOURCE(IDD_SN), NULL,
+ service_set_nickname_dlg_wndproc
+ );
+ ShowWindow(s_hwndSetNickname, SW_SHOW);
+ }
+ SetActiveWindow(s_hwndSetNickname);
+
+ return 0;
+}
+
+static void
+service_init_menus()
+{
+ CLISTMENUITEM mi;
+ memset(&mi, 0, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ /* normal contacts: beep */
+ mi.pszName = "Alert Beep";
+ mi.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_CONTACT_BELL));
+ mi.pszService = VQCHAT_PROTO SVC_CONTACT_BEEP;
+ mi.pszContactOwner = VQCHAT_PROTO;
+ mi.popupPosition = 500090000;
+ mi.pszPopupName = VQCHAT_PROTO_NAME;
+ s_hContactMenuBeep = (HANDLE)CallService(
+ MS_CLIST_ADDCONTACTMENUITEM, (WPARAM)0, (LPARAM)&mi);
+
+ /* menu item: set nickname */
+ mi.pszName = "Set &nickname";
+ mi.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_VQCHAT_PROTO));
+ mi.pszService = VQCHAT_PROTO SVC_MENU_SET_NICKNAME;
+ mi.popupPosition = 500090000;
+ mi.pszPopupName = VQCHAT_PROTO_NAME;
+ s_hMenuSetNickname = (HANDLE)CallService(
+ MS_CLIST_ADDMAINMENUITEM, (WPARAM)0, (LPARAM)&mi);
+ s_hwndSetNickname = NULL;
+
+ if(chatroom_module_installed()) {
+ /* chatrooms: channel settings */
+ mi.pszName = "Channel settings";
+ mi.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_CHATROOM));
+ mi.pszService = VQCHAT_PROTO SVC_CONTACT_CHANSETTINGS;
+ mi.pszContactOwner = VQCHAT_PROTO;
+ mi.popupPosition = 500090000;
+ mi.pszPopupName = VQCHAT_PROTO_NAME;
+ s_hContactChanSettings = (HANDLE)CallService(
+ MS_CLIST_ADDCONTACTMENUITEM, (WPARAM)0, (LPARAM)&mi);
+
+ /* menu item: join channel */
+ mi.pszName = "&Join a channel";
+ mi.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_CHATROOM));
+ mi.pszService = VQCHAT_PROTO SVC_MENU_JOIN_CHANNEL;
+ mi.popupPosition = 500090000;
+ mi.pszPopupName = VQCHAT_PROTO_NAME;
+ mi.flags = CMIM_FLAGS | CMIF_GRAYED;
+ s_hMenuJoinChannel = (HANDLE)CallService(
+ MS_CLIST_ADDMAINMENUITEM, (WPARAM)0, (LPARAM)&mi);
+ s_hwndJoinChannel = NULL;
+ }
+}
+
+static int
+service_hook_modules_loaded(WPARAM wParam, LPARAM lParam)
+{
+ options_hook_modules_loaded();
+ skin_hook_modules_loaded();
+ user_hook_modules_loaded();
+ contacts_hook_modules_loaded();
+ chatroom_hook_modules_loaded();
+
+ service_init_menus();
+
+ s_hook_menuPreBuild = HookEvent(
+ ME_CLIST_PREBUILDCONTACTMENU, service_prebuild_contact_menu);
+ s_hook_contactDeleted = HookEvent(ME_DB_CONTACT_DELETED, service_contact_deleted);
+
+ return 0;
+}
+
+/* exported routines
+ */
+void service_register_services()
+{
+ /* register protocol service funcs */
+ CreateServiceFunction(VQCHAT_PROTO PS_GETCAPS, service_GetCaps);
+ CreateServiceFunction(VQCHAT_PROTO PS_GETNAME, service_GetName);
+ CreateServiceFunction(VQCHAT_PROTO PS_LOADICON, service_LoadIcon);
+ CreateServiceFunction(VQCHAT_PROTO PS_SETSTATUS, service_SetStatus);
+ CreateServiceFunction(VQCHAT_PROTO PS_GETSTATUS, service_GetStatus);
+ CreateServiceFunction(VQCHAT_PROTO PS_SETAWAYMSG, service_SetAwayMsg);
+ CreateServiceFunction(VQCHAT_PROTO PS_BASICSEARCH, service_BasicSearch);
+ CreateServiceFunction(VQCHAT_PROTO PS_ADDTOLIST, service_AddToList);
+
+ CreateServiceFunction(VQCHAT_PROTO PSS_GETINFO, service_GetContactInfo);
+
+ CreateServiceFunction(VQCHAT_PROTO PSS_MESSAGE, service_contact_send_message);
+ CreateServiceFunction(VQCHAT_PROTO PSS_MESSAGE"W", service_contact_send_messageW);
+ CreateServiceFunction(VQCHAT_PROTO PSR_MESSAGE, service_contact_received_message);
+
+ /* contact menu services */
+ CreateServiceFunction(
+ VQCHAT_PROTO SVC_CONTACT_BEEP,
+ service_contact_beep
+ );
+ CreateServiceFunction(
+ VQCHAT_PROTO SVC_CONTACT_CHANSETTINGS,
+ service_contact_channel_settings);
+
+ /* main menu services */
+ CreateServiceFunction(
+ VQCHAT_PROTO SVC_MENU_SET_NICKNAME,
+ service_menu_set_nickname
+ );
+ CreateServiceFunction(
+ VQCHAT_PROTO SVC_MENU_JOIN_CHANNEL,
+ service_menu_join_channel);
+}
+
+void service_hook_all()
+{
+ s_hook_ModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, service_hook_modules_loaded);
+}
+
+void service_uninit()
+{
+ /* unhook service module's hooks */
+ UnhookEvent(s_hook_ModulesLoaded);
+}
+
+void service_connected(vqp_link_t link)
+{
+ CLISTMENUITEM clmi;
+ memset(&clmi, 0, sizeof(clmi));
+ clmi.cbSize = sizeof(clmi);
+
+ /* enable 'Join channel menu item' */
+ clmi.flags = CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)s_hMenuJoinChannel, (LPARAM)&clmi);
+
+ /* set link */
+ s_vqpLink = link;
+}
+
+void service_disconnected()
+{
+ CLISTMENUITEM clmi;
+ memset(&clmi, 0, sizeof(clmi));
+ clmi.cbSize = sizeof(clmi);
+
+ /* disable 'Join channel menu item' */
+ clmi.flags = CMIM_FLAGS | CMIF_GRAYED;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)s_hMenuJoinChannel, (LPARAM)&clmi);
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/service.h b/plugins/!NotAdopted/VypressChat/service.h
new file mode 100644
index 0000000000..f6468eab5c
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/service.h
@@ -0,0 +1,36 @@
+/*
+ * 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: service.h,v 1.8 2005/03/08 16:53:20 bobas Exp $
+ */
+
+
+#ifndef __SERVICE_H
+#define __SERVICE_H
+
+void service_register_services();
+void service_hook_all();
+void service_uninit();
+
+void service_connected(vqp_link_t link);
+void service_disconnected();
+
+void service_join_channel_merge_chanlist(const char * chanlist);
+
+#endif /* #ifndef __SERVICE_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/skin.c b/plugins/!NotAdopted/VypressChat/skin.c
new file mode 100644
index 0000000000..a6b665d961
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/skin.c
@@ -0,0 +1,44 @@
+/*
+ * 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: skin.c,v 1.1 2005/03/09 14:44:22 bobas Exp $
+ */
+
+#include "main.h"
+#include "skin.h"
+
+/* static data
+ */
+
+/* static routines
+ */
+
+/* exported routines
+ */
+void skin_init() {}
+
+void skin_uninit() {}
+
+void skin_hook_modules_loaded()
+{
+ /* register our sounds */
+ SkinAddNewSoundEx(
+ SKIN_SOUND_ALERT_BEEP, VQCHAT_PROTO_NAME,
+ Translate("Contact alert beep"));
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/skin.h b/plugins/!NotAdopted/VypressChat/skin.h
new file mode 100644
index 0000000000..0abe99874d
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/skin.h
@@ -0,0 +1,33 @@
+/*
+ * 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: skin.h,v 1.1 2005/03/09 14:44:22 bobas Exp $
+ */
+
+#ifndef __SKIN_H
+#define __SKIN_H
+
+void skin_init();
+void skin_uninit();
+void skin_hook_modules_loaded();
+
+/* use SkinPlaySound() to play it */
+#define SKIN_SOUND_ALERT_BEEP VQCHAT_PROTO "/AlertBeep"
+
+#endif
+
diff --git a/plugins/!NotAdopted/VypressChat/user.c b/plugins/!NotAdopted/VypressChat/user.c
new file mode 100644
index 0000000000..56e3610123
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/user.c
@@ -0,0 +1,511 @@
+/*
+ * 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;
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/user.h b/plugins/!NotAdopted/VypressChat/user.h
new file mode 100644
index 0000000000..fef1f1572e
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/user.h
@@ -0,0 +1,70 @@
+/*
+ * 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.h,v 1.10 2005/03/09 22:56:21 bobas Exp $
+ */
+
+#ifndef __USER_H
+#define __USER_H
+
+#include "libvqproto/vqproto.h"
+
+void user_init();
+void user_uninit();
+
+void user_hook_modules_loaded();
+
+enum vqp_status user_vqp_status_by_status(int status);
+int user_status_by_vqp_status(enum vqp_status user_status);
+const char * user_status_name(int status);
+
+/* user settings
+ */
+
+/* nickname */
+int user_set_nickname(const char * new_nickname, int store_in_db);
+const char * user_nickname();
+int user_is_my_nickname(const char *);
+
+/* status */
+int user_set_status(int);
+int user_status();
+#define user_offline() (user_status()==ID_STATUS_OFFLINE)
+#define user_vqp_status() user_vqp_status_by_status(user_status())
+
+/* gender */
+enum vqp_gender user_gender();
+void user_set_gender(enum vqp_gender new_gender, int store_in_db);
+
+/* guid */
+const vqp_uuid_t * user_p_uuid();
+void user_set_uuid(const vqp_uuid_t * p_new_guid, int store_in_db);
+
+/* codepage */
+enum vqp_codepage user_codepage();
+void user_set_codepage(enum vqp_codepage codepage, int store_in_db);
+
+/* awaymsg */
+void user_set_awaymsg(const char * msg);
+const char * user_awaymsg();
+
+/* chanlist */
+void user_set_chanlist(const char * new_chanlist, int store_in_db);
+char ** user_p_chanlist();
+
+#endif /* #ifndef __USER_H */
diff --git a/plugins/!NotAdopted/VypressChat/userlist.c b/plugins/!NotAdopted/VypressChat/userlist.c
new file mode 100644
index 0000000000..c55b25df0c
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/userlist.c
@@ -0,0 +1,718 @@
+/*
+ * 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: userlist.c,v 1.26 2005/03/22 19:38:13 bobas Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/hashtable.h"
+#include "contrib/strhashfunc.h"
+
+#include "miranda.h"
+#include "main.h"
+#include "user.h"
+#include "userlist.h"
+#include "contacts.h"
+#include "msgloop.h"
+#include "chatroom.h"
+#include "chanlist.h"
+#include "util.h"
+
+/* struct descriptions
+ */
+struct userlist_entry {
+ enum vqp_status vqp_status;
+ enum vqp_codepage codepage;
+ enum vqp_gender gender;
+ unsigned int swversion;
+ vqp_uuid_t uuid;
+ vqp_addr_t addr;
+
+ int alive; /* non-0, if replied to last refresh req */
+ int chat_open;
+ char * chanlist;
+};
+
+struct userlist_remove_unreplied_info_struct {
+ char ** dead_keys;
+ int dead_key_count;
+};
+
+/* forward references
+ */
+static void userlist_entry_status_change(
+ const char * user_name, struct userlist_entry * entry,
+ enum vqp_status new_user_status, enum vqp_gender new_user_gender,
+ const char * new_status_text, int add_to_log);
+
+/* static data
+ */
+static struct hashtable * s_userlistHash = NULL;
+static UINT_PTR s_userlistTimer;
+static vqp_link_t s_vqpLink;
+
+/* static routines
+ */
+#define userlist_entry_by_name(user_name) \
+ ((struct userlist_entry*)hashtable_search(s_userlistHash, (void*)(user_name)))
+
+static struct userlist_entry *
+userlist_entry_by_name_vqp(const char * vqp_user_name)
+{
+ struct userlist_entry * entry;
+ char * user_name;
+
+#ifdef VYPRESSCHAT
+ /* by default we'll try search supposing vqp_user_name is in utf encoding */
+ entry = userlist_entry_by_name(vqp_user_name);
+ if(!entry) {
+ /* next - we'll thinking the vqp_user_name is in locale encoding */
+ entry = userlist_entry_by_name(user_name = util_loc2utf(vqp_user_name));
+ free(user_name);
+ }
+#else
+ /* by default we'll try the locale encoding:
+ * qChat clients don't know a thing about utf8
+ */
+ entry = userlist_entry_by_name(user_name = util_loc2utf(vqp_user_name));
+ free(user_name);
+#endif
+
+ return entry;
+}
+
+static void
+s_userlistHash_free_entry(void * value)
+{
+ struct userlist_entry * entry = (struct userlist_entry *)value;
+
+ chanlist_free(entry->chanlist);
+ free(entry);
+}
+
+/* userlist_remove_unreplied_cb:
+ * this is used to remove 'dead' users from user list
+ * and should be invoked periodically
+ *
+ * userlist_add/userlist_status_change are used to notify
+ * that a user is alive during the refresh period
+ */
+static int
+userlist_remove_unreplied_enum_fn(
+ void * key, void * value, void * user_data)
+{
+ struct userlist_entry * entry = (struct userlist_entry*)value;
+ struct userlist_remove_unreplied_info_struct * info
+ = (struct userlist_remove_unreplied_info_struct*)user_data;
+
+ if(!entry->alive) {
+ /* add user to dead user list */
+ info->dead_keys[info->dead_key_count++] = strdup((char*)key);
+ } else {
+ /* reset the user as dead */
+ entry->alive = 0;
+ }
+
+ return 1; /* continue enumeration */
+}
+
+static void CALLBACK
+userlist_remove_unreplied_cb(
+ HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ struct userlist_remove_unreplied_info_struct info;
+ int i;
+ char * r_nickname;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(s_userlistHash));
+
+ /* perform enumeration to find the unreplied ones */
+ info.dead_keys = malloc(
+ sizeof(void*) * hashtable_count(s_userlistHash));
+ info.dead_key_count = 0;
+ hashtable_enumerate(
+ s_userlistHash,
+ userlist_remove_unreplied_enum_fn, (void*)&info);
+
+ /* quietly remove those dead */
+ for(i = 0; i < info.dead_key_count; i++) {
+ userlist_remove(info.dead_keys[i], 0);
+ free(info.dead_keys[i]);
+ }
+
+ /* free leftovers */
+ free(info.dead_keys);
+
+ /* send another user refresh list request to the network
+ */
+ r_nickname = util_utf2vqp(user_codepage(), user_nickname());
+ msgloop_send(vqp_msg_refresh_req(s_vqpLink, r_nickname, user_codepage()), 0);
+ free(r_nickname);
+}
+
+/* exported routines
+ */
+void userlist_init()
+{
+ s_userlistTimer = 0;
+
+ s_userlistHash = NULL;
+ userlist_flush();
+}
+
+void userlist_uninit()
+{
+ hashtable_destroy(s_userlistHash, 1);
+ s_userlistHash = NULL;
+
+ if(s_userlistTimer) {
+ KillTimer(NULL, s_userlistTimer);
+ s_userlistTimer = 0;
+ }
+}
+
+void userlist_flush()
+{
+ /* a rude way to cleanup the list */
+ if(s_userlistHash) {
+ hashtable_destroy(s_userlistHash, 1);
+ s_userlistHash = NULL;
+ }
+
+ s_userlistHash = create_hashtable(
+ 256, hashtable_strhashfunc, hashtable_strequalfunc,
+ s_userlistHash_free_entry);
+}
+
+void userlist_connected(vqp_link_t vqpLink)
+{
+ DWORD timeout = 1000 * db_byte_get(NULL, VQCHAT_PROTO,
+ "UserlistTimeout", VQCHAT_DEF_REFRESH_TIMEOUT);
+ s_vqpLink = vqpLink;
+
+ userlist_flush();
+
+ /* start userlist refresh timers */
+ ASSERT_RETURNIFFAIL(!s_userlistTimer);
+ s_userlistTimer = SetTimer(NULL, 0, timeout, userlist_remove_unreplied_cb);
+}
+
+void userlist_disconnected()
+{
+ ASSERT_RETURNIFFAIL(s_userlistTimer);
+
+ userlist_flush();
+
+ s_vqpLink = NULL;
+
+ KillTimer(NULL, s_userlistTimer);
+ s_userlistTimer = 0;
+}
+
+/* userlist_add:
+ * adds user to userlist, updates it's info if the user already exists
+ */
+void userlist_add(
+ const char * name,
+ enum vqp_status vqp_status, enum vqp_gender gender, const vqp_uuid_t * p_uuid,
+ enum vqp_codepage codepage, unsigned int swversion, vqp_addr_t addr,
+ int add_to_log)
+{
+ struct userlist_entry * entry;
+ HANDLE hContact;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(name) && VALIDPTR(s_userlistHash));
+
+ entry = userlist_entry_by_name(name);
+ if(entry) {
+ entry->alive = 1;
+
+ userlist_entry_status_change(
+ name, entry, vqp_status, gender, "", add_to_log);
+ return;
+ }
+
+ /* setup new user list entry struct */
+ entry = malloc(sizeof(struct userlist_entry));
+ entry->vqp_status = vqp_status;
+ entry->codepage = codepage;
+ entry->gender = gender;
+ entry->swversion = swversion;
+ entry->uuid = *p_uuid;
+ entry->addr = addr;
+ entry->alive = 1;
+ entry->chat_open = 0;
+ entry->chanlist = NULL;
+
+ /* insert into the hash table */
+ hashtable_insert(s_userlistHash, strdup(name), entry);
+
+ /* notify DB that the user has become visible (if he is on list)
+ * and set user ip
+ */
+ hContact = contacts_find_contact(name);
+ if(hContact) {
+ contacts_set_contact_status(hContact, user_status_by_vqp_status(vqp_status));
+ contacts_set_contact_addr(hContact, addr);
+ }
+
+ /* every user of QuickChat/VypressChat networks is a member
+ * of #Main channel */
+ userlist_user_channel_join(name, VQCHAT_MAIN_CHANNEL, add_to_log);
+}
+
+static int userlist_remove_channel_part_enum(
+ const char * channel, void * enum_data)
+{
+ const char * user = ((const char **)enum_data)[0],
+ * explicit_part = ((const char **)enum_data)[1];
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(channel) && VALIDPTR(enum_data), 0);
+
+ userlist_user_channel_part(user, channel, (int)explicit_part);
+ return 1;
+}
+
+/* userlist_remove:
+ * removes specified user from userlist
+ * params:
+ * @explicit_part - non-0 if the user left explicitly (he notified us of that)
+ */
+void userlist_remove(const char * user_name, int explicit_remove)
+{
+ struct userlist_entry * entry;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash));
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry) {
+ const char * enum_cb_data[2];
+
+ /* notify DB that the user has become offline
+ * (if the user is in db list)
+ */
+ HANDLE hContact = contacts_find_contact(user_name);
+ if(hContact)
+ contacts_set_contact_status(hContact, ID_STATUS_OFFLINE);
+
+ /* update user's chatroom:
+ * leave every channel the user is in */
+ enum_cb_data[0] = user_name;
+ enum_cb_data[1] = (const char *)explicit_remove;
+ chanlist_enum(
+ entry->chanlist,
+ userlist_remove_channel_part_enum, (void*)enum_cb_data);
+
+ /* remove user entry from the hash and free it */
+ hashtable_remove(s_userlistHash, (void*)user_name, 1);
+ }
+}
+
+static int
+userlist_enum_proxy_fn(void * key, void * value, void * user_data)
+{
+ void ** proxy_data = (void **) user_data;
+
+ return ((userlist_enum_fn)proxy_data[0])(
+ (const char*)key, (void*)proxy_data[1]);
+}
+
+void userlist_enum(userlist_enum_fn enum_fn, void * enum_fn_data)
+{
+ void * proxy_data[2];
+
+ ASSERT_RETURNIFFAIL(enum_fn);
+
+ proxy_data[0] = (void*)enum_fn;
+ proxy_data[1] = (void*)enum_fn_data;
+
+ hashtable_enumerate(s_userlistHash,
+ userlist_enum_proxy_fn, (void*)proxy_data);
+}
+
+void userlist_name_change(const char * user_name, const char * new_user_name)
+{
+ struct userlist_entry * entry;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(new_user_name));
+ ASSERT_RETURNIFFAIL(VALIDPTR(s_userlistHash));
+ ASSERT_RETURNIFFAIL(strcmp(user_name, new_user_name));
+
+ /* bail out if we have the new user name in the userlist already */
+ if(userlist_entry_by_name(new_user_name) )
+ return;
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry) {
+ HANDLE hContact;
+
+ /* remove entry from hash table and reinsert with different nickname */
+ hashtable_remove(s_userlistHash, (void*)user_name, 0);
+ hashtable_insert(s_userlistHash, strdup(new_user_name), entry);
+
+ entry->alive = 1;
+
+ /* update contact nickname and status
+ */
+ hContact = contacts_find_contact(user_name);
+ if(hContact) {
+ if(db_byte_get(hContact, VQCHAT_PROTO, "LockNick", 0)) {
+ contacts_set_contact_status(hContact, ID_STATUS_OFFLINE);
+ } else {
+ HANDLE hAnotherContact = contacts_find_contact(new_user_name);
+ if(hAnotherContact) {
+ /* there is another contact with destination name:
+ * set hContact to offline, and get hAnotherContact alive
+ */
+ contacts_set_contact_status(hContact, ID_STATUS_OFFLINE);
+
+ contacts_set_contact_status(
+ hAnotherContact,
+ user_status_by_vqp_status(entry->vqp_status)
+ );
+ contacts_set_contact_addr(hContact, entry->addr);
+ } else {
+ /* change nickname for the contact */
+ contacts_set_contact_nickname(hContact, new_user_name);
+ }
+ }
+ } else {
+ /* the specified contact does not exist,
+ * try to find the contact with `new_user_name' nickname
+ */
+ hContact = contacts_find_contact(new_user_name);
+ if(hContact) {
+ contacts_set_contact_status(
+ hContact,
+ user_status_by_vqp_status(entry->vqp_status)
+ );
+ contacts_set_contact_addr(hContact, entry->addr);
+ }
+ }
+
+ /* update chatrooms */
+ chatroom_channel_user_name_change(user_name, new_user_name, 1);
+ }
+}
+
+/* userlist_entry_status_change:
+ * updates user's status
+ */
+static void
+userlist_entry_status_change(
+ const char * user_name, struct userlist_entry * entry,
+ enum vqp_status new_user_status, enum vqp_gender new_user_gender,
+ const char * mode_text, int add_to_log)
+{
+ entry->alive = 1;
+
+ if(entry->vqp_status != new_user_status) {
+ HANDLE hContact;
+ int new_status = user_status_by_vqp_status(new_user_status);
+
+ entry->vqp_status = new_user_status;
+
+ /* notify db of user mode change */
+ hContact = contacts_find_contact(user_name);
+ if(hContact) {
+ contacts_set_contact_status(hContact, new_status);
+ contacts_set_contact_gender(hContact, new_user_gender);
+ }
+
+ /* update chatrooms */
+ chatroom_channel_user_status_change(user_name, new_status, mode_text, add_to_log);
+ }
+
+}
+
+void userlist_status_change(
+ const char * user_name,
+ enum vqp_status new_user_status, enum vqp_gender new_user_gender,
+ const char * status_text, int add_to_log)
+{
+ struct userlist_entry * entry;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash));
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry) {
+ userlist_entry_status_change(
+ user_name, entry, new_user_status, new_user_gender,
+ status_text, add_to_log);
+ }
+}
+
+void userlist_user_channel_join(
+ const char * user_name, const char * channel,
+ int add_to_log)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash));
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry && !chanlist_contains(entry->chanlist, channel)) {
+ /* add channel to user's channel list */
+ entry->chanlist = chanlist_add(entry->chanlist, channel);
+
+ /* notify chatrooms */
+ chatroom_channel_user_join(channel, user_name, add_to_log);
+ }
+}
+
+/* userlist_user_channel_part:
+ * this will make the user leave the specified channel
+ * parameters:
+ * @explicit_part - specifies whether the user left explicitly, or because
+ * of refresh timeout
+ */
+void userlist_user_channel_part(
+ const char * user_name, const char * channel,
+ int explicit_part)
+{
+ struct userlist_entry * entry;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash));
+ ASSERT_RETURNIFFAIL(VALIDPTR(channel));
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry && chanlist_contains(entry->chanlist, channel)) {
+ /* remove channel from user's channel list */
+ entry->chanlist = chanlist_remove(entry->chanlist, channel);
+
+ /* notify chatrooms */
+ chatroom_channel_user_part(channel, user_name, explicit_part);
+ }
+}
+
+/* userlist_user_channel_text:
+ * writes text to specified channel,
+ * and registers user and joins the channel, if not already
+ */
+void userlist_user_channel_text(
+ const char * user_name, const char * channel, const char * text,
+ vqp_addr_t src_addr, int is_action_text)
+{
+ struct userlist_entry * entry;
+
+ entry = userlist_entry_by_name(user_name);
+ if(!entry) {
+ vqp_uuid_t nil_uuid;
+ vqp_uuid_create_nil(&nil_uuid);
+
+ /* add the user to our user list */
+ userlist_add(
+ user_name, VQP_STATUS_AVAILABLE, VQP_GENDER_MALE, &nil_uuid,
+ VQCHAT_VQP_COMPAT_CODEPAGE, VQCHAT_UNDEF_SWVERSION, src_addr,
+ 0);
+ }
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry) {
+ /* join the channel first and
+ * write the text to the channel
+ */
+ userlist_user_channel_join(user_name, channel, 0);
+ chatroom_channel_user_text(channel, user_name, text, is_action_text);
+ }
+}
+
+static int userlist_user_chanlist_update_join_enum_fn(
+ const char * channel, void * user_name)
+{
+ userlist_user_channel_join((const char *)user_name, channel, 0);
+ return 1;
+}
+
+static int userlist_user_chanlist_update_part_enum_fn(
+ const char * channel, void * updated_chanlist)
+{
+ const char ** enum_fn_data = (const char **)updated_chanlist;
+
+ if(!chanlist_contains(enum_fn_data[0], channel))
+ userlist_user_channel_part(enum_fn_data[1], channel, 0);
+
+ return 1;
+}
+
+void userlist_user_chanlist_update(
+ const char * user_name,
+ const char * updated_chanlist)
+{
+ struct userlist_entry * entry;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash));
+ entry = userlist_entry_by_name(user_name);
+ if(entry) {
+ const char * enum_fn_data[2];
+ char * new_chanlist = chanlist_copy(updated_chanlist);
+
+ /* join new previously-missing channels */
+ chanlist_enum(
+ new_chanlist,
+ userlist_user_chanlist_update_join_enum_fn, (void*)user_name);
+
+ /* leave the channels that the user is off */
+ enum_fn_data[0] = new_chanlist;
+ enum_fn_data[1] = user_name;
+ chanlist_enum(
+ entry->chanlist,
+ userlist_user_chanlist_update_part_enum_fn, (void*)enum_fn_data);
+
+ /* free chanlists */
+ chanlist_free(new_chanlist);
+ }
+}
+
+int userlist_user_exists(const char * user_name)
+{
+ ASSERT_RETURNVALIFFAIL(
+ VALIDPTR(s_userlistHash) && VALIDPTR(user_name), 0);
+ return userlist_entry_by_name(user_name) != NULL;
+}
+
+/* userlist_user_status:
+ * returns miranda's status for user, or ID_STATUS_OFFLINE, if not found
+ */
+int userlist_user_status(const char * user_name)
+{
+ struct userlist_entry * entry;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash), ID_STATUS_OFFLINE);
+
+ entry = userlist_entry_by_name(user_name);
+ return entry ? user_status_by_vqp_status(entry->vqp_status): ID_STATUS_OFFLINE;
+}
+
+/* userlist_user_addr:
+ * returns user's address for user, or 0, if not found
+ */
+vqp_addr_t
+userlist_user_addr(const char * user_name)
+{
+ struct userlist_entry * entry;
+ vqp_addr_t addr;
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry)
+ return entry->addr;
+
+ memset(&addr, 0, sizeof(addr));
+ return addr;
+}
+
+/* userlist_user_chanlist:
+ * returns users' chanlist
+ */
+const char *
+userlist_user_chanlist(const char * user_name)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash), 0);
+
+ entry = userlist_entry_by_name(user_name);
+ return entry ? entry->chanlist: NULL;
+
+}
+
+/* userlist_user_gender:
+ * returns user's gender
+ */
+enum vqp_gender userlist_user_gender(const char * user_name)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash), 0);
+ entry = userlist_entry_by_name(user_name);
+ return entry ? entry->gender: VQP_GENDER_MALE;
+}
+
+/* userlist_user_swversion:
+ * returns' user's software version
+ */
+unsigned int userlist_user_swversion(const char * user_name)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash), 0);
+ entry = userlist_entry_by_name(user_name);
+ return entry ? entry->swversion: VQCHAT_UNDEF_SWVERSION;
+}
+
+/* userlist_user_is_chat_open:
+ * returns if we have a private chat opened with the user
+ */
+int userlist_user_is_chat_open(const char * user_name)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash), 0);
+ entry = userlist_entry_by_name(user_name);
+ return entry ? entry->chat_open: 0;
+}
+
+/* userlist_user_set_chat_open:
+ * sets wherether the chat is open with the user
+ */
+void userlist_user_set_chat_open(const char * user_name, int set_chat_open)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name) && VALIDPTR(s_userlistHash));
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry)
+ entry->chat_open = set_chat_open;
+}
+
+/* userlist_user_codepage:
+ * returns codepage for specified user
+ * (and guesses it, if the user was not found)
+ */
+enum vqp_codepage
+userlist_user_codepage(const char * user_name)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(user_name), VQP_CODEPAGE_LOCALE);
+
+ entry = userlist_entry_by_name(user_name);
+
+ return entry ? entry->codepage: VQCHAT_VQP_COMPAT_CODEPAGE;
+}
+
+/* userlist_user_codepage_vqp:
+ * tries to get or guess codepage for specified user name
+ * (on vypresschat it tries UTF8 encoding first, on qChat - locale)
+ */
+enum vqp_codepage
+userlist_user_codepage_vqp(const char * vqp_user_name)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(vqp_user_name), VQCHAT_VQP_COMPAT_CODEPAGE);
+
+ entry = userlist_entry_by_name_vqp(vqp_user_name);
+ return entry ? entry->codepage: VQCHAT_VQP_COMPAT_CODEPAGE;
+}
+
+void userlist_user_set_codepage(const char * user_name, enum vqp_codepage codepage)
+{
+ struct userlist_entry * entry;
+ ASSERT_RETURNIFFAIL(VALIDPTR(user_name));
+
+ entry = userlist_entry_by_name(user_name);
+ if(entry)
+ entry->codepage = codepage;
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/userlist.h b/plugins/!NotAdopted/VypressChat/userlist.h
new file mode 100644
index 0000000000..5ff06e9c15
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/userlist.h
@@ -0,0 +1,73 @@
+/*
+ * 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: userlist.h,v 1.16 2005/03/17 11:02:43 bobas Exp $
+ */
+
+#ifndef __USERLIST_H
+#define __USERLIST_h
+
+#include "libvqproto/vqproto.h"
+
+void userlist_init();
+void userlist_uninit();
+
+void userlist_flush();
+
+void userlist_connected(vqp_link_t vqpLink);
+void userlist_disconnected();
+
+void userlist_add(
+ const char * name, enum vqp_status vqp_status, enum vqp_gender gender,
+ const vqp_uuid_t * p_uuid, enum vqp_codepage codepage, unsigned int swversion,
+ vqp_addr_t addr, int add_to_log);
+void userlist_remove(const char * user_name, int add_to_log);
+typedef int (*userlist_enum_fn)(const char * user_name, void * enum_fn_data);
+void userlist_enum(userlist_enum_fn enum_fn, void * enum_fn_data);
+
+void userlist_name_change(const char * user_name, const char * new_user_name);
+void userlist_status_change(
+ const char * user_name,
+ enum vqp_status new_user_status, enum vqp_gender new_user_gender,
+ const char * status_text, int add_to_log);
+
+void userlist_user_channel_join(const char * user_name, const char * channel, int explicit_join);
+void userlist_user_channel_part(const char * user_name, const char * channel, int explicit_part);
+void userlist_user_channel_text(
+ const char * user_name, const char * channel, const char * text,
+ vqp_addr_t addr, int is_action_text);
+void userlist_user_chanlist_update(const char * user_name, const char * chanlist);
+
+int userlist_user_exists(const char * user_name);
+int userlist_user_status(const char * user_name);
+#define userlist_user_can_receive_msg(user_name) (userlist_user_status(user_name)!=ID_STATUS_NA)
+
+vqp_addr_t userlist_user_addr(const char * user_name);
+const char * userlist_user_chanlist(const char * user_name);
+enum vqp_gender userlist_user_gender(const char * user_name);
+unsigned int userlist_user_swversion(const char * user_name);
+
+void userlist_user_set_chat_open(const char * user_name, int chat_open);
+int userlist_user_is_chat_open(const char * user_name);
+
+enum vqp_codepage userlist_user_codepage(const char * user_name);
+enum vqp_codepage userlist_user_codepage_vqp(const char * vqp_user_name);
+void userlist_user_set_codepage(const char * user_name, enum vqp_codepage codepage);
+
+#endif /* #ifndef __USERLIST_H */
+
diff --git a/plugins/!NotAdopted/VypressChat/util.c b/plugins/!NotAdopted/VypressChat/util.c
new file mode 100644
index 0000000000..0cd8c758bf
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/util.c
@@ -0,0 +1,376 @@
+/*
+ * 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: util.c,v 1.7 2005/04/11 21:44:16 bobas Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "main.h"
+#include "util.h"
+
+/* exported routines
+ */
+
+/* ip/ipx address funcs
+ */
+char * util_vqpaddr2str(vqp_addr_t addr)
+{
+ char * str = malloc(32);
+
+ if(addr.conn == VQP_PROTOCOL_CONN_UDP) {
+ sprintf(str, "%u.%u.%u.%u",
+ (unsigned)(addr.node.ip >> 24) & 0xff,
+ (unsigned)(addr.node.ip >> 16) & 0xff,
+ (unsigned)(addr.node.ip >> 8) & 0xff, (unsigned)addr.node.ip & 0xff);
+ } else {
+ sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X",
+ addr.node.ipx[5], addr.node.ipx[4], addr.node.ipx[3],
+ addr.node.ipx[2], addr.node.ipx[1], addr.node.ipx[0]);
+ }
+ return str;
+}
+
+/* utf8/locale/unicode conversion
+ */
+char * util_loc2utf(const char * loc_str)
+{
+ wchar_t * uni_str = util_loc2uni(loc_str);
+ char * utf_str = util_uni2utf(uni_str);
+ free(uni_str);
+
+ return utf_str;
+}
+
+char * util_utf2loc(const char * utf_str)
+{
+ wchar_t * uni_str = util_utf2uni(utf_str);
+ char * loc_str = util_uni2loc(uni_str);
+ free(uni_str);
+
+ return loc_str;
+}
+
+wchar_t * util_loc2uni(const char * loc_str)
+{
+ int loc_len, uni_len;
+ wchar_t * uni_str;
+
+ loc_len = lstrlenA(loc_str) + 1;
+ uni_len = MultiByteToWideChar(CP_ACP, 0, loc_str, loc_len, NULL, 0);
+ uni_str = malloc(sizeof(wchar_t) * uni_len);
+ MultiByteToWideChar(CP_ACP, 0, loc_str, loc_len, uni_str, uni_len);
+
+ return uni_str;
+}
+
+char * util_uni2loc(const wchar_t * uni_str)
+{
+ int uni_len, loc_len;
+ char * loc_str;
+
+ uni_len = lstrlenW(uni_str) + 1;
+ loc_len = WideCharToMultiByte(CP_ACP, 0, uni_str, uni_len, NULL, 0, NULL, NULL);
+ loc_str = malloc(loc_len);
+ WideCharToMultiByte(CP_ACP, 0, uni_str, uni_len, loc_str, loc_len, NULL, NULL);
+
+ return loc_str;
+}
+
+wchar_t * util_utf2uni(const char * utf_str)
+{
+ int utf_len, uni_len;
+ wchar_t * uni_str;
+
+ utf_len = lstrlenA(utf_str) + 1;
+ uni_len = MultiByteToWideChar(CP_UTF8, 0, utf_str, utf_len, NULL, 0);
+ uni_str = malloc(sizeof(wchar_t) * uni_len);
+ MultiByteToWideChar(CP_UTF8, 0, utf_str, utf_len, uni_str, uni_len);
+
+ return uni_str;
+}
+
+char * util_uni2utf(const wchar_t * uni_str)
+{
+ int uni_len, utf_len;
+ char * utf_str;
+
+ uni_len = lstrlenW(uni_str) + 1;
+ utf_len = WideCharToMultiByte(CP_UTF8, 0, uni_str, uni_len, NULL, 0, NULL, NULL);
+ utf_str = malloc(utf_len);
+ WideCharToMultiByte(CP_UTF8, 0, uni_str, uni_len, utf_str, utf_len, NULL, NULL);
+
+ return utf_str;
+}
+
+/* string functions
+ */
+char * util_vqp2utf(enum vqp_codepage codepage, const char * vqp_text)
+{
+ return codepage == VQP_CODEPAGE_UTF8
+ ? strdup(vqp_text): util_loc2utf(vqp_text);
+}
+
+char * util_utf2vqp(enum vqp_codepage codepage, const char * utf_text)
+{
+ return codepage == VQP_CODEPAGE_UTF8
+ ? strdup(utf_text): util_utf2loc(utf_text);
+}
+
+/* util_split_multiline:
+ * splits a multiline string into it's lines,
+ * optionally emitting the empty ones
+ * returns:
+ * an array of (char*), with the last being NULL
+ */
+char ** util_split_multiline(const char * text, int include_empty)
+{
+ int n_lines, i;
+ const char * line, * next;
+ char ** array, * str;
+
+ ASSERT_RETURNVALIFFAIL(VALIDPTR(text), NULL);
+
+ /* get the number of lines */
+ n_lines = 1;
+ line = text;
+ while((line = strchr(line, '\n')) != NULL) {
+ n_lines ++;
+ line ++; /* skip the '\n' we've found */
+ }
+
+ /* alloc the array */
+ array = malloc(sizeof(void *) * (n_lines + 1));
+
+ /* split the strings */
+ line = text;
+ i = 0;
+ do {
+ int line_len;
+
+ /* get the end of this line */
+ next = strchr(line, '\n');
+ if(next) {
+ /* get the length of the line */
+ line_len = next - line;
+
+ /* skip the '\r' at the end of the line, if exists */
+ if(line[line_len - 1] == '\r')
+ line_len --;
+ } else {
+ line_len = strlen(line);
+ }
+
+ /* alloc and copy the string */
+ if(include_empty || (!include_empty && line_len!=0)) {
+ str = malloc(line_len + 1);
+ memcpy(str, line, line_len);
+ str[line_len] = '\0';
+
+ array[i ++] = str;
+ }
+
+ /* advance to the next line */
+ if(next) {
+ /* skip pas the \n char onto the next line */
+ line = next + 1;
+ }
+ } while(next);
+
+ /* end up the array */
+ array[i] = NULL;
+
+ /* return all we got here */
+ return array;
+}
+
+/* util_free_str_list:
+ * frees a (char*) list, like the one alloced
+ * by util_split_multiline()
+ */
+void util_free_str_list(char ** str_list)
+{
+ char ** pstr;
+
+ ASSERT_RETURNIFFAIL(VALIDPTR(str_list));
+
+ /* free each of the strings in the list */
+ for(pstr = str_list; *pstr; pstr ++)
+ free(*pstr);
+
+ free(str_list);
+}
+
+/* windows unicode functions
+ */
+void util_SetWindowTextUtf(HWND hwnd, const char * utf_str)
+{
+ wchar_t * unistr;
+
+ ASSERT_RETURNIFFAIL(utf_str);
+
+ unistr = util_utf2uni(utf_str);
+ SetWindowTextW(hwnd, unistr);
+ free(unistr);
+}
+
+char * util_GetWindowTextUtf(HWND hwnd)
+{
+ int len;
+ wchar_t * uni_str;
+ char * utf_str;
+
+ len = GetWindowTextLengthW(hwnd) + 1;
+ uni_str = malloc(sizeof(wchar_t) * len);
+ GetWindowTextW(hwnd, uni_str, len);
+
+ utf_str = util_uni2utf(uni_str);
+ free(uni_str);
+
+ return utf_str;
+}
+
+void util_SetDlgItemTextUtf(HWND hDialog, int nDlgItemN, const char * utf_str)
+{
+ util_SetWindowTextUtf(GetDlgItem(hDialog, nDlgItemN), utf_str);
+}
+
+char * util_GetDlgItemTextUtf(HWND hDialog, int nDlgItemN)
+{
+ return util_GetWindowTextUtf(GetDlgItem(hDialog, nDlgItemN));
+}
+
+/* db module extension routines
+ */
+
+int db_blob_set(
+ HANDLE hContact, const char * szModule, const char * szSetting,
+ void * pBlob, size_t cpBlob)
+{
+ DBCONTACTWRITESETTING cws;
+ cws.szModule = szModule;
+ cws.szSetting = szSetting;
+ cws.value.type = DBVT_BLOB;
+ cws.value.cpbVal = cpBlob;
+ cws.value.pbVal = pBlob;
+ return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws);
+}
+
+BOOL db_dword_list_add(
+ HANDLE hContact, const char * szModule, const char * szSetting,
+ DWORD dw, BOOL unique)
+{
+ DWORD * list, * new_list;
+ size_t list_sz;
+
+ if(unique && db_dword_list_contains(hContact, szModule, szSetting, dw))
+ return FALSE;
+
+ /* append dword and write the new list */
+ list = db_dword_list(hContact, szModule, szSetting, &list_sz);
+ if(list) {
+ new_list = malloc(sizeof(DWORD) * (list_sz + 1));
+ if(!new_list) {
+ free(list);
+ return FALSE;
+ }
+ memcpy(new_list, list, sizeof(DWORD) * list_sz);
+ new_list[list_sz] = dw;
+
+ db_blob_set(hContact, szModule, szSetting, new_list, sizeof(DWORD) * (list_sz + 1));
+
+ free(new_list);
+ free(list);
+ } else {
+ db_blob_set(hContact, szModule, szSetting, &dw, sizeof(DWORD));
+ }
+
+ return TRUE;
+}
+
+void db_dword_list_remove(
+ HANDLE hContact, const char * szModule, const char * szSetting, DWORD dw)
+{
+ DWORD * list;
+ size_t list_sz;
+
+ list = db_dword_list(hContact, szModule, szSetting, &list_sz);
+ if(list) {
+ size_t i;
+ for(i = 0; i < list_sz; i++)
+ if(list[i] == dw) {
+ if(list_sz != 1) {
+ char * new_list = malloc(sizeof(DWORD) * (list_sz - 1));
+ if(!new_list) break;
+
+ memcpy(new_list, list, sizeof(DWORD) * i);
+ memcpy(new_list + i, list + i + 1,
+ sizeof(DWORD) * (list_sz - i - 1));
+
+ db_blob_set(hContact, szModule, szSetting,
+ new_list, sizeof(DWORD) * (list_sz - 1));
+ } else {
+ db_unset(hContact, szModule, szSetting);
+ }
+
+ break;
+ }
+ }
+ free(list);
+}
+
+DWORD * db_dword_list(
+ HANDLE hContact, const char * szModule, const char * szSetting, size_t * pCount)
+{
+ DBVARIANT dbv;
+ DWORD * list = NULL;
+
+ ASSERT_RETURNVALIFFAIL(szModule!=NULL && szSetting!=NULL && pCount!=NULL, NULL);
+
+ *pCount = 0;
+
+ if(!db_get(hContact, szModule, szSetting, &dbv)) {
+ if(dbv.type == DBVT_BLOB && (dbv.cpbVal % sizeof(DWORD))==0 && dbv.cpbVal!=0) {
+ list = malloc(dbv.cpbVal);
+ if(list) {
+ memcpy(list, dbv.pbVal, dbv.cpbVal);
+ *pCount = dbv.cpbVal / sizeof(DWORD);
+ }
+ }
+ }
+
+ DBFreeVariant(&dbv);
+ return list;
+}
+
+BOOL db_dword_list_contains(
+ HANDLE hContact, const char * szModule,
+ const char * szSetting, DWORD dw)
+{
+ DWORD * list;
+ size_t list_sz, i;
+
+ list = db_dword_list(hContact, szModule, szSetting, &list_sz);
+ if(list) {
+ for(i = 0; i < list_sz; i++)
+ if(list[i]==dw) return TRUE;
+ }
+ return FALSE;
+}
+
diff --git a/plugins/!NotAdopted/VypressChat/util.h b/plugins/!NotAdopted/VypressChat/util.h
new file mode 100644
index 0000000000..c6e055932b
--- /dev/null
+++ b/plugins/!NotAdopted/VypressChat/util.h
@@ -0,0 +1,63 @@
+/*
+ * 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: util.h,v 1.8 2005/04/11 21:44:17 bobas Exp $
+ */
+
+#ifndef __UTIL_H
+#define __UTIL_H
+
+/* ip/ipx address funcs */
+char * util_vqpaddr2str(vqp_addr_t addr);
+
+/* utf8/locale/unicode conversion */
+char * util_loc2utf(const char * loc_str);
+char * util_utf2loc(const char * utf8_str);
+wchar_t * util_loc2uni(const char * loc_str);
+char * util_uni2loc(const wchar_t * uni_str);
+wchar_t * util_utf2uni(const char * utf_str);
+char * util_uni2utf(const wchar_t * uni_str);
+
+char * util_vqp2utf(enum vqp_codepage codepage, const char * vqp_text);
+char * util_utf2vqp(enum vqp_codepage codepage, const char * utf_text);
+
+/* string functions */
+char ** util_split_multiline(const char * text, int include_empty);
+void util_free_str_list(char ** str_list);
+
+/* windows unicode functions */
+void util_SetWindowTextUtf(HWND hwnd, const char * utf_str);
+char * util_GetWindowTextUtf(HWND hwnd);
+void util_SetDlgItemTextUtf(HWND hDialog, int nDlgItemN, const char * utf_str);
+char * util_GetDlgItemTextUtf(HWND hDialog, int nDlgItemN);
+
+/* additional db helper funcs */
+int db_blob_set(
+ HANDLE hContact, const char * szModule, const char * szSetting,
+ void * pBlob, size_t cpBlob);
+BOOL db_dword_list_add(
+ HANDLE hContact, const char * szModule, const char * szSetting, DWORD dw, BOOL unique);
+void db_dword_list_remove(
+ HANDLE hContact, const char * szModule, const char * szSetting, DWORD dw);
+DWORD * db_dword_list(
+ HANDLE hContact, const char * szModule, const char * szSetting, size_t * pCount);
+BOOL db_dword_list_contains(
+ HANDLE hContact, const char * szModule, const char * szSetting, DWORD dw);
+
+#endif
+