From ff5a775b94465b30897964630af600fe5915fc51 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 2 Apr 2013 13:54:21 +0000 Subject: VypressChat added (not adopted) git-svn-id: http://svn.miranda-ng.org/main/trunk@4285 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/!NotAdopted/VypressChat/Resource.h | 97 + plugins/!NotAdopted/VypressChat/chanlist.c | 390 +++ plugins/!NotAdopted/VypressChat/chanlist.h | 43 + plugins/!NotAdopted/VypressChat/chatroom.c | 1154 +++++++++ plugins/!NotAdopted/VypressChat/chatroom.h | 63 + plugins/!NotAdopted/VypressChat/contacts.c | 705 ++++++ plugins/!NotAdopted/VypressChat/contacts.h | 61 + .../!NotAdopted/VypressChat/contrib/hashtable.c | 287 +++ .../!NotAdopted/VypressChat/contrib/hashtable.h | 195 ++ .../VypressChat/contrib/hashtable_private.h | 71 + plugins/!NotAdopted/VypressChat/contrib/m_chat.h | 424 ++++ plugins/!NotAdopted/VypressChat/contrib/memwatch.c | 2664 ++++++++++++++++++++ plugins/!NotAdopted/VypressChat/contrib/memwatch.h | 707 ++++++ .../!NotAdopted/VypressChat/contrib/strhashfunc.c | 21 + .../!NotAdopted/VypressChat/contrib/strhashfunc.h | 8 + .../!NotAdopted/VypressChat/contrib/unicows.dll | Bin 0 -> 258352 bytes plugins/!NotAdopted/VypressChat/docs/AUTHORS | 1 + plugins/!NotAdopted/VypressChat/docs/COPYING | 340 +++ plugins/!NotAdopted/VypressChat/docs/CREDITS | 10 + plugins/!NotAdopted/VypressChat/docs/ChangeLog | 50 + plugins/!NotAdopted/VypressChat/docs/NEWS | 0 plugins/!NotAdopted/VypressChat/docs/README | 79 + plugins/!NotAdopted/VypressChat/docs/TODO | 36 + plugins/!NotAdopted/VypressChat/docs/dbformat.txt | 38 + plugins/!NotAdopted/VypressChat/iconpack.rc | 40 + .../!NotAdopted/VypressChat/icons/contact_bell.ico | Bin 0 -> 318 bytes .../!NotAdopted/VypressChat/icons/quickchat.ico | Bin 0 -> 318 bytes .../VypressChat/icons/quickchat_away.ico | Bin 0 -> 318 bytes .../VypressChat/icons/quickchat_channel.ico | Bin 0 -> 318 bytes .../VypressChat/icons/quickchat_dnd.ico | Bin 0 -> 318 bytes .../VypressChat/icons/quickchat_large.ico | Bin 0 -> 766 bytes .../!NotAdopted/VypressChat/icons/quickchat_na.ico | Bin 0 -> 318 bytes .../VypressChat/icons/quickchat_offline.ico | Bin 0 -> 1406 bytes .../VypressChat/icons/quickchat_online.ico | Bin 0 -> 318 bytes .../!NotAdopted/VypressChat/icons/vypresschat.ico | Bin 0 -> 318 bytes .../VypressChat/icons/vypresschat_away.ico | Bin 0 -> 318 bytes .../VypressChat/icons/vypresschat_channel.ico | Bin 0 -> 1406 bytes .../VypressChat/icons/vypresschat_dnd.ico | Bin 0 -> 318 bytes .../VypressChat/icons/vypresschat_large.ico | Bin 0 -> 2238 bytes .../VypressChat/icons/vypresschat_na.ico | Bin 0 -> 318 bytes .../VypressChat/icons/vypresschat_offline.ico | Bin 0 -> 318 bytes .../VypressChat/icons/vypresschat_online.ico | Bin 0 -> 318 bytes plugins/!NotAdopted/VypressChat/libvqproto/COPYING | 340 +++ .../!NotAdopted/VypressChat/libvqproto/PROTOCOL | 348 +++ plugins/!NotAdopted/VypressChat/libvqproto/README | 7 + plugins/!NotAdopted/VypressChat/libvqproto/link.c | 611 +++++ plugins/!NotAdopted/VypressChat/libvqproto/link.h | 106 + .../!NotAdopted/VypressChat/libvqproto/makefile | 21 + .../!NotAdopted/VypressChat/libvqproto/message.c | 1218 +++++++++ .../!NotAdopted/VypressChat/libvqproto/message.h | 57 + .../libvqproto/protocol-direct-connections | 97 + plugins/!NotAdopted/VypressChat/libvqproto/uuid.c | 108 + plugins/!NotAdopted/VypressChat/libvqproto/uuid.h | 27 + .../!NotAdopted/VypressChat/libvqproto/vqproto.h | 595 +++++ plugins/!NotAdopted/VypressChat/main.c | 132 + plugins/!NotAdopted/VypressChat/main.h | 108 + plugins/!NotAdopted/VypressChat/makefile | 243 ++ plugins/!NotAdopted/VypressChat/miranda.h | 56 + plugins/!NotAdopted/VypressChat/msghandler.c | 839 ++++++ plugins/!NotAdopted/VypressChat/msghandler.h | 33 + plugins/!NotAdopted/VypressChat/msgloop.c | 248 ++ plugins/!NotAdopted/VypressChat/msgloop.h | 35 + plugins/!NotAdopted/VypressChat/options.c | 947 +++++++ plugins/!NotAdopted/VypressChat/options.h | 33 + plugins/!NotAdopted/VypressChat/pthread.c | 58 + plugins/!NotAdopted/VypressChat/pthread.h | 30 + plugins/!NotAdopted/VypressChat/resource.rc | 258 ++ plugins/!NotAdopted/VypressChat/service.c | 730 ++++++ plugins/!NotAdopted/VypressChat/service.h | 36 + plugins/!NotAdopted/VypressChat/skin.c | 44 + plugins/!NotAdopted/VypressChat/skin.h | 33 + plugins/!NotAdopted/VypressChat/user.c | 511 ++++ plugins/!NotAdopted/VypressChat/user.h | 70 + plugins/!NotAdopted/VypressChat/userlist.c | 718 ++++++ plugins/!NotAdopted/VypressChat/userlist.h | 73 + plugins/!NotAdopted/VypressChat/util.c | 376 +++ plugins/!NotAdopted/VypressChat/util.h | 63 + 77 files changed, 16693 insertions(+) create mode 100644 plugins/!NotAdopted/VypressChat/Resource.h create mode 100644 plugins/!NotAdopted/VypressChat/chanlist.c create mode 100644 plugins/!NotAdopted/VypressChat/chanlist.h create mode 100644 plugins/!NotAdopted/VypressChat/chatroom.c create mode 100644 plugins/!NotAdopted/VypressChat/chatroom.h create mode 100644 plugins/!NotAdopted/VypressChat/contacts.c create mode 100644 plugins/!NotAdopted/VypressChat/contacts.h create mode 100644 plugins/!NotAdopted/VypressChat/contrib/hashtable.c create mode 100644 plugins/!NotAdopted/VypressChat/contrib/hashtable.h create mode 100644 plugins/!NotAdopted/VypressChat/contrib/hashtable_private.h create mode 100644 plugins/!NotAdopted/VypressChat/contrib/m_chat.h create mode 100644 plugins/!NotAdopted/VypressChat/contrib/memwatch.c create mode 100644 plugins/!NotAdopted/VypressChat/contrib/memwatch.h create mode 100644 plugins/!NotAdopted/VypressChat/contrib/strhashfunc.c create mode 100644 plugins/!NotAdopted/VypressChat/contrib/strhashfunc.h create mode 100644 plugins/!NotAdopted/VypressChat/contrib/unicows.dll create mode 100644 plugins/!NotAdopted/VypressChat/docs/AUTHORS create mode 100644 plugins/!NotAdopted/VypressChat/docs/COPYING create mode 100644 plugins/!NotAdopted/VypressChat/docs/CREDITS create mode 100644 plugins/!NotAdopted/VypressChat/docs/ChangeLog create mode 100644 plugins/!NotAdopted/VypressChat/docs/NEWS create mode 100644 plugins/!NotAdopted/VypressChat/docs/README create mode 100644 plugins/!NotAdopted/VypressChat/docs/TODO create mode 100644 plugins/!NotAdopted/VypressChat/docs/dbformat.txt create mode 100644 plugins/!NotAdopted/VypressChat/iconpack.rc create mode 100644 plugins/!NotAdopted/VypressChat/icons/contact_bell.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/quickchat.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/quickchat_away.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/quickchat_channel.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/quickchat_dnd.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/quickchat_large.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/quickchat_na.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/quickchat_offline.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/quickchat_online.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/vypresschat.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/vypresschat_away.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/vypresschat_channel.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/vypresschat_dnd.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/vypresschat_large.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/vypresschat_na.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/vypresschat_offline.ico create mode 100644 plugins/!NotAdopted/VypressChat/icons/vypresschat_online.ico create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/COPYING create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/PROTOCOL create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/README create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/link.c create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/link.h create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/makefile create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/message.c create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/message.h create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/protocol-direct-connections create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/uuid.c create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/uuid.h create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/vqproto.h create mode 100644 plugins/!NotAdopted/VypressChat/main.c create mode 100644 plugins/!NotAdopted/VypressChat/main.h create mode 100644 plugins/!NotAdopted/VypressChat/makefile create mode 100644 plugins/!NotAdopted/VypressChat/miranda.h create mode 100644 plugins/!NotAdopted/VypressChat/msghandler.c create mode 100644 plugins/!NotAdopted/VypressChat/msghandler.h create mode 100644 plugins/!NotAdopted/VypressChat/msgloop.c create mode 100644 plugins/!NotAdopted/VypressChat/msgloop.h create mode 100644 plugins/!NotAdopted/VypressChat/options.c create mode 100644 plugins/!NotAdopted/VypressChat/options.h create mode 100644 plugins/!NotAdopted/VypressChat/pthread.c create mode 100644 plugins/!NotAdopted/VypressChat/pthread.h create mode 100644 plugins/!NotAdopted/VypressChat/resource.rc create mode 100644 plugins/!NotAdopted/VypressChat/service.c create mode 100644 plugins/!NotAdopted/VypressChat/service.h create mode 100644 plugins/!NotAdopted/VypressChat/skin.c create mode 100644 plugins/!NotAdopted/VypressChat/skin.h create mode 100644 plugins/!NotAdopted/VypressChat/user.c create mode 100644 plugins/!NotAdopted/VypressChat/user.h create mode 100644 plugins/!NotAdopted/VypressChat/userlist.c create mode 100644 plugins/!NotAdopted/VypressChat/userlist.h create mode 100644 plugins/!NotAdopted/VypressChat/util.c create mode 100644 plugins/!NotAdopted/VypressChat/util.h 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: 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 +#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) ⪯ + + 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 + +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 */ + +#include "hashtable.h" +#include "hashtable_private.h" +#include +#include +#include +#include + +#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 + * + * 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 */ + +#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 + * + * 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 */ + +#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 + * + * 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 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 has left[: pszText] +// A user left the chat room +// pszUID - Unique identifier +// pszText - part message +#define GC_EVENT_PART 0x0002 + +// GC_EVENT_QUIT 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 kicked +// 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 is now known as +// 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 : +// 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 +// 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 enables '' for +// pszUID - Unique identifier +// pszText - The one enabling the status for another user +// pszStatus - The status given +#define GC_EVENT_ADDSTATUS 0x0400 + +// GC_EVENT_REMOVESTATUS disables '' for +// 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 +#include +#include +#include +#include +#include +#include +#include +#include "memwatch.h" + +#ifndef toupper +#include +#endif + +#if defined(WIN32) || defined(__WIN32__) +#define MW_HAVE_MUTEX 1 +#include +#endif + +#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) +#define MW_HAVE_MUTEX 1 +#include +#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 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; inext 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 %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 %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 ""; + } + +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 = ""; + } + 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:"", 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:"", 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 = ""; + } + else mw->file = ""; + 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 = ""; + 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 +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, "", 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 "FBIfilename(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: 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 +/*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 + +#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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/contrib/unicows.dll 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. + + + Copyright (C) + + 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. + + , 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; + * " has renamed in " 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 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/contact_bell.ico differ diff --git a/plugins/!NotAdopted/VypressChat/icons/quickchat.ico b/plugins/!NotAdopted/VypressChat/icons/quickchat.ico new file mode 100644 index 0000000000..8337ca00d0 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/quickchat.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/quickchat_away.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/quickchat_channel.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/quickchat_dnd.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/quickchat_large.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/quickchat_na.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/quickchat_offline.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/quickchat_online.ico differ diff --git a/plugins/!NotAdopted/VypressChat/icons/vypresschat.ico b/plugins/!NotAdopted/VypressChat/icons/vypresschat.ico new file mode 100644 index 0000000000..0cd8ed0fb0 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/vypresschat.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/vypresschat_away.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/vypresschat_channel.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/vypresschat_dnd.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/vypresschat_large.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/vypresschat_na.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/vypresschat_offline.ico 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 Binary files /dev/null and b/plugins/!NotAdopted/VypressChat/icons/vypresschat_online.ico 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. + + + Copyright (C) + + 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. + + , 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' + + #0 + ^^^ - its a single byte 0x00, + showing end of a line + + +********************************************************************************************** +*** Answer to the previous packet ("Im here") + + '1' + + #0 + + #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' + + #0 + + #0 + + #0 + + +********************************************************************************************** +*** Nick change + + '3' + + #0 + + #0 + Gender + ^^^^ - gender of the user + '0' - male + '1' - female + +********************************************************************************************** +*** Join to channel + + '4' + + #0 + + #0 + Status + Gender + +********************************************************************************************** +*** Leave from channel + + '5' + + #0 + + #0 + Gender + +********************************************************************************************** +*** Message + + '6' + + #0 + + #0 + + #0 + +********************************************************************************************** +*** Multiaddress message + + 'E' + + #0 + + #0 + + #0 + +********************************************************************************************** +*** Message ack + + '7' + Status + + #0 + + #0 + Gender + CurrentAA + #0 + ^^^^^^^^^ + current autoanswer (may be empty, for ex in Normal mode) + +********************************************************************************************** +*** Remote execution + + '8' + + #0 + + #0 + + #0 + + #0 + ^^^^^^^^ + may be empty + +********************************************************************************************** +*** Execution ack + + '9' + + #0 + + #0 + + #0 + +********************************************************************************************** +*** /ME on the channels + + 'A' + + #0 + + #0 + + #0 + +********************************************************************************************** +*** New topic + + 'B' + + #0 + + ' (' + + ') ' + #0 + +********************************************************************************************** +*** Current topic (sending to each who join channel) + + 'C' + + #0 + + #0 + + #0 + +********************************************************************************************** +*** Change of status + + 'D' + + #0 + Status + Gender + CurrentAA + #0 + +********************************************************************************************** +*** INFO request + + 'F' + + #0 + + #0 + +********************************************************************************************** +*** INFO request ack + + 'G' + + #0 + + #0 + + #0 + + #0 + + #0 + + '#' + #0 + CurrentAA + #0 + +********************************************************************************************** +*** BEEP + + signal + 'H' + '0' + + #0 + + #0 + + ack + 'H' + '1' + + #0 + + #0 + Gender + +********************************************************************************************** +*** Sound request + + 'I' + + #0 + + #0 + + #0 + +********************************************************************************************** +*** Private + + join to chat + 'J' + '0' + + #0 + + #0 + Gender + + leave chat + 'J' + '1' + + #0 + + #0 + Gender + + Simple string + 'J' + '2' + + #0 + + #0 + + #0 + + "/me" string + 'J' + '3' + + #0 + + #0 + + #0 + +********************************************************************************************** +*** /HERE request + + 'L' + + #0 + + #0 + +********************************************************************************************** +*** /HERE ack + + 'K' + + #0 + + #0 + + #0 + RemoteActive + +********************************************************************************************** +*** Activity change + + 'M' + + #0 + RemoteActive + +********************************************************************************************** +*** Request for channel list + + 'N' + + #0 + +********************************************************************************************** +*** Request for channel list ack + + 'O' + + #0 + + '#' + #0 + +********************************************************************************************** +*** Ping (not implemented - SeaD) + + ping + 'P' + '0' + + #0 + + #0 +