summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/m_dos.h22
-rw-r--r--spamfilter/Docs/PCRE-License.txt42
-rw-r--r--spamfilter/Docs/SpamFilter-Developer.txt59
-rw-r--r--spamfilter/Docs/SpamFilter-License.txt278
-rw-r--r--spamfilter/Docs/SpamFilter-Readme.txt977
-rw-r--r--spamfilter/Docs/SpamFilter-Translation.txt865
-rw-r--r--spamfilter/Extensions/InstallScript.xml63
-rw-r--r--spamfilter/Extensions/Spam Filter/BadWords/English.sfw89
-rw-r--r--spamfilter/Extensions/Spam Filter/FormatNumbers/Common.sfc16
-rw-r--r--spamfilter/Extensions/Spam Filter/FormatSeparators/Common.sfc13
-rw-r--r--spamfilter/Extensions/Spam Filter/FormatText/Common.sfc28
-rw-r--r--spamfilter/Extensions/Spam Filter/HoaxTexts/Empty.sft7
-rw-r--r--spamfilter/Extensions/Spam Filter/PhoneNumbers/Empty.sfc13
-rw-r--r--spamfilter/Extensions/Spam Filter/Spammers/Empty.sfc9
-rw-r--r--spamfilter/Extensions/Spam Filter/Teasers/English.sft9
-rw-r--r--spamfilter/Extensions/Spam Filter/URLs/Common.sfc64
-rw-r--r--spamfilter/Extensions/Spam Filter/URLs/English.sfc14
-rw-r--r--spamfilter/Extensions/filtered1.wavbin0 -> 27398 bytes
-rw-r--r--spamfilter/Extensions/filtered2.wavbin0 -> 82906 bytes
-rw-r--r--spamfilter/Extensions/filtered3.wavbin0 -> 59322 bytes
-rw-r--r--spamfilter/Info_Src.txt111
-rw-r--r--spamfilter/License_Appendix.txt67
-rw-r--r--spamfilter/Resources/add_button_16x16_256.icobin0 -> 1406 bytes
-rw-r--r--spamfilter/Resources/delete_button_16x16_256.icobin0 -> 1406 bytes
-rw-r--r--spamfilter/Resources/log_item.xml11
-rw-r--r--spamfilter/Resources/log_layout.xsl60
-rw-r--r--spamfilter/Resources/log_main.xml23
-rw-r--r--spamfilter/Resources/spam_16x16_256.icobin0 -> 1406 bytes
-rw-r--r--spamfilter/Resources/spam_32x32_256.icobin0 -> 2238 bytes
-rw-r--r--spamfilter/Resources/spam_definitions_16x16_256.icobin0 -> 1406 bytes
-rw-r--r--spamfilter/Resources/spam_layer_16x16_256.icobin0 -> 1406 bytes
-rw-r--r--spamfilter/SpamFilter.dsp802
-rw-r--r--spamfilter/SpamFilter.dsw29
-rw-r--r--spamfilter/SpamFilter.rc556
-rw-r--r--spamfilter/SpamFilter.sln26
-rw-r--r--spamfilter/SpamFilter.vcproj2243
-rw-r--r--spamfilter/Utilities/DebugHelper.h338
-rw-r--r--spamfilter/Utilities/PCRE/bin/pcre.dllbin0 -> 183313 bytes
-rw-r--r--spamfilter/Utilities/PCRE/bin/pcreposix.dllbin0 -> 175142 bytes
-rw-r--r--spamfilter/Utilities/PCRE/include/pcre.h239
-rw-r--r--spamfilter/Utilities/PCRE/include/pcreposix.h117
-rw-r--r--spamfilter/Utilities/PCRE/lib/libpcre-bcc.libbin0 -> 2560 bytes
-rw-r--r--spamfilter/Utilities/PCRE/lib/libpcre.def26
-rw-r--r--spamfilter/Utilities/PCRE/lib/libpcre.dll.abin0 -> 15942 bytes
-rw-r--r--spamfilter/Utilities/PCRE/lib/libpcre.libbin0 -> 6514 bytes
-rw-r--r--spamfilter/Utilities/PCRE/lib/libpcreposix-bcc.libbin0 -> 2048 bytes
-rw-r--r--spamfilter/Utilities/PCRE/lib/libpcreposix.def16
-rw-r--r--spamfilter/Utilities/PCRE/lib/libpcreposix.dll.abin0 -> 9690 bytes
-rw-r--r--spamfilter/Utilities/PCRE/lib/libpcreposix.libbin0 -> 4386 bytes
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre.3.html174
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_compile.3.html67
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_config.3.html56
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_copy_named_substring.3.html48
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_copy_substring.3.html47
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_exec.3.html71
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_free_substring.3.html37
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_free_substring_list.3.html37
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_fullinfo.3.html67
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_get_named_substring.3.html48
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_get_stringnumber.3.html42
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_get_substring.3.html47
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_get_substring_list.3.html47
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_info.3.html35
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_maketables.3.html39
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_study.3.html49
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcre_version.3.html36
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcreapi.3.html1069
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcrebuild.3.html167
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcrecallout.3.html148
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcrecompat.3.html115
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcregrep.1.html147
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcrepattern.3.html1268
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcreperform.3.html86
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcreposix.3.html187
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcresample.3.html72
-rw-r--r--spamfilter/Utilities/PCRE/man/html/pcretest.1.html433
-rw-r--r--spamfilter/Utilities/PCRE/manifest/pcre-5.0-doc.mft58
-rw-r--r--spamfilter/Utilities/PCRE/manifest/pcre-5.0-doc.ver2
-rw-r--r--spamfilter/Utilities/PCRE/manifest/pcre-5.0-lib.mft12
-rw-r--r--spamfilter/Utilities/PCRE/manifest/pcre-5.0-lib.ver2
-rw-r--r--spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.373
-rw-r--r--spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.c191
-rw-r--r--spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.h35
-rw-r--r--spamfilter/Utilities/UnicoWS/license.txt66
-rw-r--r--spamfilter/Utilities/UnicoWS/redist.txt12
-rw-r--r--spamfilter/Utilities/UnicoWS/unicows.dllbin0 -> 258352 bytes
-rw-r--r--spamfilter/Utilities/UnicoWS/unicows.libbin0 -> 2325412 bytes
-rw-r--r--spamfilter/Utilities/UnicoWS/unicows.pdbbin0 -> 347136 bytes
-rw-r--r--spamfilter/common.h284
-rw-r--r--spamfilter/contact.c818
-rw-r--r--spamfilter/contact.h41
-rw-r--r--spamfilter/dlg.c385
-rw-r--r--spamfilter/dlg.h38
-rw-r--r--spamfilter/extraimg.c118
-rw-r--r--spamfilter/extraimg.h30
-rw-r--r--spamfilter/folders.c101
-rw-r--r--spamfilter/folders.h33
-rw-r--r--spamfilter/forkthread.c112
-rw-r--r--spamfilter/forkthread.h64
-rw-r--r--spamfilter/logfile.c478
-rw-r--r--spamfilter/logfile.h28
-rw-r--r--spamfilter/m_popup.h338
-rw-r--r--spamfilter/m_spamfilter.h826
-rw-r--r--spamfilter/m_spamfilter.inc757
-rw-r--r--spamfilter/m_trigger.h348
-rw-r--r--spamfilter/main.c595
-rw-r--r--spamfilter/make.bat77
-rw-r--r--spamfilter/menuitems.c136
-rw-r--r--spamfilter/menuitems.h30
-rw-r--r--spamfilter/options.c3699
-rw-r--r--spamfilter/options.h60
-rw-r--r--spamfilter/path.c541
-rw-r--r--spamfilter/path.h52
-rw-r--r--spamfilter/popup.c285
-rw-r--r--spamfilter/popup.h39
-rw-r--r--spamfilter/regexp.c225
-rw-r--r--spamfilter/regexp.h35
-rw-r--r--spamfilter/resource.h139
-rw-r--r--spamfilter/services.c2038
-rw-r--r--spamfilter/services.h39
-rw-r--r--spamfilter/spamcheck.c683
-rw-r--r--spamfilter/spamcheck.h33
-rw-r--r--spamfilter/spamcheckdata.c273
-rw-r--r--spamfilter/spamcheckdata.h36
-rw-r--r--spamfilter/spamdefinitions.c850
-rw-r--r--spamfilter/spamdefinitions.h59
-rw-r--r--spamfilter/string.c310
-rw-r--r--spamfilter/string.h41
-rw-r--r--spamfilter/stringlist.c1007
-rw-r--r--spamfilter/stringlist.h81
-rw-r--r--spamfilter/trigger.c63
-rw-r--r--spamfilter/trigger.h32
-rw-r--r--spamfilter/utilities.c629
-rw-r--r--spamfilter/utilities.h45
-rw-r--r--spamfilter/variables.c65
-rw-r--r--spamfilter/variables.h30
-rw-r--r--spamfilter/version.h36
-rw-r--r--spamfilter/version.rc57
138 files changed, 29419 insertions, 22 deletions
diff --git a/includes/m_dos.h b/includes/m_dos.h
deleted file mode 100644
index db0aadf..0000000
--- a/includes/m_dos.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright © 2008 sss, chaos.persei
-//
-// 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.
-
-//Start sending messages to contact
-//effect depend on DOS plugin settings
-//wParam=(HANDLE)hContact
-//lParam=int
-//always returns 0
-#define MS_DOS_SERVICE "/StartDos"
diff --git a/spamfilter/Docs/PCRE-License.txt b/spamfilter/Docs/PCRE-License.txt
new file mode 100644
index 0000000..f1cbefd
--- /dev/null
+++ b/spamfilter/Docs/PCRE-License.txt
@@ -0,0 +1,42 @@
+PCRE LICENCE
+------------
+
+PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+Release 5 of PCRE is distributed under the terms of the "BSD" licence, as
+specified below.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+University of Cambridge Computing Service,
+Cambridge, England. Phone: +44 1223 334714.
+
+Copyright (c) 1997-2004 University of Cambridge
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/spamfilter/Docs/SpamFilter-Developer.txt b/spamfilter/Docs/SpamFilter-Developer.txt
new file mode 100644
index 0000000..a7a2b66
--- /dev/null
+++ b/spamfilter/Docs/SpamFilter-Developer.txt
@@ -0,0 +1,59 @@
+
+Spam Filter 2.5.2.3 for Miranda IM 0.4.3+
+------------------------------------------------------------------------
+ Developer Information
+
+ Contents: -------------------------------
+ | Translation, Services and Events (API),
+ | Debug Symbols, Coding Language, Rebase Info
+
+
+Translation
+-----------------
+ Translation strings and available bad words can be found in
+ "SpamFilter-Translation.txt".
+
+
+Services and Events (API)
+-----------------
+ For more information about which service functions are provided by
+ "Spam Filter" and about how they can be used by your plugin please
+ refer to "m_spamfilter.h".
+ If you would like to use Delphi/Pascal please refer to m_spamfilter.inc.
+
+ For example code on how to do a spam check in your plugin see "spamcheck.c" file
+ of the source code package.
+ If you need help with it, just send me an e-mail.
+
+ The functionality and the API of Spam Filter is quite complex
+ and not easy to describe or explain.
+ If you have any questions or extension whishes please send me an e-mail:
+ hrathh at users.sourceforge.net
+
+
+Debug Symbols
+-----------------
+ Debug symbols are also available for debugging purposes.
+ Copy the PDB-files into the same directory as the
+ corresponding DLL-files.
+
+
+Coding Language
+-----------------
+ "Spam Filter" is written with Microsoft Visual C++ 6.0 SP6.
+ Used Microsoft Platform SDK: 5.2.3790.1830.15 (Svr2003 SP1).
+
+
+Rebase Info
+-----------------
+ 'Spam Filter' has set its base address to:
+ 0x24000000
+
+ Please avoid using this base address for your plugins because it will
+ slow down the start of Miranda IM.
+
+ With Microsoft Visual C++ the Base Address can be configured at:
+ "Project" -> "Settings" -> "Linker" -> "Output" -> "Base Address"
+
+
+H. Herkenrath (hrathh at users.sourceforge.net) \ No newline at end of file
diff --git a/spamfilter/Docs/SpamFilter-License.txt b/spamfilter/Docs/SpamFilter-License.txt
new file mode 100644
index 0000000..a726a52
--- /dev/null
+++ b/spamfilter/Docs/SpamFilter-License.txt
@@ -0,0 +1,278 @@
+ 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.
diff --git a/spamfilter/Docs/SpamFilter-Readme.txt b/spamfilter/Docs/SpamFilter-Readme.txt
new file mode 100644
index 0000000..6316589
--- /dev/null
+++ b/spamfilter/Docs/SpamFilter-Readme.txt
@@ -0,0 +1,977 @@
+
+Spam Filter 2.5.2.3
+------------------------------------------------------------------------
+ Plugin for Miranda IM 0.4.3 and +
+
+ Plugin Info: ----------------------------
+ | Version: 2.5.2.3
+ | Filename: spamfilter.dll / spamfilterW.dll
+ | Author: H. Herkenrath (hrathh at users.sourceforge.net)
+ | Description: Filters out advertisments, robot messages and disliked messages
+ | such as hoax messages.Filters out spam messages from contacts
+
+ Contents: -------------------------------
+ | Filter Improvement, Features, Requirements, Usage,
+ | Spam Definitions, Installation, Bugs and Wishes, Filtering Tips,
+ | To-Do List, Version History, Thanks, Translation, License and Copyright
+
+
+Filter Improvement
+----------------------
+ -> Still receiving spam?
+ If you receive spam messages after installing this plugin
+ which are not filtered out just mail the text of the spam message
+ to me and I will integrate some bad phrases out of it immediately.
+ If the filtering of that message can't be done via bad phrases I
+ will try to find another way and integrate that way, too.
+
+ -> New filtering ideas?
+ If you have some other spam filtering ideas please mail me.
+
+ -> Bugs?
+ If you find any bugs please tell them to me via mail.
+ So I don't have to look everywhere around to find them. ;-)
+
+ -> Patches or Extensions?
+ If you are a developer and/or know how to code
+ feel free to send me patches or extensions to the code.
+ The best way is via e-mail.
+ I really appreciate it. :-)
+ If you need extensions to the API please contact me, too.
+
+ -> Filtering in all languages?!
+ There are already bad phrases for the following languages:
+ English, German, French, Russian, Portuguese (Brazil), Chinese (Traditional)
+ I would love it if this list could be extended further.
+ It is a little difficult to speak and understand all languages of this
+ world at the same time ;-) ...and if you speak a language that I don't...
+ (and I think that are very much ;-)...) please send some bad phrases in
+ your language to me.
+ It would be nice if spam could be filtered in all languages,
+ but I need some help... please... :-)
+ Also feel encouraged to release your own Spam Definitions on the File Listing.
+
+ -> Reporting spam log file?!
+ You can also help me via sending your spam log file to me.
+ So I can look for new/better bad phrases.
+ Such a log file can be created with the spam logging feature.
+ It doesn't include any data beside the spam messages sent to you.
+ I will look at it and intergrate some more bad phrases out of it to
+ improve the filter quality.
+
+ -> New bad phrases?
+ If you have some bad phrases to add to the existing lists
+ please mail them to me, too.
+ I will check the bad phrases and integrate them immediately.
+
+
+Features
+----------------------
+ + Filters out incoming spam messages
+ + Spam filtering with all protocols
+ + Which messages are declared as spam is changeable
+ + Improved spam filter (pretty accurate)
+ + Recognizes also URLs with spaces
+ + Supports Regular Expressions (RegExp) in its word filter (really powerful!)
+ + Spam handling: either mark read or delete messages
+ + Spaming contacts get automatically removed if there are no items in history
+ + Spammer name/id auto ignoring
+ + Easy adding of customized bad phrases
+ + Spam alert via Sound or Popup (Popup plugin is needed for this feature)
+ + Spam message logging to file
+ Supported file formats: TXT, LOG, CSV, XML (including XSL stylesheet)
+ + Supports "ballon tips" for error display
+ + Press control key to avoid messages to be checked for spam
+ + Fully configurable with many options
+ + Filter can be disabled temporarily on the options page
+ + Large list of default bad phrases (about 80 well chosen english phrases at the moment)
+ + Full translation support (default bad phrases, too)
+ + Available dialog languages: English, German
+ + Available Spam Definition languages:
+ English, German, French, Russian, Portuguese (Brazil), Chinese (Traditional)
+ + Provides spam checking service functions for other plugins
+ + Auto-Updates for Spam Definitions using Updater plugin
+ + Full Unicode support
+ + Automatic installation of all files, just unzip into Plugins directory
+ + Supported plugins/tools:
+ Miranda Installer, Popup/PopupPlus/YAPP, DatabaseEditor++, Updater,
+ Variables Plugin (http://www.cs.vu.nl/~pboon/projects.htm),
+ Trigger Plugin (http://www.cs.vu.nl/~pboon/projects.htm),
+ Icons Library Manager (IcoLib), Colour Picker, OSD Plugin/WannaBe OSD,
+ Custom Profile Folder Plugin, MultiWindow Contact List (extra icon)
+
+
+Requirements
+----------------------
+ -> Miranda IM 0.4.3+:
+ Miranda IM is needed in the version 0.4.3 or greater.
+
+ -> Popup Plugin 1.0.1.9+ (optional):
+ For using the popup function when a spam message
+ arrives it is necessary to have the popup plugin installed,
+ At least version 1.0.1.9 or greater.
+ PopupPlus is supported, too.
+ You need PopupPlus 2.0.3.97 or later.
+
+ -> Variables Plugin 0.2.0.23+ (optional):
+ For using variables in outgoing reply messages
+ you need to have the Variables Plugin installed,
+ At least version 0.2.0.23 or greater.
+
+
+Usage
+----------------------
+ "Spam Filter" options can be found here:
+ "Options" -> "Events" -> "Spam Filter"
+
+ The sound file which is played when spam arrives can be changed in:
+ "Options" -> "Events" -> "Sounds"
+
+ Mark-Read feature:
+ If you chose "mark spam messages as read" in the options recommended option)
+ the spammer shows up in a half-visible way on your contact list.
+ However, the incoming message notification is blocked.
+ When you close Miranda IM all half-visible (not-on-list) contacts get removed automatically!
+
+ To avoid the online notification go to the Ignore options and activate "suppress online notification" for unknown contacts.
+
+ If you dislike this behaviour choose "delete spam" (but be aware that you might loose messages).
+
+ Un-Ignoring spammers:
+ Recognized spammers can be easily unignored usinf the options.
+ Just go to "Options" -> "Events" -> "Spam Filter" and click on the "Spammers" button.
+
+ If a unknown user sent you a message and you want to be sure he is no longer watched by the filter, just click
+ on "Add user to contact list" in the contact's menu. (This only works if you chose "mark spam as read".)
+
+
+Spam Definitions
+----------------------
+ "Spam Definitions" are a set of files which Spam Filter uses to detect spam.
+ The files contain well selected words/phrases etc. which a common in spam messages.
+ The Spam Definitions are mostly used by the "Advertisment Filter".
+
+ The Spam Definitions make up the default filter behaviour of Spam Filter.
+ You normally should not need to modify these files. Instead you should use
+ "Options" -> "Events" -> "Spam Filter" to configure your personal filter words.
+
+ You can install as many Spam Definitions as you like, but you should at least
+ install one package in your language.
+ The filter will not work as it should without any Spam Definitions.
+ For each languge package you install the plugin can filter spam messages.
+ For example, if you install the "German" and the "English" packages,
+ most German and English spam can be filtered.
+ Spam Filter can only filter for those languages it has data sets available.
+
+ Spam Filter loads all Spam Definition Files it can find in the directories.
+ All contents of all files are considered when a message is checked for spam.
+ Spam Filter does not know which language the message is, it simply compares its contents.
+
+ The exactness and accuracy of Spam Filter depends on its Spam Definitions.
+ If the Spam Definitions are not good, the filter is not exact.
+
+ The current Spam Definitions can be viewed in
+ the "Spam Filter" subdirectory of the Miranda IM folder.
+
+ How it works:
+ There are different Spam Definition languages available because Spam Filter needs different data sets
+ (e.g. bad words) for the different languages. This is necessary, because bad word filtering consits
+ only of testing if a word exists in the message or not.
+ When there are enough bad words in the message it will be seen as spam.
+ If Spam Filter would only have a list of English bad words (like "earn money",...)
+ it would only be able to filter English spam, since the words "earn money", etc do only exist in English messages.
+ To filter German spam another data set is needed for the filter with e.g. "Sonderangebot",
+ which again probably never will exist in English spam messages.
+ The more Spam Definition languages you have installed, the more spam you can filter.
+
+ Spam Filter does not detect the language of a message. It simply loads all bad words it can find
+ in the directoriy and checks if any of the contained bad words do match with the message.
+ If they match it is spam, if they do not, it is no spam.
+
+ The file names are not important for the filter (only extension and subdirectory is considered).
+ They are named "English", "German", etc so that it is easier to determine what they contain.
+
+ Spam Filter checks if any contained word of the Spam Definitions matches with the message.
+ It counts how many matches it can find and then checks if the matches are enough to consider the message as spam.
+
+ If you get a spam message in a language for which you don't have Spam Definitions installed, it won't be a problem.
+ It won't break anything, nor will your PC be blown up, or sth. like this... ;-)
+ Spam Filter simple won't be able to filter this message. The message will be detected as no spam,
+ because there are no data sets available.
+
+ Example:
+ If you receive, for example, French spam and you don't have the French Spam Definitions installed.
+ Spam Filter will then load all bad words it can find in the Definitions to detect if it is spam.
+ But, no matches will be found, because French spam does normally not contain English or German words.
+ French spam typically contains French words... ;-)
+
+ Spam for which you don't have any Spam Definitions installed can
+ only be blocked by the Robot Filter (if enabled additionally)
+ or if (by chance) the message contains some English bad words, or some common URLs (like .org, .com), etc.
+
+
+Installation
+----------------------
+ The easiest way to install this plugin is to use the
+ installation tool "Miranda Installer" (sadly not available anymore).
+
+ If you don't want to use that tool you can install this plugin
+ manually, too.
+ Just copy all the contents of the zip-file as they are into the "Plugins" directory
+ in the Miranda IM folder.
+ "Spam Filter" will detect the files and move them into the appropriate directories
+ on it's first run.
+
+ You can also do all the installation by hand, if you want to:
+
+ Main Plugin: Copy the file "spamfilter.dll" into the "Plugins" directory
+ in the Miranda IM folder.
+ If you would like to use Unicode you need to copy the file "spamfilterW.dll"
+ instead. Using Unicode will only work for Windows NT4/2000+.
+
+ Support for Regular Expressions:
+ If you would like to use Regular Expressions with Spam Filter (recommended)
+ then you should copy the file "pcre.dll" (RegExp DLL) into the Miranda IM directory.
+
+ Spam Definitions: The contents of the "Spam Filter" folder from the zip file,
+ including its subdirectories, need to be placed into a new created
+ "Spam Filter" subdirectory in the Miranda IM main directory.
+ The filter will not work as it should without any Spam Definitions.
+
+ Documentation: The txt-files should be moved along with the sdk-zip into the
+ "Docs" directory in the Miranda IM folder.
+
+ Sounds: The wav-files should be moved into the "Sounds"
+ directory. They will get recognized automatically and be added to the sounds list.
+
+ That's it!
+
+
+Bugs and Wishes
+----------------------
+ Feel free to mail me your wishes about "Spam Filter" and tell
+ me all the bugs you may find.
+ Please tell them to me via mail so I don't have to look
+ everywhere around to find them. ;-)
+ My email address is: hrathh at users.sourceforge.net
+
+ # I would love it if you could mail me your translated bad phrases
+ in your language. I would be nice if spam could be filtered in
+ all languages, but I need some help, please... :-)
+ ... Or perhaps you want to add some bad phrases to an existing list?
+ That's also possible... ;-)
+
+ # If you have any bad phrases that you want to be integrated into
+ the standard bad phrases of the next release of "Spam Filter"
+ please mail them to me.
+
+ # If you think a string is not grammatically correct or something
+ please mail me, too. (English is not my first language.)
+
+
+Filtering Tips
+----------------------
+ #1:
+ WebAware is often used by ICQ spambots,
+ if you disable it, you'll not get as many spam as before.
+ Goto: Options -> Network -> ICQ Privacy
+
+ #2:
+ Perhaps the following tip is useful for users that don't use latin letters
+ on the system due to their language:
+
+ Tip by ZaNuDa: (thanks very much!)
+ "I have a good idea to filter out some of the spam messages sent to me by robots.
+ These programs are plain enough and insert in their messages my nick from ICQ registration
+ data regardless of my country and native language. My nickname and name is written
+ on the ICQ site in transliteration, with latin letters.
+ Russian ICQ users writing to me never use these phrases in Latin transcription.
+ So I know, that when I receive message beginning with one of these phrases, it is probably
+ spam sent to me by a robot. So I've added both phrases to customized the bad phrases list. And it works for me."
+
+
+To-Do List (random ideas)
+----------------------
+ - Some events in are not yet Unicode enabled in spam filter (Miranda unicode event documentaion is quite crappy...not existant)
+
+ ? Add a main menu item "Show spam log" (initially deactivated) or "Show Spammers", user can enable it
+ on MenuOrder options (currently not possible using menu API of miranda)
+
+ ? Button "Recognize this message as a spam" on message dialog
+ so that it will be easier to add patterns to the list without going to the options (suggested by PanTzeR)
+ >> this could show a editbox containing the message content
+ >> after clicking OK the editbox content is added as new bad word/disliked word
+ -> not a good idea IMHO, since bad words should not be taken out of one single message
+ Instead: the option "Mark this contact as spammer" should be used
+ -> MS_SPAMFILTER_SHOWFILTERDIALOG does much of this (show spam filter options)
+ -> message dialog should/could offer support for this
+
+ ? Extend the Popup message text to contain more information than just "Advertisment Alert!"
+ -> similar to log file items
+ -> making it customizable (suggested by Dominik Sebald)
+ -> I don't know if this is really necessary...
+ -> Problem: If the popup contains the mesage text the behaviour is almost the same as without spam filter :-)
+
+ ? "Filtered Spam" Window where all filtered messages are displayed (similar to log file, but no separate file)
+ >> The user can choose which one he would like to delete
+ >> and which ones he would like to be put back into the contacts history
+ (better than a "show log file" menu item)
+
+ ? Filtering based on user info data, contact details (really necessary?)
+ -> quite difficult, because user details are first available
+ after the first message was received and added to db (for new temporary contacts), only UIN is first available
+ -> Solution: need to watch Nick setting, or wait for proto acks
+
+ ? Add filter for SMS Plugin(s)
+ >> (Spam Filter offers spam checking services...
+ >> so the better way would be if the SMS Plugins would support Spam Filter)
+
+ ? Add filter for YAMN plugin (e-mail)
+ >> (Spam Filter offers spam checking services...
+ >> so the better way would be if YAMN supports Spam Filter)
+
+ ? Bayesian method: -> will probably be "Spam Filter v3" (really necessary? I think it's too much...)
+ >> This feature will be added additionally to the phrase filtering method (-> functioning as expert option)
+ >> Perhaps as an half bayesian filter, which means that spam database will be shared
+ >> and only few people (around 100) are allowed to maintain it
+ - Integrate Bayesian filtering (http://www.paulgraham.com/antispam.html) (thanks to InvisiBill)
+ - Change to percent (%) spam detection: messages declared as
+ spam at 75% spam probability
+ >> Save spam database on server to share the training data
+
+
+Version History
+----------------------
+ 2.5.2.3: - Some internal improvements
+ - Fixed: temp file sometimes resulting from VerInstallFile API was not deleted
+ - Removed: reply sending delay (caused problems)
+
+ 2.5.2.2: - Bug-Fix: Check for unicode core was wrong (spamfilterW.dll)
+ - Some other minor changes
+
+ 2.5.2.1: - Minor fixes and improvements
+ - Fixed automatic installation routine for
+ easy installation and Updater support
+ (just extract everything out of the zip-file into the Plugins folder,
+ all files will get moved accordingly)
+ - Bug-Fix: Unicode calls to VariablesPlugin were wrong
+ - Bug-Fix: Corrected some problems with spammer info dialog
+ - Removed spammer group delete question
+ - Improved: handling of users awaiting security code
+ - Added debug symbols (PDB) into SDK package
+
+ 2.5.2.0: - Robot Filter improvements
+ - Also allows quotes in securitycode reply
+ - Some minor internal improvements
+ - Revised default message texts
+ - Some unicode fixes
+ - Fixed ColourPicker support
+ - Message send delayed by short time to allow nick to be retrieved
+ - Enabled workaround for YouWereAdded event recv
+ - Added %triesleft% variable for message texts
+
+ 2.5.1.2: - Some minor improvements
+ - Fixed unicode message sending (and receiving)
+ - Revisited string functions (probably introducted new bugs...)
+ - Fixed possible mem leak in string lists
+ - Fixed possible memory corruption in string lists
+ - Fixed crash on exit
+
+ 2.5.1.1: - Improved: Invalid RegExp error to be a bit more informative
+ - Some minor internal changes to robustness
+ - Fixed some visual glitches on Spam Definitions window on WinXP
+ - Fixed a critical-section corruption
+ - Fixed/Updated Custom Folders Plugin support (new version needed)
+ - Added checks for new IcoLib version
+
+ 2.5.1.0 - Packaged with unicode DLL (spamfilterw.dll) -> highly experimental!!!
+ - Auto-Updates for Spam Definitions using Updater (uses file listing)
+ - minor improvements
+ - fixed version resource
+ - shows error when miranda version is not supported or db3x too old
+ - remembers original NotOnList-state of spammers
+ - Bug-Fix using MS_PROTO_ADDTOCONTACT (workaround for problem in core)
+ -> fixes Quick Search incompatibility
+ - Updated icolib support for latest version (requires 0.0.1.0)
+ - "Mark as spammer" not shown for chatrooms
+ - The bug that YouWereAdded messages were sent to marked spammers was
+ a problem in ICQ.dll this should be fixed in newer versions of the ICQ protocol
+ (newer than 0.3.6.14)
+ - Fixed: Invalid Regular Expressions in Spam Definitions are now notified
+ - Bug-Fix: Auto-Ignore spammers were not shown on the spammers list
+ - Bug-Fix: Sometimes short messages were considered as multiple received messages
+ - Bug-Fix: Memory corruption problems with the log file (and some other minor logging fixes)
+ - Bug-Fix: Auto-Replies were sometimes not sent out when "Delete Spam" was enabled
+
+ 2.5.0.0 NEW FEATURE: Real Unicode Support!
+ -> two DLL files: spamfilter.dll and spamfilterw.dll
+ -> spamfilterW.dll will only load if Unicode *is* supported (e.g. Win2000 and higher...)
+ -> spamfilter.dll will only load if Unicode *is not* supported (Win95/98/ME)
+ -> spamfilterW9x.dll uses the unicode layer for Win95/98/ME (highly experimental version)
+ It needs unicows.dll in Miranda IM directory.
+ -> Spam Definition files are saved/loaded as UTF8 (auto-converting at first-run)
+ - Updated: Variables Plugin support (now supports Variables v0.2.0.0)
+ - Added: Support for help dialog of Variables Plugin
+ - Added: The "Missing Spam Definitions"-Warning can be
+ deactivated for the future by pressung the Ctrl-Key whn the message occurs
+ (requested by kopec)
+ (Be careful!: Should normally not be used)
+ - Enhanced: Functionality of MS_SPAMFILTER_REGISTERMESSAGETYPE
+ - Bug-Fix: tiny memory leak with the regexp help button icon
+ - Bug-Fix: "Variables" Plugin text was not always freed (mem leak)
+ - Bug-Fix: fixed possible memory leaks with realloc when low memory
+ - Improved: spammers file format
+ - Fixed: fixed a typing mistake without any effect
+ - Bug-Fix: fixed error handling for calls to GetWindowRect
+ (did not have any effect)
+ - Bug-Fix: Corrected wronmg calling of TreeView state (however no effects)
+ - Bug-Fix: fixed large spam icon (res id was wrong)
+ - Bug-Fix: OSD support did not work yet..commented out...oups :-)
+ - Bug-Fix: Option "Allowed Spam per User" on Advertisment Filter dialog
+ was not synced correctly
+ - Bug-Fix: OpenFile-Dialog was not shown correctly on WinNT4 (I hope it does now)
+ - Bug-Fix: Restored full support for Win95 (GetLongFileName is now run-time linked)
+ - Bug-Fix: special chars in XML logging header were sometimes not replaced correctly
+ - Bug-Fix: samll memory leak in XL logging logic
+ - Bug-Fix: Read/Sent flags got corrupted when making events as unread
+ - Fixed: Updated old part in the docs (reported by PanTzeR)
+ - Added: Note is shown in options when not all SpamDefinitions are available
+ - Improved: some other internal tweaks
+ - Extended: english teaser definitions
+ - Modified: translation strings (see SpamFilter-Translation.txt)
+ - Improved: Made it more robost against database curruption
+ - Improved: Removed 256 sized text buffer for MsgType box (possibility of buffer overrun)
+ - Improved: Internal stringlist handling on low memory conditions (string pairs)
+ - Improved: revisited log file code
+ - Bug-Fix: robot filter instruction and answer text could not be changed
+ - Added/Fixed: MBCS and surrogates support for far east (improved locale handling, too)
+ - Fixed some issues with pointer arithmetic
+ - Code robustness updates
+ - Impreoved: further increased filtering performance by caching
+ - Cleaned up SetSpammerManually code
+ - Strings modified (see SpamFilter-Translation.txt)
+ - Corrected some typos in strings
+ - Minor Bug-Fix: horizontal extent of list box was sometimes miscalculated
+ - Added: Auto-Import feature to convert old ANSI files of old Spam Filter 2.x
+ into the new UTF-8 format (needed to support Unicode properly)
+ IMPORTANT!: be sure to make a backup of any self-defined Spam Definitions.
+ - Improved: removed static variables in DlgProcs/WindowProcs
+ - Added: bad words cache to further improve performance
+ - Added: Real Unicode support (separate files: spamfilterW.dll is unicode,
+ no Win9x/ME supported on Unicode)
+ including BOM support for text files
+ - Added: LogFile write UTF-16LE BOM into txt/log files to indicate
+ correct Unicode format for Rraders as Notepad (only in Unicode mode)
+ - Improved: Spam Definitions are only validated when Advertisment Filter or
+ Disliked Messages Filter is enabled
+ - Added some documenation about Regular Expressions (RegExp),
+ how to use them and a code table (quick guide for beginners)
+ (accessable via a button on the add to list dialog)
+ More info: http://perldoc.perl.org/perlrequick.html
+ - Added: Now shows a error message when the reply messages can't be sent
+ (due to network errors etc.)
+ - Added: Support for Custom Folder Plugin
+ - new service: write log file
+ - new service: send_automated_contact_message (including rec, error output)
+ - new service: show generic error_message (out of memory?, message not sent)
+ - Added: now uses Miranda's simple memory problems catcher
+ - Updated: PluginInfo of Plugins options
+ - got rid of AgressiveOptimize.h:
+ (reason: http://forums.miranda-im.org/showthread.php?t=4863)
+ does not work on DEP systems (= data execution prevention).
+ As for optimizing. Use /O1 (optimize for size), disable global optimizing and disable the
+ "optimize for Windows 98" feature of the linker (/opt:nowin98).
+ This can considerably bloat the resulting DLL/EXE for (a small) performance gain on Win 9x,
+ but does nothing on NT based systems.
+ - support new variable in Variables Plugin: %number_of_auto_ignore_users%
+ - Repackaged: in default zip is only english spam language
+ - Added: Trigger Plugin support
+ - Added: Support for extra icon on contact list (indicating spammer)
+ - Added: Spammers list manager
+ - Added: Auto-Import of old spammers of v2.0.1.0
+
+
+ 2.1.0.0 - Added: pending auth requests are automatically denied if contact is ignored
+ - Added: preparation for taken YouWereAdded events into spam detection
+ - Added: Support for OSD Plugins beside Popup Plugin support (requested by Schommel)
+ - Bug-Fix: Configure dialogs were not closed properly when created not as modal
+ - Bug-Fix: memory leak in the string lists, the stringlist main object was never freed
+ - Bug-Fix: stringlist converting to string did not work correctly (not zero terminated)
+ - Bug-Fix: internal stringlists are now thread safe (using CriticalSections)
+ - Bug-Fix: pcre.dll is now copied/installed correctly (path was wrong formated)
+ - Bug-Fix: Regular Expressions were not compiled correctly
+ -> Regular Expressions should now really work in default installation :-)
+ - Bug-Fix: possible crash in recv chain filtering
+ -> empty contact was deleted before ICQ could have the chance to add its message
+ (bug only occured when "delete spam messages" was enabled)
+ - Bug-Fix: contact that were set on the spammer list were never really hidden
+ -> ICQ did always reset the Hidden attribute; this is now fixed
+ - Bug-Fix: small memory leak in AutoIgnore settings loading
+ - Bug-Fix: Now question message will not be sent when spammer just reached it spam limit
+ - Bug-Fix: reply messages are ordered correctly in db/history
+ - Bug-Fix: No longer sending "YouWereAdded" notification when a spammer is added to ignore list
+ (reported by PanTzeR)
+ -> ICQ sent a confirmation automatically when a user was made permanent; now this is prevented
+ - Bug-Fix: Spam Log did not always mention "user ignored" (log written too early)
+ - Bug-Fix: contacts event was never filtered
+ - Bug-Fix: tiny problem with hContact of auth requests
+ - Bug-Fix: Corrected some hidden Access Violations in MessageTypes box
+ - Bug-Fix: "Disliked Messages Filter" now really works (reported by zk, Tomaso, PanTzeR)
+ * message text never got allocated
+ * did not work for contacts that passed the robot filter previously
+ * some wrong setting names were used
+ * hoax texs were checked wrongly
+ * hoax texts were not loaded (corrected file ext: .sfw -> .sft)
+ >> Ouch....quite crappy thing...did I really code that? :-o
+ Anyway...now it's finally fixed :-D
+ - Bug-Fix: Filter strings of Log-Browse dialog were not allocated correctly
+ (stringlist access violation)
+ - Bug-Fix: AddToHistory option of DislikedMessagesFilter did not get disabled
+ when other options got disabled
+ - Bug-Fix: If "Add the reply to contact history" was selected, the history showed the reply
+ before the spam message (reported by Tomaso)
+ - Bug-Fix: "Show log" button did never get activated (broken in v2.0.1.0)
+ - Bug-Fix: relative paths for spam log were broken
+ - Bug-Fix: some minor UI improvements
+ - Bug-Fix: memory leak in the DislikedMessages options (bold font was not destroyed)
+ - Bug-Fix: when a filter window was opened using the SF-API without parent then the window
+ would not be destroyed on exit of Miranda (extended/modified SF-API)
+ - Bug-Fix: added some NULL checking where I forgot it
+ - Bug-Fix: added checking for correct db setting type
+ - Bug-Fix: "Apply" button was sometimes enabled although no options
+ were changed for message types
+ - Improved: now the robot filter only workd for normal sending and receiving contacts
+ - Improved: stringlist loading speed (just a bit)
+ - Improved: spam detection speed when "show popup" is disabled (just a bit)
+ - Fixed/Improved: some small internal tweaks and resource cleanup
+ - Fixed: now uses UniqueID instead when renaming a contact to set it on the spammers list
+ -> this fixes: spammers added as "'Unknown Contact' (Spammer)" (reported by PanTzeR)
+ - Modified: "Random" button in robot filter options now creates always the same length
+ - Improved: SPAMCHECKDATA strcuture to be more cleanly implemented
+ => Code is now fully tested on different situations
+ => Code was additionally fully checked for memory leaks and/or memory access violations
+
+ 2.0.2.0 - FEATURE!: Support for Regular Expressions (using pcre.dll; included)
+ -> Support for Regular Expressions is disabled when the DLL (pcre.dll) is not present
+ -> Word filter is now much, much, much more powerful!
+ - Added: RegExp DLL (pcre.dll) is installed automatically on first run (In Miranda directory)
+ -> including DLL version checking
+ - Added: some little more documentation about the Spam Definitions (readme and in the files)
+ - Added: now the UniqueID of a protocol is appended to the nickname in the spam log
+ (reported by Lastwebpage)
+ - Added: support for Updater to update the SpamFilter plugin
+ - Bug-Fix: Making use of EnterCriticalSection to gain thread safety
+ - Bug-Fix: avoiding of some string buffer overruns in some rare situations (if ever)
+ - Bug-Fix: Message Types box gets sorted properly
+ - Bug-Fix: popups did not work (broken in 2.0.1.0)
+ - Bug-Fix: menu item icon was not updated when icons were changed (IcoLib support)
+ - Bug-Fix: fixed some memory leaks of contact name strings
+ - Extended: Documentation of Spam Definitions and how they work
+ - Fixed: corrected some strings (see SpamFilter-Translation.txt)
+ - Fixed: corrected wrong naming of teaser text files (.sfw -> .sft)
+ - Fixed: known spammer file now allows proto|uniqueid and proto|nick (if available)
+ - Modified: few changes in the strings (see SpamFilter-Translation.txt)
+
+ 2.0.1.0 - Bug-Fix: "Allowed spam per User" could not be changed (reported by ATom)
+ - Bug-Fix: problems with RTL support (:text)
+ -> I thought RTL support would only work on Arabic/Hebrew versions of windows, so I enabled it..
+ In fact it works on most Win2000/XP versions as I found out
+ (not really good documented :-/... they only speak of "RTL enabled windows versions")
+ - Bug-Fix: minor memory leak with the message type icons
+ - Bug-Fix: layout confusion with Spam Logging group (reported by Romx)
+ - Bug-Fix: auth requests were not handled correctly when setting a spammer manually
+ - Bug-Fix: "Allowed bad phrases until spam" was ignored (ATom)
+ -> caused just by a small typo in the code :-/
+ - Bug-Fix: line breaks and spaces were not cleared correctly in
+ incoming messages (silly typo in code :-/)
+ -> this fixes the following:
+ * normal words as security code were not recognized as right answers
+ (reported by Steffen Kaufmann)
+ * sometimes the reply for a good answer was not sent (reported by ATom)
+ - Bug-Fix: using mir_snprintf to avoid string buffer overruns
+ - Bug-Fix: first message from NotOnList user was from "(Unknown contact)"
+ - Bug-Fix: PreSpammers were not unique, now they are saved as "Proto|Address"
+ - Modified: Removed SFFreeMemory, replaced with miranda_sys_free (m_spamfilter.h)
+ - Modified: pszSection in MESSAGETYPEDESC can now really be NULL (m_spamfilter.h updated)
+ - Modified: AutoIgnore feature now uses real unique user ID
+ - Added: IcoLib support
+ - Added: Support for "ColourPicker" to choose Colors of Popups
+ - Added: Support for Netlib logging (Netlib_Logf) -> spam filter actions on messages are now listed
+ in the network activity log
+ - Added: ability to reset the AutoIgnore feature
+ - Improved: Better looking "Mark as spammer" dialog
+ - Improved: minor performance improvements, code cleaning, DB settings cleaned up.
+ - Changed: services for message types (no backward compatibility to 2.0.0.0, sorry
+ -> devs, please update "m_spamfilter.h")
+ -> detecting default settings for checked protocols/events is now possible (might be useful for IRC?)
+ - Added: New event for protocols to determine if they want to be checked for spam by default:
+ ME_SPAMFILTER_PREADDNEWMESSAGETYPE
+ - Modified: Functionality of MS_SPAMFILTER_SETSPAMMANUALLY and
+ MS_SPAMFILTER_GETHANDLE ("m_spamfilter.h")
+ - Fixed: phone number default definitions (all large numbers were detected as phnone codes)
+ - Fixed/Extended: translation strings (Translattor see "SpamFilter-Translation.txt")
+ => all known bugs should now be fixed :-)
+
+ 2.0.0.0 - Almost all got changed/fixed/improved/extended/... :-)
+ - Added support for logging in XML format (including XSL stylesheet)
+ -> Very clean XML format used
+ -> Characters replaced: < with &lt; , > with &gt; , & with &amp; , " with &quot; , ' with &apos;
+ - Fixed: Layout mistake on options page
+ - Added: Recognition of CAPITALIZED messages (thanks to InvisiBill)
+ - Some other small changes and cleaned up code
+ - Added possibility to change spammers (on ignore list)
+ - Added extra buttons for "filter settings"
+ - Added error handling for log file edit box and removed read only flag
+ - Fixed: "Select log file" dialog was shown sometimes on re-activating filter
+ - Removed unnecessary error message ("Please select bad phrase to delete")
+ - Added (better) error handling for "Allowed bad phrases" box
+ - Improved/Changed German translation completely
+ - Added more Russian bad phrases
+ -> Many thanks to ZaNuDa
+ - Added new section "Filtering Tips" to readme file
+ - Added recognition support for phone numbers (Idea by ZaNuDa)
+ - Spam Filter now behaves as a filter protocol
+ -> This makes the filter more faster than before
+ - Moved together the filter behaviour "delete message" and "delete contact"
+ -> The contact now gets automatically deleted if it has no other messages in history
+ - Fixed: Apply button got always enabled when options were loaded
+ - Now uses better/newer LoadImage function instead of LoadIcon function to save memory
+ -> Icons now get unloaded to save memory
+ - Added Spam Definitions instead of using a langpack string
+ - Bold heading for groups in "Configure..." options, too
+ - Added automatic adjustment of "Ignore" settings
+ - Improved: Icons in spam popups are now skinned icons of Miranda IM for message and URL icon
+ (with layered spam icon on it)
+ - Improved file size
+ - Dropped "one phrase message" recognition
+ -> Was not a good indicator for spam
+ -> Did not work with languages which do not have (or use) spaces
+ - Added support for "PluginUninstaller"
+ - Added "Allowed spam messages" feature
+ - Complete new phrase list algorithms for easy phrase list
+ loading/saving and checking
+ - Changed formating of sounds (Added: "Spam Filter: " prefix)
+ - Added "empty message" detection
+ - Added "Multiple message with same content" detection (Idea by Popovn)
+ - Recognition of bad phrases that are formated differently (like "\/!@gr@")
+ and have kinds of things in them (thanks to InvisiBill)
+ - Added disliked messages filter
+ Filter out something like: "Please send this message to all of your friends" or
+ "please forward") (Idea by Johannes)
+ - Added button "Show log" to make opening the log file more easier
+ - Changed "Select log file" dialog to use the standard "Save as" dialog instead of the "Open" dialog
+ - Improved log file format
+ - Also looking for "good phrase(s)"
+ - Recognition support for phrases with s p a c e s (included in "different formated phrases" check)
+ - Added: Position of configure dialogs gets saved
+ - Added: Now also checks for auth requests
+ - Some resource improvements
+ - Added the possibility to manually avoid spam check via pressing the control key
+ when recieving a message
+ - The Spam Filter now is implemented as a proper protocol filter plugin
+ - Improved filtering speed
+ - Added: Automatic installation of docs and sound files on first run
+ - Added: Auto-Import feature for bad phrases used with v1.0.4.0
+ - Added: DatabaseEditor++ support
+ - Extended: Phrase list editing; the list boxes now support
+ horizontal scrolls if needed, drag and drop for rearranging the phrases,
+ context menu for fast editing, possibility to input long phrases, etc...
+ - Added: "Variables" plugin support
+ - Added a second default sound file
+ - Added: Log file control now makes use of the auto complete feature
+ - Added: Relative paths can be used in the log file edit control
+ - Added: Comma Separated Values format logging
+ - Added: Robot Block functionality similar to "StopSpam" plugin:
+ Further enhanced technic and cleaner implementation.
+ - Translators: There are a lot of changes in the strings ;-)
+ - Doubly recieved messages can be removed
+ For plugin developers: (Only ;-) )
+ - Updated "m_spamfilter.h" (Almost all is new)
+ - Added "m_spamfilter.inc" for Delphi developers
+
+ 1.0.4.0 - Two entries with the same words in the customized bad words list
+ are no longer possible
+ - Added Feature: Spammer name/id remembering and blocking
+ - Added Feature: Spammers who are not recognized can be added to the spammers list via right click
+ (thanks to Rudi)
+ - Added version information to DLL file
+ - Changed logging output
+ - Added txt file item on select log file dialog
+ - Improved conrolling of the options page via keyboard
+ - Improved layout of the options page
+ - Extended URL-recognition: Now recognizes also URLs with returns in between (thanks to Rudi)
+ - Extended/Improved default bad words very much (strings, too) -> now 78
+ -> Please update your language pack (if you use one)
+ - Extended German bad words (see "SpamFilter_Translation.txt")
+ - Extended Potuguese (Brazil) bad words (thanks to Paulo)
+ - Updated German and Chinese (Traditional) dialog translations
+ (thanks to Wongsen for the Chinese one)
+ - Fixed: The string "Log spam messages to file:" was displayed in
+ German (thanks to Wongsen)
+ - Fixed: Crash on selecting logging file (thanks to Genionhandy2 and Bumper1)
+ - Fixed: URL-Recognition and one word message recognition didn't work
+ currently (thanks to Wongsen and Rudi)
+ - Fixed: Small bug with the open file dialog
+ - Fixed: URLs are now completely checked and saved to log file
+ - Updated service functions for plugin developers
+ - Improved error checking of "Add bad word" dialog
+ - Improved error checking for "Select log file" dialog
+ - New/Improved icons (smaller file size, too)
+ - Improved default settings
+ - Some other small changes
+ - Updated screenshot on website
+
+ 1.0.3.1 - Changed/Improved icons for spam popups
+ - Nearly complete rewrite of the spam recognition
+ - Fixed: Log file wasn't written if selected directory didn't exist anylonger
+ - Fixed: Log file option got always unchecked (also if enabled)
+ - Fixed: Sometimes the wrong bad word got selected on inserting a
+ customized bad word
+ - Added bad words are now checked for occurence in default list
+ - Changed some strings
+ - Changed some German translation strings
+ - Updated Chinese (Traditional) strings (thanks to Wongsen)
+ - Updated Chinese (Traditional) bad words (thanks to Wongsen)
+ - Extended English default bad words (strings, too)
+ -> Please update your language pack (if you use one)
+ - Changed/Improved included sound file and new file name
+ -> Please update your sound settings (thanks to Wongsen)
+ - Extended German bad words
+ - Added "Activate/Deactivate" item in "PopUps" submenu
+ - Improved URL filtering: URLs with spaces (h t t p :) are now
+ included in the filter, too (thanks to Rudi)
+ - Some preparing for contact name/id collecting/blocking (coming in v1.0.4.0)
+ - Changed/Improved documentation and removed some mistypings
+ - Some other small/big internal changes/improvements
+ - FOR PLUGIN DEVELOPERS!: Added more service functions!
+ -> Activation service functions
+ -> General spam checking service (with hContact)
+ (For details see "m_spamfilter.h")
+
+ 1.0.3.0 - Improved error checking for "allowed bad words" control
+ - Fixed: Customized bad words sometimes got messed up due to wrong
+ pointers (thanks to Lúcio and Wongsen)
+ - Fixed: Customized bad words now get correctly allocated
+ and released
+ - Fixed: Default settings did not now fit for all languages
+ (thanks to Wongsen)
+ - Improved performance and bad words count loader
+ - Improved spam filter
+ - Merged "Is URL" and "Contains URL" part
+ - Improved log file output
+ - Extended default bad words (Updated strings, too)
+ - Added Chinese (Traditional) bad words (thanks to Wongsen)
+ - Changed some English strings (Updated strings, too)
+ - Changed some German translation strings
+ - Added Chinese (Traditional) dialog strings (thanks to Wongsen)
+ - Improved/Changed documentation
+ - NEW FOR DEVELOPERS: Added service function for bad words
+ checking in other plugins (For details see "m_spamfilter.h")
+ - Some other small changes
+ - Updated screenshot on website
+
+ 1.0.2.4 - Fixed: Non-spamming contacts sometimes got hidden while sending
+ messages to them (thanks to Javier and Tomaso)
+ - Changed some strings in German translation
+
+ 1.0.2.3 - Fixed: Button "Cancel" in "Add bad word" dialog showed always the
+ German variant "Abbrechen" (thanks to Jef)
+ - Fixed: Database values now get freed (Oups, I forgot that...)
+ - Fixed: Auto-Deleting message and auto-deleting contact now works
+ without outputing empty message dialogs! (Nobody noticed?)
+ - Changed "Add bad word" dialog for people who don't read documentation
+ files ;-)
+ - Changed some translation strings
+ - Improved filter performance
+ - Small changes in translation documentation
+
+ 1.0.2.2 - Improved: Smaller file size (improved compiler settings)
+ - Changed documentation files
+ - Reprogrammed error checking for "Add bad word" dialog
+ - Reprogrammed key support for customized bad words list box
+ (Now uses subclass procedure)
+ - Extended recognized keys in list box:
+ Now supports "del", "-", "+", up and down keys
+ - Changed Portuguese (Brazil) bad words (thanks to Lúcio)
+ - Improved dates/times in logging:
+ -> Now uses system (language) default format for formatting dates/times
+ -> Removed translation strings for this
+ - Fixed: Corrected small checking bug with "Allowed bad words until spam"
+ (thanks to Lúcio)
+ - Fixed: Options items are now correctly grouped
+ - Fixed: Spam logging didn't work without Path plugin
+ - Some internal improvements/performance optimizations
+ - Some changes with the German translation
+ - Improved spam logging output: more detailed and cleaned up
+ - Optimized/Faster spam logging
+ - Improved/Changed documentation
+ - Some other small/minor changes
+
+ 1.0.2.1 - Improved behaviour of "Allowed bad words in messages" spin control
+ - Changed dialog font to same as in Miranda IM core
+ - Added expert options support for logging options
+ - Changed Path Plugin dependency (Path plugin is now optional)
+ -> You do not need to have the Path plugin for logging any longer
+ -> It is only needed if you would like to use relative paths
+ - Included a fix for an issue with the Path plugin
+ (kind of bug in Path plugin)
+ - Changed option group headings to bold (finally found out how to
+ switch this... ;-) )
+ - Removed positioning of options group
+ - Cleaned up/Improved resources
+ - Added button for "Select log file" dialog
+ - Changed log file box
+ - Improved spam logging: Now messages are now longer saved as only
+ lower case
+ - Added error checking for add bad word dialog
+ - Fixed: Some error box strings were not translatable
+ - Fixed: Selection of "What to do" got sometimes not set
+ - Changed the path to default sound file to the included sound file
+ - Forgot to switch the version number shown on plugins page (Oups...)
+ - Improved German translation
+ - Some other small changes
+ - Updated screenshot on website
+
+ 1.0.2.0 - Added spam message/sender logging to file (fully translatable)
+ - Added some language pack string for spam logging
+ - Added Portuguese (Brazil) bad words (thanks to Lúcio)
+ - Improved comma recognition in default bad words list in language pack
+ - Added "del" key support in customized bad words box
+ - Small changes in German translation
+ - Added some default bad words (Changed strings, too)
+ - Use of new header files
+ - Updated screenshot on website
+
+ 1.0.1.0 - Added spamming contacts handling
+ - Improved option page layout
+ - Moved translation strings out of "SpamFilter_Developers.txt" to
+ separate file
+ - Added some default bad words (Changed strings, too)
+ - Added French bad words (thanks to Poischack)
+ - Added Russian bad words (thanks to NeptunE)
+ - "SpamFilter" speaks German!
+ (Completed German standard translation in "SpamFilter_Translation.txt")
+ - Rearranged and optimized code
+ - Some other small changes
+ - Updated screenshot on website
+
+ 1.0.0.2 - Changed selection style of customized bad words box
+ - Fixed: I forgot to mark some strings translatable...
+ - Changed some texts (Changed strings, too)
+ - Better position for "Add Bad Word" dialog
+ - Customized bad words list gets now sorted
+ - Added "Fast delete": Improved and faster deleting of customized
+ bad words in list
+ - Improved adding of customized bad words (small change)
+ - Added sound file
+ - Cleaned up code a bit
+ - Some other minor changes
+
+ 1.0.0.1 - Removed some debug output messages (Oups...)
+ - Extended default bad words
+ - Fixed: Customized bad words list doubled itself every time it
+ got loaded
+ - Smaller file size
+ - Some other tiny changes
+
+ 1.0.0.0 - Initial release
+
+
+Thanks
+----------------------
+ * To Roland Rabien the author of "SpamBlock" for the
+ first idea of spam recognition by bad words
+
+ * To Daniel Walter who improved "SpamBlock" to "SpamBlock+"
+ for the idea of an extensible bad words list
+
+ * To romamik the author of "StopSpam" for the
+ idea to block robots via reply messages
+
+ * To Lúcio for the Portuguese (Brazil) bad words
+ and his much feedback ;-)
+
+ * To Wongsen for the Chinese (Traditional) bad words and dialog translation,
+ his much feedback, his idea for an sound file and his helpful "beta testing" ;-)
+
+ * To NeptunE for the Russian bad words
+
+ * To Poischack for the French bad words
+
+ * To Paulo for some more Portuguese (Brazil) bad words
+
+ * To cyesto for creating a great bug-fix for the crash-on-ignore issue
+ in version 2.1.0.0. Thanks a lot!
+
+ * To Rudi for his many reported spam messages
+
+ * To Javier, Jef, Lúcio, Tomaso, Wongsen, Rudi, Genionhandy2, Bumper1,
+ ATom, Lastwebpage, Steffen Kaufmann, Romx, zk, Sarcago, sh8an, PanTzeR,
+ DaSilva, TioDuke, kurd, gerk, Vosoni, Avalanche, adderek, Schommel,
+ PsychodelEKS, Petr and pro100_tak for their bug reports
+
+ * To Johannes, Pickle Pete, Popovn, PanTzeR for their improvement ideas
+
+ * To Genionhandy2 for advertising this plugin ;-)
+
+ * To InvisiBill for his much feedback and his improvement ideas
+
+ * To Tigerix for his massive feature suggestions and bug-reporting :-)
+
+ * To ZaNuDa for some more Russian bad words and the filtering tips
+ and especially for his much effort in beta testing version 2.0.0.0.
+
+
+Translation
+----------------------
+ Translation strings and available bad words strings can be found
+ in "SpamFilter-Translation.txt" of the SDK package.
+
+
+License and Copyright
+----------------------
+
+ "Spam Filter" is released under the terms of the GNU General Public License.
+ See "SpamFilter-License.txt" for more details.
+
+ "Spam Filter" is copyright (c) 2003-2006 by Heiko Herkenrath.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ RegExp Library:
+ PCRE (pcre.dll) is a library of functions to support regular expressions whose syntax
+ and semantics are as close as possible to those of the Perl 5 language.
+
+ "Perl Compatible Regular Expressions" (PCRE) is distributed under the terms of
+ the "BSD" license. See "PCRE-License.txt" for more details.
+
+ "PCRE" is copyright (c) 1997-2004 University of Cambridge
+
+
+H. Herkenrath (hrathh at users.sourceforge.net) \ No newline at end of file
diff --git a/spamfilter/Docs/SpamFilter-Translation.txt b/spamfilter/Docs/SpamFilter-Translation.txt
new file mode 100644
index 0000000..b9a1e1c
--- /dev/null
+++ b/spamfilter/Docs/SpamFilter-Translation.txt
@@ -0,0 +1,865 @@
+
+Spam Filter 2.5.2.3 for Miranda IM 0.4.3+
+------------------------------------------------------------------------
+ Translator Information
+
+ Contents: -------------------------------
+ | General Info, Default Bad Phrases/Patterns, German Strings,
+ | Chinese (Traditional) Strings, String History
+
+
+General Info
+-----------------------------
+ "Spam Filter" can be translated with the Miranda IM
+ language files.
+
+ Put the following strings in a file called "langpack_<language>.txt"
+ in the Miranda IM directory and translate them into your
+ language or take the German ones... ;-)
+
+ If you need more info on Miranda IM language files visit:
+ http://cvs.sourceforge.net/viewcvs.py/*checkout*/miranda/miranda/i18n/readme.txt
+
+
+Default Bad Phrases/Patterns
+-----------------------------
+ If you think a string has to be added to the translated bad phrases
+ please mail me.
+
+ If you notice some bad phrases that are
+ missing in the lists, please mail them to me. this would be very nice.
+ And..ehm...If it is another language than German, English or French
+ then a translation would be nice, so that I can look if all bad phrases
+ are usable and perhaps I can take some over into the english ones...
+
+ More bad phrase languages will (hopefully) come soon... please help me :-)
+
+
+German Strings
+-----------------------------
+
+; --- Plugin: Spam Filter 2.5.2.3 (German) ---
+
+; Translation by H. Herkenrath (hrathh at users.sourceforge.net)
+; Please report any mistakes or missing strings in here.
+
+[Spam Filter]
+Spam-Filter
+[Filters out advertisment messages, messages sent by robots and other disliked messages such as hoax messages.]
+Dieses Plugin filtert Werbe-Nachrichten, automatisch versandte Nachrichten und unerwünschte Nachrichten, wie Kettennachrichten, heraus.
+
+; Message Types (same as "Options->Contact List->Icons")
+[Authorization Request]
+Erlaubnisanfrage
+[Added Notification]
+Hinzufügungsmeldung
+[Message]
+Nachricht
+[URL]
+URL-Adresse
+[File]
+Dateiübertragung
+[Contacts]
+Kontakte
+
+; Menu Items
+[Enable &spam popups]
+&Spam-Meldungen aktivieren
+[Disable &spam popups]
+&Spam-Meldungen deaktivieren
+[&Unmark spammer]
+&Kein Spam-Versender
+[&Mark as spammer]
+&Als Spam-Versender markieren...
+[Mark Contact as Spammer]
+Kontakt als Spam-Versender markieren
+[Are you sure that the contact \"%s\" shall be marked as spammer?\r\n\r\nMarking a contact as spammer means that all associated messages will be recognized as spam and the contact will be completely ignored in the future.\r\n\r\nA contact that was marked as spammer can be restored using the options dialog.]
+Soll der Kontakt \"%s\" wirklich als Spam-Versender markiert werden?\r\n\r\nEinen Kontakt als Spam-Versender markieren bedeutet, dass alle seine bisherigen Nachrichten als Spam erkannt werden und der Kontakt für die Zukunft komplett ignoriert wird.\r\nSpam-Versender können in den Einstellungen wieder reaktiviert werden.
+
+; Popups
+[Disliked Message Alert!]
+Unerwünschte Nachricht-Alarm!
+[Advertisment Alert!]
+Werbung-Alarm!
+[Robot Alert!]
+Automatische Nachricht-Alarm!
+
+; Balloon Tips
+[Spam Filter Error]
+Spam-Filter Fehler
+[Spam Filter Error"), TranslateT("The log file \"%s\"\r\ncan not be written.\r\n\r\nProblem:\r\n%s\r\nPlease check the settings.]
+Die Protokollierdatei\r\n\"%s\" kann nicht geschrieben werden.\r\n\r\nProblem:%s\r\nBitte überprüfen Sie die Einstellungen.
+[You did not install any Spam Definition files! Spam Filter can not work as powerful as it could.\r\n\r\nPlease install at least one language of the Spam Definitions.]
+Es wurden keine Spam-Definitionen installiert! Der Spam-Filter kann nicht so ausgeprägt funktionieren, wie er könnte.\r\n\r\nBitte installieren Sie mindestens eine Sprache der Spam-Definitionen.
+[Spam Filter Critical Error]
+Kritischer Fehler im Spam-Filter
+[A very critical internal error was detected!\r\nProbably the filter will not work at all.\r\n\r\nPlease update to the most current version if this problem persists.]
+Ein sehr schwerwiegender interner Fehler wurde festgestellt!\r\nWahrscheinlich wird der Filter überhaupt nicht funktionieren.\r\n\r\nWenn das Problem fortbesteht bitte auf eine neuere Version aktualisieren.
+[Spam Filter was not able to send an automated message to the user \"%s\".]
+[Spam Filter Warning]
+Spam-Filter Warnung
+[The filter has detected %u incorrect Regular Expression(s) in the default bad words!\r\n\r\nPlease update your installed Spam Definitions to the latest versions to resolve this problem.\r\nThis problem might also be caused by Spam Definitions that are not compatible with your system codepage.]
+In den Standard-Spam-Wörtern wurde ein inkorrekter Regulärer Ausdruck gefunden (%u Stück)!.\r\n\r\nDie Spam-Definitionen müssen auf die neustenn Versionen aktualisiert werden um dieses Problemn zu beheben.\r\nEs kann auch sein, dass Spam-Definitionen installiert sind, die nicht kompatibel mit der verwendeten Windows-Codepage sind.
+
+; Sounds
+;[Spam]
+[Advertisment Alert]
+Werbung eingegangen
+[Disliked Message Alert]
+Unerwünschte Nachicht eingegangen
+[Robot Alert]
+Automatische Nachricht eingegangen
+
+; Icons
+[Spam Message Overlay]
+Spam-Nachricht Indikator
+;[Mark Contact as Spammer]
+[Spammer Indicator]
+Spam-Versender
+
+; Variables Plugin
+; %autoignore_count%
+[Number of users currently being in danger of being ignored.]
+Anzahl der Benutzer die gefährdet sind automatisch ignoriert zu werden.
+
+; Trigger Plugin
+[Spam Filter: Incoming Spam]
+Spam-Filter: Eingehende Spam-Nachricht
+
+; Custom Folders Plugin
+[Spam Definitions]
+Spam-Definitionen
+[Customized Words]
+Selbstdefinierte Wörter
+
+; Logging
+[Contact set as spammer]
+Kontakt ist Spam-Versender
+[Contains only spaces]
+Enthält keinen Text
+[Is multiple]
+Ist mehrfach empfangene Nachricht
+[Is capitalized]
+Enthält nur große Buchstaben
+[Contains URL address]
+Enthält URL-Adresse
+[Contains phone number]
+Enthält Telefonnummer
+[Is teaser]
+Ist Locknachricht
+[User is known as spammer]
+Benutzer ist als Spam-Versender bekannt
+[Robot message]
+Automatisch versendete Nachricht
+[Contains %u bad phrase(s)/pattern(s)]
+Enthält %u Spam-Worte
+[Contains %u differently formated bad phrase(s)/pattern(s)]
+Enthält %u abweichend formatierte Spam-Wörter
+[Contains %u good phrase(s)/pattern(s)]
+Enthält %u positive Wörter
+[Is hoax text]
+Ist Kettennachricht
+[Contains %u disliked indicating phrase(s)/pattern(s)]
+Enthält %u unerwünschte Wortkombinationen
+; Separator:
+;[, ]
+[Message deleted]
+Nachricht gelöscht
+[Message marked read]
+Nachricht als gelesen markiert
+[User ignored]
+Benutzer ignoriert
+; %log_user% format: Nick (UniqueID)
+;[%s (%s)]
+
+; Logging: Plain Text Format
+[[%log_date% %log_time%, User: %log_user%, Type: %log_type%]\r\nRecognition: %log_recognition%\t\r\nResult: %log_result%\r\n%log_message%\r\n\r\n]
+[%log_date% %log_time%, Benutzer: %log_user%, Typ: %log_type%]\r\nErkennung: %log_recognition%\t\r\nErgebnis: %log_result%\r\n%log_message%\r\n\r\n]
+
+; Logging: CSV Format
+;[\"%log_date%\";\"%log_time%\";\"%log_user%\";\"%log_type%\";\"%log_recognition%\";\"%log_result%\";\"%log_message%\"\r\n]
+["Date";"Time";"User";"Type";"Recognition";"Result";"Message"\r\n]
+"Datum";"Uhrzeit";"Benutzer";"Typ";"Erkennung";"Ergebnis";"Nachricht"\r\n
+
+; Logging: XML Format
+[Received Spam]
+Empfangene Spam-Nachrichten
+[Logged since]
+Protokolliert seit
+[Date/Time:]
+Datum/Uhrzeit:
+[User:]
+Benutzer:
+[Type:]
+Typ:
+[Recognition:]
+Erkennung:
+[Result:]
+Ergebnis:
+
+
+; Options: Main
+[&Advertisment Filter (recommended)]
+&Werbung-Filter (empfohlen)
+[Filters out advertisments from unknown users that are not on the contact list.]
+Filtert Werbe-Nachrichten von Benutzern, die nicht in der Kontaktliste aufgeführt sind, heraus.
+[C&onfigure...]
+K&onfigurieren...
+[&Robot Filter]
+&Automatische Nachrichten-Filter
+[Filters out all automated messages from unknown contacts via requesting a correct answer from the sender.]
+Filtert alle automatisch versandten Nachrichten heraus, durch Anforderung einer korrekten Antwort von unbekannten Versendern.
+[Co&nfigure...]
+Ko&nfigurieren...
+[&Disliked Messages Filter]
+&Unerwünschte Nachrichten-Filter
+[Filters out disliked messages such as hoax messages from known contacts that are on your contact list.]
+Filtert unerwünschte Nachrichten, wie Kettennachrichten, von Kontakten, die in der Kontaktliste aufgeführt sind, heraus.
+[Con&figure...]
+Kon&figurieren...
+[Behaviour]
+Filterverhalten
+[Mar&k spam messages as read]
+Spam als &gelesen markieren
+[D&elete spam messages]
+Spam-Nachrichten &löschen
+[Auto Ignore]
+Automatisch Ignorieren
+[Automatically &ignore spamming users]
+Spam-Versender automatisch &ignorieren
+[Allowed spam per &user:]
+&Erlaubte Spam-Nachrichten:
+[&Rese&t...]
+Zurückse&tzen...
+[Spa&mmers]
+Spa&m-Versender
+[Reset Auto Ignore]
+Automatisches Ignorieren zurücksetzen
+[Are you sure that the Auto Ignore counters shall really be reset?\r\n\r\nSpammers that are not already ignored will then not be remembered any longer.]
+Sollen die Zähler für das Automatische Ignorieren wirklich zurückgesetzt werden?\r\nSpam-Versender die noch nicht ignoriert sind werden dann nicht mehr vorgemerkt sein.
+[Message Types]
+Nachrichtentypen
+[Types of incoming e&vents which shall be checked for spam:]
+Eingehende Nachrichtent&ypen die überprüft werden sollen:
+[Spam Notification]
+Meldungen
+[Show &popup when a spam message is received]
+&Popup-Fenster anzeigen, wenn eine Spam-Nachricht eingeht
+[&Log spam messages to file:]
+Spam in Datei &speichern:
+; Default Log file name:
+;[Spam.xml]
+;[&...]
+[&Show]
+&Anzeigen
+
+; Options: Spammers Info
+[Ignored Spammers]
+Ignorierte Spam-Versender
+[The following contacts were recognized as spammers:]
+Die folgenden Kontakte wurde als Spam-Versender erkannt:
+[Spammer]
+Spam-Versender
+[Accept]
+Akzeptieren
+[Delete]
+Löschen
+[Store spammers in special &group:]
+Spam-Versender in folgender &Gruppe aufbewahren:
+[Spammers]
+Spam-Versender
+
+; Options: Advertisment Filter
+[Configure Advertisment Filter]
+Werbung-Filter konfigurieren
+[Bad Phrases/Patterns]
+Spam-Wortkombinationen
+[Look for standard &bad phrases in received messages]
+Auf &Standard Spam-Wörter in empfangenen Nachrichten prüfen
+[Also watch for following &customized bad phrases:]
+Auch nach &folgenden Spam-Wörtern suchen:
+[Automated message]
+Automatisch generierte Nachricht
+[All&owed bad phrases until spam:]
+Erlaubte &Anzahl an Spam-Wörtern:
+[Recogni&ze different formating]
+Abweichende &Formatierung erkennen
+[Allowed &formated bad phrases:]
+Erlaubte formatierte &Wörter:
+[Known Spammers]
+Spam-Versender
+[Filter out messages from standard &known spammers]
+Nachrichten von standardmäßigen Spam-Versendern herausfiltern
+[Bad Contents]
+Spam-Inhalte
+[&URL addresses]
+&URL-Adressen
+[&Phone numbers]
+&Telefonnummern
+[C&apitalized messages]
+&Großgeschriebene Nachrichten
+[Messages containing &spaces only]
+&Leere Nachrichten
+[&Multiple messages with same content]
+&Mehrere Nachrichten mit gleichem Inhalt
+[&Teaser messages]
+&Locknachrichten
+[Good Phrases/Patterns]
+Positive Wortkombinationen
+[Messages are &no spam when containing some of the following phrases:]
+Nachrichten &nicht als Spam ansehen, wenn folgende Wörter enthalten sind:
+;[Miranda IM]
+[Allo&wed phrases until no spam:]
+Erlaubte positive Wörter:
+
+; Options: Robot Filter
+[Configure Robot Filter]
+Filter für automatich versendete Nachrichten konfigurieren
+[Message Texts]
+Nachrichtentexte
+[&Instruction:]
+&Anleitung:
+[Hello %sender%,\r\nPlease reply \"%securitycode%\" if you want to contact me. If you don't reply correctly your messages will be ignored. Sorry for the inconvenience.\r\nThis spam check quarantees that you are a real person.\r\nYou have %triesleft% tries left.\r\nThanks in advance!\r\n\r\n(Automated message)]
+Hallo %sender%,\r\nBitte antworte mit \"%securitycode%\", um mit mit mir in Kontakt zu treten. Alle anderen Nachrichten werden zunächst ignoriert.\r\nEs verbleiben noch %triesleft% Versuche.\r\nDanke im Voraus!\r\n(Automatisch generierte Nachricht)
+[&Confirmation:]
+&Bestätigung:
+[Thank you for your help!\r\nAll your previous messages were delivered. New messages do now reach me directly without an additional spam check.\r\n\r\n(Automated message)]
+Danke für die Hilfe!\r\nAlle bisherigen Nachrichten wurden zugestellt. Weitere Nachrichten unterliegen keiner erneuten Spam-Prüfung.\r\n\r\n(Automatisch generierte Nachricht)
+[(You can use the substitute symbols %sender%, %message%, %securitycode% and %triesleft% for the appropriate contents.)]
+(Die Platzhalter %sender%, %message%, %securitycode% and %triesleft% können für die jeweiligen Inhalte verwendet werden.)
+; Value of %triesleft% when auto ignore feature disabled
+[unlimited]
+unbegrenzte
+[Add the message texts to contact &history]
+Zum &Nachrichtenverlauf des Kontakts hinzufügen
+[Security Code]
+Richtige Antwort
+[The following text needs to be replied by the contact to confirm that the message was not sent automatically:]
+Der folgende Text muss vom Benutzer zurückgesendet werden um zu bestätigen, dass seine Nachricht nicht automatisch versandt wurde:
+[&Random]
+&Zufall
+; Random chars base (Note: misunderstandable chars removed)
+;[abcdefghkmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWXYZ23456789]
+[(You can use readable words, too.)]
+(Normale Wörter sind auch möglich.)
+[&Hide users until they identified themselves]
+Absender &verstecken bis sie bestätigt wurden
+[Note: This filter gets invoked after the Advertisment Filter.\r\nThus, no instruction message will be sent in reply to advertisment messages.]
+Hinweis: Dieser Filter wird nach dem Werbung-Filter angewendet,\r\nd.h. es wird nicht mit einer Anleitungsnachricht auf Werbenachrichten geantwortet.
+
+; Options: Disliked Messages Filter
+[Configure Disliked Messages Filter]
+Filter für unerwünschte Nachrichten konfigurieren
+[Disliked Contents]
+Unerwünschte Inhalte
+[Messages containing &known hoax message texts]
+Auf &bekannte Texte von Kettennachrichten prüfen
+[Indicating Phrases/Patterns]
+Wortkombinationen
+[Look for the following indicating &phrases:]
+Auf folgende &Worte prüfen:
+[Please forward]
+Bitte weiterleiten
+[Auto Reply]
+Automatische Antwort
+[Auto &reply to disliked messages using the following text:]
+Automatisch auf unerwünschte Nachrichten mit dem folgenden Text &antworten:
+[Hello %sender%,\r\nI don't like such messages. Please don't send them to me anymore.\r\nThanks.\r\n\r\n(Automated message)]
+Hallo %sender%\r\nIch kann solche Nachrichten nicht ausstehen. Bitte nicht mehr senden!\r\nDanke.\r\n\r\n(Automatisch generierte Nachricht)
+[(You can use the substitute symbols %sender% and %message% for the appropriate contents.)]
+(Die Platzhalter %sender% und %message% können für die jeweiligen Inhalte verwendet werden.)
+[Add the reply to contact &history]
+Zum &Nachrichtenverlauf hinzufügen
+
+; Options: Select Log File
+[Select Log File]
+Protokollierdatei festlegen
+[Text Files (*.txt)]
+Textdokumente (*.txt)
+[Log Files (*.log)]
+Log-Dateien (*.log)
+[Comma Separated Values Files (*.csv)]
+CSV (Trenzeichen-getrennt) (*.csv)
+[XML Files (*.xml; *.xsl)]
+XML-Dateien (*.xml; *.xsl)
+[All Logging Files (*.log; *.txt; *.csv; *.xml)]
+Alle Protokollierdateien (*.log; *.txt; *.csv; *.xml)
+[All Files (*.*)]
+Alle Dateien (*.*)
+
+; Options: Add/Remove List Item
+[&Add...]
+&Hinzufügen..
+[&Remove]
+&Entfernen
+[&New indicating phrase/pattern:]
+&Neue Wortkombination:
+[Add bad phrase/pattern]
+Spam-Wort hinzufügen
+[Remove bad phrase/pattern]
+Spam-Wort entfernen
+[The entered bad phrase \"%s\" already exists in the list.\r\n\r\nThe filter doesn't differentiate between lower and upper case.\r\nTwo entries with the same phrase are not neccessary\nand will decrease the filter quality.\r\n\r\nShall the bad phrase be added anyway?]
+Das soeben eingegebene Spam-Wort \"%s\" existiert bereits in der Liste.\r\n\r\nDer Filter unterscheidet nicht zwischen Groß- und Kleinschreibung\nin den Wörtern, deshalb sind zwei Einträge mit demselben Wort nicht notwendig\nund würden die Filterqualität negativ beeinflussen.\r\n\r\nSoll das Spam-Wort dennoch hinzugefügt werden?
+[The entered bad phrase \"%s\" already exists in the default bad phrases lists.\r\n\r\nAllowing the same bad phrase in the customized bad phrases lists will\npull down the filter quality.\r\n\r\nShall the bad phrase be added anyway?]
+Das soeben eingegebene Spam-Wort \"%s\" existiert bereits in den Standard-Wörtern.\r\n\r\nEin selbsteingegebenes Spam-Wort sollte jedoch nicht in den Standard-Wörtern\nvorkommen, da dadurch die Filter-Qualität negativ beeinflusst werden könnte.\r\n\r\nSoll das Spam-Wort dennoch hinzugefügt werden?
+[Add good phrase/pattern]
+Positives Wort hinzufügen
+[Remove good phrase/pattern]
+Positives Wort entfernen
+[The entered good phrase \"%s\" already exists in the list.\r\n\r\nThe filter doesn't differentiate between lower and upper case.\r\nTwo entries with the same phrase are not neccessary\nand will decrease the filter quality.\r\n\r\nShall the good phrase be added anyway?]
+Das soeben eingegebene positive Wort \"%s\" existiert bereits in der Liste.\r\n\r\nDer Filter unterscheidet nicht zwischen Groß- und Kleinschreibung\nin den Wörtern, deshalb sind zwei Einträge mit demselben Wort nicht notwendig\nund würden die Filterqualität negativ beeinflussen.\r\n\r\nSoll das positive Wort dennoch hinzugefügt werden?
+[Add indicating phrase/pattern]
+Wortkombination hinzufügen
+[Remove indicating phrase/pattern]
+Wortkombination entfernen
+[The entered indicating phrase \"%s\" already exists in the list.\r\n\r\nThe filter doesn't differentiate between lower and upper case.\r\nTwo entries with the same phrase are not neccessary\nand will decrease the filter quality.\r\n\r\nShall the chain indicating phrase be added anyway?]
+Die soeben eingegebene Wortkombination \"%s\" existiert bereits in der Liste.\r\n\r\nDer Filter unterscheidet nicht zwischen Groß- und Kleinschreibung\nin den Wörtern, deshalb sind zwei Einträge mit demselben Wort nicht notwendig\nund würden die Filterqualität negativ beeinflussen.\r\n\r\nSoll die Wortkombination dennoch hinzugefügt werden?
+[(You can use normal words and Regular Expressions)]
+(Reguläre Ausdrücke und normale Wörter sind möglich)
+[(Regular Expressions disabled)]
+(Reguläre Ausdrucke sind deaktiviert)
+[Syntax Error in Regular Expression]
+Syntaxfehler in Regulärem Ausdruck (RegExp)
+[The entered phrase \"%s\" consists of a Regular Expression containing a syntax error!\r\n\r\nAdding an malformed Regular Expression will cause the filter\r\nto treat the expression as a normal phrase.\r\n\r\nShall the malformed Regular Expression be added anyway?]
+Die eingegebene Wortkombination enthält einen ungültigen Regulären Ausdruck (RegExp)!\r\nEin ungültiger Regulärer Ausruck wird vom Filter als normale Wortkombination angesehen.\r\n\r\nSoll der ungültige Reguläre Ausruck trotzdem hinzugefügt werden?
+
+; Options: Regular Expressions Help Section
+[Quick Quide to Regular Expressions:]
+Schnellanleitung für Reguläre Ausdrücke:
+[Alternatives:]
+Alternativen:
+[gilbert|sullivan finds \"gilbert\" or \"sullivan\"]
+gilbert|sullivan findet \"gilbert\" oder \"sullivan\"
+[Subpatterns]
+Unterausdrücke:
+[cat(aract|erpillar) finds "cataract\" or \"caterpillar\"]
+cat(aract|erpillar) findet "cataract\" oder \"caterpillar\"
+[Repetitions]
+Wiederholungen:
+[z{2,4} matches \"zz\", \"zzz\" or \"zzzz\"\r\n* is equivalent to {0,}\r\n+ is equivalent to {1,}\r\n? is equivalent to {0,1}]
+z{2,4} findet \"zz\", \"zzz\" oder \"zzzz\"\r\n* ist gleichbedeutend mit {0,}\r\n+ ist gleichbedeutend mit {1,}\r\n? ist gleichbedeutend mit {0,1}
+[Generic Types:]
+Allgemeine Typen:
+[\\d represents any decimal digit\r\n\\D represents any character that is not a decimal digit]
+\\d steht für irgendeine Dezimalzahl\r\n\\D steht für irgendein nicht-numerisches Zeichen
+[Assertions:]
+Zusicherungen:
+[(?<!foo)bar finds an occurrence of \"bar\" that is not preceded by \"foo\"\r\n(?<= for positive lookahead assertions\r\n(?<! for negative lookahead assertions\r\n(?= for positive lookbehind assertions\r\n(?! for negative lookbehind assertions]
+(?<!foo)bar findet ein Vorkommen von \"bar\" dem nicht \"foo\" vorangestellt ist\r\n(?<= steht für postiv vorausblickend\r\n(?<! steht für negativ vorausblickend\r\n(?= steht für positiv rückblickend\r\n(?! steht für negativ rückblickend
+[Back References:]
+Rückverweise:
+[(sens|respons)e and \\1ibility matches \"sense and sensibility\" and \"response and responsibility\", but not \"sense and responsibility\"]
+(sens|respons)e and \\1ibility findet \"sense and sensibility\" und \"response and responsibility\", jedoch nicht \"sense and responsibility\"
+[All syntax elements can be combined or nested using subpatterns.]
+Syntaxelemente können mithilfe von Unterausdrücken kombiniert werden.
+
+; Options: Spam Definitions Info Dialog
+[Spam Definitions: Advertisment Filter]
+Spam-Definitionen: Werbung-Filter
+[Advertisment Filter]
+Werbung-Filter
+[Spam Definitions: Disliked Messages Filter]
+Spam-Definitionen: Unerwünschte Nachrichten-Filter
+[Disliked Messages Filter]
+Unerwünschte Nachrichten-Filter
+[Installed Spam Definitions for filter:]
+Installierte Spam-Definitionen für den Filter:
+[Not Available]
+Nicht verfügbar
+[Packet]
+Paket
+;[Version]
+[Type]
+Typ
+[Default Bad Words]
+Standard Spam-Wörter
+[Known Spammers]
+Bekannte Spam-Versender
+[Known Teaser Texts]
+Bekannte Lock-Texte
+[URL Recognition]
+[URL-Erkennung
+[Phone Number Recognition]
+Telefonnummer-Erkennung
+[Different Format Recognition: Text]
+Format-Erkennung: Text
+[Different Format Recognition: Numbers]
+Format-Erkennung: Nummern
+[Different Format Recognition: Separators]
+Format-Erkennung: Trennzeichen
+[Known Hoax Texts]
+Bekannte Hoax-Texte
+[Download more Spam Definitions]
+Weitere Spam-Definitionen herunterladen
+;[http://addons.miranda-im.org/index.php?action=display&id=92]
+[&Remove...]
+&Entfernen...
+
+; Options: Spam Definitions Info
+[Spam &Definitions]
+Spam-&Definitionen
+[Warning: Not all Spam Definitions are available!\r\nSome features are disabled.]
+Achtung: Nicht alle Spam-Definitionen sind verfügbar!\r\nEinige Optionen sind deaktiviert.
+
+; Spam Definitions Info File Names
+; (Strings prepended by "Spam Definitions:" are used by Updater plugin)
+[Common]
+Allgemein
+[English]
+Englisch
+[Spam Definitions: English]
+Spam-Definitionen: Englisch
+[Chinese]
+Chinesisch
+[Spam Definitions: Chinese]
+Spam-Definitionen: Chinesisch
+[French]
+Französisch
+[Spam Definitions: French]
+Spam-Definitionen: Französisch
+[Portuguese]
+Portugiesisch
+[Spam Definitions: Portuguese]
+Spam-Definitionen: Portugiesisch
+[Russian]
+Russisch
+[Spam Definitions: Russian]
+Spam-Definitionen: Russisch
+
+; Options: Variables Button
+[Use variables in the message text]
+Variablen im Nachrichtentext verwenden
+
+; Options: Ignore Options Extension
+[You chose to ignore all kind of messages from contacts that are not on your contact list although the spam filter is activated.\r\n\r\nShall the spam filter be deactivated?]
+Nachrichten von Benutzern, die nicht auf der Kontaktliste sind, werden im Moment ignoriert.\r\nJedoch ist der Spam-Filter aktiviert, der unerwünschte Nachrichten von solchen Benutzern herausfiltert.\r\n\r\nSoll der Spam-Filter deaktiviert werden?
+
+; ---
+
+
+
+String History:
+-----------------------------
+ 2.5.2.1: Added: [&Hide users until they identified themselves]
+ Changed: [The filter has detected %u malformed Regular Expression(s) in the default bad words!\r\n\r\n
+ Please update your installed Spam Definitions to the latest versions to resolve this problem.\r\n
+ This problem might also be caused by Spam Definitions that are not compatible
+ with your system codepage.]
+ To: [The filter has detected %u malformed Regular Expression(s) in the default bad words!\r\n\r\n
+ Please update your installed Spam Definitions to the latest versions to resolve this problem.\r\n
+ This problem might also be caused by Spam Definitions that are not compatible
+ with your system codepage.
+ Changed: [The entered phrase \"%s\" consists of a Regular Expression which is invalid!\r\n\r\n
+ Adding an invalid Regular Expression will cause the filter\r\nto treat the expression as a normal
+ phrase.\r\n\r\nShall the invalid Regular Expression be added anyway?]
+ To: [The entered phrase \"%s\" consists of a Regular Expression containing a syntax error!\r\n\r\n
+ Adding an malformed Regular Expression will cause the filter\r\nto treat the expression as a normal
+ phrase.\r\n\r\nShall the malformed Regular Expression be added anyway?]
+ Changed: [(You can use the substitute symbols %sender%, %message%, %triesleft% and
+ %securitycode% for the appropriate contents.)]
+ To: [(You can use the substitute symbols %sender%, %message%, %securitycode% and
+ %triesleft% for the appropriate contents.)]
+ Changed: [Hello %sender%,\r\nPlease reply \"%securitycode%\" if you want to contact me.
+ If you don't reply correctly your messages will be ignored. Sorry for the inconvenience.\r\n
+ This spam check quarantees that you are a real person.\r\nYou have %triesleft% tries left.\r\n
+ Thanks in advance!\r\n\r\n(Automated message)]
+ To: [Hello %sender%,\r\nPlease reply \"%securitycode%\" if you want to contact me.
+ If you don't reply correctly your messages will be ignored. Sorry for the inconvenience.\r\n
+ This spam check guarantees that you are a real person.\r\nYou have %triesleft% tries left.\r\n
+ Thanks in advance!\r\n\r\n(Automated message)]
+
+ 2.5.2.0:
+ Added: [unlimited]
+ Changed: [Please reply \"%securitycode%\" if you want to contact me. If you don't reply correctly your
+ messages will be ignored. Sorry for the inconvenience.\r\n\r\n(Automated message)]
+ To: [Hello %sender%,\r\nPlease reply \"%securitycode%\" if you want to contact me.
+ If you don't reply correctly your messages will be ignored. Sorry for the inconvenience.\r\n
+ This spam check quarantees that you are a real person.\r\nYou have %triesleft% tries left.\r\n
+ Thanks in advance!\r\n\r\n(Automated message)]
+ Changed: [Hi %sender%,\r\nI don't like such messages. Please don't send them to me
+ anymore.\r\nThanks.\r\n\r\n(Automated message)]
+ To: [Hello %sender%,\r\nI don't like such messages. Please don't send them to me anymore.\r\n
+ Thanks.\r\n\r\n(Automated message)]
+ Changed: [Thank you for your help!\r\nAll your previous messages were delivered.
+ New messages do now reach me directly without an additional spam check.\r\n\r\n
+ (Automated message)]
+ To: [Thank you for your help!\r\nAll your previous messages were delivered. New messages do reach
+ me directly, without an additional spam check.\r\n\r\n(Automated message)]
+ Changed: [Automatically &ignore spammers]
+ To: [Automatically &ignore spamming users]
+ Changed: [&Empty messages]
+ To: [Messages containing &spaces only]
+ Changed: [Contains spaces only]
+ To: [Is empty]
+ Changed: [Allowed &bad phrases until spam:]
+ To: [All&owed bad phrases until spam:]
+ Changed: [Recognize &different formating]
+ To: [Recogni&ze different formating]
+ Changed: [&Spammers]
+ To: [Spa&mmers]
+ Changed: [&Reset...]
+ To: [Rese&t...]
+ Changed: [&Mark spam messages as read]
+ To: [Mar&k spam messages as read]
+ Changed: [&Types of incoming events which shall be checked for spam:]
+ To: [Types of incoming e&vents which shall be checked for spam:]
+ Changed: [(You can use the substitute symbols %sender%, %message%and %securitycode%
+ for the appropriate contents.)]
+ To: [(You can use the substitute symbols %sender%, %message%, %triesleft% and %securitycode% for the appropriate contents.)]
+
+ * corrected some typos
+
+ 2.5.1.2:
+ Added: [Store spammers in special &group:]
+ Added: [Spammers]
+ Added: [&Unmark spammer]
+ Changed: [Normal]
+ To: [Accept]
+
+ 2.5.1.1:
+ Changed: [Spam Filter has detected %u incorrect Regular Expression(s) in the default bad words!\r\n\r\n
+ Please update your installed Spam Definitions to the latest versions to resolve this problem.]
+ To: [The filter has detected %u incorrect Regular Expression(s) in the default bad words!\r\n\r\n
+ Please update your installed Spam Definitions to the latest versions to resolve this problem.\r\n
+ This problem might also be caused by Spam Definitions that are not compatible
+ with your system codepage.]
+
+ 2.5.1.0:
+ Added: [Spammer Indicator]
+ Changed: [Spam Filter has detected a incorrect Regular Expression in the default bad words!\r\n\r\n
+ Please update your installed Spam Definitions to the latest versions to resolve this problem.]
+ To: [Spam Filter has detected %u incorrect Regular Expression(s) in the default bad words!\r\n\r\n
+ Please update your installed Spam Definitions to the latest versions to resolve this problem.]
+
+ * Corrected some typos
+
+ 2.5.0.0:
+ Added: [Use variables in the message text]
+ Added: [Disliked Contents]
+ Added: [Number of users currently being in danger of being ignored.]
+ Added: [Spam Filter Critical Error]
+ Added: [A very critical internal error was detected!\r\nProbably the filter will not work at all.\r\n\r\n
+ Please update to the most current version if this problem persists.]
+ Added: [Spam Filter was not able to send an automated message to the user \"%s\".]
+ Added: [Spam Filter Warning]
+ Added: [Spam Filter has detected a incorrect Regular Expression in the default bad words!\r\n\r\n
+ Please update your installed Spam Definitions to the latest versions to resolve this problem.]
+ Added: [(Regular Expressions disabled)]
+ Added [Spam &Definitions]
+ Added: [Warning: Not all Spam Definitions are available!\r\nSome features are disabled.]
+ Added: [Spam Definitions]
+ Added: [Installed Spam Definitions for filter:]
+ Added: [Download more Spam Definitions]
+ Added: [http://www.miranda-im.org/download/index.php?action=display&id=92]
+ Added: [Not available]
+ Added: [Packet]
+ Added: [Version]
+ Added: [Type]
+ Added: [Default Bad Words]
+ Added: [Known Spammers]
+ Added: [Known Teaser Texts]
+ Added: [URL Recognition]
+ Added: [Phone Number Recognition]
+ Added: [Different Format Recognition: Text]
+ Added: [Different Format Recognition: Numbers]
+ Added: [Different Format Recognition: Separators]
+ Added: [Known Hoax Texts]
+ Added: [Quick Quide to Regular Expressions:]
+ Added: [Alternatives:]
+ Added: [gilbert|sullivan finds \"gilbert\" or \"sullivan\"]
+ Added: [Subpatterns]
+ Added: [cat(aract|erpillar) finds "cataract\" or \"caterpillar\"]
+ Added: [Repetitions]
+ Added: [z{2,4} matches \"zz\", \"zzz\" or \"zzzz\"\r\n* is equivalent to {0,}\r\n
+ + is equivalent to {1,}\r\n? is equivalent to {0,1}]
+ Added: [Generic Types:]
+ Added: [\\d represents any decimal digit\r\n\\D represents any character that is not a decimal digit]
+ Added: [Assertions:]
+ Added: [(?<!foo)bar finds an occurrence of \"bar\" that is not preceded by \"foo\"\r\n
+ (?<= for positive lookahead assertions\r\n(?<! for negative lookahead assertions\r\n
+ (?= for positive lookbehind assertions\r\n(?! for negative lookbehind assertions]
+ Added: [Back References:]
+ Added: [(sens|respons)e and \\1ibility matches \"sense and sensibility\" and \"response and responsibility\",
+ but not \"sense and responsibility\"]
+ Added: [All syntax elements can be combined or nested using subpatterns.]
+ Added: [Spam Definitions]
+ Added: [Customized Words]
+ Added: [Spam Filter: Incoming Spam]
+ Added: [&Remove...]
+ Added: [Note: The filter gets invoked after the Advertisment Filter.\n
+ Thus, no instruction message will be sent in reply to advertisment messages.]
+ Added: [Spammers]
+ Added: [Ignored Spammers]
+ Added: [The following contacts were recognized as spammers:]
+ Added: [Spammer]
+ Added: [Normal]
+ Added: [Delete]
+ Removed: [ (Spammer)]
+ Removed: [Hoax Messages]
+ Removed: [Contains 1 disliked indicating phrase/pattern]
+ Removed: [Contains 1 bad phrase/pattern]
+ Removed: [Contains 1 differently formated bad phrase/pattern]
+ Removed: [Contains 1 good phrase/pattern]
+ Changed: [Contains %u disliked indicating phrases/patterns]
+ To: [Contains %u disliked indicating phrase(s)/pattern(s)]
+ Changed: [Contains %u bad phrases/patterns]
+ To: [Contains %u bad phrase(s)/pattern(s)]
+ Changed: [Contains %u differently formated bad phrases/patterns]
+ To: [Contains %u differently formated bad phrase(s)/pattern(s)]
+ Changed: [Date;Time;User;Type;Recognition;Result;Message\r\n]
+ To: ["Date";"Time";"User";"Type";"Recognition";"Result";"Message"\r\n]
+ Changed: [Contains %u good phrases/patterns]
+ To: [Contains %u good phrase(s)/pattern(s)]
+ Changed: [The log file \"%s\"\r\ncan not be written.\r\n\r\nPlease check the settings.]
+ To: [The log file \"%s\"\r\ncan not be written.\r\n\r\nProblem:\r\n%s\r\nPlease check the settings.]
+ Changed: [Manually set as spam]
+ To: [Contact set as spammer]
+ Changed: [Look for &known hoax message texts]
+ To: [Messages containing &known hoax message texts]
+
+ * Corrected some typos
+
+ 2.1.0.0:
+ Changed: [&Scramble]
+ To: [&Random]
+ Added: [Added Notification]
+ Added: [Is hoax text]
+
+ 2.0.2.0:
+ Added: [Syntax Error in Regular Expression]
+ Added: [The entered phrase \"%s\" consists of a Regular Expression which is invalid!\r\n\r\n
+ Adding an invalid Regular Expression will cause the filter\r\nto treat the expression as a normal
+ phrase.\r\n\r\nShall the invalid Regular Expression be added anyway?]
+ Added: [Robot message]
+ Changed: [ (spammer)]
+ To: [ (Spammer)]
+
+ * Renamed most occurences of "phrase" to "phrase/pattern" (only in captions): Compare with German strings.
+ * Changed all occurences of a simple "\n" to "\r\n": Compare with German strings.
+
+ 2.0.1.0:
+ Added: [Spam Message Overlay]
+ Added: [&Reset]
+ Added: [(You can use readable words, too.)]
+ Added: [abcdefghkmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWXYZ23456789]
+ Added: [Reset Auto Ignore]
+ Added: [Are you sure that the Auto Ignore counters shall really be reset?\r\n\r\n
+ Spammers that are not already ignored will then not be remembered any longer.]
+ Changed: [A&llowed phrases until no spam:]
+ To: [Allo&wed phrases until no spam:]
+ Changed: [Allo&wed bad phrases until spam:]
+ To: [Allowed &bad phrases until spam:]
+ Changed: [&Mark contact as spammer]
+ To: [&Mark as spammer]
+ Changed: [Filters out advertisments, robot messages and disliked messages such as hoax messages..]
+ To: [Filters out advertisment messages, messages sent by robots and
+ other disliked messages such as hoax messages.]
+ Changed: [Are you sure that the contact \"%s\" shall be marked as spammer?]
+ To: [Are you sure that the contact \"%s\" shall be marked as spammer?\r\n\r\n
+ Marking a contact as spammer means that all associated messages will be recognized as spam
+ and the contact will be completely ignored in the future.\r\n\r\nA contact that was marked as
+ spammer can be unignored using the options dialog.]
+
+ 2.0.0.0:
+ * The strings have been changed almost completely.
+ Please compare with German strings.
+
+ 1.0.4.0:
+ Added: [User is known as spammer]
+ Added: [The entered bad word \"%s\" already exists in the list.\r\n\r\n
+ The filter doesn't differentiate between lower and upper case.\n
+ Two entries with the same word are not neccessary\nand would decrease the filter quality.]
+ Added: [Add contact to spammer list...]
+ Added: [Spamer Remembering]
+ Added: [Filter out all messages from users who once sent spam]
+ Added: [Notify users that they are blocked]
+ Added: [Hey %s, I don't like spam!\nYou are now put onto the spammers list to stop
+ you from sending me such things.\r\n\r\nHere is your message back:\n%s\r\n\r\n\n
+ (Automated message)]
+ Added: [Text Files]
+ Removed: [Contact kept]
+
+
+ 1.0.3.1:
+ Added: [The entered bad word \"%s\" already exists in the default bad words list.\r\n\r\n
+ Allowing the same bad words in the customized bad words list would\n
+ pull down the filter quality.\r\n\r\nShould the bad word be added anyway?]
+ Added: [Enable &spam popups]
+ Added: [Disable &spam popups]
+ Added: [Type:]
+ Changed: [Website adresses are bad]
+ To: [Website addresses are bad]
+ Changed: [(URL)]
+ To: [URL]
+ Changed: [(Message)]
+ To: [Message]
+
+
+ 1.0.3.0:
+ Added: [(URL)]
+ Added: [(Message)]
+ Removed: [Is URL]
+ Removed: [Spam Contacts]
+ Changed: [Result:\t]
+ To: [Result:]
+ Changed: [Recognition:\t]
+ To: [Recognition:]
+ Changed: [Bad words (%u)]
+ To: [Contains &u bad words]
+ Changed: [Urls are bad]
+ To: [Website addresses are bad]
+ Changed: [Bad Messages]
+ To: [Bad Contents]
+ Changed: [Additionally look for standard bad words in bad messages]
+ To: [Look for standard bad words in received messages]
+
+ 1.0.2.3:
+ Added: [If you have any bad words that could be added to the default list please mail them to me.\n
+ So that the filter can be further improved. This would be very nice.]
+ Added: [User:]
+ Added: [Result:\t]
+ Changed: [Recognition:]
+ To: [Recognition:\t]
+
+ 1.0.2.2:
+ Added: [Recognition:]
+ Added: [Contains only one word]
+ Added: [Contains URL]
+ Added: [Is URL]
+ Added: [Bad words (%u)]
+ Added: [Message deleted]
+ Added: [Message marked read]
+ Added: [Contact deleted]
+ Added: [Contact kept]
+ Removed: [(Found Bad Words: %u)]
+ Removed: [HH:mm]
+ Removed: [dd/MM/yyyy]
+
+ 1.0.2.1:
+ Added: [Select Log File]
+ Added: [Log Files]
+ Added: [All Files]
+ Changed: [Logs\\Spam.log]
+ To: [Spam.log]
+
+ 1.0.2.0:
+ Added: [Spam Logging]
+ Added: [Log spam messages to file:]
+ Added: [Logs\\Spam.log]
+ Added: [HH:mm]
+ Added: [dd/MM/yyyy]
+ Added: [(Found Bad Words: %u)]
+
+ 1.0.1.0:
+ Added: [No spam filter]
+ Added: [Filter out spam messages from users that are not on the contact list (recommended)]
+ Added: [Spam Contacts]
+ Added: [Delete spamming contacts]
+ Removed: [Activate spam checking for users that are not on the contact list]
+
+ 1.0.0.1:
+ Changed: [Number of words until spam:]
+ To: [Allowed bad words until spam:]
+
+
+H. Herkenrath (hrathh at users.sourceforge.net) \ No newline at end of file
diff --git a/spamfilter/Extensions/InstallScript.xml b/spamfilter/Extensions/InstallScript.xml
new file mode 100644
index 0000000..af12df5
--- /dev/null
+++ b/spamfilter/Extensions/InstallScript.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<installscript>
+ <!-- Package description file for Miranda Installer (mir-files) -->
+ <info>
+ <name>Spam Filter</name>
+ <author>Heiko Herkenrath</author>
+ <version>2.5.2.3</version>
+ <type>Plugin</type>
+ </info>
+
+ <packageinfo>
+ <title>Main Plugin</title>
+ <file>spamfilter.dll</file>
+ <file>Spam Filter\FormatText\Common.sfc</file>
+ <file>Spam Filter\FormatNumbers\Common.sfc</file>
+ <file>Spam Filter\FormatSeparators\Common.sfc</file>
+ <file>Spam Filter\URLs\Common.sfc</file>
+ <file>Spam Filter\HoaxTexts\Empty.sft</file>
+ <file>Spam Filter\PhoneNumbers\Empty.sfc</file>
+ </packageinfo>
+
+ <packageinfo>
+ <title>Support for Unicode (recommended for Windows NT4/2000+)</title>
+ <file>spamfilterw.dll</file>
+ <optional/>
+ </packageinfo>
+
+<packageinfo>
+ <title>Spam Definitions: English (recommended)</title>
+ <file>Spam Filter\BadWords\English.sfw</file>
+ <file>Spam Filter\Teasers\English.sft</file>
+ <file>Spam Filter\URLs\English.sfc</file>
+ <optional/>
+ </packageinfo>
+
+ <packageinfo>
+ <title>Support for Regular Expressions (recommended)</title>
+ <file>pcre.dll</file>
+ <file>PCRE-License.txt</file>
+ <optional/>
+ </packageinfo>
+
+ <packageinfo>
+ <title>Documentation</title>
+ <file>SpamFilter-Readme.txt</file>
+ <file>SpamFilter-License.txt</file>
+ <file>SpamFilter-SDK.zip</file>
+ <optional/>
+ </packageinfo>
+
+ <packageinfo>
+ <title>Sounds</title>
+ <file>filtered1.wav</file>
+ <file>filtered2.wav</file>
+ <file>filtered3.wav</file>
+ <optional/>
+ </packageinfo>
+
+ <autorun>
+ <file>SpamFilter-Readme.txt</file>
+ <document/>
+ </autorun>
+</installscript>
diff --git a/spamfilter/Extensions/Spam Filter/BadWords/English.sfw b/spamfilter/Extensions/Spam Filter/BadWords/English.sfw
new file mode 100644
index 0000000..4715e2b
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/BadWords/English.sfw
@@ -0,0 +1,89 @@
+
+; English default bad words for "Spam Filter" plugin
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - Linefeeds/Tabs get converted to spaces in original text before checking
+; - Regular Expressions are possible
+; - ";" in front of a line marks the line as comment
+
+Adult
+Anal
+Animal
+Ass
+Balls
+Blowjob
+Buy
+Call
+Cam
+Chat
+Check
+Click
+Crack
+Cum
+Cute
+Date
+Dating
+Dollar
+Download
+Dialer
+Earn
+Enter Now
+Erotic
+Free
+Frenulum
+Fuck
+Gang bang
+Gay
+Girl
+Guarantee
+Go to
+Hardcore
+Hentai
+Hot
+Huge selection
+Join
+Lesbian
+Login
+Lolita
+Lowest Price
+Make money
+Money back
+Natural talent
+No cost
+Offer
+Oral
+Order
+Orgasm
+Over 18
+Over 21
+Password
+Penetration
+Penis
+Pic
+Porn
+private number
+Promotion
+Prostate
+Pussy
+Rape
+Register
+Sell
+Sex
+Site
+Slut
+Stimulation
+Subscribe
+Talk to me
+Teen
+Tits
+Toy
+Try it now
+Use Our Service
+Viagra
+Vibro
+Video
+Visit
+Warez
+XXX
+100% free
+$$$
diff --git a/spamfilter/Extensions/Spam Filter/FormatNumbers/Common.sfc b/spamfilter/Extensions/Spam Filter/FormatNumbers/Common.sfc
new file mode 100644
index 0000000..24221dc
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/FormatNumbers/Common.sfc
@@ -0,0 +1,16 @@
+
+; Special Number Translation
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
+; - some spammers use different characters to cover up the content (-> translation into normal characters)
+
+; Format:
+;From
+;To
+
+I
+1
+
+O
+0
diff --git a/spamfilter/Extensions/Spam Filter/FormatSeparators/Common.sfc b/spamfilter/Extensions/Spam Filter/FormatSeparators/Common.sfc
new file mode 100644
index 0000000..3cef71a
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/FormatSeparators/Common.sfc
@@ -0,0 +1,13 @@
+
+; Special Separator Translation
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
+; - some spammers use different characters to cover up the content (-> translation into normal characters)
+
+; Format:
+; Separator
+
+\
+/
+
diff --git a/spamfilter/Extensions/Spam Filter/FormatText/Common.sfc b/spamfilter/Extensions/Spam Filter/FormatText/Common.sfc
new file mode 100644
index 0000000..167ce05
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/FormatText/Common.sfc
@@ -0,0 +1,28 @@
+
+; Special Char Translation
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - some spammers use different characters to cover up the content (-> translation into normal characters)
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
+
+; Format:
+;From
+;To
+
+\/
+v
+
+!
+i
+
+@
+a
+
+î
+i
+
+1
+I
+
+0
+O
diff --git a/spamfilter/Extensions/Spam Filter/HoaxTexts/Empty.sft b/spamfilter/Extensions/Spam Filter/HoaxTexts/Empty.sft
new file mode 100644
index 0000000..4508c7e
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/HoaxTexts/Empty.sft
@@ -0,0 +1,7 @@
+
+; Currently empty
+; Add here message texts, one per line, multiple files allowed
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - Linefeeds/Tabs get converted to spaces in original text before checking
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
diff --git a/spamfilter/Extensions/Spam Filter/PhoneNumbers/Empty.sfc b/spamfilter/Extensions/Spam Filter/PhoneNumbers/Empty.sfc
new file mode 100644
index 0000000..5d395ca
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/PhoneNumbers/Empty.sfc
@@ -0,0 +1,13 @@
+
+; Currently empty
+; Add here phone number formats
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - "#" are placeholders for digits
+; - Backslashs get converted to normal slashs
+; - Spaces get removed in original text before checking
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
+
+; Example:
+;+#(###)#-###-###
+;Tel.: #####
diff --git a/spamfilter/Extensions/Spam Filter/Spammers/Empty.sfc b/spamfilter/Extensions/Spam Filter/Spammers/Empty.sfc
new file mode 100644
index 0000000..0f097a4
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/Spammers/Empty.sfc
@@ -0,0 +1,9 @@
+
+; Well known spammers
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
+; - Format: <protocol>:<uniqueid> or <protocol>:<nick>
+; - Nick will only be checked for if it is available
+
+;ICQ:ExampleNick
diff --git a/spamfilter/Extensions/Spam Filter/Teasers/English.sft b/spamfilter/Extensions/Spam Filter/Teasers/English.sft
new file mode 100644
index 0000000..4b1006f
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/Teasers/English.sft
@@ -0,0 +1,9 @@
+
+; English default teaser messages for "Spam Filter" plugin
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - Linefeeds/Tabs get converted to spaces in original text before checking
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
+
+This is santa clause
+U have photo? ;)
diff --git a/spamfilter/Extensions/Spam Filter/URLs/Common.sfc b/spamfilter/Extensions/Spam Filter/URLs/Common.sfc
new file mode 100644
index 0000000..bb596d9
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/URLs/Common.sfc
@@ -0,0 +1,64 @@
+
+; Common elements that can't be associated to any language/country
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - Linefeeds/Tabs get converted to spaces in original text before checking
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
+; - The search for URL items is exactly the same as for bad words (but only if URL filtering is activated)
+
+; Prefixes
+http:
+https:
+;ftp:
+;gopher:
+;news:
+mailto:
+ed2k:
+www.
+ftp.
+
+; Extensions
+;.php
+;.htm
+;.html
+
+; All are eligible to register
+.com
+.net
+.info
+
+; Noncommercial community, all are eligible to register
+.org
+
+; Degree-granting educational institutions of higher education
+;.edu
+
+; United States Government
+;.gov
+
+; United States Military
+;.mil
+
+; Organizations established by international treaties
+;.int
+
+; Members of the air-transport industry
+;.aero
+
+; Businesses
+.biz
+
+; Cooperative associations
+.coop
+
+; Museums
+;.museum
+
+; Individuals
+.name
+
+; Credentialed professionals
+.pro
+
+; European Union (not yet)
+;.eu
diff --git a/spamfilter/Extensions/Spam Filter/URLs/English.sfc b/spamfilter/Extensions/Spam Filter/URLs/English.sfc
new file mode 100644
index 0000000..1aa6ade
--- /dev/null
+++ b/spamfilter/Extensions/Spam Filter/URLs/English.sfc
@@ -0,0 +1,14 @@
+
+; English url tld
+
+; - File must be saved as UTF-8 encoded (Unicode)
+; - Linefeeds/Tabs get converted to spaces in original text before checking
+; - ";" in front of a line marks the line as a comment (place a "\" in front of ";" to un-comment it)
+; - The search for URL items is exactly the same as for bad words (but only if URL filtering is activated)
+
+; Australia
+.at
+
+; England
+.co.uk
+;.gov.uk
diff --git a/spamfilter/Extensions/filtered1.wav b/spamfilter/Extensions/filtered1.wav
new file mode 100644
index 0000000..056e611
--- /dev/null
+++ b/spamfilter/Extensions/filtered1.wav
Binary files differ
diff --git a/spamfilter/Extensions/filtered2.wav b/spamfilter/Extensions/filtered2.wav
new file mode 100644
index 0000000..48b62cf
--- /dev/null
+++ b/spamfilter/Extensions/filtered2.wav
Binary files differ
diff --git a/spamfilter/Extensions/filtered3.wav b/spamfilter/Extensions/filtered3.wav
new file mode 100644
index 0000000..3a17215
--- /dev/null
+++ b/spamfilter/Extensions/filtered3.wav
Binary files differ
diff --git a/spamfilter/Info_Src.txt b/spamfilter/Info_Src.txt
new file mode 100644
index 0000000..6f0c1f4
--- /dev/null
+++ b/spamfilter/Info_Src.txt
@@ -0,0 +1,111 @@
+
+Spam Filter 2.5.2.3 for Miranda IM 0.4.3+
+------------------------------------------------------------------------
+ Source Code
+
+Reminder:
+"Spam Filter" is released under the terms of the GNU General Public License.
+See "Spam Filter-License.txt" for more details.
+"Spam Filter" is copyright 2003-2005 by Heiko Herkenrath.
+
+
+Please notify me of any changes that improve
+"Spam Filter" or add new features.
+If you have any questions on the code, feel free
+to contact me at my email address.
+
+ H. Herkenrath (hrathh at users.sourceforge.net)
+
+
+Notes
+------------------------------------------------------------------------
+
+## Version ###############################
+
+The following files need to be changed to bump the version number:
+
+Info_Src.txt (1 place)
+version.h (1 place)
+version.rc (5 places)
+m_spamfilter.h (1 place)
+m_spamfilter.inc (1 place)
+Extensions\InstallScript.xml (1 place)
+Docs\SpamFilter-Readme.txt (3 places)
+Docs\SpamFilter-Translation.txt (2 or 3 places)
+Docs\SpamFilter-Developer.txt (1 place)
+
+
+## Unicode ##############################
+TCHAR is a generic type for strings that maps to a WCHAR if compiled as Unicode
+or to a regular char when compiled as ANSI.
+
+All WinAPI functions arre mapped automatically to Unicode/ANSI.
+iIf not called directly via their A or W prefixes.
+
+C-Runtime functions need to be used in their _wcs* form to be mapped (ANSI/Unicode)
+
+UnicoWS function overriding example:
+static UINT __stdcall MyOleUIInsertObjectW (LPOLEUIINSERTOBJECTW lpouiiow)
+{
+ UINT result = OLEUI_CANCEL;
+ OLEUIINSERTOBJECTA ouiioa;
+ memcpy(&ouiioa, lpouiiow, sizeof (OLEUIINSERTOBJECTA));
+ ouiioa.lpszFile = (char *)alloca (ouiioa.cchFile + 1);
+ ouiioa.lpszFile [0] = '\0';
+ result = OleUIInsertObjectA(&ouiioa);
+
+ if (result == OLEUI_SUCCESS)
+ {
+ memcpy(lpouiiow,
+ &ouiioa,
+ sizeof(OLEUIINSERTOBJECTW));
+ MultiByteToWideChar(CP_ACP,
+ 0,
+ ouiioa.lpSzFile,
+ ouiioa.cchFile,
+ lpowiiow->lpszFile,
+ lpowiiow->cchFile);
+ }
+ return result;
+}
+extern "C" FARPROC Unicows_OleUIInsertObjectW = (FARPROC)&MyOleUIInsertObjectW;
+
+
+## Dialogs #############################
+Use always for dialogs:
+type: DIALOGEX
+font: MS Shell Dlg
+styles : DS_SHELLFONT | WS_VISIBLE | ...
+
+NO use of: DS_3DLOOK or DISCARDABLE (deprecated)
+DS_SYSMODAL -> WS_EX_TOPMOST
+DS_FIXEDSYS -> DS_SHELLFONT
+
+Warning: WS_EX_RTLREADING works also on non-arabic systems (Win 2000/XP)
+
+No use of SetFocus(), instead:
+PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)hwndCtrl, (LPARAM)TRUE);
+
+For dialogs use DWLP_USER instead of GWLP_USERDATA.
+
+WM_USER+n messages are defined by the owner of the class (RegisterClass); 0<=n<=31000
+WM_APP+n messages are defined by the owner of the window (CreateWindow); 0<=n<=16000
+
+WM_CLOSE msg is transformed into an IDCANCEL of WM_COMMAND
+
+
+## Sounds ##############################
+
+Sound files should be converted into a
+WAV-file, PCM format, 22,050Hz, 16bit, Stereo.
+
+
+## Other ################################
+
+Compiler Warnings at level 4 are sometimes too restrictive:
+C4100: http://msdn2.microsoft.com/en-us/library/26kb9fy0
+
+Random stuff a always tend to forget ;-)
+
+o No use of static variables in DlgProcs/WindowProcs
+o ^= operator is the inverted operator |=
diff --git a/spamfilter/License_Appendix.txt b/spamfilter/License_Appendix.txt
new file mode 100644
index 0000000..22e70a9
--- /dev/null
+++ b/spamfilter/License_Appendix.txt
@@ -0,0 +1,67 @@
+
+Excecpt of GNU General Public License (Appendix):
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+GNU General Public License:
+For license conditions see "SpamFilter-License.txt".
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/spamfilter/Resources/add_button_16x16_256.ico b/spamfilter/Resources/add_button_16x16_256.ico
new file mode 100644
index 0000000..530923d
--- /dev/null
+++ b/spamfilter/Resources/add_button_16x16_256.ico
Binary files differ
diff --git a/spamfilter/Resources/delete_button_16x16_256.ico b/spamfilter/Resources/delete_button_16x16_256.ico
new file mode 100644
index 0000000..6259321
--- /dev/null
+++ b/spamfilter/Resources/delete_button_16x16_256.ico
Binary files differ
diff --git a/spamfilter/Resources/log_item.xml b/spamfilter/Resources/log_item.xml
new file mode 100644
index 0000000..22aac67
--- /dev/null
+++ b/spamfilter/Resources/log_item.xml
@@ -0,0 +1,11 @@
+ <item>
+ <date>%log_date%</date>
+ <time>%log_time%</time>
+ <user>%log_user%</user>
+ <type>%log_type%</type>
+ <recognition>%log_recognition%</recognition>
+ <result>%log_result%</result>
+ <message>%log_message%</message>
+ </item>
+
+</spamlog>
diff --git a/spamfilter/Resources/log_layout.xsl b/spamfilter/Resources/log_layout.xsl
new file mode 100644
index 0000000..0e3196d
--- /dev/null
+++ b/spamfilter/Resources/log_layout.xsl
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:template match="/">
+
+<html>
+<head>
+<title>Received Spam</title>
+</head>
+
+<body bgcolor="#FFFFCC">
+
+<h3><u><font face="Arial" size="6" color="#484480">Received Spam</font></u></h3>
+<p align="right"><font face="Arial" color="#484480">Logged since <xsl:value-of select="spamlog/since"/></font></p>
+
+<xsl:for-each select="spamlog/item">
+<table border="1" cellspacing="1" gbcolor="#000000" bordercolor="#000000" style="width: 100%">
+ <tr>
+ <td bgcolor="#484480" width="97" bordercolor="#FFFFCC" height="7">
+ <font face="Arial" color="#FFFFCC"><b>Date/Time:</b></font></td>
+ <td bgcolor="#484480" width="880" bordercolor="#FFFFCC" height="7">
+ <font color="#FFFFCC" face="Arial"><xsl:value-of select="date"/></font> <font color="#FFFFCC" face="Arial"><xsl:value-of select="time"/></font></td>
+ </tr>
+ <tr>
+ <td bgcolor="#484480" width="97" bordercolor="#FFFFCC" height="7">
+ <font face="Arial" color="#FFFFCC"><b>User:</b></font></td>
+ <td bgcolor="#484480" width="880" bordercolor="#FFFFCC" height="7">
+ <font color="#FFFFCC" face="Arial"><xsl:value-of select="user"/></font></td>
+ </tr>
+ <tr>
+ <td bgcolor="#484480" width="97" bordercolor="#FFFFCC" height="7">
+ <font face="Arial" color="#FFFFCC"><b>Type:</b></font></td>
+ <td bgcolor="#484480" width="880" bordercolor="#FFFFCC" height="7">
+ <font color="#FFFFCC" face="Arial"><xsl:value-of select="type"/></font></td>
+ </tr>
+ <tr>
+ <td bgcolor="#484480" width="97" bordercolor="#FFFFCC" height="7">
+ <font face="Arial" color="#FFFFCC"><b>Recognition:</b></font></td>
+ <td bgcolor="#484480" width="880" bordercolor="#FFFFCC" height="7">
+ <font color="#FFFFCC" face="Arial"><xsl:value-of select="recognition"/></font></td>
+ </tr>
+ <tr>
+ <td bgcolor="#484480" width="97" bordercolor="#FFFFCC" height="7">
+ <font face="Arial" color="#FFFFCC"><b>Result:</b></font></td>
+ <td bgcolor="#484480" width="880" bordercolor="#FFFFCC" height="7">
+ <font color="#FFFFCC" face="Arial"><xsl:value-of select="result"/></font></td>
+ </tr>
+ <tr>
+ <td bgcolor="#000000" colspan="2" height="50" bordercolor="#FFFFCC">
+ <textarea readonly="1" style="width: 100%; height: 100%; color: #FFFFCC; font-family: Arial; border: 1px solid #000000; padding-left: 4; padding-right: 4; padding-top: 1; padding-bottom: 1; background-color: #000000" cols="20" rows="3" name="message"><xsl:value-of select="message"/></textarea></td>
+ </tr>
+</table>
+<p></p>
+</xsl:for-each>
+
+</body>
+
+</html>
+
+</xsl:template>
+</xsl:stylesheet>
diff --git a/spamfilter/Resources/log_main.xml b/spamfilter/Resources/log_main.xml
new file mode 100644
index 0000000..fda04c5
--- /dev/null
+++ b/spamfilter/Resources/log_main.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<?xml-stylesheet href="%log_xslfile%" type="text/xsl"?>
+
+<!DOCTYPE spamlog [
+ <!ELEMENT spamlog (since, item*)>
+ <!ELEMENT since (#PCDATA)>
+ <!ELEMENT item (date,time,user,type,recognition,result,message)>
+ <!ELEMENT date (#PCDATA)>
+ <!ELEMENT time (#PCDATA)>
+ <!ELEMENT user (#PCDATA)>
+ <!ELEMENT type (#PCDATA)>
+ <!ELEMENT recognition (#PCDATA)>
+ <!ELEMENT result (#PCDATA)>
+ <!ELEMENT message (#PCDATA)>
+ <!ENTITY nbsp " ">
+]>
+<!-- Generated by Spam Filter plugin -->
+
+<spamlog>
+
+ <since>%log_date%</since>
+
+</spamlog> \ No newline at end of file
diff --git a/spamfilter/Resources/spam_16x16_256.ico b/spamfilter/Resources/spam_16x16_256.ico
new file mode 100644
index 0000000..46f6bd3
--- /dev/null
+++ b/spamfilter/Resources/spam_16x16_256.ico
Binary files differ
diff --git a/spamfilter/Resources/spam_32x32_256.ico b/spamfilter/Resources/spam_32x32_256.ico
new file mode 100644
index 0000000..b727965
--- /dev/null
+++ b/spamfilter/Resources/spam_32x32_256.ico
Binary files differ
diff --git a/spamfilter/Resources/spam_definitions_16x16_256.ico b/spamfilter/Resources/spam_definitions_16x16_256.ico
new file mode 100644
index 0000000..1e99a38
--- /dev/null
+++ b/spamfilter/Resources/spam_definitions_16x16_256.ico
Binary files differ
diff --git a/spamfilter/Resources/spam_layer_16x16_256.ico b/spamfilter/Resources/spam_layer_16x16_256.ico
new file mode 100644
index 0000000..fce8bcc
--- /dev/null
+++ b/spamfilter/Resources/spam_layer_16x16_256.ico
Binary files differ
diff --git a/spamfilter/SpamFilter.dsp b/spamfilter/SpamFilter.dsp
new file mode 100644
index 0000000..7c67382
--- /dev/null
+++ b/spamfilter/SpamFilter.dsp
@@ -0,0 +1,802 @@
+# Microsoft Developer Studio Project File - Name="SpamFilter" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** NICHT BEARBEITEN **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=SpamFilter - Win32 Debug Unicode
+!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE
+!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl
+!MESSAGE
+!MESSAGE NMAKE /f "SpamFilter.mak".
+!MESSAGE
+!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben
+!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:
+!MESSAGE
+!MESSAGE NMAKE /f "SpamFilter.mak" CFG="SpamFilter - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Für die Konfiguration stehen zur Auswahl:
+!MESSAGE
+!MESSAGE "SpamFilter - Win32 Release" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "SpamFilter - Win32 Debug" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "SpamFilter - Win32 Release Unicode" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "SpamFilter - Win32 Debug Unicode" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "SpamFilter - Win32 Release Unicode 9x/ME" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "SpamFilter - Win32 Debug Unicode 9x/ME" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "SpamFilter - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Temp\Release"
+# PROP BASE Target_Dir "Release"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Temp\Release"
+# PROP Intermediate_Dir "Temp\Release"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir "Release"
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SPAMFILTER_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /O2 /I ".\Include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "STRICT" /D "SPAMFILTER_EXPORTS" /U "NO_STRICT" /FR /YX /FD /opt:nowin98 /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+# SUBTRACT MTL /mktyplib203 /Oicf
+# ADD BASE RSC /l 0x1009 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib comctl32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib shlwapi.lib ole32.lib version.lib /nologo /base:"" /dll /pdb:".\Release\spamfilter.pdb" /debug /machine:I386 /nodefaultlib:"uuid.lib" /nodefaultlib:"OLDNAMES" /out:".\Release\spamfilter.dll" /opt:nowin98 /ignore:4078 /RELEASE
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "SpamFilter - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Temp\Debug"
+# PROP BASE Intermediate_Dir "Temp\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Temp\Debug"
+# PROP Intermediate_Dir "Temp\Debug"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SPAMFILTER_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /Gi /GX /ZI /Od /I ".\Include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "STRICT" /D "SPAMFILTER_EXPORTS" /U "NO_STRICT" /FR /YX /FD /GZ /c
+# SUBTRACT CPP /WX
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# SUBTRACT MTL /Oicf
+# ADD BASE RSC /l 0x1009 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib comctl32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib shlwapi.lib ole32.lib version.lib /nologo /base:"" /dll /pdb:"C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilter.pdb" /map /debug /machine:I386 /nodefaultlib:"uuid.lib" /nodefaultlib:"OLDNAMES" /out:"C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilter.dll" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "SpamFilter - Win32 Release Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Temp\ReleaseW"
+# PROP BASE Target_Dir "ReleaseW"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Temp\ReleaseW"
+# PROP Intermediate_Dir "Temp\ReleaseW"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir "Release"
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SPAMFILTER_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /O2 /I ".\Include" /D "NDEBUG" /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "STRICT" /D "SPAMFILTER_EXPORTS" /U "_MBCS" /U "NO_STRICT" /FR /YX /FD /opt:nowin98 /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+# SUBTRACT MTL /mktyplib203 /Oicf
+# ADD BASE RSC /l 0x1009 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib comctl32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib shlwapi.lib ole32.lib version.lib /nologo /base:"" /dll /pdb:".\Release\spamfilterW.pdb" /debug /machine:I386 /nodefaultlib:"uuid.lib" /nodefaultlib:"OLDNAMES" /out:".\Release\spamfilterW.dll" /opt:nowin98 /ignore:4078 /RELEASE
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "SpamFilter - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Temp\DebugW"
+# PROP BASE Intermediate_Dir "Temp\DebugW"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Temp\DebugW"
+# PROP Intermediate_Dir "Temp\DebugW"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SPAMFILTER_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /Gi /GX /ZI /Od /I ".\Include" /D "_DEBUG" /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "STRICT" /D "SPAMFILTER_EXPORTS" /U "_MBCS" /U "NO_STRICT" /FR /YX /FD /GZ /c
+# SUBTRACT CPP /WX
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# SUBTRACT MTL /Oicf
+# ADD BASE RSC /l 0x1009 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib comctl32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib shlwapi.lib ole32.lib version.lib /nologo /base:"" /dll /pdb:"C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilterW.pdb" /map /debug /machine:I386 /nodefaultlib:"uuid.lib" /nodefaultlib:"OLDNAMES" /out:"C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilterW.dll" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none /nodefaultlib
+
+!ELSEIF "$(CFG)" == "SpamFilter - Win32 Release Unicode 9x/ME"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Temp\ReleaseW9x"
+# PROP BASE Target_Dir "ReleaseW9x"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Temp\ReleaseW9x"
+# PROP Intermediate_Dir "Temp\ReleaseW9x"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir "ReleaseW9x"
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SPAMFILTER_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W4 /GX /O2 /I ".\Include" /D "NDEBUG" /D "UNICOWS" /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "STRICT" /D "SPAMFILTER_EXPORTS" /U "_MBCS" /U "NO_STRICT" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+# SUBTRACT MTL /mktyplib203 /Oicf
+# ADD BASE RSC /l 0x1009 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 unicows.lib kernel32.lib comctl32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib shlwapi.lib ole32.lib version.lib /nologo /base:"" /dll /pdb:".\Release\spamfilterW9x.pdb" /debug /machine:I386 /nodefaultlib:"kernel32.lib" /nodefaultlib:"advapi32.lib" /nodefaultlib:"user32.lib" /nodefaultlib:"gdi32.lib" /nodefaultlib:"shell32.lib" /nodefaultlib:"comdlg32.lib" /nodefaultlib:"version.lib" /nodefaultlib:"mpr.lib" /nodefaultlib:"rasapi32.lib" /nodefaultlib:"winmm.lib" /nodefaultlib:"winspool.lib" /nodefaultlib:"vfw32.lib" /nodefaultlib:"secur32.lib" /nodefaultlib:"oleacc.lib" /nodefaultlib:"oledlg.lib" /nodefaultlib:"sensapi.lib" /nodefaultlib:"uuid.lib" /nodefaultlib:"OLDNAMES" /out:".\Release\spamfilterW9x.dll" /ignore:4078 /RELEASE
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "SpamFilter - Win32 Debug Unicode 9x/ME"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Temp\DebugW9x"
+# PROP BASE Intermediate_Dir "Temp\DebugW9x"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Temp\DebugW9x"
+# PROP Intermediate_Dir "Temp\DebugW9x"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SPAMFILTER_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /Gi /GX /ZI /Od /I ".\Include" /D "_DEBUG" /D "UNICOWS" /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "_WINDOWS" /D "_USRDLL" /D "STRICT" /D "SPAMFILTER_EXPORTS" /U "_MBCS" /U "NO_STRICT" /FR /YX /FD /GZ /c
+# SUBTRACT CPP /WX
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# SUBTRACT MTL /Oicf
+# ADD BASE RSC /l 0x1009 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 unicows.lib kernel32.lib comctl32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib shlwapi.lib ole32.lib version.lib /nologo /base:"" /dll /pdb:"C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilterW9x.pdb" /map /debug /machine:I386 /nodefaultlib:"kernel32.lib" /nodefaultlib:"advapi32.lib" /nodefaultlib:"user32.lib" /nodefaultlib:"gdi32.lib" /nodefaultlib:"shell32.lib" /nodefaultlib:"comdlg32.lib" /nodefaultlib:"version.lib" /nodefaultlib:"mpr.lib" /nodefaultlib:"rasapi32.lib" /nodefaultlib:"winmm.lib" /nodefaultlib:"winspool.lib" /nodefaultlib:"vfw32.lib" /nodefaultlib:"secur32.lib" /nodefaultlib:"oleacc.lib" /nodefaultlib:"oledlg.lib" /nodefaultlib:"sensapi.lib" /nodefaultlib:"uuid.lib" /nodefaultlib:"OLDNAMES" /out:"C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilterW9x.dll" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "SpamFilter - Win32 Release"
+# Name "SpamFilter - Win32 Debug"
+# Name "SpamFilter - Win32 Release Unicode"
+# Name "SpamFilter - Win32 Debug Unicode"
+# Name "SpamFilter - Win32 Release Unicode 9x/ME"
+# Name "SpamFilter - Win32 Debug Unicode 9x/ME"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\contact.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\dlg.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\extraimg.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\folders.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\forkthread.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\logfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\menuitems.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\path.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\regexp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\services.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\spamcheck.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\spamcheckdata.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\spamdefinitions.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\string.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\stringlist.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\trigger.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\unicode.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\update.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\utilities.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\variables.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\common.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\contact.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\extraimg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\folders.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\forkthread.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\logfile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\menuitems.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\path.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\popup.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\regexp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\services.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\spamcheck.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\spamcheckdata.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\spamdefinitions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\string.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\stringlist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\trigger.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\unicode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\update.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\utilities.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\variables.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\version.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\Resources\add_button_16x16_256.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\delete_button_16x16_256.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\log_item.xml
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\log_layout.xsl
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\log_main.xml
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\spam_16x16_256.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\spam_32x32_256.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\spam_definitions_16x16_256.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\spam_layer_16x16_256.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\SpamFilter.rc
+# End Source File
+# End Group
+# Begin Group "Documentation"
+
+# PROP Default_Filter "*.txt"
+# Begin Source File
+
+SOURCE=".\Docs\PCRE-License.txt"
+# End Source File
+# Begin Source File
+
+SOURCE=".\Docs\SpamFilter-Developer.txt"
+# End Source File
+# Begin Source File
+
+SOURCE=".\Docs\SpamFilter-License.txt"
+# End Source File
+# Begin Source File
+
+SOURCE=".\Docs\SpamFilter-Readme.txt"
+# End Source File
+# Begin Source File
+
+SOURCE=".\Docs\SpamFilter-Translation.txt"
+# End Source File
+# End Group
+# Begin Group "Extensions"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Extensions\filtered1.wav
+# End Source File
+# Begin Source File
+
+SOURCE=.\Extensions\filtered2.wav
+# End Source File
+# Begin Source File
+
+SOURCE=.\Extensions\filtered3.wav
+# End Source File
+# Begin Source File
+
+SOURCE=.\Extensions\InstallScript.xml
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_spamfilter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\m_spamfilter.inc
+# End Source File
+# End Group
+# Begin Group "Utilities"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Utilities\DebugHelper.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Utilities\Pcre\include\pcre.h
+# End Source File
+# End Group
+# Begin Group "Include"
+
+# PROP Default_Filter "c;h"
+# Begin Source File
+
+SOURCE=.\Include\m_addcontact.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_alarms.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_autoshutdown.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_avatars.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_awaymsg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_bkgrcfg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_button.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_chat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_clc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_clist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_clistint.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_clui.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_cluiframes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_colourpicker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_contactdir.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_contacts.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_contactsex.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_contactstransfer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_database.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_email.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_file.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_findadd.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_folders.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_fontservice.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_fuse.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_genmenu.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_history.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_httpserver.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_icolib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_icq.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_idle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_ieview.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_ignore.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_langpack.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_mathmodule.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_message.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_metacontacts.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_netlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_options.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_plugins.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_png.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_popup.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_popupw.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_protocols.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_protomod.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_protosvc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_sessions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_skin.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_skin_eng.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_smileyadd.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_snapping_windows.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_spamfilter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_statusplugins.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_system.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_toptoolbar.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_trigger.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_uninstaller.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_updater.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_url.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_userinfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_utils.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_variables.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\m_weather.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\newpluginapi.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\statusmodes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Include\win2k.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\Info_Src.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\License_Appendix.txt
+# End Source File
+# End Target
+# End Project
diff --git a/spamfilter/SpamFilter.dsw b/spamfilter/SpamFilter.dsw
new file mode 100644
index 0000000..99809ac
--- /dev/null
+++ b/spamfilter/SpamFilter.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "SpamFilter"=".\SpamFilter.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/spamfilter/SpamFilter.rc b/spamfilter/SpamFilter.rc
new file mode 100644
index 0000000..2f7f7cb
--- /dev/null
+++ b/spamfilter/SpamFilter.rc
@@ -0,0 +1,556 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <windows.h>
+#include <winres.h>
+#include <m_utils.h>
+#include <m_button.h>
+#include <m_clc.h>
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Englisch (USA) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_LISTBOX_ADD_ITEM DIALOGEX 0, 0, 240, 230
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_NOFAILCREATE |
+ WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Add %s"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "&New phrase/pattern:",IDC_TEXT_DESCRIPTION,5,5,120,8
+ EDITTEXT IDC_EDITBOX_NEWITEM,5,15,158,12,ES_AUTOHSCROLL |
+ ES_NOHIDESEL | WS_GROUP
+ CONTROL "",IDC_BUTTON_REGEXP,"MButtonClass",WS_TABSTOP,165,15,13,
+ 12,0x18000000L
+ LTEXT "(You can use normal words or Regular Expressions)",
+ IDC_TEXT_REGEXP,7,29,176,8
+ DEFPUSHBUTTON "OK",IDOK,185,6,50,14,WS_DISABLED
+ PUSHBUTTON "Cancel",IDCANCEL,185,23,50,14
+ CONTROL "",IDC_RECT_SEPARATOR,"Static",SS_ETCHEDHORZ,0,43,246,1,
+ WS_EX_STATICEDGE
+ LTEXT "Quick Quide to Regular Expressions:",IDC_TEXT_QUICKHELP,
+ 5,48,223,10
+ LTEXT "Alternatives:",IDC_STATIC,5,60,60,8
+ LTEXT "gilbert|sullivan finds ""gilbert"" or ""sullivan""",
+ IDC_STATIC,72,60,163,10
+ LTEXT "Subpatterns:",IDC_STATIC,5,70,60,8
+ LTEXT "cat(aract|erpillar) finds ""cataract"" or ""caterpillar""",
+ IDC_STATIC,72,70,163,8
+ LTEXT "Repetitions:",IDC_STATIC,5,80,60,8
+ LTEXT "z{2,4} matches ""zz"", ""zzz"" or ""zzzz""\r\n* is equivalent to {0,}\r\n+ is equivalent to {1,}\r\n? is equivalent to {0,1}",
+ IDC_STATIC,72,80,163,34
+ LTEXT "Generic Types:",IDC_STATIC,5,114,60,8
+ LTEXT "\\d represents any decimal digit\r\n\\D represents any character that is not a decimal digit",
+ IDC_STATIC,72,114,163,26
+ LTEXT "Assertions:",IDC_STATIC,5,140,60,8
+ LTEXT "(?<!foo)bar finds an occurrence of ""bar"" that is not preceded by ""foo""\r\n(?<= for positive lookahead assertions\r\n(?<! for negative lookahead assertions\r\n(?= for positive lookbehind assertions\r\n(?! for negative lookbehind assertions",
+ IDC_STATIC,72,140,163,51
+ LTEXT "Back References:",IDC_STATIC,5,191,60,8
+ LTEXT "(sens|respons)e and \\1ibility matches ""sense and sensibility"" and ""response and responsibility"", but not ""sense and responsibility""",
+ IDC_STATIC,72,191,163,26
+ LTEXT "All syntax elements can be combined or nested using subpatterns.",
+ IDC_TEXT_HELPNESTED,5,218,230,8
+END
+
+IDD_OPTIONS_CONFIGURE_ADVERTISMENTFILTER DIALOG DISCARDABLE 0, 0, 345, 264
+STYLE DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+CAPTION "Configure Advertisment Filter"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX "Bad Phrases/Patterns",IDC_GROUP_BADWORDS,7,7,160,173
+ CONTROL "Look for standard &bad phrases in received messages",
+ IDC_CHECKBOX_DEFAULTBADWORDS,"Button",BS_AUTOCHECKBOX |
+ BS_TOP | BS_MULTILINE | WS_GROUP | WS_TABSTOP,17,19,142,
+ 18
+ CONTROL "Also watch for following &customized bad phrases:",
+ IDC_CHECKBOX_CUSTOMIZEDBADWORDS,"Button",BS_AUTOCHECKBOX |
+ BS_TOP | BS_MULTILINE | WS_TABSTOP,17,39,142,16
+ LISTBOX IDC_LISTBOX_CUSTOMIZEDBADWORDS,29,59,112,69,NOT
+ LBS_NOTIFY | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT |
+ LBS_DISABLENOSCROLL | WS_VSCROLL | WS_HSCROLL
+ CONTROL "",IDC_BUTTON_ADDBADWORD,"MButtonClass",WS_TABSTOP,145,
+ 59,14,13,0x18000000L
+ CONTROL "",IDC_BUTTON_DELETEBADWORD,"MButtonClass",WS_TABSTOP,
+ 145,75,14,13,0x18000000L
+ LTEXT "All&owed bad phrases until spam:",
+ IDC_TEXT_ALLOWEDBADWORDS,18,136,112,8,NOT WS_GROUP
+ EDITTEXT IDC_EDITBOX_ALLOWEDBADWORDS,133,135,27,12,ES_AUTOHSCROLL |
+ ES_NUMBER
+ CONTROL "",IDC_SPIN_ALLOWEDBADWORDS,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,155,135,9,
+ 11
+ CONTROL "Recogni&ze different formating",IDC_CHECKBOX_FORMATING,
+ "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,17,151,
+ 146,8
+ CONTROL "Allowed &formated bad phrases:",
+ IDC_TEXT_ALLOWEDFORMATEDWORDS,"Static",SS_LEFTNOWORDWRAP,
+ 30,163,102,8
+ EDITTEXT IDC_EDITBOX_ALLOWEDFORMATEDWORDS,133,162,27,12,
+ ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_SPIN_ALLOWEDFORMATEDWORDS,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,155,162,9,
+ 11
+ GROUPBOX "Known Spammers",IDC_GROUP_SPAMMERS,7,187,160,38
+ CONTROL "Filter out messages from standard &known spammers",
+ IDC_CHECKBOX_DEFAULTSPAMMERS,"Button",BS_AUTOCHECKBOX |
+ BS_TOP | BS_MULTILINE | WS_GROUP | WS_TABSTOP,17,199,142,
+ 18
+ GROUPBOX "Bad Contents",IDC_GROUP_OTHERCONTENTS,177,7,160,86
+ CONTROL "&URL addresses",IDC_CHECKBOX_URL,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,187,21,148,8
+ CONTROL "&Phone numbers",IDC_CHECKBOX_PHONENUMBERS,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,187,32,148,8
+ CONTROL "C&apitalized messages",IDC_CHECKBOX_CAPITALIZED,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,187,43,148,8
+ CONTROL "Messages containing &spaces only",IDC_CHECKBOX_EMPTY,
+ "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,187,54,
+ 148,8
+ CONTROL "&Multiple messages with same content",
+ IDC_CHECKBOX_MULTIPLEMESSAGES,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,187,65,147,8
+ CONTROL "&Teaser messages",IDC_CHECKBOX_TEASERMESSAGES,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,187,76,147,8
+ GROUPBOX "Good Phrases/Patterns",IDC_GROUP_GOODWORDS,177,100,160,
+ 125
+ CONTROL "Messages are &no spam when containing some of the following phrases:",
+ IDC_CHECKBOX_CUSTOMIZEDGOODWORDS,"Button",
+ BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,187,
+ 112,142,17
+ LISTBOX IDC_LISTBOX_CUSTOMIZEDGOODWORDS,199,132,112,70,NOT
+ LBS_NOTIFY | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT |
+ LBS_DISABLENOSCROLL | WS_VSCROLL | WS_HSCROLL
+ CONTROL "",IDC_BUTTON_ADDGOODWORD,"MButtonClass",WS_TABSTOP,315,
+ 132,14,13,0x18000000L
+ CONTROL "",IDC_BUTTON_DELETEGOODWORD,"MButtonClass",WS_TABSTOP,
+ 315,148,14,13,0x18000000L
+ CONTROL "Allo&wed phrases until no spam:",
+ IDC_TEXT_ALLOWEDGOODWORDS,"Static",SS_LEFTNOWORDWRAP,188,
+ 208,112,8
+ EDITTEXT IDC_EDITBOX_ALLOWEDGOODWORDS,303,206,27,12,
+ ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_SPIN_ALLOWEDGOODWORDS,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,325,206,9,
+ 11
+ LTEXT "Warning: Not all Spam Definitions are available! Some features are disabled.",
+ IDC_TEXT_WARNING,18,229,318,10,NOT WS_VISIBLE
+ PUSHBUTTON "Spam &Definitions",IDC_BUTTON_SPAMDEFINITIONS,17,240,84,
+ 14
+ DEFPUSHBUTTON "OK",IDOK,221,240,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,279,240,50,14
+END
+
+IDD_OPTIONS_MAIN DIALOGEX 0, 0, 314, 240
+STYLE DS_FIXEDSYS | DS_NOFAILCREATE | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Spam Filter",IDC_STATIC,0,0,314,112
+ CONTROL "&Advertisment Filter (recommended)",
+ IDC_CHECKBOX_ADVERTISMENTFILTER,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,10,14,213,8
+ LTEXT "Filters out advertisments from unknown users that are not on the contact list.",
+ IDC_TEXT_ADVERTISMENTFILTER,22,24,201,17
+ PUSHBUTTON "C&onfigure...",IDC_BUTTON_ADVERTISMENTFILTER,237,22,61,
+ 14
+ CONTROL "&Robot Filter",IDC_CHECKBOX_ROBOTFILTER,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,45,213,8
+ LTEXT "Filters out all automated messages from unknown contacts via requesting a correct answer from the sender.",
+ IDC_TEXT_ROBOTFILTER,22,55,201,17
+ PUSHBUTTON "Co&nfigure...",IDC_BUTTON_ROBOTFILTER,237,53,61,14
+ CONTROL "&Disliked Messages Filter",
+ IDC_CHECKBOX_DISLIKEDMESSAGEFILTER,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,10,76,213,8
+ LTEXT "Filters out disliked messages such as hoax messages from known contacts that are on your contact list.",
+ IDC_TEXT_DISLIKEDMESSAGEFILTER,22,86,201,17
+ PUSHBUTTON "Con&figure...",IDC_BUTTON_DISLIKEDMESSAGEFILTER,237,84,
+ 61,14
+ GROUPBOX "Auto Ignore",IDC_GROUP_IGNORE,0,117,171,64
+ CONTROL "Automatically &ignore spamming users",
+ IDC_CHECKBOX_AUTOADDSPAMMERS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,7,129,158,10
+ CONTROL "Allowed spam per &user:",IDC_TEXT_ALLOWEDSPAMMESSAGES,
+ "Static",SS_LEFTNOWORDWRAP,20,142,108,8
+ EDITTEXT IDC_EDITBOX_ALLOWEDSPAMMESSAGES,131,140,32,12,
+ ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_SPIN_ALLOWEDSPAMMESSAGES,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,157,140,8,
+ 11
+ PUSHBUTTON "Rese&t...",IDC_BUTTON_RESETAUTOIGNORE,19,159,70,14,
+ WS_DISABLED
+ PUSHBUTTON "Spa&mmers",IDC_BUTTON_SHOWSPAMMERS,93,159,70,14
+ GROUPBOX "Spam Notification",IDC_GROUP_LOGGING,0,185,171,55
+ CONTROL "Show &popup on spam message",IDC_CHECKBOX_POPUP,"Button",
+ BS_AUTOCHECKBOX | BS_TOP | WS_GROUP | WS_TABSTOP,7,197,
+ 158,9
+ CONTROL "&Log spam messages to file:",IDC_CHECKBOX_LOGGING,
+ "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,7,210,
+ 157,8
+ EDITTEXT IDC_EDITBOX_LOGFILE,19,220,102,13,ES_AUTOHSCROLL
+ PUSHBUTTON "&...",IDC_BUTTON_LOGBROWSE,107,220,15,13,BS_ICON |
+ BS_CENTER | BS_VCENTER
+ PUSHBUTTON "&Show",IDC_BUTTON_OPENLOG,124,220,41,13,WS_DISABLED
+ GROUPBOX "Behaviour",IDC_GROUP_BEHAVIOUR,178,117,135,40
+ CONTROL "Mar&k spam messages as read",IDC_RADIO_MARKREADMSG,
+ "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,186,
+ 130,121,8
+ CONTROL "D&elete spam messages",IDC_RADIO_DELETEMSG,"Button",
+ BS_AUTORADIOBUTTON | WS_TABSTOP,186,142,121,8
+ GROUPBOX "Message Types",IDC_GROUP_MESSAGETYPES,178,161,135,79
+ LTEXT "Types of incoming e&vents which shall be checked for spam:",
+ IDC_TEXT_MESSAGETYPES,183,171,124,17
+ CONTROL "",IDC_TREEVIEW_MESSAGETYPES,"SysTreeView32",
+ TVS_DISABLEDRAGDROP | WS_BORDER | WS_TABSTOP | 0x8000,
+ 184,189,123,45
+END
+
+IDD_OPTIONS_CONFIGURE_DISLIKEDMESSAGEFILTER DIALOGEX 0, 0, 353, 227
+STYLE DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+CAPTION "Configure Disliked Messages Filter"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Disliked Contents",IDC_GROUP_OTHERCONTENTS,7,7,160,53
+ CONTROL "&Multiple messages with same content",
+ IDC_CHECKBOX_MULTIPLEMESSAGES,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,18,20,147,8
+ CONTROL "Messages containing &known hoax message texts",
+ IDC_CHECKBOX_HOAXTEXTS,"Button",BS_AUTOCHECKBOX | BS_TOP |
+ BS_MULTILINE | WS_GROUP | WS_TABSTOP,18,34,146,19
+ GROUPBOX "Indicating Phrases/Patterns",IDC_GROUP_WORDS,7,67,160,
+ 122
+ CONTROL "Look for the following indicating &phrases:",
+ IDC_CHECKBOX_CUSTOMIZEDWORDS,"Button",BS_AUTOCHECKBOX |
+ BS_TOP | BS_MULTILINE | WS_TABSTOP,18,79,142,16
+ LISTBOX IDC_LISTBOX_CUSTOMIZEDWORDS,30,99,112,78,NOT LBS_NOTIFY |
+ LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT |
+ LBS_DISABLENOSCROLL | WS_VSCROLL | WS_HSCROLL
+ CONTROL "",IDC_BUTTON_ADDWORD,"MButtonClass",WS_TABSTOP,145,98,
+ 14,13,0x18000000L
+ CONTROL "",IDC_BUTTON_DELETEWORD,"MButtonClass",WS_TABSTOP,145,
+ 114,14,13,0x18000000L
+ GROUPBOX "Auto Reply",IDC_GROUP_NOTIFYSENDER,177,7,168,182
+ CONTROL "Auto &reply to disliked messages using the following text:",
+ IDC_CHECKBOX_NOTIFYSENDER,"Button",BS_AUTOCHECKBOX |
+ BS_TOP | BS_MULTILINE | WS_TABSTOP,187,20,153,17
+ EDITTEXT IDC_EDITBOX_NOTIFYSENDER,200,39,137,92,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+ LTEXT "(You can use the substitute symbols %sender% and %message% for the appropriate contents.)",
+ IDC_TEXT_NOTIFYSENDER,200,134,122,26
+ CONTROL "",IDC_BUTTON_NOTIFYSENDER_VARIABLES,"MButtonClass",
+ WS_TABSTOP,323,134,14,13,0x18000000L
+ CONTROL "Add the reply to contact &history",
+ IDC_CHECKBOX_ADDTOHISTORY,"Button",BS_AUTOCHECKBOX |
+ BS_TOP | BS_MULTILINE | WS_TABSTOP,187,163,153,18
+ LTEXT "Warning: Not all Spam Definitions are available! Some features are disabled.",
+ IDC_TEXT_WARNING,18,192,327,10,NOT WS_VISIBLE
+ PUSHBUTTON "Spam &Definitions",IDC_BUTTON_SPAMDEFINITIONS,17,203,84,
+ 14
+ DEFPUSHBUTTON "OK",IDOK,225,203,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,283,203,50,14
+END
+
+IDD_OPTIONS_CONFIGURE_ROBOTFILTER DIALOG DISCARDABLE 0, 0, 329, 287
+STYLE DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+CAPTION "Configure Robot Filter"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ GROUPBOX "Message Texts",IDC_GROUP_MESSAGETEXTS,7,7,314,161
+ LTEXT "&Instruction:",IDC_STATIC,20,20,290,8
+ EDITTEXT IDC_EDITBOX_INSTRUCTION,20,29,290,40,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+ CONTROL "",IDC_BUTTON_INSTRUCTION_VARIABLES,"MButtonClass",
+ WS_TABSTOP,296,70,14,13,0x18000000L
+ LTEXT "&Confirmation:",IDC_STATIC,20,75,271,8
+ EDITTEXT IDC_EDITBOX_CONFIRMATION,20,85,290,40,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+ CONTROL "",IDC_BUTTON_CONFIRMATION_VARIABLES,"MButtonClass",
+ WS_TABSTOP,296,127,14,13,0x18000000L
+ LTEXT "(You can use the substitute symbols %sender%, %message%, %securitycode% and %triesleft% for the appropriate contents.)",
+ IDC_TEXT_VARS,20,130,271,18
+ CONTROL "Add the message texts to contact &history",
+ IDC_CHECKBOX_ADDTOHISTORY,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,20,149,290,10
+ GROUPBOX "Security Code",IDC_GROUP_ANSWER,7,177,314,59
+ LTEXT "The following text needs to be replied by the contact to confirm that the message was not sent automatically:",
+ IDC_STATIC,20,188,163,32
+ EDITTEXT IDC_EDITBOX_ANSWER,187,195,79,12,ES_AUTOHSCROLL
+ PUSHBUTTON "&Random",IDC_BUTTON_ANSWER,268,195,42,12
+ LTEXT "(You can use readable words, too.)",IDC_STATIC,187,210,
+ 123,8
+ CONTROL "&Hide users until they identified themselves",
+ IDC_CHECKBOX_HIDE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 20,219,290,10
+ LTEXT "Note: The filter gets invoked after the Advertisment Filter.\r\nThus, no instruction message will be sent in reply to advertisment messages.",
+ IDC_TEXT_WARNING,20,241,301,19
+ DEFPUSHBUTTON "OK",IDOK,204,265,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,260,265,50,14
+END
+
+IDD_INFO_SPAMDEFINITIONS DIALOGEX 0, 0, 248, 194
+STYLE DS_SETFOREGROUND | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER |
+ WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+CAPTION "Spam Definitions: %s"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "Installed Spam Definitions for filter:",
+ IDC_TEXT_DESCRIPTION,3,3,242,10
+ CONTROL "",IDC_LISTVIEW_SPAMDEFINITIONS,"SysListView32",
+ LVS_REPORT | LVS_SINGLESEL | LVS_SORTASCENDING |
+ LVS_NOSORTHEADER | WS_TABSTOP,5,29,238,138,
+ WS_EX_STATICEDGE
+ CONTROL "",IDC_TAB_SPAMDEFINITIONS,"SysTabControl32",
+ TCS_HOTTRACK | TCS_MULTILINE | TCS_FOCUSNEVER |
+ WS_TABSTOP,2,14,244,156
+ CONTROL "Download more Spam Definitions",IDC_URL_GETMORE,
+ "Hyperlink",WS_TABSTOP | 0x1,2,177,187,9
+ SCROLLBAR IDC_SCROLLBAR_SIZEGRIP,242,188,6,6,
+ SBS_SIZEBOXBOTTOMRIGHTALIGN | SBS_SIZEGRIP
+ DEFPUSHBUTTON "OK",IDCANCEL,190,174,50,14
+END
+
+IDD_INFO_SPAMMERS DIALOGEX 0, 0, 253, 142
+STYLE DS_SETFOREGROUND | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER |
+ WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+CAPTION "Ignored Spammers"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ PUSHBUTTON "OK",IDOK,193,102,53,14
+ PUSHBUTTON "Cancel",IDCANCEL,193,120,53,14
+ LTEXT "The following contacts were recognized as spammers:",
+ IDC_TEXT_DESCRIPTION,4,5,247,8
+ CONTROL "",IDC_CLIST,"CListControl",WS_TABSTOP | 0x193,4,17,180,
+ 69,WS_EX_CLIENTEDGE
+ ICON "",IDC_ICON_SPAM,187,13,20,20,SS_NOTIFY | SS_CENTERIMAGE
+ LTEXT "Spammer",IDC_TEXT_SPAM,205,19,45,8,SS_NOPREFIX |
+ SS_CENTERIMAGE
+ ICON "",IDC_ICON_DELETE,187,28,20,20,SS_NOTIFY |
+ SS_CENTERIMAGE
+ LTEXT "Delete",IDC_TEXT_DELETE,205,34,45,8,SS_NOPREFIX |
+ SS_CENTERIMAGE
+ ICON "",IDC_ICON_NORMAL,187,43,20,20,SS_NOTIFY |
+ SS_CENTERIMAGE
+ LTEXT "Accept",IDC_TEXT_NORMAL,205,49,45,8,SS_NOPREFIX |
+ SS_CENTERIMAGE
+ GROUPBOX "",IDC_GROUP,4,87,180,51
+ CONTROL "&Hide spammers from contact list",IDC_CHECKBOX_HIDE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,96,166,10
+ CONTROL "Store spammers in special &group:",IDC_CHECKBOX_GROUP,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,108,166,10
+ EDITTEXT IDC_EDITBOX_GROUP,22,119,155,13,ES_AUTOHSCROLL
+ SCROLLBAR IDC_SCROLLBAR_SIZEGRIP,247,136,6,6,
+ SBS_SIZEBOXBOTTOMRIGHTALIGN | SBS_SIZEGRIP
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO MOVEABLE PURE
+BEGIN
+ IDD_LISTBOX_ADD_ITEM, DIALOG
+ BEGIN
+ VERTGUIDE, 65
+ VERTGUIDE, 72
+ VERTGUIDE, 235
+ HORZGUIDE, 15
+ HORZGUIDE, 60
+ HORZGUIDE, 70
+ HORZGUIDE, 80
+ HORZGUIDE, 114
+ HORZGUIDE, 140
+ HORZGUIDE, 191
+ END
+
+ IDD_OPTIONS_CONFIGURE_ADVERTISMENTFILTER, DIALOG
+ BEGIN
+ VERTGUIDE, 7
+ VERTGUIDE, 12
+ VERTGUIDE, 18
+ VERTGUIDE, 29
+ VERTGUIDE, 159
+ VERTGUIDE, 187
+ VERTGUIDE, 199
+ VERTGUIDE, 211
+ VERTGUIDE, 329
+ HORZGUIDE, 19
+ HORZGUIDE, 39
+ HORZGUIDE, 59
+ HORZGUIDE, 107
+ HORZGUIDE, 112
+ HORZGUIDE, 139
+ HORZGUIDE, 255
+ END
+
+ IDD_OPTIONS_MAIN, DIALOG
+ BEGIN
+ VERTGUIDE, 3
+ VERTGUIDE, 10
+ VERTGUIDE, 16
+ VERTGUIDE, 19
+ VERTGUIDE, 163
+ VERTGUIDE, 171
+ VERTGUIDE, 174
+ VERTGUIDE, 182
+ VERTGUIDE, 212
+ VERTGUIDE, 226
+ VERTGUIDE, 287
+ VERTGUIDE, 308
+ HORZGUIDE, 115
+ HORZGUIDE, 128
+ HORZGUIDE, 138
+ HORZGUIDE, 150
+ HORZGUIDE, 170
+ HORZGUIDE, 172
+ HORZGUIDE, 238
+ END
+
+ IDD_OPTIONS_CONFIGURE_DISLIKEDMESSAGEFILTER, DIALOG
+ BEGIN
+ VERTGUIDE, 7
+ VERTGUIDE, 12
+ VERTGUIDE, 18
+ VERTGUIDE, 159
+ VERTGUIDE, 167
+ VERTGUIDE, 187
+ VERTGUIDE, 201
+ VERTGUIDE, 337
+ HORZGUIDE, 7
+ HORZGUIDE, 20
+ HORZGUIDE, 189
+ HORZGUIDE, 217
+ END
+
+ IDD_OPTIONS_CONFIGURE_ROBOTFILTER, DIALOG
+ BEGIN
+ VERTGUIDE, 7
+ VERTGUIDE, 13
+ VERTGUIDE, 20
+ VERTGUIDE, 310
+ VERTGUIDE, 321
+ HORZGUIDE, 11
+ END
+
+ IDD_INFO_SPAMMERS, DIALOG
+ BEGIN
+ VERTGUIDE, 4
+ VERTGUIDE, 10
+ VERTGUIDE, 184
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "#include <windows.h>\r\n"
+ "#include <winres.h>\r\n"
+ "#include ""Include\\m_utils.h""\r\n"
+ "#include ""Include\\m_button.h""\r\n"
+ "#include ""Include\\m_clc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "#include ""version.rc""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_DEFAULT ICON DISCARDABLE "Resources\\spam_16x16_256.ico"
+IDI_SPAM_LAYER ICON DISCARDABLE "Resources\\spam_layer_16x16_256.ico"
+IDI_ADD_BUTTON ICON DISCARDABLE "Resources\\add_button_16x16_256.ico"
+IDI_DELETE_BUTTON ICON DISCARDABLE "Resources\\delete_button_16x16_256.ico"
+IDI_DEFAULT_LARGE ICON DISCARDABLE "Resources\\spam_32x32_256.ico"
+IDI_SPAM_DEFINITIONS ICON DISCARDABLE "resources\\spam_definitions_16x16_256.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_CONTEXT MENU DISCARDABLE
+BEGIN
+ POPUP "WordsList"
+ BEGIN
+ MENUITEM "&Remove", ID_MENU_DELETE
+ MENUITEM "&Add...", ID_MENU_ADD
+ END
+ POPUP "SpamDef"
+ BEGIN
+ MENUITEM "&Remove...", ID_MENU_DELETE
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXT
+//
+
+IDR_XMLFILETEMPLATE TEXT MOVEABLE PURE "Resources\\log_main.xml"
+IDR_XMLITEMTEMPLATE TEXT MOVEABLE PURE "Resources\\log_item.xml"
+IDR_XSLLAYOUTFILETEMPLATE TEXT MOVEABLE PURE "Resources\\log_layout.xsl"
+#endif // Englisch (USA) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/spamfilter/SpamFilter.sln b/spamfilter/SpamFilter.sln
new file mode 100644
index 0000000..f393f7c
--- /dev/null
+++ b/spamfilter/SpamFilter.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpamFilter", "SpamFilter.vcproj", "{844ADB70-77B1-45F4-8295-B8246E5066E4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug Unicode|Win32 = Debug Unicode|Win32
+ Debug|Win32 = Debug|Win32
+ Release Unicode|Win32 = Release Unicode|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {844ADB70-77B1-45F4-8295-B8246E5066E4}.Debug Unicode|Win32.ActiveCfg = Release Unicode 9x/ME|Win32
+ {844ADB70-77B1-45F4-8295-B8246E5066E4}.Debug Unicode|Win32.Build.0 = Debug Unicode|Win32
+ {844ADB70-77B1-45F4-8295-B8246E5066E4}.Debug|Win32.ActiveCfg = Debug Unicode 9x/ME|Win32
+ {844ADB70-77B1-45F4-8295-B8246E5066E4}.Debug|Win32.Build.0 = Debug|Win32
+ {844ADB70-77B1-45F4-8295-B8246E5066E4}.Release Unicode|Win32.ActiveCfg = Release Unicode|Win32
+ {844ADB70-77B1-45F4-8295-B8246E5066E4}.Release Unicode|Win32.Build.0 = Release Unicode|Win32
+ {844ADB70-77B1-45F4-8295-B8246E5066E4}.Release|Win32.ActiveCfg = Release|Win32
+ {844ADB70-77B1-45F4-8295-B8246E5066E4}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/spamfilter/SpamFilter.vcproj b/spamfilter/SpamFilter.vcproj
new file mode 100644
index 0000000..c7d2041
--- /dev/null
+++ b/spamfilter/SpamFilter.vcproj
@@ -0,0 +1,2243 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="SpamFilter"
+ ProjectGUID="{844ADB70-77B1-45F4-8295-B8246E5066E4}"
+ RootNamespace="SpamFilter"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Release Unicode 9x/ME|Win32"
+ OutputDirectory=".\Temp\ReleaseW9x"
+ IntermediateDirectory=".\Temp\ReleaseW9x"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Temp\ReleaseW9x/SpamFilter.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories=".\Include"
+ PreprocessorDefinitions="NDEBUG;UNICOWS;UNICODE;WIN32;_WINDOWS;_USRDLL;STRICT;SPAMFILTER_EXPORTS;_CRT_SECURE_NO_DEPRECATE;_USE_32BIT_TIME_T"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Temp\ReleaseW9x/SpamFilter.pch"
+ AssemblerListingLocation=".\Temp\ReleaseW9x/"
+ ObjectFile=".\Temp\ReleaseW9x/"
+ ProgramDataBaseFileName=".\Temp\ReleaseW9x/"
+ BrowseInformation="1"
+ WarningLevel="4"
+ SuppressStartupBanner="true"
+ UndefinePreprocessorDefinitions="_MBCS,NO_STRICT"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalOptions="/ignore:4078"
+ AdditionalDependencies="unicows.lib comctl32.lib shlwapi.lib version.lib"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreDefaultLibraryNames="kernel32.lib,advapi32.lib,user32.lib,gdi32.lib,shell32.lib,comdlg32.lib,version.lib,mpr.lib,rasapi32.lib,winmm.lib,winspool.lib,vfw32.lib,secur32.lib,oleacc.lib,oledlg.lib,sensapi.lib,uuid.lib,OLDNAMES"
+ GenerateDebugInformation="true"
+ SetChecksum="true"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Temp\ReleaseW9x/spamfilterW9x.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Temp\ReleaseW9x/SpamFilter.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory=".\Temp\DebugW"
+ IntermediateDirectory=".\Temp\DebugW"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Temp\DebugW/SpamFilter.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=".\Include"
+ PreprocessorDefinitions="_DEBUG;UNICODE;WIN32;_WINDOWS;_USRDLL;STRICT;SPAMFILTER_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Temp\DebugW/SpamFilter.pch"
+ AssemblerListingLocation=".\Temp\DebugW/"
+ ObjectFile=".\Temp\DebugW/"
+ ProgramDataBaseFileName=".\Temp\DebugW/"
+ BrowseInformation="1"
+ WarningLevel="4"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ UndefinePreprocessorDefinitions="_MBCS,NO_STRICT"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="comctl32.lib shlwapi.lib version.lib"
+ OutputFile="C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilterW.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ IgnoreDefaultLibraryNames="uuid.lib,OLDNAMES"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilterW.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Temp\DebugW/spamfilterW.map"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Temp\DebugW/spamfilterW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Temp\DebugW/SpamFilter.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Temp\Release"
+ IntermediateDirectory=".\Temp\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Temp\Release/SpamFilter.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/opt:nowin98 "
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories=".\Include"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_USRDLL;STRICT;SPAMFILTER_EXPORTS;_USE_32BIT_TIME_T;_CRT_SECURE_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Temp\Release/SpamFilter.pch"
+ AssemblerListingLocation=".\Temp\Release/"
+ ObjectFile=".\Temp\Release/"
+ ProgramDataBaseFileName=".\Temp\Release/"
+ BrowseInformation="1"
+ WarningLevel="4"
+ SuppressStartupBanner="true"
+ UndefinePreprocessorDefinitions="NO_STRICT"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalOptions="/ignore:4078 "
+ AdditionalDependencies="comctl32.lib shlwapi.lib version.lib"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreDefaultLibraryNames="uuid.lib,OLDNAMES"
+ GenerateDebugInformation="true"
+ OptimizeForWindows98="1"
+ SetChecksum="true"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Temp\Release/spamfilter.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Temp\Release/SpamFilter.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode 9x/ME|Win32"
+ OutputDirectory=".\Temp\DebugW9x"
+ IntermediateDirectory=".\Temp\DebugW9x"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Temp\DebugW9x/SpamFilter.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=".\Include"
+ PreprocessorDefinitions="_DEBUG;UNICOWS;UNICODE;WIN32;_WINDOWS;_USRDLL;STRICT;SPAMFILTER_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Temp\DebugW9x/SpamFilter.pch"
+ AssemblerListingLocation=".\Temp\DebugW9x/"
+ ObjectFile=".\Temp\DebugW9x/"
+ ProgramDataBaseFileName=".\Temp\DebugW9x/"
+ BrowseInformation="1"
+ WarningLevel="4"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ UndefinePreprocessorDefinitions="_MBCS,NO_STRICT"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="unicows.lib comctl32.lib shlwapi.lib version.lib"
+ OutputFile="C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilterW9x.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ IgnoreDefaultLibraryNames="kernel32.lib,advapi32.lib,user32.lib,gdi32.lib,shell32.lib,comdlg32.lib,version.lib,mpr.lib,rasapi32.lib,winmm.lib,winspool.lib,vfw32.lib,secur32.lib,oleacc.lib,oledlg.lib,sensapi.lib,uuid.lib,OLDNAMES"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilterW9x.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Temp\DebugW9x/spamfilterW9x.map"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Temp\DebugW9x/spamfilterW9x.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Temp\DebugW9x/SpamFilter.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Temp\ReleaseW/SpamFilter.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/opt:nowin98 "
+ Optimization="1"
+ InlineFunctionExpansion="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories=".\Include"
+ PreprocessorDefinitions="NDEBUG;UNICODE;WIN32;_WINDOWS;_USRDLL;STRICT;SPAMFILTER_EXPORTS;_USE_32BIT_TIME_T;_CRT_SECURE_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="common.h"
+ PrecompiledHeaderFile=".\Temp\ReleaseW/SpamFilter.pch"
+ AssemblerListingLocation=".\Temp\ReleaseW/"
+ ObjectFile=".\Temp\ReleaseW/"
+ ProgramDataBaseFileName=".\Temp\ReleaseW/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ UndefinePreprocessorDefinitions="_MBCS,NO_STRICT"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalOptions="/ignore:4078 "
+ AdditionalDependencies="comctl32.lib shlwapi.lib version.lib"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ IgnoreDefaultLibraryNames="uuid.lib,OLDNAMES"
+ GenerateDebugInformation="true"
+ OptimizeForWindows98="1"
+ SetChecksum="true"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Temp\ReleaseW/spamfilterW.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Temp\ReleaseW/SpamFilter.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Temp\Debug"
+ IntermediateDirectory=".\Temp\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Temp\Debug/SpamFilter.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=".\Include"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_USRDLL;STRICT;SPAMFILTER_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ PrecompiledHeaderFile=".\Temp\Debug/SpamFilter.pch"
+ AssemblerListingLocation=".\Temp\Debug/"
+ ObjectFile=".\Temp\Debug/"
+ ProgramDataBaseFileName=".\Temp\Debug/"
+ BrowseInformation="1"
+ WarningLevel="4"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ UndefinePreprocessorDefinitions="NO_STRICT"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="true"
+ AdditionalDependencies="comctl32.lib shlwapi.lib version.lib"
+ OutputFile="C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilter.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ IgnoreDefaultLibraryNames="uuid.lib,OLDNAMES"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="C:\PROGRA~1\MIRAND~1\PLUGINS\spamfilter.pdb"
+ GenerateMapFile="true"
+ MapFileName=".\Temp\Debug/spamfilter.map"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Temp\Debug/spamfilter.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Temp\Debug/SpamFilter.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="contact.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="dlg.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="extraimg.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="folders.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="forkthread.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="logfile.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="main.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UsePrecompiledHeader="1"
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="menuitems.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="options.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="path.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="popup.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="regexp.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="services.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="spamcheck.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="spamcheckdata.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="spamdefinitions.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="string.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="stringlist.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="trigger.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="utilities.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="variables.c"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UndefinePreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="common.h"
+ >
+ </File>
+ <File
+ RelativePath="contact.h"
+ >
+ </File>
+ <File
+ RelativePath="dlg.h"
+ >
+ </File>
+ <File
+ RelativePath="extraimg.h"
+ >
+ </File>
+ <File
+ RelativePath="folders.h"
+ >
+ </File>
+ <File
+ RelativePath="forkthread.h"
+ >
+ </File>
+ <File
+ RelativePath="logfile.h"
+ >
+ </File>
+ <File
+ RelativePath="menuitems.h"
+ >
+ </File>
+ <File
+ RelativePath="options.h"
+ >
+ </File>
+ <File
+ RelativePath="path.h"
+ >
+ </File>
+ <File
+ RelativePath="popup.h"
+ >
+ </File>
+ <File
+ RelativePath="regexp.h"
+ >
+ </File>
+ <File
+ RelativePath="services.h"
+ >
+ </File>
+ <File
+ RelativePath="spamcheck.h"
+ >
+ </File>
+ <File
+ RelativePath="spamcheckdata.h"
+ >
+ </File>
+ <File
+ RelativePath="spamdefinitions.h"
+ >
+ </File>
+ <File
+ RelativePath="string.h"
+ >
+ </File>
+ <File
+ RelativePath="stringlist.h"
+ >
+ </File>
+ <File
+ RelativePath="trigger.h"
+ >
+ </File>
+ <File
+ RelativePath="utilities.h"
+ >
+ </File>
+ <File
+ RelativePath="variables.h"
+ >
+ </File>
+ <File
+ RelativePath="version.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="Resources\add_button_16x16_256.ico"
+ >
+ </File>
+ <File
+ RelativePath="Resources\delete_button_16x16_256.ico"
+ >
+ </File>
+ <File
+ RelativePath="Resources\log_item.xml"
+ >
+ </File>
+ <File
+ RelativePath="Resources\log_layout.xsl"
+ >
+ </File>
+ <File
+ RelativePath="Resources\log_main.xml"
+ >
+ </File>
+ <File
+ RelativePath="resource.h"
+ >
+ </File>
+ <File
+ RelativePath="Resources\spam_16x16_256.ico"
+ >
+ </File>
+ <File
+ RelativePath="Resources\spam_32x32_256.ico"
+ >
+ </File>
+ <File
+ RelativePath="Resources\spam_definitions_16x16_256.ico"
+ >
+ </File>
+ <File
+ RelativePath="Resources\spam_layer_16x16_256.ico"
+ >
+ </File>
+ <File
+ RelativePath="SpamFilter.rc"
+ >
+ <FileConfiguration
+ Name="Release Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode 9x/ME|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Documentation"
+ Filter="*.txt"
+ >
+ <File
+ RelativePath="Docs\PCRE-License.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\SpamFilter-Developer.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\SpamFilter-License.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\SpamFilter-Readme.txt"
+ >
+ </File>
+ <File
+ RelativePath="Docs\SpamFilter-Translation.txt"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Extensions"
+ >
+ <File
+ RelativePath="Extensions\filtered1.wav"
+ >
+ </File>
+ <File
+ RelativePath="Extensions\filtered2.wav"
+ >
+ </File>
+ <File
+ RelativePath="Extensions\filtered3.wav"
+ >
+ </File>
+ <File
+ RelativePath="Extensions\InstallScript.xml"
+ >
+ </File>
+ <File
+ RelativePath="m_spamfilter.h"
+ >
+ </File>
+ <File
+ RelativePath="m_spamfilter.inc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Utilities"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="Utilities\DebugHelper.h"
+ >
+ </File>
+ <File
+ RelativePath="Utilities\Pcre\include\pcre.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath="Info_Src.txt"
+ >
+ </File>
+ <File
+ RelativePath="License_Appendix.txt"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/spamfilter/Utilities/DebugHelper.h b/spamfilter/Utilities/DebugHelper.h
new file mode 100644
index 0000000..cf1b4b3
--- /dev/null
+++ b/spamfilter/Utilities/DebugHelper.h
@@ -0,0 +1,338 @@
+/*
+
+DebugHelper.h
+Copyright © 2004-2006 Heiko Herkenrath
+
+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.
+*/
+
+// Version 3.0, Sept 26 2005
+// Heiko Herkenrath
+
+// -----------------------------
+
+// Switches:
+
+//#define DEACTIVATE_DEBUG_HELPER
+//#define NO_DEBUG_HELPER
+//#define ALSO_USE_DEBUG_HELPER_FOR_RELEASE
+
+// -----------------------------
+
+#if !defined(ALSO_USE_DEBUG_HELPER_FOR_RELEASE)
+ #if !defined(_DEBUG)
+ #define DEACTIVATE_DEBUG_HELPER
+ #endif
+#endif
+
+// -----------------------------
+
+#ifndef NO_DEBUG_HELPERS
+
+ #define DEBUG_BUFFER_SIZE 1024
+
+ // Creating display text for all functions
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+ __inline WCHAR* CreateDebugText(const WCHAR* pszFmt, const WCHAR* pszFile, unsigned int uLine, va_list va)
+ {
+ static int iDebugCallCount = 0;
+ TCHAR* pszDebugText;
+ TCHAR* pszDebugBuf;
+ TCHAR* ptszLastError;
+ DWORD dwLastError;
+ #if defined(UNICODE)
+ char* pszLastError;
+ #endif
+
+ dwLastError = GetLastError();
+ iDebugCallCount++;
+ MessageBeep(MB_ICONQUESTION);
+
+ if (!pszFmt) return NULL;
+
+ pszDebugText = (TCHAR*)malloc(DEBUG_BUFFER_SIZE*sizeof(TCHAR));
+ if (pszDebugText)
+ {
+ pszDebugBuf = (TCHAR*)malloc(DEBUG_BUFFER_SIZE*sizeof(TCHAR));
+ if (pszDebugBuf)
+ {
+ ptszLastError = NULL;
+
+ #if defined(UNICODE) // FormatMessageW does not work with UnicoWS layer on Win9x/ME
+
+ pszLastError = NULL;
+
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPWSTR)&ptszLastError, 0, NULL) == 0)
+ FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPSTR)&pszLastError, 0, NULL);
+
+ mir_sntprintf(pszDebugBuf, DEBUG_BUFFER_SIZE, _T("[Message %i]\r\nLast Error: %u\r\nDescription: %hs%sCall: %s, %u\r\n\r\n%s"), iDebugCallCount, dwLastError, pszLastError?pszLastError:"", ptszLastError?ptszLastError:_T(""), PathFindFileName(pszFile), uLine, pszFmt);
+
+ if (pszLastError) LocalFree(pszLastError);
+ if (ptszLastError) LocalFree(ptszLastError);
+
+ #else
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPTSTR)&ptszLastError, 0, NULL);
+ mir_sntprintf(pszDebugBuf, DEBUG_BUFFER_SIZE, _T("[Message %i]\r\nLast Error: %u\r\nDescription: %sCall: %s, %u\r\n\r\n%s"), iDebugCallCount, dwLastError, ptszLastError?ptszLastError:_T(""), PathFindFileName(pszFile), uLine, pszFmt);
+ if (ptszLastError) LocalFree(ptszLastError);
+
+ #endif
+
+ mir_vsnprintf(pszDebugText, DEBUG_BUFFER_SIZE, pszDebugBuf, va);
+ free(pszDebugBuf);
+ }
+ }
+
+ SetLastError(ERROR_SUCCESS);
+ return pszDebugText;
+ }
+ #endif
+
+
+ // Check if a specific flag is bitwise-or'ed into a flags variable
+ // (returns flag name as string or empty string if not contained)
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+ #define INFLAGS(flags, flag) ( ((flags&flag) && ((flags^=flag)||TRUE))?_T("|"#flag):_T(""))
+ #else
+ #define INFLAGS(flags, flag)
+ #endif
+
+
+ // BOOLSTR [make a string out of a boolean value]
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+ #define BOOLSTR(b) ((b)?_T("TRUE"):_T("FALSE"))
+ #else
+ #define BOOLSTR(b)
+ #endif
+
+
+ // BOX [show debug message box with text]
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+
+ // Functions:
+ #define BOX(str) BOX_Helper(_T(__FILE__), __LINE__, _T("%s"), _T(str))
+ #define BOX1(fmt, p1) BOX_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), NULL, NULL, NULL)
+ #define BOX2(fmt, p1, p2) BOX_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), NULL, NULL)
+ #define BOX3(fmt, p1, p2, p3) BOX_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), NULL)
+ #define BOX4(fmt, p1, p2, p3, p4) BOX_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), (p4))
+ // ---
+
+ __inline void BOX_Helper(const TCHAR* pszFile, unsigned int uLine, const TCHAR* pszFmt, ...)
+ {
+ va_list va;
+ TCHAR* pszText;
+
+ va_start(va, pszFmt);
+ pszText = CreateDebugText(pszFmt, pszFile, uLine, va);
+ va_end(va);
+
+ if (pszText)
+ {
+ // Only show if CTRL is not pressed
+ if (!(GetAsyncKeyState(VK_CONTROL)&0x8000))
+ MessageBoxEx(NULL, pszText, _T("Debug"), MB_OK|MB_TASKMODAL|MB_SETFOREGROUND, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
+
+ free(pszText);
+ }
+ }
+
+ #else
+ #define BOX(str)
+ #define BOX1(fmt, p1)
+ #define BOX2(fmt, p1, p2)
+ #define BOX3(fmt, p1, p2, p3)
+ #define BOX4(fmt, p1, p2, p3, p4)
+ #endif
+
+
+
+ // STEP [show debug message box with current code step]
+ #define STEP(section, id) BOX2("Reached Step %s|%u", _T(section), id)
+
+
+
+ // STR [write line into debugger output]
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+
+ // Functions:
+ #define STR(str) STR_Helper(_T(__FILE__), __LINE__, _T("%s"), _T(str))
+ #define STR1(fmt, p1) STR_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), NULL, NULL, NULL)
+ #define STR2(fmt, p1, p2) STR_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), NULL, NULL)
+ #define STR3(fmt, p1, p2, p3) STR_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), NULL, NULL)
+ #define STR4(fmt, p1, p2, p3, p4) STR_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), (p4))
+ // ---
+
+ __inline void STR_Helper(const TCHAR* pszFile, unsigned int uLine, const TCHAR* pszFmt, ...)
+ {
+ va_list va;
+ TCHAR* pszText;
+
+ va_start(va, pszFmt);
+ pszText = CreateDebugText(pszFmt, pszFile, uLine, va);
+ va_end(va);
+
+ if (pszText)
+ {
+ OutputDebugString(pszText);
+ OutputDebugString(_T("\r\n"));
+
+ free(pszText);
+ }
+ }
+
+ #else
+ #define STR(str)
+ #define STR1(fmt, p1)
+ #define STR2(fmt, p1, p2)
+ #define STR3(fmt, p1, p2, p3)
+ #define STR4(fmt, p1, p2, p3, p4)
+ #endif
+
+
+
+ // DBG [simple wrapper around OutputDebugString]
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+ #define DBGT(str) OutputDebugString(_T(str)_T("\r\n"))
+ #define DBG(str) { OutputDebugString(str); OutputDebugString(_T("\r\n")); }
+ #else
+ #define DBGT(str)
+ #define DBG(str)
+ #endif
+
+
+
+ // LOG [append line to debug log file]
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+
+ // Functions:
+ #define LOG(str) LOG_Helper(_T(__FILE__), __LINE__, _T("%s"), _T(str))
+ #define LOG1(fmt, p1) LOG_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), NULL, NULL, NULL)
+ #define LOG2(fmt, p1, p2) LOG_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), NULL, NULL)
+ #define LOG3(fmt, p1, p2, p3) LOG_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), NULL)
+ #define LOG4(fmt, p1, p2, p3, p4) LOG_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), (p4))
+ // ---
+
+ __inline void LOG_Helper(const TCHAR* pszFile, unsigned int uLine, const TCHAR* pszFmt, ...)
+ {
+ va_list va;
+ DWORD dwDebugFileWritten = 0;
+ TCHAR* pszText;
+ TCHAR szDebugLogFile[MAX_PATH];
+ HANDLE hDebugLogFile;
+
+ static BOOL bFirstCall = TRUE;
+
+ va_start(va, pszFmt);
+ pszText = CreateDebugText(pszFmt, pszFile, uLine, va);
+ va_end(va);
+
+ // Filename
+ if (pszText)
+ {
+ GetModuleFileName(NULL, szDebugLogFile, sizeof(szDebugLogFile)-1);
+ PathRemoveFileSpec(szDebugLogFile);
+ PathAppend(szDebugLogFile, _T("Debug.log"));
+
+ hDebugLogFile = CreateFile(szDebugLogFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hDebugLogFile == INVALID_HANDLE_VALUE)
+ BOX("LOG ERROR: INVALID_HANDLE_VALUE");
+
+ SetFilePointer(hDebugLogFile, 0, NULL, bFirstCall?FILE_BEGIN:FILE_END);
+ WriteFile(hDebugLogFile, "\r\n", 2*sizeof(TCHAR), &dwDebugFileWritten, NULL);
+ WriteFile(hDebugLogFile, (PBYTE*)pszText, (DWORD)(lstrlen(pszText)*sizeof(TCHAR)), &dwDebugFileWritten, NULL);
+ SetEndOfFile(hDebugLogFile);
+
+ CloseHandle(hDebugLogFile);
+ free(pszText);
+
+ bFirstCall = FALSE;
+ }
+ }
+
+ #else
+ #define LOG(str)
+ #define LOG1(fmt, p1)
+ #define LOG2(fmt, p1, p2)
+ #define LOG3(fmt, p1, p2, p3)
+ #define LOG4(fmt, p1, p2, p3, p4)
+ #endif
+
+
+
+ // POP [show debug popup (Popup Plugin) with text]
+ /*
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+
+ // Functions:
+ #define POP(str) POP_Helper(_T(__FILE__), __LINE__, _T("%s"), _T(str))
+ #define POP1(fmt, p1) POP_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), NULL, NULL, NULL)
+ #define POP2(fmt, p1, p2) POP_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), NULL, NULL)
+ #define POP3(fmt, p1, p2, p3) POP_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), NULL)
+ #define POP4(fmt, p1, p2, p3, p4) POP_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), (p4))
+ // ---
+
+ __inline void POP_Helper(const TCHAR* pszFile, unsigned int uLine, const TCHAR* pszFmt, ...)
+ {
+ #ifdef MS_POPUP_SHOWMESSAGE
+ va_list va;
+ TCHAR* pszText;
+
+ va_start(va, pszFmt);
+ pszText = CreateDebugText(pszFmt, pszFile, uLine, va);
+ va_end(va);
+
+ // Only show if CTRL is not pressed
+ if (pszText)
+ {
+ if (!(GetAsyncKeyState(VK_CONTROL)&0x8000))
+ CallServiceSync(MS_POPUP_SHOWMESSAGE, (WPARAM)pszText, (LPARAM)SM_NOTIFY);
+
+ free(pszText);
+ }
+ #endif
+ }
+ #else
+ #define POP(str)
+ #define POP1(fmt, p1)
+ #define POP2(fmt, p1, p2)
+ #define POP3(fmt, p1, p2, p3)
+ #define POP4(fmt, p1, p2, p3, p4)
+ #endif
+ */
+
+
+ // LOOPPROT [prevent a loop (for/while/repeat) from becoming an infinite loop]
+ #if !defined(DEACTIVATE_DEBUG_HELPER)
+
+ // LOOPROT(0) breaks the loop when "Esc" is pressed
+
+ #define LOOPPROT(max) { \
+ static unsigned int uDebugLoopCount = 1; \
+ uDebugLoopCount++; \
+ if (GetAsyncKeyState(VK_ESCAPE)&0x8000) break; \
+ MessageBeep(MB_ICONEXCLAMATION); \
+ if (uDebugLoopCount > (max)) { \
+ break; \
+ BOX1("LOOPPROT:\nThe execution of the loop was stopped because it looped %u times.", max); \
+ uDebugLoopCount = 1;\
+ } \
+ }
+
+ #else
+ #define LOOPPROT(max)
+ #endif
+
+#endif
diff --git a/spamfilter/Utilities/PCRE/bin/pcre.dll b/spamfilter/Utilities/PCRE/bin/pcre.dll
new file mode 100644
index 0000000..54f5ae7
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/bin/pcre.dll
Binary files differ
diff --git a/spamfilter/Utilities/PCRE/bin/pcreposix.dll b/spamfilter/Utilities/PCRE/bin/pcreposix.dll
new file mode 100644
index 0000000..06881cf
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/bin/pcreposix.dll
Binary files differ
diff --git a/spamfilter/Utilities/PCRE/include/pcre.h b/spamfilter/Utilities/PCRE/include/pcre.h
new file mode 100644
index 0000000..aa37389
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/include/pcre.h
@@ -0,0 +1,239 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* In its original form, this is the .in file that is transformed by
+"configure" into pcre.h.
+
+ Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+/* The file pcre.h is build by "configure". Do not edit it; instead
+make changes to pcre.in. */
+
+#define PCRE_MAJOR 5
+#define PCRE_MINOR 0
+#define PCRE_DATE 13-Sep-2004
+
+/* Win32 uses DLL by default */
+
+#ifdef _WIN32
+# ifdef PCRE_DEFINITION
+# ifdef DLL_EXPORT
+# define PCRE_DATA_SCOPE __declspec(dllexport)
+# endif
+# else
+# ifndef PCRE_STATIC
+# define PCRE_DATA_SCOPE extern __declspec(dllimport)
+# endif
+# endif
+#endif
+#ifndef PCRE_DATA_SCOPE
+# define PCRE_DATA_SCOPE extern
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS 0x0001
+#define PCRE_MULTILINE 0x0002
+#define PCRE_DOTALL 0x0004
+#define PCRE_EXTENDED 0x0008
+#define PCRE_ANCHORED 0x0010
+#define PCRE_DOLLAR_ENDONLY 0x0020
+#define PCRE_EXTRA 0x0040
+#define PCRE_NOTBOL 0x0080
+#define PCRE_NOTEOL 0x0100
+#define PCRE_UNGREEDY 0x0200
+#define PCRE_NOTEMPTY 0x0400
+#define PCRE_UTF8 0x0800
+#define PCRE_NO_AUTO_CAPTURE 0x1000
+#define PCRE_NO_UTF8_CHECK 0x2000
+#define PCRE_AUTO_CALLOUT 0x4000
+#define PCRE_PARTIAL 0x8000
+
+/* Exec-time and get/set-time error codes */
+
+#define PCRE_ERROR_NOMATCH (-1)
+#define PCRE_ERROR_NULL (-2)
+#define PCRE_ERROR_BADOPTION (-3)
+#define PCRE_ERROR_BADMAGIC (-4)
+#define PCRE_ERROR_UNKNOWN_NODE (-5)
+#define PCRE_ERROR_NOMEMORY (-6)
+#define PCRE_ERROR_NOSUBSTRING (-7)
+#define PCRE_ERROR_MATCHLIMIT (-8)
+#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */
+#define PCRE_ERROR_BADUTF8 (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+#define PCRE_ERROR_PARTIAL (-12)
+#define PCRE_ERROR_BADPARTIAL (-13)
+#define PCRE_ERROR_INTERNAL (-14)
+#define PCRE_ERROR_BADCOUNT (-15)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS 0
+#define PCRE_INFO_SIZE 1
+#define PCRE_INFO_CAPTURECOUNT 2
+#define PCRE_INFO_BACKREFMAX 3
+#define PCRE_INFO_FIRSTBYTE 4
+#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */
+#define PCRE_INFO_FIRSTTABLE 5
+#define PCRE_INFO_LASTLITERAL 6
+#define PCRE_INFO_NAMEENTRYSIZE 7
+#define PCRE_INFO_NAMECOUNT 8
+#define PCRE_INFO_NAMETABLE 9
+#define PCRE_INFO_STUDYSIZE 10
+#define PCRE_INFO_DEFAULT_TABLES 11
+
+/* Request types for pcre_config() */
+
+#define PCRE_CONFIG_UTF8 0
+#define PCRE_CONFIG_NEWLINE 1
+#define PCRE_CONFIG_LINK_SIZE 2
+#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3
+#define PCRE_CONFIG_MATCH_LIMIT 4
+#define PCRE_CONFIG_STACKRECURSE 5
+#define PCRE_CONFIG_UNICODE_PROPERTIES 6
+
+/* Bit flags for the pcre_extra structure */
+
+#define PCRE_EXTRA_STUDY_DATA 0x0001
+#define PCRE_EXTRA_MATCH_LIMIT 0x0002
+#define PCRE_EXTRA_CALLOUT_DATA 0x0004
+#define PCRE_EXTRA_TABLES 0x0008
+
+/* Types */
+
+struct real_pcre; /* declaration; the definition is private */
+typedef struct real_pcre pcre;
+
+/* The structure for passing additional data to pcre_exec(). This is defined in
+such as way as to be extensible. Always add new fields at the end, in order to
+remain compatible. */
+
+typedef struct pcre_extra {
+ unsigned long int flags; /* Bits for which fields are set */
+ void *study_data; /* Opaque data from pcre_study() */
+ unsigned long int match_limit; /* Maximum number of calls to match() */
+ void *callout_data; /* Data passed back in callouts */
+ const unsigned char *tables; /* Pointer to character tables */
+} pcre_extra;
+
+/* The structure for passing out data via the pcre_callout_function. We use a
+structure so that new fields can be added on the end in future versions,
+without changing the API of the function, thereby allowing old clients to work
+without modification. */
+
+typedef struct pcre_callout_block {
+ int version; /* Identifies version of block */
+ /* ------------------------ Version 0 ------------------------------- */
+ int callout_number; /* Number compiled into pattern */
+ int *offset_vector; /* The offset vector */
+ const char *subject; /* The subject being matched */
+ int subject_length; /* The length of the subject */
+ int start_match; /* Offset to start of this match attempt */
+ int current_position; /* Where we currently are in the subject */
+ int capture_top; /* Max current capture */
+ int capture_last; /* Most recently closed capture */
+ void *callout_data; /* Data passed in with the call */
+ /* ------------------- Added for Version 1 -------------------------- */
+ int pattern_position; /* Offset to next item in the pattern */
+ int next_item_length; /* Length of next item in the pattern */
+ /* ------------------------------------------------------------------ */
+} pcre_callout_block;
+
+/* Indirection for store get and free functions. These can be set to
+alternative malloc/free functions if required. Special ones are used in the
+non-recursive case for "frames". There is also an optional callout function
+that is triggered by the (?) regex item. Some magic is required for Win32 DLL;
+it is null on other OS. For Virtual Pascal, these have to be different again.
+*/
+
+#ifndef VPCOMPAT
+PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_free)(void *);
+PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_stack_free)(void *);
+PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *);
+#else /* VPCOMPAT */
+extern void *pcre_malloc(size_t);
+extern void pcre_free(void *);
+extern void *pcre_stack_malloc(size_t);
+extern void pcre_stack_free(void *);
+extern int pcre_callout(pcre_callout_block *);
+#endif /* VPCOMPAT */
+
+/* Exported PCRE functions */
+
+extern pcre *pcre_compile(const char *, int, const char **,
+ int *, const unsigned char *);
+extern int pcre_config(int, void *);
+extern int pcre_copy_named_substring(const pcre *, const char *,
+ int *, int, const char *, char *, int);
+extern int pcre_copy_substring(const char *, int *, int, int,
+ char *, int);
+extern int pcre_exec(const pcre *, const pcre_extra *,
+ const char *, int, int, int, int *, int);
+extern void pcre_free_substring(const char *);
+extern void pcre_free_substring_list(const char **);
+extern int pcre_fullinfo(const pcre *, const pcre_extra *, int,
+ void *);
+extern int pcre_get_named_substring(const pcre *, const char *,
+ int *, int, const char *, const char **);
+extern int pcre_get_stringnumber(const pcre *, const char *);
+extern int pcre_get_substring(const char *, int *, int, int,
+ const char **);
+extern int pcre_get_substring_list(const char *, int *, int,
+ const char ***);
+extern int pcre_info(const pcre *, int *, int *);
+extern const unsigned char *pcre_maketables(void);
+extern pcre_extra *pcre_study(const pcre *, int, const char **);
+extern const char *pcre_version(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
diff --git a/spamfilter/Utilities/PCRE/include/pcreposix.h b/spamfilter/Utilities/PCRE/include/pcreposix.h
new file mode 100644
index 0000000..a8056bd
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/include/pcreposix.h
@@ -0,0 +1,117 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+#ifndef _PCREPOSIX_H
+#define _PCREPOSIX_H
+
+/* This is the header for the POSIX wrapper interface to the PCRE Perl-
+Compatible Regular Expression library. It defines the things POSIX says should
+be there. I hope.
+
+ Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* Have to include stdlib.h in order to ensure that size_t is defined. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options defined by POSIX. */
+
+#define REG_ICASE 0x01
+#define REG_NEWLINE 0x02
+#define REG_NOTBOL 0x04
+#define REG_NOTEOL 0x08
+
+/* These are not used by PCRE, but by defining them we make it easier
+to slot PCRE into existing programs that make POSIX calls. */
+
+#define REG_EXTENDED 0
+#define REG_NOSUB 0
+
+/* Error values. Not all these are relevant or used by the wrapper. */
+
+enum {
+ REG_ASSERT = 1, /* internal error ? */
+ REG_BADBR, /* invalid repeat counts in {} */
+ REG_BADPAT, /* pattern error */
+ REG_BADRPT, /* ? * + invalid */
+ REG_EBRACE, /* unbalanced {} */
+ REG_EBRACK, /* unbalanced [] */
+ REG_ECOLLATE, /* collation error - not relevant */
+ REG_ECTYPE, /* bad class */
+ REG_EESCAPE, /* bad escape sequence */
+ REG_EMPTY, /* empty expression */
+ REG_EPAREN, /* unbalanced () */
+ REG_ERANGE, /* bad range inside [] */
+ REG_ESIZE, /* expression too big */
+ REG_ESPACE, /* failed to get memory */
+ REG_ESUBREG, /* bad back reference */
+ REG_INVARG, /* bad argument */
+ REG_NOMATCH /* match failed */
+};
+
+
+/* The structure representing a compiled regular expression. */
+
+typedef struct {
+ void *re_pcre;
+ size_t re_nsub;
+ size_t re_erroffset;
+} regex_t;
+
+/* The structure in which a captured offset is returned. */
+
+typedef int regoff_t;
+
+typedef struct {
+ regoff_t rm_so;
+ regoff_t rm_eo;
+} regmatch_t;
+
+/* The functions */
+
+extern int regcomp(regex_t *, const char *, int);
+extern int regexec(const regex_t *, const char *, size_t, regmatch_t *, int);
+extern size_t regerror(int, const regex_t *, char *, size_t);
+extern void regfree(regex_t *);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcreposix.h */
diff --git a/spamfilter/Utilities/PCRE/lib/libpcre-bcc.lib b/spamfilter/Utilities/PCRE/lib/libpcre-bcc.lib
new file mode 100644
index 0000000..706c7af
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/lib/libpcre-bcc.lib
Binary files differ
diff --git a/spamfilter/Utilities/PCRE/lib/libpcre.def b/spamfilter/Utilities/PCRE/lib/libpcre.def
new file mode 100644
index 0000000..a7bdcbf
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/lib/libpcre.def
@@ -0,0 +1,26 @@
+EXPORTS
+ pcre_callout @1 DATA
+ pcre_compile @2
+ pcre_config @3
+ pcre_copy_named_substring @4
+ pcre_copy_substring @5
+ pcre_exec @6
+ pcre_free @7 DATA
+ pcre_free_substring @8
+ pcre_free_substring_list @9
+ pcre_fullinfo @10
+ pcre_get_named_substring @11
+ pcre_get_stringnumber @12
+ pcre_get_substring @13
+ pcre_get_substring_list @14
+ pcre_info @15
+ pcre_maketables @16
+ pcre_malloc @17 DATA
+ pcre_stack_free @18 DATA
+ pcre_stack_malloc @19 DATA
+ pcre_study @20
+ pcre_version @21
+ regcomp @22
+ regerror @23
+ regexec @24
+ regfree @25
diff --git a/spamfilter/Utilities/PCRE/lib/libpcre.dll.a b/spamfilter/Utilities/PCRE/lib/libpcre.dll.a
new file mode 100644
index 0000000..2191488
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/lib/libpcre.dll.a
Binary files differ
diff --git a/spamfilter/Utilities/PCRE/lib/libpcre.lib b/spamfilter/Utilities/PCRE/lib/libpcre.lib
new file mode 100644
index 0000000..71c1835
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/lib/libpcre.lib
Binary files differ
diff --git a/spamfilter/Utilities/PCRE/lib/libpcreposix-bcc.lib b/spamfilter/Utilities/PCRE/lib/libpcreposix-bcc.lib
new file mode 100644
index 0000000..b532b5b
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/lib/libpcreposix-bcc.lib
Binary files differ
diff --git a/spamfilter/Utilities/PCRE/lib/libpcreposix.def b/spamfilter/Utilities/PCRE/lib/libpcreposix.def
new file mode 100644
index 0000000..8dca4c8
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/lib/libpcreposix.def
@@ -0,0 +1,16 @@
+EXPORTS
+ pcre_callout @1 DATA
+ pcre_compile @2
+ pcre_config @3
+ pcre_exec @4
+ pcre_free @5 DATA
+ pcre_fullinfo @6
+ pcre_info @7
+ pcre_malloc @8 DATA
+ pcre_stack_free @9 DATA
+ pcre_stack_malloc @10 DATA
+ pcre_version @11
+ regcomp @12
+ regerror @13
+ regexec @14
+ regfree @15
diff --git a/spamfilter/Utilities/PCRE/lib/libpcreposix.dll.a b/spamfilter/Utilities/PCRE/lib/libpcreposix.dll.a
new file mode 100644
index 0000000..1208b41
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/lib/libpcreposix.dll.a
Binary files differ
diff --git a/spamfilter/Utilities/PCRE/lib/libpcreposix.lib b/spamfilter/Utilities/PCRE/lib/libpcreposix.lib
new file mode 100644
index 0000000..a4bfe43
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/lib/libpcreposix.lib
Binary files differ
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre.3.html b/spamfilter/Utilities/PCRE/man/html/pcre.3.html
new file mode 100644
index 0000000..93f32fa
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre.3.html
@@ -0,0 +1,174 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Introduction</a></h2>
+ <p>
+The PCRE library
+is a set of functions that implement regular expression pattern matching
+using the same syntax and semantics as Perl, with just a few differences.
+The current implementation of PCRE (release 5.x) corresponds approximately
+with Perl 5.8, including support for UTF-8 encoded strings and Unicode general
+category properties. However, this support has to be explicitly enabled;
+it is not the default. <p>
+PCRE is written in C and released as a C library.
+A number of people have written wrappers and interfaces of various kinds.
+A C++ class is included in these contributions, which can be found in the
+<i>Contrib</i> directory at the primary FTP site, which is: <p>
+ ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre
+<p>
+Details of exactly which Perl regular expression features are and are not
+supported by PCRE are given in separate documents. See the <b>pcrepattern</b>
+ and <b>pcrecompat</b> pages. <p>
+Some features of PCRE can be included, excluded,
+or changed when the library is built. The <b>pcre_config()</b> function makes
+it possible for a client to discover which features are available. The features
+themselves are described in the <b>pcrebuild</b> page. Documentation about building
+PCRE for various operating systems can be found in the <b>README</b> file in the
+source distribution.
+<h2><a name='sect2' href='#toc2'>User Documentation</a></h2>
+ <p>
+The user documentation for PCRE
+comprises a number of different sections. In the "man" format, each of these
+is a separate "man page". In the HTML format, each is a separate page, linked
+from the index page. In the plain text format, all the sections are concatenated,
+for ease of searching. The sections are as follows: <p>
+ pcre
+this document<br>
+ pcreapi details of PCRE&rsquo;s native API<br>
+ pcrebuild options for building PCRE<br>
+ pcrecallout details of the callout feature<br>
+ pcrecompat discussion of Perl compatibility<br>
+ pcregrep description of the <b>pcregrep</b> command<br>
+ pcrepartial details of the partial matching facility<br>
+ pcrepattern syntax and semantics of supported<br>
+ regular expressions<br>
+ pcreperform discussion of performance issues<br>
+ pcreposix the POSIX-compatible API<br>
+ pcreprecompile details of saving and re-using precompiled patterns<br>
+ pcresample discussion of the sample program<br>
+ pcretest description of the <b>pcretest</b> testing command<br>
+ <p>
+In addition, in the "man" and HTML formats, there is a short page for
+each library function, listing its arguments and results.
+<h2><a name='sect3' href='#toc3'>Limitations</a></h2>
+
+<p>
+There are some size limitations in PCRE but it is hoped that they will
+never in practice be relevant. <p>
+The maximum length of a compiled pattern
+is 65539 (sic) bytes if PCRE is compiled with the default internal linkage
+size of 2. If you want to process regular expressions that are truly enormous,
+you can compile PCRE with an internal linkage size of 3 or 4 (see the <b>README</b>
+file in the source distribution and the <b>pcrebuild</b> documentation for details).
+In these cases the limit is substantially larger. However, the speed of
+execution will be slower. <p>
+All values in repeating quantifiers must be less
+than 65536. The maximum number of capturing subpatterns is 65535. <p>
+There is
+no limit to the number of non-capturing subpatterns, but the maximum depth
+of nesting of all kinds of parenthesized subpattern, including capturing
+subpatterns, assertions, and other types of subpattern, is 200. <p>
+The maximum
+length of a subject string is the largest positive number that an integer
+variable can hold. However, PCRE uses recursion to handle subpatterns and
+indefinite repetition. This means that the available stack space may limit
+the size of a subject string that can be processed by certain patterns.
+<p>
+
+<h2><a name='sect4' href='#toc4'>Utf-8 and Unicode Property Support</a></h2>
+ <p>
+From release 3.3, PCRE has had some
+support for character strings encoded in the UTF-8 format. For release 4.0
+this was greatly extended to cover most common requirements, and in release
+5.0 additional support for Unicode general category properties was added.
+<p>
+In order process UTF-8 strings, you must build PCRE to include UTF-8 support
+in the code, and, in addition, you must call <b>pcre_compile()</b> with the
+PCRE_UTF8 option flag. When you do this, both the pattern and any subject
+strings that are matched against it are treated as UTF-8 strings instead
+of just strings of bytes. <p>
+If you compile PCRE with UTF-8 support, but do
+not use it at run time, the library will be a bit bigger, but the additional
+run time overhead is limited to testing the PCRE_UTF8 flag in several places,
+so should not be very large. <p>
+If PCRE is built with Unicode character property
+support (which implies UTF-8 support), the escape sequences \p{..}, \P{..}, and
+\X are supported. The available properties that can be tested are limited
+to the general category properties such as Lu for an upper case letter
+or Nd for a decimal number. A full list is given in the <b>pcrepattern</b> documentation.
+The PCRE library is increased in size by about 90K when Unicode property
+support is included. <p>
+The following comments apply when PCRE is running in
+UTF-8 mode: <p>
+1. When you set the PCRE_UTF8 flag, the strings passed as patterns
+and subjects are checked for validity on entry to the relevant functions.
+If an invalid UTF-8 string is passed, an error return is given. In some situations,
+you may already know that your strings are valid, and therefore want to
+skip these checks in order to improve performance. If you set the PCRE_NO_UTF8_CHECK
+flag at compile time or at run time, PCRE assumes that the pattern or subject
+it is given (respectively) contains only valid UTF-8 codes. In this case,
+it does not diagnose an invalid UTF-8 string. If you pass an invalid UTF-8
+string to PCRE when PCRE_NO_UTF8_CHECK is set, the results are undefined.
+Your program may crash. <p>
+2. In a pattern, the escape sequence \x{...}, where the
+contents of the braces is a string of hexadecimal digits, is interpreted
+as a UTF-8 character whose code number is the given hexadecimal number,
+for example: \x{1234}. If a non-hexadecimal digit appears between the braces,
+the item is not recognized. This escape sequence can be used either as a
+literal, or within a character class. <p>
+3. The original hexadecimal escape
+sequence, \xhh, matches a two-byte UTF-8 character if the value is greater
+than 127. <p>
+4. Repeat quantifiers apply to complete UTF-8 characters, not to
+individual bytes, for example: \x{100}{3}. <p>
+5. The dot metacharacter matches
+one UTF-8 character instead of a single byte. <p>
+6. The escape sequence \C can
+be used to match a single byte in UTF-8 mode, but its use can lead to some
+strange effects. <p>
+7. The character escapes \b, \B, \d, \D, \s, \S, \w, and \W correctly
+test characters of any code value, but the characters that PCRE recognizes
+as digits, spaces, or word characters remain the same set as before, all
+with values less than 256. This remains true even when PCRE includes Unicode
+property support, because to do otherwise would slow down PCRE in many
+common cases. If you really want to test for a wider sense of, say, "digit",
+you must use Unicode property tests such as \p{Nd}. <p>
+8. Similarly, characters
+that match the POSIX named character classes are all low-valued characters.
+<p>
+9. Case-insensitive matching applies only to characters whose values are
+less than 128, unless PCRE is built with Unicode property support. Even
+when Unicode property support is available, PCRE still uses its own character
+tables when checking the case of low-valued characters, so as not to degrade
+performance. The Unicode property information is used only for characters
+with higher values.
+<h2><a name='sect5' href='#toc5'>Author</a></h2>
+ <p>
+Philip Hazel &lt;ph10@cam.ac.uk&gt; <br>
+University Computing Service, <br>
+Cambridge CB2 3QG, England. <br>
+Phone: +44 1223 334714 <p>
+ Last updated: 09 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Introduction</a></li>
+<li><a name='toc2' href='#sect2'>User Documentation</a></li>
+<li><a name='toc3' href='#sect3'>Limitations</a></li>
+<li><a name='toc4' href='#sect4'>Utf-8 and Unicode Property Support</a></li>
+<li><a name='toc5' href='#sect5'>Author</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_compile.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_compile.3.html
new file mode 100644
index 0000000..bd9272c
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_compile.3.html
@@ -0,0 +1,67 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>pcre *pcre_compile(const char *<i>pattern</i>, int <i>options</i>,</b> <b>const char **<i>errptr</i>,
+int *<i>erroffset</i>,</b> <b>const unsigned char *<i>tableptr</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This function
+compiles a regular expression into an internal form. Its arguments are:
+<p>
+ <i>pattern</i> A zero-terminated string containing the<br>
+ regular expression to be compiled<br>
+ <i>options</i> Zero or more option bits<br>
+ <i>errptr</i> Where to put an error message<br>
+ <i>erroffset</i> Offset in pattern where error was found<br>
+ <i>tableptr</i> Pointer to character tables, or NULL to<br>
+ use the built-in default<br>
+ <p>
+The option bits are: <p>
+ PCRE_ANCHORED Force pattern anchoring<br>
+ PCRE_AUTO_CALLOUT Compile automatic callouts<br>
+ PCRE_CASELESS Do caseless matching<br>
+ PCRE_DOLLAR_ENDONLY $ not to match newline at end<br>
+ PCRE_DOTALL . matches anything including NL<br>
+ PCRE_EXTENDED Ignore whitespace and # comments<br>
+ PCRE_EXTRA PCRE extra features<br>
+ (not much use currently)<br>
+ PCRE_MULTILINE ^ and $ match newlines within data<br>
+ PCRE_NO_AUTO_CAPTURE Disable numbered capturing paren-<br>
+ theses (named ones available)<br>
+ PCRE_UNGREEDY Invert greediness of quantifiers<br>
+ PCRE_UTF8 Run in UTF-8 mode<br>
+ PCRE_NO_UTF8_CHECK Do not check the pattern for UTF-8<br>
+ validity (only relevant if<br>
+ PCRE_UTF8 is set)<br>
+ <p>
+PCRE must be built with UTF-8 support in order to use PCRE_UTF8 and PCRE_NO_UTF8_CHECK.
+<p>
+The yield of the function is a pointer to a private data structure that
+contains the compiled pattern, or NULL if an error was detected. <p>
+There is
+a complete description of the PCRE native API in the <b>pcreapi</b> page and
+a description of the POSIX API in the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_config.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_config.3.html
new file mode 100644
index 0000000..edf2450
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_config.3.html
@@ -0,0 +1,56 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_config(int <i>what</i>, void *<i>where</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This function makes
+it possible for a client program to find out which optional features are
+available in the version of the PCRE library it is using. Its arguments
+are as follows: <p>
+ <i>what</i> A code specifying what information is required<br>
+ <i>where</i> Points to where to put the data<br>
+ <p>
+The available codes are: <p>
+ PCRE_CONFIG_LINK_SIZE Internal link size:
+2, 3, or 4<br>
+ PCRE_CONFIG_MATCH_LIMIT Internal resource limit<br>
+ PCRE_CONFIG_NEWLINE Value of the newline character<br>
+ PCRE_CONFIG_POSIX_MALLOC_THRESHOLD<br>
+ Threshold of return slots, above<br>
+ which <b>malloc()</b> is used by<br>
+ the POSIX API<br>
+ PCRE_CONFIG_STACKRECURSE Recursion implementation (1=stack 0=heap)<br>
+ PCRE_CONFIG_UTF8 Availability of UTF-8 support (1=yes 0=no)<br>
+ PCRE_CONFIG_UNICODE_PROPERTIES<br>
+ Availability of Unicode property support<br>
+ (1=yes 0=no)<br>
+ <p>
+The function yields 0 on success or PCRE_ERROR_BADOPTION otherwise. <p>
+There
+is a complete description of the PCRE native API in the <b>pcreapi</b> page
+and a description of the POSIX API in the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_copy_named_substring.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_copy_named_substring.3.html
new file mode 100644
index 0000000..09c341f
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_copy_named_substring.3.html
@@ -0,0 +1,48 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_copy_named_substring(const pcre *<i>code</i>,</b> <b>const char *<i>subject</i>, int
+*<i>ovector</i>,</b> <b>int <i>stringcount</i>, const char *<i>stringname</i>,</b> <b>char *<i>buffer</i>, int
+<i>buffersize</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This is a convenience function for extracting
+a captured substring, identified by name, into a given buffer. The arguments
+are: <p>
+ <i>code</i> Pattern that was successfully matched<br>
+ <i>subject</i> Subject that has been successfully matched<br>
+ <i>ovector</i> Offset vector that <b>pcre_exec()</b> used<br>
+ <i>stringcount</i> Value returned by <b>pcre_exec()</b><br>
+ <i>stringname</i> Name of the required substring<br>
+ <i>buffer</i> Buffer to receive the string<br>
+ <i>buffersize</i> Size of buffer<br>
+ <p>
+The yield is the length of the substring, PCRE_ERROR_NOMEMORY if the buffer
+was too small, or PCRE_ERROR_NOSUBSTRING if the string name is invalid.
+<p>
+There is a complete description of the PCRE native API in the <b>pcreapi</b>
+ page and a description of the POSIX API in the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_copy_substring.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_copy_substring.3.html
new file mode 100644
index 0000000..c706691
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_copy_substring.3.html
@@ -0,0 +1,47 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_copy_substring(const char *<i>subject</i>, int *<i>ovector</i>,</b> <b>int <i>stringcount</i>,
+int <i>stringnumber</i>, char *<i>buffer</i>,</b> <b>int <i>buffersize</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This is
+a convenience function for extracting a captured substring into a given
+buffer. The arguments are: <p>
+ <i>subject</i> Subject that has been successfully
+matched<br>
+ <i>ovector</i> Offset vector that <b>pcre_exec()</b> used<br>
+ <i>stringcount</i> Value returned by <b>pcre_exec()</b><br>
+ <i>stringnumber</i> Number of the required substring<br>
+ <i>buffer</i> Buffer to receive the string<br>
+ <i>buffersize</i> Size of buffer<br>
+ <p>
+The yield is the legnth of the string, PCRE_ERROR_NOMEMORY if the buffer
+was too small, or PCRE_ERROR_NOSUBSTRING if the string number is invalid.
+<p>
+There is a complete description of the PCRE native API in the <b>pcreapi</b>
+ page and a description of the POSIX API in the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_exec.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_exec.3.html
new file mode 100644
index 0000000..c985429
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_exec.3.html
@@ -0,0 +1,71 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_exec(const pcre *<i>code</i>, "const pcre_extra *<i>extra</i>,"</b> <b>const char
+*<i>subject</i>, int <i>length</i>, int <i>startoffset</i>,</b> <b>int <i>options</i>, int *<i>ovector</i>, int
+<i>ovecsize</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This function matches a compiled regular expression
+against a given subject string, and returns offsets to capturing subexpressions.
+Its arguments are: <p>
+ <i>code</i> Points to the compiled pattern<br>
+ <i>extra</i> Points to an associated <b>pcre_extra</b> structure,<br>
+ or is NULL<br>
+ <i>subject</i> Points to the subject string<br>
+ <i>length</i> Length of the subject string, in bytes<br>
+ <i>startoffset</i> Offset in bytes in the subject at which to<br>
+ start matching<br>
+ <i>options</i> Option bits<br>
+ <i>ovector</i> Points to a vector of ints for result offsets<br>
+ <i>ovecsize</i> Number of elements in the vector (a multiple of 3)<br>
+ <p>
+The options are: <p>
+ PCRE_ANCHORED Match only at the first position<br>
+ PCRE_NOTBOL Subject is not the beginning of a line<br>
+ PCRE_NOTEOL Subject is not the end of a line<br>
+ PCRE_NOTEMPTY An empty string is not a valid match<br>
+ PCRE_NO_UTF8_CHECK Do not check the subject for UTF-8<br>
+ validity (only relevant if PCRE_UTF8<br>
+ was set at compile time)<br>
+ PCRE_PARTIAL Return PCRE_ERROR_PARTIAL for a partial match<br>
+ <p>
+There are restrictions on what may appear in a pattern when partial matching
+is requested. <p>
+A <b>pcre_extra</b> structure contains the following fields: <p>
+ <i>flags</i>
+ Bits indicating which fields are set<br>
+ <i>study_data</i> Opaque data from <b>pcre_study()</b><br>
+ <i>match_limit</i> Limit on internal recursion<br>
+ <i>callout_data</i> Opaque data passed back to callouts<br>
+ <i>tables</i> Points to character tables or is NULL<br>
+ <p>
+The flag bits are PCRE_EXTRA_STUDY_DATA, PCRE_EXTRA_MATCH_LIMIT, PCRE_EXTRA_CALLOUT_DATA,
+and PCRE_EXTRA_TABLES. <p>
+There is a complete description of the PCRE native
+API in the <b>pcreapi</b> page and a description of the POSIX API in the <b>pcreposix</b>
+ page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_free_substring.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_free_substring.3.html
new file mode 100644
index 0000000..60a97b6
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_free_substring.3.html
@@ -0,0 +1,37 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>void pcre_free_substring(const char *<i>stringptr</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This is a
+convenience function for freeing the store obtained by a previous call
+to <b>pcre_get_substring()</b> or <b>pcre_get_named_substring()</b>. Its only argument
+is a pointer to the string. <p>
+There is a complete description of the PCRE
+native API in the <b>pcreapi</b> page and a description of the POSIX API in
+the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_free_substring_list.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_free_substring_list.3.html
new file mode 100644
index 0000000..a66f3cc
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_free_substring_list.3.html
@@ -0,0 +1,37 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>void pcre_free_substring_list(const char **<i>stringptr</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This
+is a convenience function for freeing the store obtained by a previous
+call to <b>pcre_get_substring_list()</b>. Its only argument is a pointer to the
+list of string pointers. <p>
+There is a complete description of the PCRE native
+API in the <b>pcreapi</b> page and a description of the POSIX API in the <b>pcreposix</b>
+ page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_fullinfo.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_fullinfo.3.html
new file mode 100644
index 0000000..30392fc
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_fullinfo.3.html
@@ -0,0 +1,67 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_fullinfo(const pcre *<i>code</i>, "const pcre_extra *<i>extra</i>,"</b> <b>int <i>what</i>,
+void *<i>where</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This function returns information about a compiled
+pattern. Its arguments are: <p>
+ <i>code</i> Compiled regular
+expression<br>
+ <i>extra</i> Result of <b>pcre_study()</b> or NULL<br>
+ <i>what</i> What information is required<br>
+ <i>where</i> Where to put the information<br>
+ <p>
+The following information is available: <p>
+ PCRE_INFO_BACKREFMAX Number
+of highest back reference<br>
+ PCRE_INFO_CAPTURECOUNT Number of capturing subpatterns<br>
+ PCRE_INFO_DEFAULT_TABLES Pointer to default tables<br>
+ PCRE_INFO_FIRSTBYTE Fixed first byte for a match, or<br>
+ -1 for start of string<br>
+ or after newline, or<br>
+ -2 otherwise<br>
+ PCRE_INFO_FIRSTTABLE Table of first bytes<br>
+ (after studying)<br>
+ PCRE_INFO_LASTLITERAL Literal last byte required<br>
+ PCRE_INFO_NAMECOUNT Number of named subpatterns<br>
+ PCRE_INFO_NAMEENTRYSIZE Size of name table entry<br>
+ PCRE_INFO_NAMETABLE Pointer to name table<br>
+ PCRE_INFO_OPTIONS Options used for compilation<br>
+ PCRE_INFO_SIZE Size of compiled pattern<br>
+ PCRE_INFO_STUDYSIZE Size of study data<br>
+ <p>
+The yield of the function is zero on success or: <p>
+ PCRE_ERROR_NULL
+ the argument <i>code</i> was NULL<br>
+ the argument <i>where</i> was NULL<br>
+ PCRE_ERROR_BADMAGIC the "magic number" was not found<br>
+ PCRE_ERROR_BADOPTION the value of <i>what</i> was invalid<br>
+ <p>
+There is a complete description of the PCRE native API in the <b>pcreapi</b>
+ page and a description of the POSIX API in the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_get_named_substring.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_get_named_substring.3.html
new file mode 100644
index 0000000..14f1c49
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_get_named_substring.3.html
@@ -0,0 +1,48 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_get_named_substring(const pcre *<i>code</i>,</b> <b>const char *<i>subject</i>, int
+*<i>ovector</i>,</b> <b>int <i>stringcount</i>, const char *<i>stringname</i>,</b> <b>const char **<i>stringptr</i>);</b>
+
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This is a convenience function for extracting a captured substring
+by name. The arguments are: <p>
+ <i>code</i> Compiled pattern<br>
+ <i>subject</i> Subject that has been successfully matched<br>
+ <i>ovector</i> Offset vector that <b>pcre_exec()</b> used<br>
+ <i>stringcount</i> Value returned by <b>pcre_exec()</b><br>
+ <i>stringname</i> Name of the required substring<br>
+ <i>stringptr</i> Where to put the string pointer<br>
+ <p>
+The memory in which the substring is placed is obtained by calling <b>pcre_malloc()</b>.
+The yield of the function is the length of the extracted substring, PCRE_ERROR_NOMEMORY
+if sufficient memory could not be obtained, or PCRE_ERROR_NOSUBSTRING if
+the string name is invalid. <p>
+There is a complete description of the PCRE
+native API in the <b>pcreapi</b> page and a description of the POSIX API in
+the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_get_stringnumber.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_get_stringnumber.3.html
new file mode 100644
index 0000000..1ca280c
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_get_stringnumber.3.html
@@ -0,0 +1,42 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_get_stringnumber(const pcre *<i>code</i>,</b> <b>const char *<i>name</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+
+<p>
+This convenience function finds the number of a named substring capturing
+parenthesis in a compiled pattern. Its arguments are: <p>
+ <i>code</i> Compiled
+regular expression<br>
+ <i>name</i> Name whose number is required<br>
+ <p>
+The yield of the function is the number of the parenthesis if the name
+is found, or PCRE_ERROR_NOSUBSTRING otherwise. <p>
+There is a complete description
+of the PCRE native API in the <b>pcreapi</b> page and a description of the POSIX
+API in the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_get_substring.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_get_substring.3.html
new file mode 100644
index 0000000..566a393
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_get_substring.3.html
@@ -0,0 +1,47 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_get_substring(const char *<i>subject</i>, int *<i>ovector</i>,</b> <b>int <i>stringcount</i>,
+int <i>stringnumber</i>,</b> <b>const char **<i>stringptr</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This is a convenience
+function for extracting a captured substring. The arguments are: <p>
+ <i>subject</i>
+ Subject that has been successfully matched<br>
+ <i>ovector</i> Offset vector that <b>pcre_exec()</b> used<br>
+ <i>stringcount</i> Value returned by <b>pcre_exec()</b><br>
+ <i>stringnumber</i> Number of the required substring<br>
+ <i>stringptr</i> Where to put the string pointer<br>
+ <p>
+The memory in which the substring is placed is obtained by calling <b>pcre_malloc()</b>.
+The yield of the function is the length of the substring, PCRE_ERROR_NOMEMORY
+if sufficient memory could not be obtained, or PCRE_ERROR_NOSUBSTRING if
+the string number is invalid. <p>
+There is a complete description of the PCRE
+native API in the <b>pcreapi</b> page and a description of the POSIX API in
+the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_get_substring_list.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_get_substring_list.3.html
new file mode 100644
index 0000000..f4e5931
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_get_substring_list.3.html
@@ -0,0 +1,47 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_get_substring_list(const char *<i>subject</i>,</b> <b>int *<i>ovector</i>, int <i>stringcount</i>,
+"const char ***<i>listptr</i>);"</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This is a convenience function for
+extracting a list of all the captured substrings. The arguments are: <p>
+ <i>subject</i>
+ Subject that has been successfully matched<br>
+ <i>ovector</i> Offset vector that <b>pcre_exec</b> used<br>
+ <i>stringcount</i> Value returned by <b>pcre_exec</b><br>
+ <i>listptr</i> Where to put a pointer to the list<br>
+ <p>
+The memory in which the substrings and the list are placed is obtained
+by calling <b>pcre_malloc()</b>. A pointer to a list of pointers is put in the
+variable whose address is in <i>listptr</i>. The list is terminated by a NULL pointer.
+The yield of the function is zero on success or PCRE_ERROR_NOMEMORY if
+sufficient memory could not be obtained. <p>
+There is a complete description
+of the PCRE native API in the <b>pcreapi</b> page and a description of the POSIX
+API in the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_info.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_info.3.html
new file mode 100644
index 0000000..caf66db
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_info.3.html
@@ -0,0 +1,35 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int pcre_info(const pcre *<i>code</i>, int *<i>optptr</i>, int</b> <b>*<i>firstcharptr</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+
+<p>
+This function is obsolete. You should be using <b>pcre_fullinfo()</b> instead. <p>
+There
+is a complete description of the PCRE native API in the <b>pcreapi</b> page
+and a description of the POSIX API in the <b>pcreposix</b> page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_maketables.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_maketables.3.html
new file mode 100644
index 0000000..7a73848
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_maketables.3.html
@@ -0,0 +1,39 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>const unsigned char *pcre_maketables(void);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This function
+builds a set of character tables for character values less than 256. These
+can be passed to <b>pcre_compile()</b> to override PCRE&rsquo;s internal, built-in tables
+(which were made by <b>pcre_maketables()</b> when PCRE was compiled). You might
+want to do this if you are using a non-standard locale. The function yields
+a pointer to the tables. <p>
+There is a complete description of the PCRE native
+API in the <b>pcreapi</b> page and a description of the POSIX API in the <b>pcreposix</b>
+ page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_study.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_study.3.html
new file mode 100644
index 0000000..20a7a67
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_study.3.html
@@ -0,0 +1,49 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>pcre_extra *pcre_study(const pcre *<i>code</i>, int <i>options</i>,</b> <b>const char **<i>errptr</i>);</b>
+
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This function studies a compiled pattern, to see if additional
+information can be extracted that might speed up matching. Its arguments
+are: <p>
+ <i>code</i> A compiled regular expression<br>
+ <i>options</i> Options for <b>pcre_study()</b><br>
+ <i>errptr</i> Where to put an error message<br>
+ <p>
+If the function succeeds, it returns a value that can be passed to <b>pcre_exec()</b>
+via its <i>extra</i> argument. <p>
+If the function returns NULL, either it could not
+find any additional information, or there was an error. You can tell the
+difference by looking at the error value. It is NULL in first case. <p>
+There
+are currently no options defined; the value of the second argument should
+always be zero. <p>
+There is a complete description of the PCRE native API in
+the <b>pcreapi</b> page and a description of the POSIX API in the <b>pcreposix</b>
+ page. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcre_version.3.html b/spamfilter/Utilities/PCRE/man/html/pcre_version.3.html
new file mode 100644
index 0000000..fe50a95
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcre_version.3.html
@@ -0,0 +1,36 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>char *pcre_version(void);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This function returns a character
+string that gives the version number of the PCRE library and the date of
+its release. <p>
+There is a complete description of the PCRE native API in the
+ <b>pcreapi</b> page and a description of the POSIX API in the <b>pcreposix</b> page.
+<p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcreapi.3.html b/spamfilter/Utilities/PCRE/man/html/pcreapi.3.html
new file mode 100644
index 0000000..a083204
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcreapi.3.html
@@ -0,0 +1,1069 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Pcre Native API</a></h2>
+ <p>
+<b>#include &lt;pcre.h&gt;</b>
+<p>
+<font size='-1'></font>
+ <br>
+<b>pcre *pcre_compile(const char *<i>pattern</i>, int <i>options</i>,</b> <b>const char **<i>errptr</i>,
+int *<i>erroffset</i>,</b> <b>const unsigned char *<i>tableptr</i>);</b> <p>
+<br>
+<b>pcre_extra *pcre_study(const pcre *<i>code</i>, int <i>options</i>,</b> <b>const char **<i>errptr</i>);</b>
+<p>
+<br>
+<b>int pcre_exec(const pcre *<i>code</i>, "const pcre_extra *<i>extra</i>,"</b> <b>const char
+*<i>subject</i>, int <i>length</i>, int <i>startoffset</i>,</b> <b>int <i>options</i>, int *<i>ovector</i>, int
+<i>ovecsize</i>);</b> <p>
+<br>
+<b>int pcre_copy_named_substring(const pcre *<i>code</i>,</b> <b>const char *<i>subject</i>, int
+*<i>ovector</i>,</b> <b>int <i>stringcount</i>, const char *<i>stringname</i>,</b> <b>char *<i>buffer</i>, int
+<i>buffersize</i>);</b> <p>
+<br>
+<b>int pcre_copy_substring(const char *<i>subject</i>, int *<i>ovector</i>,</b> <b>int <i>stringcount</i>,
+int <i>stringnumber</i>, char *<i>buffer</i>,</b> <b>int <i>buffersize</i>);</b> <p>
+<br>
+<b>int pcre_get_named_substring(const pcre *<i>code</i>,</b> <b>const char *<i>subject</i>, int
+*<i>ovector</i>,</b> <b>int <i>stringcount</i>, const char *<i>stringname</i>,</b> <b>const char **<i>stringptr</i>);</b>
+<p>
+<br>
+<b>int pcre_get_stringnumber(const pcre *<i>code</i>,</b> <b>const char *<i>name</i>);</b> <p>
+<br>
+<b>int pcre_get_substring(const char *<i>subject</i>, int *<i>ovector</i>,</b> <b>int <i>stringcount</i>,
+int <i>stringnumber</i>,</b> <b>const char **<i>stringptr</i>);</b> <p>
+<br>
+<b>int pcre_get_substring_list(const char *<i>subject</i>,</b> <b>int *<i>ovector</i>, int <i>stringcount</i>,
+"const char ***<i>listptr</i>);"</b> <p>
+<br>
+<b>void pcre_free_substring(const char *<i>stringptr</i>);</b> <p>
+<br>
+<b>void pcre_free_substring_list(const char **<i>stringptr</i>);</b> <p>
+<br>
+<b>const unsigned char *pcre_maketables(void);</b> <p>
+<br>
+<b>int pcre_fullinfo(const pcre *<i>code</i>, "const pcre_extra *<i>extra</i>,"</b> <b>int <i>what</i>,
+void *<i>where</i>);</b> <p>
+<br>
+<b>int pcre_info(const pcre *<i>code</i>, int *<i>optptr</i>, int</b> <b>*<i>firstcharptr</i>);</b> <p>
+<br>
+<b>int pcre_config(int <i>what</i>, void *<i>where</i>);</b> <p>
+<br>
+<b>char *pcre_version(void);</b> <p>
+<br>
+<b>void *(*pcre_malloc)(size_t);</b> <p>
+<br>
+<b>void (*pcre_free)(void *);</b> <p>
+<br>
+<b>void *(*pcre_stack_malloc)(size_t);</b> <p>
+<br>
+<b>void (*pcre_stack_free)(void *);</b> <p>
+<br>
+<b>int (*pcre_callout)(pcre_callout_block *);</b>
+<h2><a name='sect2' href='#toc2'>Pcre API Overview</a></h2>
+ <p>
+PCRE has
+its own native API, which is described in this document. There is also a
+set of wrapper functions that correspond to the POSIX regular expression
+API. These are described in the <b>pcreposix</b> documentation. <p>
+The native API
+function prototypes are defined in the header file <b>pcre.h</b>, and on Unix systems
+the library itself is called <b>libpcre</b>. It can normally be accessed by adding
+<b>-lpcre</b> to the command for linking an application that uses PCRE. The header
+file defines the macros PCRE_MAJOR and PCRE_MINOR to contain the major
+and minor release numbers for the library. Applications can use these to
+include support for different releases of PCRE. <p>
+The functions <b>pcre_compile()</b>,
+<b>pcre_study()</b>, and <b>pcre_exec()</b> are used for compiling and matching regular
+expressions. A sample program that demonstrates the simplest way of using
+them is provided in the file called <i>pcredemo.c</i> in the source distribution.
+The <b>pcresample</b> documentation describes how to run it. <p>
+In addition to the
+main compiling and matching functions, there are convenience functions
+for extracting captured substrings from a matched subject string. They are:
+<p>
+ <b>pcre_copy_substring()</b><br>
+ <b>pcre_copy_named_substring()</b><br>
+ <b>pcre_get_substring()</b><br>
+ <b>pcre_get_named_substring()</b><br>
+ <b>pcre_get_substring_list()</b><br>
+ <b>pcre_get_stringnumber()</b><br>
+ <p>
+<b>pcre_free_substring()</b> and <b>pcre_free_substring_list()</b> are also provided,
+to free the memory used for extracted strings. <p>
+The function <b>pcre_maketables()</b>
+is used to build a set of character tables in the current locale for passing
+to <b>pcre_compile()</b> or <b>pcre_exec()</b>. This is an optional facility that is provided
+for specialist use. Most commonly, no special tables are passed, in which
+case internal tables that are generated when PCRE is built are used. <p>
+The
+function <b>pcre_fullinfo()</b> is used to find out information about a compiled
+pattern; <b>pcre_info()</b> is an obsolete version that returns only some of the
+available information, but is retained for backwards compatibility. The
+function <b>pcre_version()</b> returns a pointer to a string containing the version
+of PCRE and its date of release. <p>
+The global variables <b>pcre_malloc</b> and <b>pcre_free</b>
+initially contain the entry points of the standard <b>malloc()</b> and <b>free()</b>
+functions, respectively. PCRE calls the memory management functions via
+these variables, so a calling program can replace them if it wishes to
+intercept the calls. This should be done before calling any PCRE functions.
+<p>
+The global variables <b>pcre_stack_malloc</b> and <b>pcre_stack_free</b> are also indirections
+to memory management functions. These special functions are used only when
+PCRE is compiled to use the heap for remembering data, instead of recursive
+function calls. This is a non-standard way of building PCRE, for use in environments
+that have limited stacks. Because of the greater use of memory management,
+it runs more slowly. Separate functions are provided so that special-purpose
+external code can be used for this case. When used, these functions are
+always called in a stack-like manner (last obtained, first freed), and always
+for memory blocks of the same size. <p>
+The global variable <b>pcre_callout</b> initially
+contains NULL. It can be set by the caller to a "callout" function, which
+PCRE will then call at specified points during a matching operation. Details
+are given in the <b>pcrecallout</b> documentation.
+<h2><a name='sect3' href='#toc3'>Multithreading</a></h2>
+ <p>
+The PCRE
+functions can be used in multi-threading applications, with the proviso
+that the memory management functions pointed to by <b>pcre_malloc</b>, <b>pcre_free</b>,
+<b>pcre_stack_malloc</b>, and <b>pcre_stack_free</b>, and the callout function pointed
+to by <b>pcre_callout</b>, are shared by all threads. <p>
+The compiled form of a regular
+expression is not altered during matching, so the same compiled pattern
+can safely be used by several threads at once.
+<h2><a name='sect4' href='#toc4'>Saving Precompiled Patterns
+for Later Use</a></h2>
+ <p>
+The compiled form of a regular expression can be saved and
+re-used at a later time, possibly by a different program, and even on a
+host other than the one on which it was compiled. Details are given in the
+ <b>pcreprecompile</b> documentation.
+<h2><a name='sect5' href='#toc5'>Checking Build-time Options</a></h2>
+ <p>
+<b>int pcre_config(int
+<i>what</i>, void *<i>where</i>);</b> <p>
+The function <b>pcre_config()</b> makes it possible for a
+PCRE client to discover which optional features have been compiled into
+the PCRE library. The <b>pcrebuild</b> documentation has more details about these
+optional features. <p>
+The first argument for <b>pcre_config()</b> is an integer, specifying
+which information is required; the second argument is a pointer to a variable
+into which the information is placed. The following information is available:
+<p>
+ PCRE_CONFIG_UTF8<br>
+ <p>
+The output is an integer that is set to one if UTF-8 support is available;
+otherwise it is set to zero. <p>
+ PCRE_CONFIG_UNICODE_PROPERTIES<br>
+ <p>
+The output is an integer that is set to one if support for Unicode character
+properties is available; otherwise it is set to zero. <p>
+ PCRE_CONFIG_NEWLINE<br>
+ <p>
+The output is an integer that is set to the value of the code that is
+used for the newline character. It is either linefeed (10) or carriage return
+(13), and should normally be the standard character for your operating
+system. <p>
+ PCRE_CONFIG_LINK_SIZE<br>
+ <p>
+The output is an integer that contains the number of bytes used for internal
+linkage in compiled regular expressions. The value is 2, 3, or 4. Larger
+values allow larger regular expressions to be compiled, at the expense
+of slower matching. The default value of 2 is sufficient for all but the
+most massive patterns, since it allows the compiled pattern to be up to
+64K in size. <p>
+ PCRE_CONFIG_POSIX_MALLOC_THRESHOLD<br>
+ <p>
+The output is an integer that contains the threshold above which the POSIX
+interface uses <b>malloc()</b> for output vectors. Further details are given in
+the <b>pcreposix</b> documentation. <p>
+ PCRE_CONFIG_MATCH_LIMIT<br>
+ <p>
+The output is an integer that gives the default limit for the number of
+internal matching function calls in a <b>pcre_exec()</b> execution. Further details
+are given with <b>pcre_exec()</b> below. <p>
+ PCRE_CONFIG_STACKRECURSE<br>
+ <p>
+The output is an integer that is set to one if internal recursion is implemented
+by recursive function calls that use the stack to remember their state.
+This is the usual way that PCRE is compiled. The output is zero if PCRE
+was compiled to use blocks of data on the heap instead of recursive function
+calls. In this case, <b>pcre_stack_malloc</b> and <b>pcre_stack_free</b> are called to
+manage memory blocks on the heap, thus avoiding the use of the stack.
+
+<h2><a name='sect6' href='#toc6'>Compiling a Pattern</a></h2>
+ <p>
+<b>pcre *pcre_compile(const char *<i>pattern</i>, int <i>options</i>,</b>
+ <b>const char **<i>errptr</i>, int *<i>erroffset</i>,</b> <b>const unsigned char *<i>tableptr</i>);</b>
+<p>
+The function <b>pcre_compile()</b> is called to compile a pattern into an internal
+form. The pattern is a C string terminated by a binary zero, and is passed
+in the <i>pattern</i> argument. A pointer to a single block of memory that is obtained
+via <b>pcre_malloc</b> is returned. This contains the compiled code and related
+data. The <b>pcre</b> type is defined for the returned block; this is a typedef
+for a structure whose contents are not externally defined. It is up to the
+caller to free the memory when it is no longer required. <p>
+Although the compiled
+code of a PCRE regex is relocatable, that is, it does not depend on memory
+location, the complete <b>pcre</b> data block is not fully relocatable, because
+it may contain a copy of the <i>tableptr</i> argument, which is an address (see
+below). <p>
+The <i>options</i> argument contains independent bits that affect the compilation.
+It should be zero if no options are required. The available options are
+described below. Some of them, in particular, those that are compatible
+with Perl, can also be set and unset from within the pattern (see the detailed
+description in the <b>pcrepattern</b> documentation). For these options, the
+contents of the <i>options</i> argument specifies their initial settings at the
+start of compilation and execution. The PCRE_ANCHORED option can be set
+at the time of matching as well as at compile time. <p>
+If <i>errptr</i> is NULL, <b>pcre_compile()</b>
+returns NULL immediately. Otherwise, if compilation of a pattern fails,
+<b>pcre_compile()</b> returns NULL, and sets the variable pointed to by <i>errptr</i>
+to point to a textual error message. The offset from the start of the pattern
+to the character where the error was discovered is placed in the variable
+pointed to by <i>erroffset</i>, which must not be NULL. If it is, an immediate
+error is given. <p>
+If the final argument, <i>tableptr</i>, is NULL, PCRE uses a default
+set of character tables that are built when PCRE is compiled, using the
+default C locale. Otherwise, <i>tableptr</i> must be an address that is the result
+of a call to <b>pcre_maketables()</b>. This value is stored with the compiled pattern,
+and used again by <b>pcre_exec()</b>, unless another table pointer is passed to
+it. For more discussion, see the section on locale support below. <p>
+This code
+fragment shows a typical straightforward call to <b>pcre_compile()</b>: <p>
+ pcre
+*re;<br>
+ const char *error;<br>
+ int erroffset;<br>
+ re = pcre_compile(<br>
+ "^A.*Z", /* the pattern */<br>
+ 0, /* default options */<br>
+ &amp;error, /* for error message */<br>
+ &amp;erroffset, /* for error offset */<br>
+ NULL); /* use default character tables */<br>
+ <p>
+The following names for option bits are defined in the <b>pcre.h</b> header file:
+<p>
+ PCRE_ANCHORED<br>
+ <p>
+If this bit is set, the pattern is forced to be "anchored", that is, it
+is constrained to match only at the first matching point in the string
+that is being searched (the "subject string"). This effect can also be achieved
+by appropriate constructs in the pattern itself, which is the only way
+to do it in Perl. <p>
+ PCRE_AUTO_CALLOUT<br>
+ <p>
+If this bit is set, <b>pcre_compile()</b> automatically inserts callout items,
+all with number 255, before each pattern item. For discussion of the callout
+facility, see the <b>pcrecallout</b> documentation. <p>
+ PCRE_CASELESS<br>
+ <p>
+If this bit is set, letters in the pattern match both upper and lower
+case letters. It is equivalent to Perl&rsquo;s /i option, and it can be changed
+within a pattern by a (?i) option setting. When running in UTF-8 mode, case
+support for high-valued characters is available only when PCRE is built
+with Unicode character property support. <p>
+ PCRE_DOLLAR_ENDONLY<br>
+ <p>
+If this bit is set, a dollar metacharacter in the pattern matches only
+at the end of the subject string. Without this option, a dollar also matches
+immediately before the final character if it is a newline (but not before
+any other newlines). The PCRE_DOLLAR_ENDONLY option is ignored if PCRE_MULTILINE
+is set. There is no equivalent to this option in Perl, and no way to set
+it within a pattern. <p>
+ PCRE_DOTALL<br>
+ <p>
+If this bit is set, a dot metacharater in the pattern matches all characters,
+including newlines. Without it, newlines are excluded. This option is equivalent
+to Perl&rsquo;s /s option, and it can be changed within a pattern by a (?s) option
+setting. A negative class such as [^a] always matches a newline character,
+independent of the setting of this option. <p>
+ PCRE_EXTENDED<br>
+ <p>
+If this bit is set, whitespace data characters in the pattern are totally
+ignored except when escaped or inside a character class. Whitespace does
+not include the VT character (code 11). In addition, characters between
+an unescaped # outside a character class and the next newline character,
+inclusive, are also ignored. This is equivalent to Perl&rsquo;s /x option, and
+it can be changed within a pattern by a (?x) option setting. <p>
+This option
+makes it possible to include comments inside complicated patterns. Note,
+however, that this applies only to data characters. Whitespace characters
+may never appear within special character sequences in a pattern, for example
+within the sequence (?( which introduces a conditional subpattern. <p>
+ PCRE_EXTRA<br>
+ <p>
+This option was invented in order to turn on additional functionality
+of PCRE that is incompatible with Perl, but it is currently of very little
+use. When set, any backslash in a pattern that is followed by a letter that
+has no special meaning causes an error, thus reserving these combinations
+for future expansion. By default, as in Perl, a backslash followed by a
+letter with no special meaning is treated as a literal. There are at present
+no other features controlled by this option. It can also be set by a (?X)
+option setting within a pattern. <p>
+ PCRE_MULTILINE<br>
+ <p>
+By default, PCRE treats the subject string as consisting of a single line
+of characters (even if it actually contains newlines). The "start of line"
+metacharacter (^) matches only at the start of the string, while the "end
+of line" metacharacter ($) matches only at the end of the string, or before
+a terminating newline (unless PCRE_DOLLAR_ENDONLY is set). This is the same
+as Perl. <p>
+When PCRE_MULTILINE it is set, the "start of line" and "end of
+line" constructs match immediately following or immediately before any
+newline in the subject string, respectively, as well as at the very start
+and end. This is equivalent to Perl&rsquo;s /m option, and it can be changed within
+a pattern by a (?m) option setting. If there are no "\n" characters in a
+subject string, or no occurrences of ^ or $ in a pattern, setting PCRE_MULTILINE
+has no effect. <p>
+ PCRE_NO_AUTO_CAPTURE<br>
+ <p>
+If this option is set, it disables the use of numbered capturing parentheses
+in the pattern. Any opening parenthesis that is not followed by ? behaves
+as if it were followed by ?: but named parentheses can still be used for
+capturing (and they acquire numbers in the usual way). There is no equivalent
+of this option in Perl. <p>
+ PCRE_UNGREEDY<br>
+ <p>
+This option inverts the "greediness" of the quantifiers so that they are
+not greedy by default, but become greedy if followed by "?". It is not compatible
+with Perl. It can also be set by a (?U) option setting within the pattern.
+<p>
+ PCRE_UTF8<br>
+ <p>
+This option causes PCRE to regard both the pattern and the subject as
+strings of UTF-8 characters instead of single-byte character strings. However,
+it is available only when PCRE is built to include UTF-8 support. If not,
+the use of this option provokes an error. Details of how this option changes
+the behaviour of PCRE are given in the section on UTF-8 support in the
+main <b>pcre</b> page. <p>
+ PCRE_NO_UTF8_CHECK<br>
+ <p>
+When PCRE_UTF8 is set, the validity of the pattern as a UTF-8 string is
+automatically checked. If an invalid UTF-8 sequence of bytes is found, <b>pcre_compile()</b>
+returns an error. If you already know that your pattern is valid, and you
+want to skip this check for performance reasons, you can set the PCRE_NO_UTF8_CHECK
+option. When it is set, the effect of passing an invalid UTF-8 string as
+a pattern is undefined. It may cause your program to crash. Note that this
+option can also be passed to <b>pcre_exec()</b>, to suppress the UTF-8 validity
+checking of subject strings.
+<h2><a name='sect7' href='#toc7'>Studying a Pattern</a></h2>
+ <p>
+<b>pcre_extra *pcre_study(const
+pcre *<i>code</i>, int <i>options</i>,</b> <b>const char **<i>errptr</i>);</b> <p>
+If a compiled pattern is
+going to be used several times, it is worth spending more time analyzing
+it in order to speed up the time taken for matching. The function <b>pcre_study()</b>
+takes a pointer to a compiled pattern as its first argument. If studying
+the pattern produces additional information that will help speed up matching,
+<b>pcre_study()</b> returns a pointer to a <b>pcre_extra</b> block, in which the <i>study_data</i>
+field points to the results of the study. <p>
+The returned value from <b>pcre_study()</b>
+can be passed directly to <b>pcre_exec()</b>. However, a <b>pcre_extra</b> block also
+contains other fields that can be set by the caller before the block is
+passed; these are described below in the section on matching a pattern.
+<p>
+If studying the pattern does not produce any additional information, <b>pcre_study()</b>
+returns NULL. In that circumstance, if the calling program wants to pass
+any of the other fields to <b>pcre_exec()</b>, it must set up its own <b>pcre_extra</b>
+block. <p>
+The second argument of <b>pcre_study()</b> contains option bits. At present,
+no options are defined, and this argument should always be zero. <p>
+The third
+argument for <b>pcre_study()</b> is a pointer for an error message. If studying
+succeeds (even if no data is returned), the variable it points to is set
+to NULL. Otherwise it points to a textual error message. You should therefore
+test the error pointer for NULL after calling <b>pcre_study()</b>, to be sure
+that it has run successfully. <p>
+This is a typical call to <b>pcre_study</b>(): <p>
+
+pcre_extra *pe;<br>
+ pe = pcre_study(<br>
+ re, /* result of pcre_compile() */<br>
+ 0, /* no options exist */<br>
+ &amp;error); /* set to NULL or points to a message */<br>
+ <p>
+At present, studying a pattern is useful only for non-anchored patterns
+that do not have a single fixed starting character. A bitmap of possible
+starting bytes is created.
+<h2><a name='sect8' href='#toc8'>Locale Support</a></h2>
+ <p>
+PCRE handles caseless matching,
+and determines whether characters are letters, digits, or whatever, by
+reference to a set of tables, indexed by character value. (When running
+in UTF-8 mode, this applies only to characters with codes less than 128.
+Higher-valued codes never match escapes such as \w or \d, but can be tested
+with \p if PCRE is built with Unicode character property support.) <p>
+An internal
+set of tables is created in the default C locale when PCRE is built. This
+is used when the final argument of <b>pcre_compile()</b> is NULL, and is sufficient
+for many applications. An alternative set of tables can, however, be supplied.
+These may be created in a different locale from the default. As more and
+more applications change to using Unicode, the need for this locale support
+is expected to die away. <p>
+External tables are built by calling the <b>pcre_maketables()</b>
+function, which has no arguments, in the relevant locale. The result can
+then be passed to <b>pcre_compile()</b> or <b>pcre_exec()</b> as often as necessary. For
+example, to build and use tables that are appropriate for the French locale
+(where accented characters with values greater than 128 are treated as
+letters), the following code could be used: <p>
+ setlocale(LC_CTYPE, "fr_FR");<br>
+ tables = pcre_maketables();<br>
+ re = pcre_compile(..., tables);<br>
+ <p>
+When <b>pcre_maketables()</b> runs, the tables are built in memory that is obtained
+via <b>pcre_malloc</b>. It is the caller&rsquo;s responsibility to ensure that the memory
+containing the tables remains available for as long as it is needed. <p>
+The
+pointer that is passed to <b>pcre_compile()</b> is saved with the compiled pattern,
+and the same tables are used via this pointer by <b>pcre_study()</b> and normally
+also by <b>pcre_exec()</b>. Thus, by default, for any single pattern, compilation,
+studying and matching all happen in the same locale, but different patterns
+can be compiled in different locales. <p>
+It is possible to pass a table pointer
+or NULL (indicating the use of the internal tables) to <b>pcre_exec()</b>. Although
+not intended for this purpose, this facility could be used to match a pattern
+in a different locale from the one in which it was compiled. Passing table
+pointers at run time is discussed below in the section on matching a pattern.
+
+<h2><a name='sect9' href='#toc9'>Information About a Pattern</a></h2>
+ <p>
+<b>int pcre_fullinfo(const pcre *<i>code</i>, "const
+pcre_extra *<i>extra</i>,"</b> <b>int <i>what</i>, void *<i>where</i>);</b> <p>
+The <b>pcre_fullinfo()</b> function
+returns information about a compiled pattern. It replaces the obsolete <b>pcre_info()</b>
+function, which is nevertheless retained for backwards compability (and
+is documented below). <p>
+The first argument for <b>pcre_fullinfo()</b> is a pointer
+to the compiled pattern. The second argument is the result of <b>pcre_study()</b>,
+or NULL if the pattern was not studied. The third argument specifies which
+piece of information is required, and the fourth argument is a pointer
+to a variable to receive the data. The yield of the function is zero for
+success, or one of the following negative numbers: <p>
+ PCRE_ERROR_NULL
+ the argument <i>code</i> was NULL<br>
+ the argument <i>where</i> was NULL<br>
+ PCRE_ERROR_BADMAGIC the "magic number" was not found<br>
+ PCRE_ERROR_BADOPTION the value of <i>what</i> was invalid<br>
+ <p>
+The "magic number" is placed at the start of each compiled pattern as
+an simple check against passing an arbitrary memory pointer. Here is a typical
+call of <b>pcre_fullinfo()</b>, to obtain the length of the compiled pattern:
+<p>
+ int rc;<br>
+ unsigned long int length;<br>
+ rc = pcre_fullinfo(<br>
+ re, /* result of pcre_compile() */<br>
+ pe, /* result of pcre_study(), or NULL */<br>
+ PCRE_INFO_SIZE, /* what is required */<br>
+ &amp;length); /* where to put the data */<br>
+ <p>
+The possible values for the third argument are defined in <b>pcre.h</b>, and are
+as follows: <p>
+ PCRE_INFO_BACKREFMAX<br>
+ <p>
+Return the number of the highest back reference in the pattern. The fourth
+argument should point to an <b>int</b> variable. Zero is returned if there are
+no back references. <p>
+ PCRE_INFO_CAPTURECOUNT<br>
+ <p>
+Return the number of capturing subpatterns in the pattern. The fourth argument
+should point to an <b>int</b> variable. <p>
+ PCRE_INFO_DEFAULTTABLES<br>
+ <p>
+Return a pointer to the internal default character tables within PCRE.
+The fourth argument should point to an <b>unsigned char *</b> variable. This information
+call is provided for internal use by the <b>pcre_study()</b> function. External
+callers can cause PCRE to use its internal tables by passing a NULL table
+pointer. <p>
+ PCRE_INFO_FIRSTBYTE<br>
+ <p>
+Return information about the first byte of any matched string, for a non-anchored
+pattern. (This option used to be called PCRE_INFO_FIRSTCHAR; the old name
+is still recognized for backwards compatibility.) <p>
+If there is a fixed first
+byte, for example, from a pattern such as (cat|cow|coyote), it is returned
+in the integer pointed to by <i>where</i>. Otherwise, if either <p>
+(a) the pattern
+was compiled with the PCRE_MULTILINE option, and every branch starts with
+"^", or <p>
+(b) every branch of the pattern starts with ".*" and PCRE_DOTALL
+is not set (if it were set, the pattern would be anchored), <p>
+-1 is returned,
+indicating that the pattern matches only at the start of a subject string
+or after any newline within the string. Otherwise -2 is returned. For anchored
+patterns, -2 is returned. <p>
+ PCRE_INFO_FIRSTTABLE<br>
+ <p>
+If the pattern was studied, and this resulted in the construction of a
+256-bit table indicating a fixed set of bytes for the first byte in any
+matching string, a pointer to the table is returned. Otherwise NULL is returned.
+The fourth argument should point to an <b>unsigned char *</b> variable. <p>
+ PCRE_INFO_LASTLITERAL<br>
+ <p>
+Return the value of the rightmost literal byte that must exist in any
+matched string, other than at its start, if such a byte has been recorded.
+The fourth argument should point to an <b>int</b> variable. If there is no such
+byte, -1 is returned. For anchored patterns, a last literal byte is recorded
+only if it follows something of variable length. For example, for the pattern
+/^a\d+z\d+/ the returned value is "z", but for /^a\dz\d/ the returned value is
+-1. <p>
+ PCRE_INFO_NAMECOUNT<br>
+ PCRE_INFO_NAMEENTRYSIZE<br>
+ PCRE_INFO_NAMETABLE<br>
+ <p>
+PCRE supports the use of named as well as numbered capturing parentheses.
+The names are just an additional way of identifying the parentheses, which
+still acquire numbers. A convenience function called <b>pcre_get_named_substring()</b>
+is provided for extracting an individual captured substring by name. It
+is also possible to extract the data directly, by first converting the
+name to a number in order to access the correct pointers in the output
+vector (described with <b>pcre_exec()</b> below). To do the conversion, you need
+to use the name-to-number map, which is described by these three values. <p>
+The
+map consists of a number of fixed-size entries. PCRE_INFO_NAMECOUNT gives
+the number of entries, and PCRE_INFO_NAMEENTRYSIZE gives the size of each
+entry; both of these return an <b>int</b> value. The entry size depends on the
+length of the longest name. PCRE_INFO_NAMETABLE returns a pointer to the
+first entry of the table (a pointer to <b>char</b>). The first two bytes of each
+entry are the number of the capturing parenthesis, most significant byte
+first. The rest of the entry is the corresponding name, zero terminated.
+The names are in alphabetical order. For example, consider the following
+pattern (assume PCRE_EXTENDED is set, so white space - including newlines
+- is ignored): <p>
+ (?P&lt;date&gt; (?P&lt;year&gt;(\d\d)?\d\d) -<br>
+ (?P&lt;month&gt;\d\d) - (?P&lt;day&gt;\d\d) )<br>
+ <p>
+There are four named subpatterns, so the table has four entries, and each
+entry in the table is eight bytes long. The table is as follows, with non-printing
+bytes shows in hexadecimal, and undefined bytes shown as ??: <p>
+ 00 01 d
+ a t e 00 ??<br>
+ 00 05 d a y 00 ?? ??<br>
+ 00 04 m o n t h 00<br>
+ 00 02 y e a r 00 ??<br>
+ <p>
+When writing code to extract data from named subpatterns using the name-to-number
+map, remember that the length of each entry is likely to be different for
+each compiled pattern. <p>
+ PCRE_INFO_OPTIONS<br>
+ <p>
+Return a copy of the options with which the pattern was compiled. The fourth
+argument should point to an <b>unsigned long int</b> variable. These option bits
+are those specified in the call to <b>pcre_compile()</b>, modified by any top-level
+option settings within the pattern itself. <p>
+A pattern is automatically anchored
+by PCRE if all of its top-level alternatives begin with one of the following:
+<p>
+ ^ unless PCRE_MULTILINE is set<br>
+ \A always<br>
+ \G always<br>
+ .* if PCRE_DOTALL is set and there are no back<br>
+ references to the subpattern in which .* appears<br>
+ <p>
+For such patterns, the PCRE_ANCHORED bit is set in the options returned
+by <b>pcre_fullinfo()</b>. <p>
+ PCRE_INFO_SIZE<br>
+ <p>
+Return the size of the compiled pattern, that is, the value that was passed
+as the argument to <b>pcre_malloc()</b> when PCRE was getting memory in which
+to place the compiled data. The fourth argument should point to a <b>size_t</b>
+variable. <p>
+ PCRE_INFO_STUDYSIZE<br>
+ <p>
+Return the size of the data block pointed to by the <i>study_data</i> field in
+a <b>pcre_extra</b> block. That is, it is the value that was passed to <b>pcre_malloc()</b>
+when PCRE was getting memory into which to place the data created by <b>pcre_study()</b>.
+The fourth argument should point to a <b>size_t</b> variable.
+<h2><a name='sect10' href='#toc10'>Obsolete Info Function</a></h2>
+
+<p>
+<b>int pcre_info(const pcre *<i>code</i>, int *<i>optptr</i>, int</b> <b>*<i>firstcharptr</i>);</b> <p>
+The <b>pcre_info()</b>
+function is now obsolete because its interface is too restrictive to return
+all the available data about a compiled pattern. New programs should use
+<b>pcre_fullinfo()</b> instead. The yield of <b>pcre_info()</b> is the number of capturing
+subpatterns, or one of the following negative numbers: <p>
+ PCRE_ERROR_NULL
+ the argument <i>code</i> was NULL<br>
+ PCRE_ERROR_BADMAGIC the "magic number" was not found<br>
+ <p>
+If the <i>optptr</i> argument is not NULL, a copy of the options with which the
+pattern was compiled is placed in the integer it points to (see PCRE_INFO_OPTIONS
+above). <p>
+If the pattern is not anchored and the <i>firstcharptr</i> argument is
+not NULL, it is used to pass back information about the first character
+of any matched string (see PCRE_INFO_FIRSTBYTE above).
+<h2><a name='sect11' href='#toc11'>Matching a Pattern</a></h2>
+
+<p>
+<b>int pcre_exec(const pcre *<i>code</i>, "const pcre_extra *<i>extra</i>,"</b> <b>const char
+*<i>subject</i>, int <i>length</i>, int <i>startoffset</i>,</b> <b>int <i>options</i>, int *<i>ovector</i>, int
+<i>ovecsize</i>);</b> <p>
+The function <b>pcre_exec()</b> is called to match a subject string
+against a compiled pattern, which is passed in the <i>code</i> argument. If the
+pattern has been studied, the result of the study should be passed in the
+<i>extra</i> argument. <p>
+In most applications, the pattern will have been compiled
+(and optionally studied) in the same process that calls <b>pcre_exec()</b>. However,
+it is possible to save compiled patterns and study data, and then use them
+later in different processes, possibly even on different hosts. For a discussion
+about this, see the <b>pcreprecompile</b> documentation. <p>
+Here is an example of
+a simple call to <b>pcre_exec()</b>: <p>
+ int rc;<br>
+ int ovector[30];<br>
+ rc = pcre_exec(<br>
+ re, /* result of pcre_compile() */<br>
+ NULL, /* we didn&rsquo;t study the pattern */<br>
+ "some string", /* the subject string */<br>
+ 11, /* the length of the subject string */<br>
+ 0, /* start at offset 0 in the subject */<br>
+ 0, /* default options */<br>
+ ovector, /* vector of integers for substring information */<br>
+ 30); /* number of elements in the vector (NOT size in bytes)
+*/<br>
+
+<h3><a name='sect12' href='#toc12'>Extra data for <b>pcre_exec()</b></a></h3>
+ <p>
+If the <i>extra</i> argument is not NULL, it must
+point to a <b>pcre_extra</b> data block. The <b>pcre_study()</b> function returns such
+a block (when it doesn&rsquo;t return NULL), but you can also create one for yourself,
+and pass additional information in it. The fields in a <b>pcre_extra</b> block
+are as follows: <p>
+ unsigned long int <i>flags</i>;<br>
+ void *<i>study_data</i>;<br>
+ unsigned long int <i>match_limit</i>;<br>
+ void *<i>callout_data</i>;<br>
+ const unsigned char *<i>tables</i>;<br>
+ <p>
+The <i>flags</i> field is a bitmap that specifies which of the other fields are
+set. The flag bits are: <p>
+ PCRE_EXTRA_STUDY_DATA<br>
+ PCRE_EXTRA_MATCH_LIMIT<br>
+ PCRE_EXTRA_CALLOUT_DATA<br>
+ PCRE_EXTRA_TABLES<br>
+ <p>
+Other flag bits should be set to zero. The <i>study_data</i> field is set in the
+<b>pcre_extra</b> block that is returned by <b>pcre_study()</b>, together with the appropriate
+flag bit. You should not set this yourself, but you may add to the block
+by setting the other fields and their corresponding flag bits. <p>
+The <i>match_limit</i>
+field provides a means of preventing PCRE from using up a vast amount of
+resources when running patterns that are not going to match, but which
+have a very large number of possibilities in their search trees. The classic
+example is the use of nested unlimited repeats. <p>
+Internally, PCRE uses a
+function called <b>match()</b> which it calls repeatedly (sometimes recursively).
+The limit is imposed on the number of times this function is called during
+a match, which has the effect of limiting the amount of recursion and backtracking
+that can take place. For patterns that are not anchored, the count starts
+from zero for each position in the subject string. <p>
+The default limit for
+the library can be set when PCRE is built; the default default is 10 million,
+which handles all but the most extreme cases. You can reduce the default
+by suppling <b>pcre_exec()</b> with a <b>pcre_extra</b> block in which <i>match_limit</i> is
+set to a smaller value, and PCRE_EXTRA_MATCH_LIMIT is set in the <i>flags</i>
+field. If the limit is exceeded, <b>pcre_exec()</b> returns PCRE_ERROR_MATCHLIMIT.
+<p>
+The <i>pcre_callout</i> field is used in conjunction with the "callout" feature,
+which is described in the <b>pcrecallout</b> documentation. <p>
+The <i>tables</i> field
+is used to pass a character tables pointer to <b>pcre_exec()</b>; this overrides
+the value that is stored with the compiled pattern. A non-NULL value is stored
+with the compiled pattern only if custom tables were supplied to <b>pcre_compile()</b>
+via its <i>tableptr</i> argument. If NULL is passed to <b>pcre_exec()</b> using this mechanism,
+it forces PCRE&rsquo;s internal tables to be used. This facility is helpful when
+re-using patterns that have been saved after compiling with an external
+set of tables, because the external tables might be at a different address
+when <b>pcre_exec()</b> is called. See the <b>pcreprecompile</b> documentation for a
+discussion of saving compiled patterns for later use.
+<h3><a name='sect13' href='#toc13'>Option bits for <b>pcre_exec()</b></a></h3>
+
+<p>
+The unused bits of the <i>options</i> argument for <b>pcre_exec()</b> must be zero. The
+only bits that may be set are PCRE_ANCHORED, PCRE_NOTBOL, PCRE_NOTEOL,
+PCRE_NOTEMPTY, PCRE_NO_UTF8_CHECK and PCRE_PARTIAL. <p>
+ PCRE_ANCHORED<br>
+ <p>
+The PCRE_ANCHORED option limits <b>pcre_exec()</b> to matching at the first matching
+position. If a pattern was compiled with PCRE_ANCHORED, or turned out to
+be anchored by virtue of its contents, it cannot be made unachored at matching
+time. <p>
+ PCRE_NOTBOL<br>
+ <p>
+This option specifies that first character of the subject string is not
+the beginning of a line, so the circumflex metacharacter should not match
+before it. Setting this without PCRE_MULTILINE (at compile time) causes
+circumflex never to match. This option affects only the behaviour of the
+circumflex metacharacter. It does not affect \A. <p>
+ PCRE_NOTEOL<br>
+ <p>
+This option specifies that the end of the subject string is not the end
+of a line, so the dollar metacharacter should not match it nor (except
+in multiline mode) a newline immediately before it. Setting this without
+PCRE_MULTILINE (at compile time) causes dollar never to match. This option
+affects only the behaviour of the dollar metacharacter. It does not affect
+\Z or \z. <p>
+ PCRE_NOTEMPTY<br>
+ <p>
+An empty string is not considered to be a valid match if this option is
+set. If there are alternatives in the pattern, they are tried. If all the
+alternatives match the empty string, the entire match fails. For example,
+if the pattern <p>
+ a?b?<br>
+ <p>
+is applied to a string not beginning with "a" or "b", it matches the empty
+string at the start of the subject. With PCRE_NOTEMPTY set, this match is
+not valid, so PCRE searches further into the string for occurrences of
+"a" or "b". <p>
+Perl has no direct equivalent of PCRE_NOTEMPTY, but it does
+make a special case of a pattern match of the empty string within its <b>split()</b>
+function, and when using the /g modifier. It is possible to emulate Perl&rsquo;s
+behaviour after matching a null string by first trying the match again
+at the same offset with PCRE_NOTEMPTY and PCRE_ANCHORED, and then if that
+fails by advancing the starting offset (see below) and trying an ordinary
+match again. There is some code that demonstrates how to do this in the
+<i>pcredemo.c</i> sample program. <p>
+ PCRE_NO_UTF8_CHECK<br>
+ <p>
+When PCRE_UTF8 is set at compile time, the validity of the subject as
+a UTF-8 string is automatically checked when <b>pcre_exec()</b> is subsequently
+called. The value of <i>startoffset</i> is also checked to ensure that it points
+to the start of a UTF-8 character. If an invalid UTF-8 sequence of bytes is
+found, <b>pcre_exec()</b> returns the error PCRE_ERROR_BADUTF8. If <i>startoffset</i>
+contains an invalid value, PCRE_ERROR_BADUTF8_OFFSET is returned. <p>
+If you
+already know that your subject is valid, and you want to skip these checks
+for performance reasons, you can set the PCRE_NO_UTF8_CHECK option when
+calling <b>pcre_exec()</b>. You might want to do this for the second and subsequent
+calls to <b>pcre_exec()</b> if you are making repeated calls to find all the matches
+in a single subject string. However, you should be sure that the value of
+<i>startoffset</i> points to the start of a UTF-8 character. When PCRE_NO_UTF8_CHECK
+is set, the effect of passing an invalid UTF-8 string as a subject, or a
+value of <i>startoffset</i> that does not point to the start of a UTF-8 character,
+is undefined. Your program may crash. <p>
+ PCRE_PARTIAL<br>
+ <p>
+This option turns on the partial matching feature. If the subject string
+fails to match the pattern, but at some point during the matching process
+the end of the subject was reached (that is, the subject partially matches
+the pattern and the failure to match occurred only because there were not
+enough subject characters), <b>pcre_exec()</b> returns PCRE_ERROR_PARTIAL instead
+of PCRE_ERROR_NOMATCH. When PCRE_PARTIAL is used, there are restrictions
+on what may appear in the pattern. These are discussed in the <b>pcrepartial</b>
+ documentation.
+<h3><a name='sect14' href='#toc14'>The string to be matched by <b>pcre_exec()</b></a></h3>
+ <p>
+The subject string
+is passed to <b>pcre_exec()</b> as a pointer in <i>subject</i>, a length in <i>length</i>, and
+a starting byte offset in <i>startoffset</i>. In UTF-8 mode, the byte offset must
+point to the start of a UTF-8 character. Unlike the pattern string, the subject
+may contain binary zero bytes. When the starting offset is zero, the search
+for a match starts at the beginning of the subject, and this is by far
+the most common case. <p>
+A non-zero starting offset is useful when searching
+for another match in the same subject by calling <b>pcre_exec()</b> again after
+a previous success. Setting <i>startoffset</i> differs from just passing over a
+shortened string and setting PCRE_NOTBOL in the case of a pattern that
+begins with any kind of lookbehind. For example, consider the pattern <p>
+
+\Biss\B<br>
+ <p>
+which finds occurrences of "iss" in the middle of words. (\B matches only
+if the current position in the subject is not a word boundary.) When applied
+to the string "Mississipi" the first call to <b>pcre_exec()</b> finds the first
+occurrence. If <b>pcre_exec()</b> is called again with just the remainder of the
+subject, namely "issipi", it does not match, because \B is always false
+at the start of the subject, which is deemed to be a word boundary. However,
+if <b>pcre_exec()</b> is passed the entire string again, but with <i>startoffset</i>
+set to 4, it finds the second occurrence of "iss" because it is able to
+look behind the starting point to discover that it is preceded by a letter.
+<p>
+If a non-zero starting offset is passed when the pattern is anchored, one
+attempt to match at the given offset is made. This can only succeed if the
+pattern does not require the match to be at the start of the subject.
+<h3><a name='sect15' href='#toc15'>How
+<b>pcre_exec()</b> returns captured substrings</a></h3>
+ <p>
+In general, a pattern matches a
+certain portion of the subject, and in addition, further substrings from
+the subject may be picked out by parts of the pattern. Following the usage
+in Jeffrey Friedl&rsquo;s book, this is called "capturing" in what follows, and
+the phrase "capturing subpattern" is used for a fragment of a pattern that
+picks out a substring. PCRE supports several other kinds of parenthesized
+subpattern that do not cause substrings to be captured. <p>
+Captured substrings
+are returned to the caller via a vector of integer offsets whose address
+is passed in <i>ovector</i>. The number of elements in the vector is passed in
+<i>ovecsize</i>, which must be a non-negative number. <b>Note</b>: this argument is NOT
+the size of <i>ovector</i> in bytes. <p>
+The first two-thirds of the vector is used
+to pass back captured substrings, each substring using a pair of integers.
+The remaining third of the vector is used as workspace by <b>pcre_exec()</b> while
+matching capturing subpatterns, and is not available for passing back information.
+The length passed in <i>ovecsize</i> should always be a multiple of three. If it
+is not, it is rounded down. <p>
+When a match is successful, information about
+captured substrings is returned in pairs of integers, starting at the beginning
+of <i>ovector</i>, and continuing up to two-thirds of its length at the most. The
+first element of a pair is set to the offset of the first character in
+a substring, and the second is set to the offset of the first character
+after the end of a substring. The first pair, <i>ovector[0]</i> and <i>ovector[1]</i>,
+identify the portion of the subject string matched by the entire pattern.
+The next pair is used for the first capturing subpattern, and so on. The
+value returned by <b>pcre_exec()</b> is the number of pairs that have been set.
+If there are no capturing subpatterns, the return value from a successful
+match is 1, indicating that just the first pair of offsets has been set.
+<p>
+Some convenience functions are provided for extracting the captured substrings
+as separate strings. These are described in the following section. <p>
+It is
+possible for an capturing subpattern number <i>n+1</i> to match some part of the
+subject when subpattern <i>n</i> has not been used at all. For example, if the
+string "abc" is matched against the pattern (a|(z))(bc) subpatterns 1 and
+3 are matched, but 2 is not. When this happens, both offset values corresponding
+to the unused subpattern are set to -1. <p>
+If a capturing subpattern is matched
+repeatedly, it is the last portion of the string that it matched that is
+returned. <p>
+If the vector is too small to hold all the captured substring
+offsets, it is used as far as possible (up to two-thirds of its length),
+and the function returns a value of zero. In particular, if the substring
+offsets are not of interest, <b>pcre_exec()</b> may be called with <i>ovector</i> passed
+as NULL and <i>ovecsize</i> as zero. However, if the pattern contains back references
+and the <i>ovector</i> is not big enough to remember the related substrings, PCRE
+has to get additional memory for use during matching. Thus it is usually
+advisable to supply an <i>ovector</i>. <p>
+Note that <b>pcre_info()</b> can be used to find
+out how many capturing subpatterns there are in a compiled pattern. The
+smallest size for <i>ovector</i> that will allow for <i>n</i> captured substrings, in
+addition to the offsets of the substring matched by the whole pattern,
+is (<i>n</i>+1)*3.
+<h3><a name='sect16' href='#toc16'>Return values from <b>pcre_exec()</b></a></h3>
+ <p>
+If <b>pcre_exec()</b> fails, it returns
+a negative number. The following are defined in the header file: <p>
+ PCRE_ERROR_NOMATCH
+ (-1)<br>
+ <p>
+The subject string did not match the pattern. <p>
+ PCRE_ERROR_NULL
+ (-2)<br>
+ <p>
+Either <i>code</i> or <i>subject</i> was passed as NULL, or <i>ovector</i> was NULL and <i>ovecsize</i>
+was not zero. <p>
+ PCRE_ERROR_BADOPTION (-3)<br>
+ <p>
+An unrecognized bit was set in the <i>options</i> argument. <p>
+ PCRE_ERROR_BADMAGIC
+ (-4)<br>
+ <p>
+PCRE stores a 4-byte "magic number" at the start of the compiled code,
+to catch the case when it is passed a junk pointer and to detect when a
+pattern that was compiled in an environment of one endianness is run in
+an environment with the other endianness. This is the error that PCRE gives
+when the magic number is not present. <p>
+ PCRE_ERROR_UNKNOWN_NODE (-5)<br>
+ <p>
+While running the pattern match, an unknown item was encountered in the
+compiled pattern. This error could be caused by a bug in PCRE or by overwriting
+of the compiled pattern. <p>
+ PCRE_ERROR_NOMEMORY (-6)<br>
+ <p>
+If a pattern contains back references, but the <i>ovector</i> that is passed
+to <b>pcre_exec()</b> is not big enough to remember the referenced substrings,
+PCRE gets a block of memory at the start of matching to use for this purpose.
+If the call via <b>pcre_malloc()</b> fails, this error is given. The memory is
+automatically freed at the end of matching. <p>
+ PCRE_ERROR_NOSUBSTRING
+(-7)<br>
+ <p>
+This error is used by the <b>pcre_copy_substring()</b>, <b>pcre_get_substring()</b>,
+and <b>pcre_get_substring_list()</b> functions (see below). It is never returned
+by <b>pcre_exec()</b>. <p>
+ PCRE_ERROR_MATCHLIMIT (-8)<br>
+ <p>
+The recursion and backtracking limit, as specified by the <i>match_limit</i>
+field in a <b>pcre_extra</b> structure (or defaulted) was reached. See the description
+above. <p>
+ PCRE_ERROR_CALLOUT (-9)<br>
+ <p>
+This error is never generated by <b>pcre_exec()</b> itself. It is provided for
+use by callout functions that want to yield a distinctive error code. See
+the <b>pcrecallout</b> documentation for details. <p>
+ PCRE_ERROR_BADUTF8
+ (-10)<br>
+ <p>
+A string that contains an invalid UTF-8 byte sequence was passed as a subject.
+<p>
+ PCRE_ERROR_BADUTF8_OFFSET (-11)<br>
+ <p>
+The UTF-8 byte sequence that was passed as a subject was valid, but the
+value of <i>startoffset</i> did not point to the beginning of a UTF-8 character.
+<p>
+ PCRE_ERROR_PARTIAL (-12)<br>
+ <p>
+The subject string did not match, but it did match partially. See the
+<b>pcrepartial</b> documentation for details of partial matching. <p>
+ PCRE_ERROR_BAD_PARTIAL
+(-13)<br>
+ <p>
+The PCRE_PARTIAL option was used with a compiled pattern containing items
+that are not supported for partial matching. See the <b>pcrepartial</b> documentation
+for details of partial matching. <p>
+ PCRE_ERROR_INTERNAL (-14)<br>
+ <p>
+An unexpected internal error has occurred. This error could be caused by
+a bug in PCRE or by overwriting of the compiled pattern. <p>
+ PCRE_ERROR_BADCOUNT
+(-15)<br>
+ <p>
+This error is given if the value of the <i>ovecsize</i> argument is negative.
+
+<h2><a name='sect17' href='#toc17'>Extracting Captured Substrings by Number</a></h2>
+ <p>
+<b>int pcre_copy_substring(const
+char *<i>subject</i>, int *<i>ovector</i>,</b> <b>int <i>stringcount</i>, int <i>stringnumber</i>, char *<i>buffer</i>,</b>
+ <b>int <i>buffersize</i>);</b> <p>
+<br>
+<b>int pcre_get_substring(const char *<i>subject</i>, int *<i>ovector</i>,</b> <b>int <i>stringcount</i>,
+int <i>stringnumber</i>,</b> <b>const char **<i>stringptr</i>);</b> <p>
+<br>
+<b>int pcre_get_substring_list(const char *<i>subject</i>,</b> <b>int *<i>ovector</i>, int <i>stringcount</i>,
+"const char ***<i>listptr</i>);"</b> <p>
+Captured substrings can be accessed directly
+by using the offsets returned by <b>pcre_exec()</b> in <i>ovector</i>. For convenience,
+the functions <b>pcre_copy_substring()</b>, <b>pcre_get_substring()</b>, and <b>pcre_get_substring_list()</b>
+are provided for extracting captured substrings as new, separate, zero-terminated
+strings. These functions identify substrings by number. The next section
+describes functions for extracting named substrings. A substring that contains
+a binary zero is correctly extracted and has a further zero added on the
+end, but the result is not, of course, a C string. <p>
+The first three arguments
+are the same for all three of these functions: <i>subject</i> is the subject string
+that has just been successfully matched, <i>ovector</i> is a pointer to the vector
+of integer offsets that was passed to <b>pcre_exec()</b>, and <i>stringcount</i> is the
+number of substrings that were captured by the match, including the substring
+that matched the entire regular expression. This is the value returned by
+<b>pcre_exec()</b> if it is greater than zero. If <b>pcre_exec()</b> returned zero, indicating
+that it ran out of space in <i>ovector</i>, the value passed as <i>stringcount</i> should
+be the number of elements in the vector divided by three. <p>
+The functions
+<b>pcre_copy_substring()</b> and <b>pcre_get_substring()</b> extract a single substring,
+whose number is given as <i>stringnumber</i>. A value of zero extracts the substring
+that matched the entire pattern, whereas higher values extract the captured
+substrings. For <b>pcre_copy_substring()</b>, the string is placed in <i>buffer</i>, whose
+length is given by <i>buffersize</i>, while for <b>pcre_get_substring()</b> a new block
+of memory is obtained via <b>pcre_malloc</b>, and its address is returned via
+<i>stringptr</i>. The yield of the function is the length of the string, not including
+the terminating zero, or one of <p>
+ PCRE_ERROR_NOMEMORY (-6)<br>
+ <p>
+The buffer was too small for <b>pcre_copy_substring()</b>, or the attempt to
+get memory failed for <b>pcre_get_substring()</b>. <p>
+ PCRE_ERROR_NOSUBSTRING
+(-7)<br>
+ <p>
+There is no substring whose number is <i>stringnumber</i>. <p>
+The <b>pcre_get_substring_list()</b>
+function extracts all available substrings and builds a list of pointers
+to them. All this is done in a single block of memory that is obtained via
+<b>pcre_malloc</b>. The address of the memory block is returned via <i>listptr</i>, which
+is also the start of the list of string pointers. The end of the list is
+marked by a NULL pointer. The yield of the function is zero if all went
+well, or <p>
+ PCRE_ERROR_NOMEMORY (-6)<br>
+ <p>
+if the attempt to get the memory block failed. <p>
+When any of these functions
+encounter a substring that is unset, which can happen when capturing subpattern
+number <i>n+1</i> matches some part of the subject, but subpattern <i>n</i> has not been
+used at all, they return an empty string. This can be distinguished from
+a genuine zero-length substring by inspecting the appropriate offset in
+<i>ovector</i>, which is negative for unset substrings. <p>
+The two convenience functions
+<b>pcre_free_substring()</b> and <b>pcre_free_substring_list()</b> can be used to free
+the memory returned by a previous call of <b>pcre_get_substring()</b> or <b>pcre_get_substring_list()</b>,
+respectively. They do nothing more than call the function pointed to by
+<b>pcre_free</b>, which of course could be called directly from a C program. However,
+PCRE is used in some situations where it is linked via a special interface
+to another programming language which cannot use <b>pcre_free</b> directly; it
+is for these cases that the functions are provided.
+<h2><a name='sect18' href='#toc18'>Extracting Captured
+Substrings by Name</a></h2>
+ <p>
+<b>int pcre_get_stringnumber(const pcre *<i>code</i>,</b> <b>const char
+*<i>name</i>);</b> <p>
+<br>
+<b>int pcre_copy_named_substring(const pcre *<i>code</i>,</b> <b>const char *<i>subject</i>, int
+*<i>ovector</i>,</b> <b>int <i>stringcount</i>, const char *<i>stringname</i>,</b> <b>char *<i>buffer</i>, int
+<i>buffersize</i>);</b> <p>
+<br>
+<b>int pcre_get_named_substring(const pcre *<i>code</i>,</b> <b>const char *<i>subject</i>, int
+*<i>ovector</i>,</b> <b>int <i>stringcount</i>, const char *<i>stringname</i>,</b> <b>const char **<i>stringptr</i>);</b>
+<p>
+To extract a substring by name, you first have to find associated number.
+For example, for this pattern <p>
+ (a+)b(?&lt;xxx&gt;\d+)...<br>
+ <p>
+the number of the subpattern called "xxx" is 2. You can find the number
+from the name by calling <b>pcre_get_stringnumber()</b>. The first argument is
+the compiled pattern, and the second is the name. The yield of the function
+is the subpattern number, or PCRE_ERROR_NOSUBSTRING (-7) if there is no
+subpattern of that name. <p>
+Given the number, you can extract the substring
+directly, or use one of the functions described in the previous section.
+For convenience, there are also two functions that do the whole job. <p>
+Most
+of the arguments of <i>pcre_copy_named_substring()</i> and <i>pcre_get_named_substring()</i>
+are the same as those for the similarly named functions that extract by
+number. As these are described in the previous section, they are not re-described
+here. There are just two differences: <p>
+First, instead of a substring number,
+a substring name is given. Second, there is an extra argument, given at
+the start, which is a pointer to the compiled pattern. This is needed in
+order to gain access to the name-to-number translation table. <p>
+These functions
+call <b>pcre_get_stringnumber()</b>, and if it succeeds, they then call <i>pcre_copy_substring()</i>
+or <i>pcre_get_substring()</i>, as appropriate. <p>
+ Last updated: 09 September 2004
+<br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Pcre Native API</a></li>
+<li><a name='toc2' href='#sect2'>Pcre API Overview</a></li>
+<li><a name='toc3' href='#sect3'>Multithreading</a></li>
+<li><a name='toc4' href='#sect4'>Saving Precompiled Patterns for Later Use</a></li>
+<li><a name='toc5' href='#sect5'>Checking Build-time Options</a></li>
+<li><a name='toc6' href='#sect6'>Compiling a Pattern</a></li>
+<li><a name='toc7' href='#sect7'>Studying a Pattern</a></li>
+<li><a name='toc8' href='#sect8'>Locale Support</a></li>
+<li><a name='toc9' href='#sect9'>Information About a Pattern</a></li>
+<li><a name='toc10' href='#sect10'>Obsolete Info Function</a></li>
+<li><a name='toc11' href='#sect11'>Matching a Pattern</a></li>
+<ul>
+<li><a name='toc12' href='#sect12'>Extra data for pcre_exec()</a></li>
+<li><a name='toc13' href='#sect13'>Option bits for pcre_exec()</a></li>
+<li><a name='toc14' href='#sect14'>The string to be matched by pcre_exec()</a></li>
+<li><a name='toc15' href='#sect15'>How pcre_exec() returns captured substrings</a></li>
+<li><a name='toc16' href='#sect16'>Return values from pcre_exec()</a></li>
+</ul>
+<li><a name='toc17' href='#sect17'>Extracting Captured Substrings by Number</a></li>
+<li><a name='toc18' href='#sect18'>Extracting Captured Substrings by Name</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcrebuild.3.html b/spamfilter/Utilities/PCRE/man/html/pcrebuild.3.html
new file mode 100644
index 0000000..950a1f0
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcrebuild.3.html
@@ -0,0 +1,167 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Pcre Build-time Options</a></h2>
+ <p>
+This
+document describes the optional features of PCRE that can be selected when
+the library is compiled. They are all selected, or deselected, by providing
+options to the <b>configure</b> script that is run before the <b>make</b> command. The
+complete list of options for <b>configure</b> (which includes the standard ones
+such as the selection of the installation directory) can be obtained by
+running <p>
+ ./configure --help<br>
+ <p>
+The following sections describe certain options whose names begin with
+--enable or --disable. These settings specify changes to the defaults for the
+<b>configure</b> command. Because of the way that <b>configure</b> works, --enable and --disable
+always come in pairs, so the complementary option always exists as well,
+but as it specifies the default, it is not described.
+<h2><a name='sect2' href='#toc2'>Utf-8 Support</a></h2>
+ <p>
+To build
+PCRE with support for UTF-8 character strings, add <p>
+ --enable-utf8<br>
+ <p>
+to the <b>configure</b> command. Of itself, this does not make PCRE treat strings
+as UTF-8. As well as compiling PCRE with this option, you also have have
+to set the PCRE_UTF8 option when you call the <b>pcre_compile()</b> function.
+
+<h2><a name='sect3' href='#toc3'>Unicode Character Property Support</a></h2>
+ <p>
+UTF-8 support allows PCRE to process
+character values greater than 255 in the strings that it handles. On its
+own, however, it does not provide any facilities for accessing the properties
+of such characters. If you want to be able to use the pattern escapes \P,
+\p, and \X, which refer to Unicode character properties, you must add <p>
+ --enable-unicode-properties<br>
+ <p>
+to the <b>configure</b> command. This implies UTF-8 support, even if you have not
+explicitly requested it. <p>
+Including Unicode property support adds around
+90K of tables to the PCRE library, approximately doubling its size. Only
+the general category properties such as <i>Lu</i> and <i>Nd</i> are supported. Details
+are given in the <b>pcrepattern</b> documentation.
+<h2><a name='sect4' href='#toc4'>Code Value of Newline</a></h2>
+ <p>
+By
+default, PCRE treats character 10 (linefeed) as the newline character. This
+is the normal newline character on Unix-like systems. You can compile PCRE
+to use character 13 (carriage return) instead by adding <p>
+ --enable-newline-is-cr<br>
+ <p>
+to the <b>configure</b> command. For completeness there is also a --enable-newline-is-lf
+option, which explicitly specifies linefeed as the newline character.
+<h2><a name='sect5' href='#toc5'>Building
+Shared and Static Libraries</a></h2>
+ <p>
+The PCRE building process uses <b>libtool</b> to build
+both shared and static Unix libraries by default. You can suppress one of
+these by adding one of <p>
+ --disable-shared<br>
+ --disable-static<br>
+ <p>
+to the <b>configure</b> command, as required.
+<h2><a name='sect6' href='#toc6'>Posix Malloc Usage</a></h2>
+ <p>
+When PCRE is
+called through the POSIX interface (see the <b>pcreposix</b> documentation),
+additional working storage is required for holding the pointers to capturing
+substrings, because PCRE requires three integers per substring, whereas
+the POSIX interface provides only two. If the number of expected substrings
+is small, the wrapper function uses space on the stack, because this is
+faster than using <b>malloc()</b> for each call. The default threshold above which
+the stack is no longer used is 10; it can be changed by adding a setting
+such as <p>
+ --with-posix-malloc-threshold=20<br>
+ <p>
+to the <b>configure</b> command.
+<h2><a name='sect7' href='#toc7'>Limiting Pcre Resource Usage</a></h2>
+ <p>
+Internally, PCRE
+has a function called <b>match()</b>, which it calls repeatedly (possibly recursively)
+when matching a pattern. By controlling the maximum number of times this
+function may be called during a single matching operation, a limit can
+be placed on the resources used by a single call to <b>pcre_exec()</b>. The limit
+can be changed at run time, as described in the <b>pcreapi</b> documentation.
+The default is 10 million, but this can be changed by adding a setting
+such as <p>
+ --with-match-limit=500000<br>
+ <p>
+to the <b>configure</b> command.
+<h2><a name='sect8' href='#toc8'>Handling Very Large Patterns</a></h2>
+ <p>
+Within a compiled
+pattern, offset values are used to point from one part to another (for
+example, from an opening parenthesis to an alternation metacharacter). By
+default, two-byte values are used for these offsets, leading to a maximum
+size for a compiled pattern of around 64K. This is sufficient to handle
+all but the most gigantic patterns. Nevertheless, some people do want to
+process enormous patterns, so it is possible to compile PCRE to use three-byte
+or four-byte offsets by adding a setting such as <p>
+ --with-link-size=3<br>
+ <p>
+to the <b>configure</b> command. The value given must be 2, 3, or 4. Using longer
+offsets slows down the operation of PCRE because it has to load additional
+bytes when handling them. <p>
+If you build PCRE with an increased link size,
+test 2 (and test 5 if you are using UTF-8) will fail. Part of the output
+of these tests is a representation of the compiled pattern, and this changes
+with the link size.
+<h2><a name='sect9' href='#toc9'>Avoiding Excessive Stack Usage</a></h2>
+ <p>
+PCRE implements backtracking
+while matching by making recursive calls to an internal function called
+<b>match()</b>. In environments where the size of the stack is limited, this can
+severely limit PCRE&rsquo;s operation. (The Unix environment does not usually suffer
+from this problem.) An alternative approach that uses memory from the heap
+to remember data, instead of using recursive function calls, has been implemented
+to work round this problem. If you want to build a version of PCRE that
+works this way, add <p>
+ --disable-stack-for-recursion<br>
+ <p>
+to the <b>configure</b> command. With this configuration, PCRE will use the <b>pcre_stack_malloc</b>
+and <b>pcre_stack_free</b> variables to call memory management functions. Separate
+functions are provided because the usage is very predictable: the block
+sizes requested are always the same, and the blocks are always freed in
+reverse order. A calling program might be able to implement optimized functions
+that perform better than the standard <b>malloc()</b> and <b>free()</b> functions. PCRE
+runs noticeably more slowly when built in this way.
+<h2><a name='sect10' href='#toc10'>Using Ebcdic Code</a></h2>
+ <p>
+PCRE
+assumes by default that it will run in an environment where the character
+code is ASCII (or Unicode, which is a superset of ASCII). PCRE can, however,
+be compiled to run in an EBCDIC environment by adding <p>
+ --enable-ebcdic<br>
+ <p>
+to the <b>configure</b> command. <p>
+ Last updated: 09 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Pcre Build-time Options</a></li>
+<li><a name='toc2' href='#sect2'>Utf-8 Support</a></li>
+<li><a name='toc3' href='#sect3'>Unicode Character Property Support</a></li>
+<li><a name='toc4' href='#sect4'>Code Value of Newline</a></li>
+<li><a name='toc5' href='#sect5'>Building Shared and Static Libraries</a></li>
+<li><a name='toc6' href='#sect6'>Posix Malloc Usage</a></li>
+<li><a name='toc7' href='#sect7'>Limiting Pcre Resource Usage</a></li>
+<li><a name='toc8' href='#sect8'>Handling Very Large Patterns</a></li>
+<li><a name='toc9' href='#sect9'>Avoiding Excessive Stack Usage</a></li>
+<li><a name='toc10' href='#sect10'>Using Ebcdic Code</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcrecallout.3.html b/spamfilter/Utilities/PCRE/man/html/pcrecallout.3.html
new file mode 100644
index 0000000..83e61b2
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcrecallout.3.html
@@ -0,0 +1,148 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Pcre Callouts</a></h2>
+ <p>
+<b>int (*pcre_callout)(pcre_callout_block
+*);</b> <p>
+PCRE provides a feature called "callout", which is a means of temporarily
+passing control to the caller of PCRE in the middle of pattern matching.
+The caller of PCRE provides an external function by putting its entry point
+in the global variable <i>pcre_callout</i>. By default, this variable contains
+NULL, which disables all calling out. <p>
+Within a regular expression, (?C)
+indicates the points at which the external function is to be called. Different
+callout points can be identified by putting a number less than 256 after
+the letter C. The default value is zero. For example, this pattern has two
+callout points: <p>
+ (?C1)deabc(?C2)def<br>
+ <p>
+If the PCRE_AUTO_CALLOUT option bit is set when <b>pcre_compile()</b> is called,
+PCRE automatically inserts callouts, all with number 255, before each item
+in the pattern. For example, if PCRE_AUTO_CALLOUT is used with the pattern
+<p>
+ A(\d{2}|--)<br>
+ <p>
+it is processed as if it were <p>
+(?C255)A(?C255)((?C255)\d{2}(?C255)|(?C255)-(?C255)-(?C255))(?C255)
+<p>
+Notice that there is a callout before and after each parenthesis and alternation
+bar. Automatic callouts can be used for tracking the progress of pattern
+matching. The <b>pcretest</b> command has an option that sets automatic callouts;
+when it is used, the output indicates how the pattern is matched. This is
+useful information when you are trying to optimize the performance of a
+particular pattern.
+<h2><a name='sect2' href='#toc2'>Missing Callouts</a></h2>
+ <p>
+You should be aware that, because
+of optimizations in the way PCRE matches patterns, callouts sometimes do
+not happen. For example, if the pattern is <p>
+ ab(?C4)cd<br>
+ <p>
+PCRE knows that any matching string must contain the letter "d". If the
+subject string is "abyz", the lack of "d" means that matching doesn&rsquo;t ever
+start, and the callout is never reached. However, with "abyd", though the
+result is still no match, the callout is obeyed.
+<h2><a name='sect3' href='#toc3'>the Callout Interface</a></h2>
+
+<p>
+During matching, when PCRE reaches a callout point, the external function
+defined by <i>pcre_callout</i> is called (if it is set). The only argument is a
+pointer to a <b>pcre_callout</b> block. This structure contains the following fields:
+<p>
+ int <i>version</i>;<br>
+ int <i>callout_number</i>;<br>
+ int *<i>offset_vector</i>;<br>
+ const char *<i>subject</i>;<br>
+ int <i>subject_length</i>;<br>
+ int <i>start_match</i>;<br>
+ int <i>current_position</i>;<br>
+ int <i>capture_top</i>;<br>
+ int <i>capture_last</i>;<br>
+ void *<i>callout_data</i>;<br>
+ int <i>pattern_position</i>;<br>
+ int <i>next_item_length</i>;<br>
+ <p>
+The <i>version</i> field is an integer containing the version number of the block
+format. The initial version was 0; the current version is 1. The version
+number will change again in future if additional fields are added, but
+the intention is never to remove any of the existing fields. <p>
+The <i>callout_number</i>
+field contains the number of the callout, as compiled into the pattern
+(that is, the number after ?C for manual callouts, and 255 for automatically
+generated callouts). <p>
+The <i>offset_vector</i> field is a pointer to the vector
+of offsets that was passed by the caller to <b>pcre_exec()</b>. The contents can
+be inspected in order to extract substrings that have been matched so far,
+in the same way as for extracting substrings after a match has completed.
+<p>
+The <i>subject</i> and <i>subject_length</i> fields contain copies of the values that
+were passed to <b>pcre_exec()</b>. <p>
+The <i>start_match</i> field contains the offset within
+the subject at which the current match attempt started. If the pattern is
+not anchored, the callout function may be called several times from the
+same point in the pattern for different starting points in the subject.
+<p>
+The <i>current_position</i> field contains the offset within the subject of the
+current match pointer. <p>
+The <i>capture_top</i> field contains one more than the
+number of the highest numbered captured substring so far. If no substrings
+have been captured, the value of <i>capture_top</i> is one. <p>
+The <i>capture_last</i> field
+contains the number of the most recently captured substring. If no substrings
+have been captured, its value is -1. <p>
+The <i>callout_data</i> field contains a value
+that is passed to <b>pcre_exec()</b> by the caller specifically so that it can
+be passed back in callouts. It is passed in the <i>pcre_callout</i> field of the
+<b>pcre_extra</b> data structure. If no such data was passed, the value of <i>callout_data</i>
+in a <b>pcre_callout</b> block is NULL. There is a description of the <b>pcre_extra</b>
+structure in the <b>pcreapi</b> documentation. <p>
+The <i>pattern_position</i> field is
+present from version 1 of the <i>pcre_callout</i> structure. It contains the offset
+to the next item to be matched in the pattern string. <p>
+The <i>next_item_length</i>
+field is present from version 1 of the <i>pcre_callout</i> structure. It contains
+the length of the next item to be matched in the pattern string. When the
+callout immediately precedes an alternation bar, a closing parenthesis,
+or the end of the pattern, the length is zero. When the callout precedes
+an opening parenthesis, the length is that of the entire subpattern. <p>
+The
+<i>pattern_position</i> and <i>next_item_length</i> fields are intended to help in distinguishing
+between different automatic callouts, which all have the same callout number.
+However, they are set for all callouts.
+<h2><a name='sect4' href='#toc4'>Return Values</a></h2>
+ <p>
+The external callout
+function returns an integer to PCRE. If the value is zero, matching proceeds
+as normal. If the value is greater than zero, matching fails at the current
+point, but backtracking to test other matching possibilities goes ahead,
+just as if a lookahead assertion had failed. If the value is less than zero,
+the match is abandoned, and <b>pcre_exec()</b> returns the negative value. <p>
+Negative
+values should normally be chosen from the set of PCRE_ERROR_xxx values.
+In particular, PCRE_ERROR_NOMATCH forces a standard "no match" failure.
+The error number PCRE_ERROR_CALLOUT is reserved for use by callout functions;
+it will never be used by PCRE itself. <p>
+ Last updated: 09 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Pcre Callouts</a></li>
+<li><a name='toc2' href='#sect2'>Missing Callouts</a></li>
+<li><a name='toc3' href='#sect3'>the Callout Interface</a></li>
+<li><a name='toc4' href='#sect4'>Return Values</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcrecompat.3.html b/spamfilter/Utilities/PCRE/man/html/pcrecompat.3.html
new file mode 100644
index 0000000..af67000
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcrecompat.3.html
@@ -0,0 +1,115 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Differences Between Pcre and
+Perl</a></h2>
+ <p>
+This document describes the differences in the ways that PCRE and
+Perl handle regular expressions. The differences described here are with
+respect to Perl 5.8. <p>
+1. PCRE does not have full UTF-8 support. Details of what
+it does have are given in the section on UTF-8 support in the main <b>pcre</b>
+ page. <p>
+2. PCRE does not allow repeat quantifiers on lookahead assertions.
+Perl permits them, but they do not mean what you might think. For example,
+(?!a){3} does not assert that the next three characters are not "a". It
+just asserts that the next character is not "a" three times. <p>
+3. Capturing
+subpatterns that occur inside negative lookahead assertions are counted,
+but their entries in the offsets vector are never set. Perl sets its numerical
+variables from any such patterns that are matched before the assertion
+fails to match something (thereby succeeding), but only if the negative
+lookahead assertion contains just one branch. <p>
+4. Though binary zero characters
+are supported in the subject string, they are not allowed in a pattern
+string because it is passed as a normal C string, terminated by zero. The
+escape sequence \0 can be used in the pattern to represent a binary zero.
+<p>
+5. The following Perl escape sequences are not supported: \l, \u, \L, \U, and
+\N. In fact these are implemented by Perl&rsquo;s general string-handling and are
+not part of its pattern matching engine. If any of these are encountered
+by PCRE, an error is generated. <p>
+6. The Perl escape sequences \p, \P, and \X
+are supported only if PCRE is built with Unicode character property support.
+The properties that can be tested with \p and \P are limited to the general
+category properties such as Lu and Nd. <p>
+7. PCRE does support the \Q...\E escape
+for quoting substrings. Characters in between are treated as literals. This
+is slightly different from Perl in that $ and @ are also handled as literals
+inside the quotes. In Perl, they cause variable interpolation (but of course
+PCRE does not have variables). Note the following examples: <p>
+ Pattern
+ PCRE matches Perl matches<br>
+ <p>
+ \Qabc$xyz\E abc$xyz abc followed by the<br>
+ contents of $xyz<br>
+ \Qabc\$xyz\E abc\$xyz abc\$xyz<br>
+ \Qabc\E\$\Qxyz\E abc$xyz abc$xyz<br>
+ <p>
+The \Q...\E sequence is recognized both inside and outside character classes.
+<p>
+8. Fairly obviously, PCRE does not support the (?{code}) and (?p{code})
+constructions. However, there is support for recursive patterns using the
+non-Perl items (?R), (?number), and (?P&gt;name). Also, the PCRE "callout" feature
+allows an external function to be called during pattern matching. See the
+ <b>pcrecallout</b> documentation for details. <p>
+9. There are some differences that
+are concerned with the settings of captured strings when part of a pattern
+is repeated. For example, matching "aba" against the pattern /^(a(b)?)+$/
+in Perl leaves $2 unset, but in PCRE it is set to "b". <p>
+10. PCRE provides
+some extensions to the Perl regular expression facilities: <p>
+(a) Although
+lookbehind assertions must match fixed length strings, each alternative
+branch of a lookbehind assertion can match a different length of string.
+Perl requires them all to have the same length. <p>
+(b) If PCRE_DOLLAR_ENDONLY
+is set and PCRE_MULTILINE is not set, the $ meta-character matches only
+at the very end of the string. <p>
+(c) If PCRE_EXTRA is set, a backslash followed
+by a letter with no special meaning is faulted. <p>
+(d) If PCRE_UNGREEDY is
+set, the greediness of the repetition quantifiers is inverted, that is,
+by default they are not greedy, but if followed by a question mark they
+are. <p>
+(e) PCRE_ANCHORED can be used at matching time to force a pattern to
+be tried only at the first matching position in the subject string. <p>
+(f)
+The PCRE_NOTBOL, PCRE_NOTEOL, PCRE_NOTEMPTY, and PCRE_NO_AUTO_CAPTURE options
+for <b>pcre_exec()</b> have no Perl equivalents. <p>
+(g) The (?R), (?number), and (?P&gt;name)
+constructs allows for recursive pattern matching (Perl can do this using
+the (?p{code}) construct, which PCRE cannot support.) <p>
+(h) PCRE supports
+named capturing substrings, using the Python syntax. <p>
+(i) PCRE supports the
+possessive quantifier "++" syntax, taken from Sun&rsquo;s Java package. <p>
+(j) The
+(R) condition, for testing recursion, is a PCRE extension. <p>
+(k) The callout
+facility is PCRE-specific. <p>
+(l) The partial matching facility is PCRE-specific.
+<p>
+(m) Patterns compiled by PCRE can be saved and re-used at a later time,
+even on different hosts that have the other endianness. <p>
+ Last updated: 09
+September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Differences Between Pcre and Perl</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcregrep.1.html b/spamfilter/Utilities/PCRE/man/html/pcregrep.1.html
new file mode 100644
index 0000000..726e8b7
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcregrep.1.html
@@ -0,0 +1,147 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCREGREP(1) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+pcregrep - a grep with Perl-compatible regular expressions.
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+<b>pcregrep
+[-Vcfhilnrsuvx] [long options] [pattern] [file1 file2 ...]</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+<b>pcregrep</b>
+searches files for character patterns, in the same way as other grep commands
+do, but it uses the PCRE regular expression library to support patterns
+that are compatible with the regular expressions of Perl 5. See <b>pcrepattern</b>
+ for a full description of syntax and semantics of the regular expressions
+that PCRE supports. <p>
+A pattern must be specified on the command line unless
+the <b>-f</b> option is used (see below). <p>
+If no files are specified, <b>pcregrep</b> reads
+the standard input. By default, each line that matches the pattern is copied
+to the standard output, and if there is more than one file, the file name
+is printed before each line of output. However, there are options that can
+change how <b>pcregrep</b> behaves. <p>
+Lines are limited to BUFSIZ characters. BUFSIZ
+is defined in <b>&lt;stdio.h&gt;</b>. The newline character is removed from the end of each
+line before it is matched against the pattern.
+<h2><a name='sect3' href='#toc3'>Options</a></h2>
+ <p>
+
+<dl>
+
+<dt><b>-V</b> </dt>
+<dd>Write the version
+number of the PCRE library being used to the standard error stream. </dd>
+
+<dt><b>-c</b> </dt>
+<dd>Do
+not print individual lines; instead just print a count of the number of
+lines that would otherwise have been printed. If several files are given,
+a count is printed for each of them. </dd>
+
+<dt><b>-f</b><i>filename</i> </dt>
+<dd>Read a number of patterns
+from the file, one per line, and match all of them against each line of
+input. A line is output if any of the patterns match it. When <b>-f</b> is used,
+no pattern is taken from the command line; all arguments are treated as
+file names. There is a maximum of 100 patterns. Trailing white space is removed,
+and blank lines are ignored. An empty file contains no patterns and therefore
+matches nothing. </dd>
+
+<dt><b>-h</b> </dt>
+<dd>Suppress printing of filenames when searching multiple
+files. </dd>
+
+<dt><b>-i</b> </dt>
+<dd>Ignore upper/lower case distinctions during comparisons. </dd>
+
+<dt><b>-l</b> </dt>
+<dd>Instead
+of printing lines from the files, just print the names of the files containing
+lines that would have been printed. Each file name is printed once, on a
+separate line. </dd>
+
+<dt><b>-n</b> </dt>
+<dd>Precede each line by its line number in the file. </dd>
+
+<dt><b>-r</b> </dt>
+<dd>If any
+file is a directory, recursively scan the files it contains. Without <b>-r</b> a
+directory is scanned as a normal file. </dd>
+
+<dt><b>-s</b> </dt>
+<dd>Work silently, that is, display
+nothing except error messages. The exit status indicates whether any matches
+were found. </dd>
+
+<dt><b>-u</b> </dt>
+<dd>Operate in UTF-8 mode. This option is available only if PCRE
+has been compiled with UTF-8 support. Both the pattern and each subject line
+must be valid strings of UTF-8 characters. </dd>
+
+<dt><b>-v</b> </dt>
+<dd>Invert the sense of the match,
+so that lines which do <i>not</i> match the pattern are now the ones that are
+found. </dd>
+
+<dt><b>-x</b> </dt>
+<dd>Force the pattern to be anchored (it must start matching at the
+beginning of the line) and in addition, require it to match the entire
+line. This is equivalent to having ^ and $ characters at the start and end
+of each alternative branch in the regular expression. </dd>
+</dl>
+
+<h2><a name='sect4' href='#toc4'>Long Options</a></h2>
+ <p>
+Long
+forms of all the options are available, as in GNU grep. They are shown in
+the following table: <p>
+ -c --count<br>
+ -h --no-filename<br>
+ -i --ignore-case<br>
+ -l --files-with-matches<br>
+ -n --line-number<br>
+ -r --recursive<br>
+ -s --no-messages<br>
+ -u --utf-8<br>
+ -V --version<br>
+ -v --invert-match<br>
+ -x --line-regex<br>
+ -x --line-regexp<br>
+ <p>
+In addition, --file=<i>filename</i> is equivalent to -f<i>filename</i>, and --help shows
+the list of options and then exits.
+<h2><a name='sect5' href='#toc5'>Diagnostics</a></h2>
+ <p>
+Exit status is 0 if any
+matches were found, 1 if no matches were found, and 2 for syntax errors
+or inacessible files (even if matches were found).
+<h2><a name='sect6' href='#toc6'>Author</a></h2>
+ <p>
+Philip Hazel
+&lt;ph10@cam.ac.uk&gt; <br>
+University Computing Service <br>
+Cambridge CB2 3QG, England. <p>
+ Last updated: 09 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+<li><a name='toc3' href='#sect3'>Options</a></li>
+<li><a name='toc4' href='#sect4'>Long Options</a></li>
+<li><a name='toc5' href='#sect5'>Diagnostics</a></li>
+<li><a name='toc6' href='#sect6'>Author</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcrepattern.3.html b/spamfilter/Utilities/PCRE/man/html/pcrepattern.3.html
new file mode 100644
index 0000000..11bb198
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcrepattern.3.html
@@ -0,0 +1,1268 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Pcre Regular Expression Details</a></h2>
+
+<p>
+The syntax and semantics of the regular expressions supported by PCRE are
+described below. Regular expressions are also described in the Perl documentation
+and in a number of books, some of which have copious examples. Jeffrey Friedl&rsquo;s
+"Mastering Regular Expressions", published by O&rsquo;Reilly, covers regular expressions
+in great detail. This description of PCRE&rsquo;s regular expressions is intended
+as reference material. <p>
+The original operation of PCRE was on strings of
+one-byte characters. However, there is now also support for UTF-8 character
+strings. To use this, you must build PCRE to include UTF-8 support, and then
+call <b>pcre_compile()</b> with the PCRE_UTF8 option. How this affects pattern
+matching is mentioned in several places below. There is also a summary of
+UTF-8 features in the section on UTF-8 support in the main <b>pcre</b> page.
+<p>
+A regular expression is a pattern that is matched against a subject string
+from left to right. Most characters stand for themselves in a pattern, and
+match the corresponding characters in the subject. As a trivial example,
+the pattern <p>
+ The quick brown fox<br>
+ <p>
+matches a portion of a subject string that is identical to itself. The
+power of regular expressions comes from the ability to include alternatives
+and repetitions in the pattern. These are encoded in the pattern by the
+use of <i>metacharacters</i>, which do not stand for themselves but instead are
+interpreted in some special way. <p>
+There are two different sets of metacharacters:
+those that are recognized anywhere in the pattern except within square
+brackets, and those that are recognized in square brackets. Outside square
+brackets, the metacharacters are as follows: <p>
+ \ general escape character
+with several uses<br>
+ ^ assert start of string (or line, in multiline mode)<br>
+ $ assert end of string (or line, in multiline mode)<br>
+ . match any character except newline (by default)<br>
+ [ start character class definition<br>
+ | start of alternative branch<br>
+ ( start subpattern<br>
+ ) end subpattern<br>
+ ? extends the meaning of (<br>
+ also 0 or 1 quantifier<br>
+ also quantifier minimizer<br>
+ * 0 or more quantifier<br>
+ + 1 or more quantifier<br>
+ also "possessive quantifier"<br>
+ { start min/max quantifier<br>
+ <p>
+Part of a pattern that is in square brackets is called a "character class".
+In a character class the only metacharacters are: <p>
+ \ general escape
+character<br>
+ ^ negate the class, but only if the first character<br>
+ - indicates character range<br>
+ [ POSIX character class (only if followed by POSIX<br>
+ syntax)<br>
+ ] terminates the character class<br>
+ <p>
+The following sections describe the use of each of the metacharacters.
+
+<h2><a name='sect2' href='#toc2'>Backslash</a></h2>
+ <p>
+The backslash character has several uses. Firstly, if it is followed
+by a non-alphanumeric character, it takes away any special meaning that
+character may have. This use of backslash as an escape character applies
+both inside and outside character classes. <p>
+For example, if you want to match
+a * character, you write \* in the pattern. This escaping action applies
+whether or not the following character would otherwise be interpreted as
+a metacharacter, so it is always safe to precede a non-alphanumeric with
+backslash to specify that it stands for itself. In particular, if you want
+to match a backslash, you write \\. <p>
+If a pattern is compiled with the PCRE_EXTENDED
+option, whitespace in the pattern (other than in a character class) and
+characters between a # outside a character class and the next newline character
+are ignored. An escaping backslash can be used to include a whitespace or
+# character as part of the pattern. <p>
+If you want to remove the special meaning
+from a sequence of characters, you can do so by putting them between \Q
+and \E. This is different from Perl in that $ and @ are handled as literals
+in \Q...\E sequences in PCRE, whereas in Perl, $ and @ cause variable interpolation.
+Note the following examples: <p>
+ Pattern PCRE matches Perl matches<br>
+ <p>
+ \Qabc$xyz\E abc$xyz abc followed by the<br>
+ contents of $xyz<br>
+ \Qabc\$xyz\E abc\$xyz abc\$xyz<br>
+ \Qabc\E\$\Qxyz\E abc$xyz abc$xyz<br>
+ <p>
+The \Q...\E sequence is recognized both inside and outside character classes.
+
+<h3><a name='sect3' href='#toc3'>Non-printing characters</a></h3>
+ <p>
+A second use of backslash provides a way of encoding
+non-printing characters in patterns in a visible manner. There is no restriction
+on the appearance of non-printing characters, apart from the binary zero
+that terminates a pattern, but when a pattern is being prepared by text
+editing, it is usually easier to use one of the following escape sequences
+than the binary character it represents: <p>
+ \a alarm, that is, the
+BEL character (hex 07)<br>
+ \cx "control-x", where x is any character<br>
+ \e escape (hex 1B)<br>
+ \f formfeed (hex 0C)<br>
+ \n newline (hex 0A)<br>
+ \r carriage return (hex 0D)<br>
+ \t tab (hex 09)<br>
+ \ddd character with octal code ddd, or backreference<br>
+ \xhh character with hex code hh<br>
+ \x{hhh..} character with hex code hhh... (UTF-8 mode only)<br>
+ <p>
+The precise effect of \cx is as follows: if x is a lower case letter, it
+is converted to upper case. Then bit 6 of the character (hex 40) is inverted.
+Thus \cz becomes hex 1A, but \c{ becomes hex 3B, while \c; becomes hex 7B.
+<p>
+After \x, from zero to two hexadecimal digits are read (letters can be in
+upper or lower case). In UTF-8 mode, any number of hexadecimal digits may
+appear between \x{ and }, but the value of the character code must be less
+than 2**31 (that is, the maximum hexadecimal value is 7FFFFFFF). If characters
+other than hexadecimal digits appear between \x{ and }, or if there is no
+terminating }, this form of escape is not recognized. Instead, the initial
+\x will be interpreted as a basic hexadecimal escape, with no following
+digits, giving a character whose value is zero. <p>
+Characters whose value is
+less than 256 can be defined by either of the two syntaxes for \x when PCRE
+is in UTF-8 mode. There is no difference in the way they are handled. For
+example, \xdc is exactly the same as \x{dc}. <p>
+After \0 up to two further octal
+digits are read. In both cases, if there are fewer than two digits, just
+those that are present are used. Thus the sequence \0\x\07 specifies two binary
+zeros followed by a BEL character (code value 7). Make sure you supply two
+digits after the initial zero if the pattern character that follows is
+itself an octal digit. <p>
+The handling of a backslash followed by a digit other
+than 0 is complicated. Outside a character class, PCRE reads it and any
+following digits as a decimal number. If the number is less than 10, or
+if there have been at least that many previous capturing left parentheses
+in the expression, the entire sequence is taken as a <i>back reference</i>. A description
+of how this works is given later, following the discussion of parenthesized
+subpatterns. <p>
+Inside a character class, or if the decimal number is greater
+than 9 and there have not been that many capturing subpatterns, PCRE re-reads
+up to three octal digits following the backslash, and generates a single
+byte from the least significant 8 bits of the value. Any subsequent digits
+stand for themselves. For example: <p>
+ \040 is another way of writing a space<br>
+ \40 is the same, provided there are fewer than 40<br>
+ previous capturing subpatterns<br>
+ \7 is always a back reference<br>
+ \11 might be a back reference, or another way of<br>
+ writing a tab<br>
+ \011 is always a tab<br>
+ \0113 is a tab followed by the character "3"<br>
+ \113 might be a back reference, otherwise the<br>
+ character with octal code 113<br>
+ \377 might be a back reference, otherwise<br>
+ the byte consisting entirely of 1 bits<br>
+ \81 is either a back reference, or a binary zero<br>
+ followed by the two characters "8" and "1"<br>
+ <p>
+Note that octal values of 100 or greater must not be introduced by a leading
+zero, because no more than three octal digits are ever read. <p>
+All the sequences
+that define a single byte value or a single UTF-8 character (in UTF-8 mode)
+can be used both inside and outside character classes. In addition, inside
+a character class, the sequence \b is interpreted as the backspace character
+(hex 08), and the sequence \X is interpreted as the character "X". Outside
+a character class, these sequences have different meanings (see below).
+
+<h3><a name='sect4' href='#toc4'>Generic character types</a></h3>
+ <p>
+The third use of backslash is for specifying
+generic character types. The following are always recognized: <p>
+ \d any
+decimal digit<br>
+ \D any character that is not a decimal digit<br>
+ \s any whitespace character<br>
+ \S any character that is not a whitespace character<br>
+ \w any "word" character<br>
+ \W any "non-word" character<br>
+ <p>
+Each pair of escape sequences partitions the complete set of characters
+into two disjoint sets. Any given character matches one, and only one, of
+each pair. <p>
+These character type sequences can appear both inside and outside
+character classes. They each match one character of the appropriate type.
+If the current matching point is at the end of the subject string, all
+of them fail, since there is no character to match. <p>
+For compatibility with
+Perl, \s does not match the VT character (code 11). This makes it different
+from the the POSIX "space" class. The \s characters are <a href='HT.9.html'>HT (9)</a>
+, LF (10),
+FF (12), CR (13), and space (32). <p>
+A "word" character is an underscore or
+any character less than 256 that is a letter or digit. The definition of
+letters and digits is controlled by PCRE&rsquo;s low-valued character tables, and
+may vary if locale-specific matching is taking place (see "Locale support"
+ in the <b>pcreapi</b> page). For example, in the "fr_FR" (French) locale, some
+character codes greater than 128 are used for accented letters, and these
+are matched by \w. <p>
+In UTF-8 mode, characters with values greater than 128
+never match \d, \s, or \w, and always match \D, \S, and \W. This is true even
+when Unicode character property support is available.
+<h3><a name='sect5' href='#toc5'>Unicode character
+properties</a></h3>
+ <p>
+When PCRE is built with Unicode character property support,
+three additional escape sequences to match generic character types are
+available when UTF-8 mode is selected. They are: <p>
+ \p{<i>xx</i>} a character with
+the <i>xx</i> property<br>
+ \P{<i>xx</i>} a character without the <i>xx</i> property<br>
+ \X an extended Unicode sequence<br>
+ <p>
+The property names represented by <i>xx</i> above are limited to the Unicode
+general category properties. Each character has exactly one such property,
+specified by a two-letter abbreviation. For compatibility with Perl, negation
+can be specified by including a circumflex between the opening brace and
+the property name. For example, \p{^Lu} is the same as \P{Lu}. <p>
+If only one letter
+is specified with \p or \P, it includes all the properties that start with
+that letter. In this case, in the absence of negation, the curly brackets
+in the escape sequence are optional; these two examples have the same effect:
+<p>
+ \p{L}<br>
+ \pL<br>
+ <p>
+The following property codes are supported: <p>
+ C Other<br>
+ Cc Control<br>
+ Cf Format<br>
+ Cn Unassigned<br>
+ Co Private use<br>
+ Cs Surrogate<br>
+ <p>
+ L Letter<br>
+ Ll Lower case letter<br>
+ Lm Modifier letter<br>
+ Lo Other letter<br>
+ Lt Title case letter<br>
+ Lu Upper case letter<br>
+ <p>
+ M Mark<br>
+ Mc Spacing mark<br>
+ Me Enclosing mark<br>
+ Mn Non-spacing mark<br>
+ <p>
+ N Number<br>
+ Nd Decimal number<br>
+ Nl Letter number<br>
+ No Other number<br>
+ <p>
+ P Punctuation<br>
+ Pc Connector punctuation<br>
+ Pd Dash punctuation<br>
+ Pe Close punctuation<br>
+ Pf Final punctuation<br>
+ Pi Initial punctuation<br>
+ Po Other punctuation<br>
+ Ps Open punctuation<br>
+ <p>
+ S Symbol<br>
+ Sc Currency symbol<br>
+ Sk Modifier symbol<br>
+ Sm Mathematical symbol<br>
+ So Other symbol<br>
+ <p>
+ Z Separator<br>
+ Zl Line separator<br>
+ Zp Paragraph separator<br>
+ Zs Space separator<br>
+ <p>
+Extended properties such as "Greek" or "InMusicalSymbols" are not supported
+by PCRE. <p>
+Specifying caseless matching does not affect these escape sequences.
+For example, \p{Lu} always matches only upper case letters. <p>
+The \X escape
+matches any number of Unicode characters that form an extended Unicode
+sequence. \X is equivalent to <p>
+ (?&gt;\PM\pM*)<br>
+ <p>
+That is, it matches a character without the "mark" property, followed
+by zero or more characters with the "mark" property, and treats the sequence
+as an atomic group (see below). Characters with the "mark" property are
+typically accents that affect the preceding character. <p>
+Matching characters
+by Unicode property is not fast, because PCRE has to search a structure
+that contains data for over fifteen thousand characters. That is why the
+traditional escape sequences such as \d and \w do not use Unicode properties
+in PCRE.
+<h3><a name='sect6' href='#toc6'>Simple assertions</a></h3>
+ <p>
+The fourth use of backslash is for certain
+simple assertions. An assertion specifies a condition that has to be met
+at a particular point in a match, without consuming any characters from
+the subject string. The use of subpatterns for more complicated assertions
+is described below. The backslashed assertions are: <p>
+ \b matches at
+a word boundary<br>
+ \B matches when not at a word boundary<br>
+ \A matches at start of subject<br>
+ \Z matches at end of subject or before newline at end<br>
+ \z matches at end of subject<br>
+ \G matches at first matching position in subject<br>
+ <p>
+These assertions may not appear in character classes (but note that \b
+has a different meaning, namely the backspace character, inside a character
+class). <p>
+A word boundary is a position in the subject string where the current
+character and the previous character do not both match \w or \W (i.e. one matches
+\w and the other matches \W), or the start or end of the string if the first
+or last character matches \w, respectively. <p>
+The \A, \Z, and \z assertions differ
+from the traditional circumflex and dollar (described in the next section)
+in that they only ever match at the very start and end of the subject string,
+whatever options are set. Thus, they are independent of multiline mode. These
+three assertions are not affected by the PCRE_NOTBOL or PCRE_NOTEOL options,
+which affect only the behaviour of the circumflex and dollar metacharacters.
+However, if the <i>startoffset</i> argument of <b>pcre_exec()</b> is non-zero, indicating
+that matching is to start at a point other than the beginning of the subject,
+\A can never match. The difference between \Z and \z is that \Z matches before
+a newline that is the last character of the string as well as at the end
+of the string, whereas \z matches only at the end. <p>
+The \G assertion is true
+only when the current matching position is at the start point of the match,
+as specified by the <i>startoffset</i> argument of <b>pcre_exec()</b>. It differs from
+\A when the value of <i>startoffset</i> is non-zero. By calling <b>pcre_exec()</b> multiple
+times with appropriate arguments, you can mimic Perl&rsquo;s /g option, and it
+is in this kind of implementation where \G can be useful. <p>
+Note, however,
+that PCRE&rsquo;s interpretation of \G, as the start of the current match, is subtly
+different from Perl&rsquo;s, which defines it as the end of the previous match.
+In Perl, these can be different when the previously matched string was
+empty. Because PCRE does just one match at a time, it cannot reproduce this
+behaviour. <p>
+If all the alternatives of a pattern begin with \G, the expression
+is anchored to the starting match position, and the "anchored" flag is
+set in the compiled regular expression.
+<h2><a name='sect7' href='#toc7'>Circumflex and Dollar</a></h2>
+ <p>
+Outside
+a character class, in the default matching mode, the circumflex character
+is an assertion that is true only if the current matching point is at the
+start of the subject string. If the <i>startoffset</i> argument of <b>pcre_exec()</b>
+is non-zero, circumflex can never match if the PCRE_MULTILINE option is
+unset. Inside a character class, circumflex has an entirely different meaning
+ (see below). <p>
+Circumflex need not be the first character of the pattern
+if a number of alternatives are involved, but it should be the first thing
+in each alternative in which it appears if the pattern is ever to match
+that branch. If all possible alternatives start with a circumflex, that
+is, if the pattern is constrained to match only at the start of the subject,
+it is said to be an "anchored" pattern. (There are also other constructs
+that can cause a pattern to be anchored.) <p>
+A dollar character is an assertion
+that is true only if the current matching point is at the end of the subject
+string, or immediately before a newline character that is the last character
+in the string (by default). Dollar need not be the last character of the
+pattern if a number of alternatives are involved, but it should be the
+last item in any branch in which it appears. Dollar has no special meaning
+in a character class. <p>
+The meaning of dollar can be changed so that it matches
+only at the very end of the string, by setting the PCRE_DOLLAR_ENDONLY
+option at compile time. This does not affect the \Z assertion. <p>
+The meanings
+of the circumflex and dollar characters are changed if the PCRE_MULTILINE
+option is set. When this is the case, they match immediately after and immediately
+before an internal newline character, respectively, in addition to matching
+at the start and end of the subject string. For example, the pattern /^abc$/
+matches the subject string "def\nabc" (where \n represents a newline character)
+in multiline mode, but not otherwise. Consequently, patterns that are anchored
+in single line mode because all branches start with ^ are not anchored in
+multiline mode, and a match for circumflex is possible when the <i>startoffset</i>
+argument of <b>pcre_exec()</b> is non-zero. The PCRE_DOLLAR_ENDONLY option is ignored
+if PCRE_MULTILINE is set. <p>
+Note that the sequences \A, \Z, and \z can be used
+to match the start and end of the subject in both modes, and if all branches
+of a pattern start with \A it is always anchored, whether PCRE_MULTILINE
+is set or not.
+<h2><a name='sect8' href='#toc8'>Full Stop (period, Dot)</a></h2>
+ <p>
+Outside a character class, a dot
+in the pattern matches any one character in the subject, including a non-printing
+character, but not (by default) newline. In UTF-8 mode, a dot matches any
+UTF-8 character, which might be more than one byte long, except (by default)
+newline. If the PCRE_DOTALL option is set, dots match newlines as well. The
+handling of dot is entirely independent of the handling of circumflex and
+dollar, the only relationship being that they both involve newline characters.
+Dot has no special meaning in a character class.
+<h2><a name='sect9' href='#toc9'>Matching a Single Byte</a></h2>
+
+<p>
+Outside a character class, the escape sequence \C matches any one byte,
+both in and out of UTF-8 mode. Unlike a dot, it can match a newline. The feature
+is provided in Perl in order to match individual bytes in UTF-8 mode. Because
+it breaks up UTF-8 characters into individual bytes, what remains in the
+string may be a malformed UTF-8 string. For this reason, the \C escape sequence
+is best avoided. <p>
+PCRE does not allow \C to appear in lookbehind assertions
+ (described below), because in UTF-8 mode this would make it impossible
+to calculate the length of the lookbehind.
+<h2><a name='sect10' href='#toc10'>Square Brackets and Character
+Classes</a></h2>
+ <p>
+An opening square bracket introduces a character class, terminated
+by a closing square bracket. A closing square bracket on its own is not
+special. If a closing square bracket is required as a member of the class,
+it should be the first data character in the class (after an initial circumflex,
+if present) or escaped with a backslash. <p>
+A character class matches a single
+character in the subject. In UTF-8 mode, the character may occupy more than
+one byte. A matched character must be in the set of characters defined by
+the class, unless the first character in the class definition is a circumflex,
+in which case the subject character must not be in the set defined by the
+class. If a circumflex is actually required as a member of the class, ensure
+it is not the first character, or escape it with a backslash. <p>
+For example,
+the character class [aeiou] matches any lower case vowel, while [^aeiou]
+matches any character that is not a lower case vowel. Note that a circumflex
+is just a convenient notation for specifying the characters that are in
+the class by enumerating those that are not. A class that starts with a
+circumflex is not an assertion: it still consumes a character from the
+subject string, and therefore it fails if the current pointer is at the
+end of the string. <p>
+In UTF-8 mode, characters with values greater than 255
+can be included in a class as a literal string of bytes, or by using the
+\x{ escaping mechanism. <p>
+When caseless matching is set, any letters in a class
+represent both their upper case and lower case versions, so for example,
+a caseless [aeiou] matches "A" as well as "a", and a caseless [^aeiou] does
+not match "A", whereas a caseful version would. When running in UTF-8 mode,
+PCRE supports the concept of case for characters with values greater than
+128 only when it is compiled with Unicode property support. <p>
+The newline
+character is never treated in any special way in character classes, whatever
+the setting of the PCRE_DOTALL or PCRE_MULTILINE options is. A class such
+as [^a] will always match a newline. <p>
+The minus (hyphen) character can be
+used to specify a range of characters in a character class. For example,
+[d-m] matches any letter between d and m, inclusive. If a minus character
+is required in a class, it must be escaped with a backslash or appear in
+a position where it cannot be interpreted as indicating a range, typically
+as the first or last character in the class. <p>
+It is not possible to have
+the literal character "]" as the end character of a range. A pattern such
+as [W-]46] is interpreted as a class of two characters ("W" and "-") followed
+by a literal string "46]", so it would match "W46]" or "-46]". However, if
+the "]" is escaped with a backslash it is interpreted as the end of range,
+so [W-\]46] is interpreted as a class containing a range followed by two
+other characters. The octal or hexadecimal representation of "]" can also
+be used to end a range. <p>
+Ranges operate in the collating sequence of character
+values. They can also be used for characters specified numerically, for
+example [\000-\037]. In UTF-8 mode, ranges can include characters whose values
+are greater than 255, for example [\x{100}-\x{2ff}]. <p>
+If a range that includes
+letters is used when caseless matching is set, it matches the letters in
+either case. For example, [W-c] is equivalent to [][\\^_&lsquo;wxyzabc], matched caselessly,
+and in non-UTF-8 mode, if character tables for the "fr_FR" locale are in
+use, [\xc8-\xcb] matches accented E characters in both cases. In UTF-8 mode,
+PCRE supports the concept of case for characters with values greater than
+128 only when it is compiled with Unicode property support. <p>
+The character
+types \d, \D, \p, \P, \s, \S, \w, and \W may also appear in a character class,
+and add the characters that they match to the class. For example, [\dABCDEF]
+matches any hexadecimal digit. A circumflex can conveniently be used with
+the upper case character types to specify a more restricted set of characters
+than the matching lower case type. For example, the class [^\W_] matches any
+letter or digit, but not underscore. <p>
+The only metacharacters that are recognized
+in character classes are backslash, hyphen (only where it can be interpreted
+as specifying a range), circumflex (only at the start), opening square
+bracket (only when it can be interpreted as introducing a POSIX class name
+- see the next section), and the terminating closing square bracket. However,
+escaping other non-alphanumeric characters does no harm.
+<h2><a name='sect11' href='#toc11'>Posix Character
+Classes</a></h2>
+ <p>
+Perl supports the POSIX notation for character classes. This uses
+names enclosed by [: and :] within the enclosing square brackets. PCRE also
+supports this notation. For example, <p>
+ [01[:alpha:]%]<br>
+ <p>
+matches "0", "1", any alphabetic character, or "%". The supported class
+names are <p>
+ alnum letters and digits<br>
+ alpha letters<br>
+ ascii character codes 0 - 127<br>
+ blank space or tab only<br>
+ cntrl control characters<br>
+ digit decimal digits (same as \d)<br>
+ graph printing characters, excluding space<br>
+ lower lower case letters<br>
+ print printing characters, including space<br>
+ punct printing characters, excluding letters and digits<br>
+ space white space (not quite the same as \s)<br>
+ upper upper case letters<br>
+ word "word" characters (same as \w)<br>
+ xdigit hexadecimal digits<br>
+ <p>
+The "space" characters are <a href='HT.9.html'>HT (9)</a>
+, LF (10), VT (11), FF (12), CR (13),
+and space (32). Notice that this list includes the VT character (code 11).
+This makes "space" different to \s, which does not include VT (for Perl
+compatibility). <p>
+The name "word" is a Perl extension, and "blank" is a GNU
+extension from Perl 5.8. Another Perl extension is negation, which is indicated
+by a ^ character after the colon. For example, <p>
+ [12[:^digit:]]<br>
+ <p>
+matches "1", "2", or any non-digit. PCRE (and Perl) also recognize the POSIX
+syntax [.ch.] and [=ch=] where "ch" is a "collating element", but these are
+not supported, and an error is given if they are encountered. <p>
+In UTF-8 mode,
+characters with values greater than 128 do not match any of the POSIX character
+classes.
+<h2><a name='sect12' href='#toc12'>Vertical Bar</a></h2>
+ <p>
+Vertical bar characters are used to separate alternative
+patterns. For example, the pattern <p>
+ gilbert|sullivan<br>
+ <p>
+matches either "gilbert" or "sullivan". Any number of alternatives may
+appear, and an empty alternative is permitted (matching the empty string).
+The matching process tries each alternative in turn, from left to right,
+and the first one that succeeds is used. If the alternatives are within
+a subpattern (defined below), "succeeds" means matching the rest of
+the main pattern as well as the alternative in the subpattern.
+<h2><a name='sect13' href='#toc13'>Internal
+Option Setting</a></h2>
+ <p>
+The settings of the PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL,
+and PCRE_EXTENDED options can be changed from within the pattern by a sequence
+of Perl option letters enclosed between "(?" and ")". The option letters
+are <p>
+ i for PCRE_CASELESS<br>
+ m for PCRE_MULTILINE<br>
+ s for PCRE_DOTALL<br>
+ x for PCRE_EXTENDED<br>
+ <p>
+For example, (?im) sets caseless, multiline matching. It is also possible
+to unset these options by preceding the letter with a hyphen, and a combined
+setting and unsetting such as (?im-sx), which sets PCRE_CASELESS and PCRE_MULTILINE
+while unsetting PCRE_DOTALL and PCRE_EXTENDED, is also permitted. If a letter
+appears both before and after the hyphen, the option is unset. <p>
+When an option
+change occurs at top level (that is, not inside subpattern parentheses),
+the change applies to the remainder of the pattern that follows. If the
+change is placed right at the start of a pattern, PCRE extracts it into
+the global options (and it will therefore show up in data extracted by
+the <b>pcre_fullinfo()</b> function). <p>
+An option change within a subpattern affects
+only that part of the current pattern that follows it, so <p>
+ (a(?i)b)c<br>
+ <p>
+matches abc and aBc and no other strings (assuming PCRE_CASELESS is not
+used). By this means, options can be made to have different settings in
+different parts of the pattern. Any changes made in one alternative do carry
+on into subsequent branches within the same subpattern. For example, <p>
+ (a(?i)b|c)<br>
+ <p>
+matches "ab", "aB", "c", and "C", even though when matching "C" the first
+branch is abandoned before the option setting. This is because the effects
+of option settings happen at compile time. There would be some very weird
+behaviour otherwise. <p>
+The PCRE-specific options PCRE_UNGREEDY and PCRE_EXTRA
+can be changed in the same way as the Perl-compatible options by using the
+characters U and X respectively. The (?X) flag setting is special in that
+it must always occur earlier in the pattern than any of the additional
+features it turns on, even when it is at top level. It is best to put it
+at the start.
+<h2><a name='sect14' href='#toc14'>Subpatterns</a></h2>
+ <p>
+Subpatterns are delimited by parentheses (round
+brackets), which can be nested. Turning part of a pattern into a subpattern
+does two things: <p>
+1. It localizes a set of alternatives. For example, the
+pattern <p>
+ cat(aract|erpillar|)<br>
+ <p>
+matches one of the words "cat", "cataract", or "caterpillar". Without the
+parentheses, it would match "cataract", "erpillar" or the empty string.
+<p>
+2. It sets up the subpattern as a capturing subpattern. This means that,
+when the whole pattern matches, that portion of the subject string that
+matched the subpattern is passed back to the caller via the <i>ovector</i> argument
+of <b>pcre_exec()</b>. Opening parentheses are counted from left to right (starting
+from 1) to obtain numbers for the capturing subpatterns. <p>
+For example, if
+the string "the red king" is matched against the pattern <p>
+ the ((red|white)
+(king|queen))<br>
+ <p>
+the captured substrings are "red king", "red", and "king", and are numbered
+1, 2, and 3, respectively. <p>
+The fact that plain parentheses fulfil two functions
+is not always helpful. There are often times when a grouping subpattern
+is required without a capturing requirement. If an opening parenthesis is
+followed by a question mark and a colon, the subpattern does not do any
+capturing, and is not counted when computing the number of any subsequent
+capturing subpatterns. For example, if the string "the white queen" is matched
+against the pattern <p>
+ the ((?:red|white) (king|queen))<br>
+ <p>
+the captured substrings are "white queen" and "queen", and are numbered
+1 and 2. The maximum number of capturing subpatterns is 65535, and the maximum
+depth of nesting of all subpatterns, both capturing and non-capturing, is
+200. <p>
+As a convenient shorthand, if any option settings are required at the
+start of a non-capturing subpattern, the option letters may appear between
+the "?" and the ":". Thus the two patterns <p>
+ (?i:saturday|sunday)<br>
+ (?:(?i)saturday|sunday)<br>
+ <p>
+match exactly the same set of strings. Because alternative branches are
+tried from left to right, and options are not reset until the end of the
+subpattern is reached, an option setting in one branch does affect subsequent
+branches, so the above patterns match "SUNDAY" as well as "Saturday".
+
+<h2><a name='sect15' href='#toc15'>Named Subpatterns</a></h2>
+ <p>
+Identifying capturing parentheses by number is simple,
+but it can be very hard to keep track of the numbers in complicated regular
+expressions. Furthermore, if an expression is modified, the numbers may
+change. To help with this difficulty, PCRE supports the naming of subpatterns,
+something that Perl does not provide. The Python syntax (?P&lt;name&gt;...) is used.
+Names consist of alphanumeric characters and underscores, and must be unique
+within a pattern. <p>
+Named capturing parentheses are still allocated numbers
+as well as names. The PCRE API provides function calls for extracting the
+name-to-number translation table from a compiled pattern. There is also a
+convenience function for extracting a captured substring by name. For further
+details see the <b>pcreapi</b> documentation.
+<h2><a name='sect16' href='#toc16'>Repetition</a></h2>
+ <p>
+Repetition is specified
+by quantifiers, which can follow any of the following items: <p>
+ a literal
+data character<br>
+ the . metacharacter<br>
+ the \C escape sequence<br>
+ the \X escape sequence (in UTF-8 mode with Unicode properties)<br>
+ an escape such as \d that matches a single character<br>
+ a character class<br>
+ a back reference (see next section)<br>
+ a parenthesized subpattern (unless it is an assertion)<br>
+ <p>
+The general repetition quantifier specifies a minimum and maximum number
+of permitted matches, by giving the two numbers in curly brackets (braces),
+separated by a comma. The numbers must be less than 65536, and the first
+must be less than or equal to the second. For example: <p>
+ z{2,4}<br>
+ <p>
+matches "zz", "zzz", or "zzzz". A closing brace on its own is not a special
+character. If the second number is omitted, but the comma is present, there
+is no upper limit; if the second number and the comma are both omitted,
+the quantifier specifies an exact number of required matches. Thus <p>
+ [aeiou]{3,}<br>
+ <p>
+matches at least 3 successive vowels, but may match many more, while <p>
+
+ \d{8}<br>
+ <p>
+matches exactly 8 digits. An opening curly bracket that appears in a position
+where a quantifier is not allowed, or one that does not match the syntax
+of a quantifier, is taken as a literal character. For example, {,6} is not
+a quantifier, but a literal string of four characters. <p>
+In UTF-8 mode, quantifiers
+apply to UTF-8 characters rather than to individual bytes. Thus, for example,
+\x{100}{2} matches two UTF-8 characters, each of which is represented by
+a two-byte sequence. Similarly, when Unicode property support is available,
+\X{3} matches three Unicode extended sequences, each of which may be several
+bytes long (and they may be of different lengths). <p>
+The quantifier {0} is
+permitted, causing the expression to behave as if the previous item and
+the quantifier were not present. <p>
+For convenience (and historical compatibility)
+the three most common quantifiers have single-character abbreviations: <p>
+
+ * is equivalent to {0,}<br>
+ + is equivalent to {1,}<br>
+ ? is equivalent to {0,1}<br>
+ <p>
+It is possible to construct infinite loops by following a subpattern that
+can match no characters with a quantifier that has no upper limit, for
+example: <p>
+ (a?)*<br>
+ <p>
+Earlier versions of Perl and PCRE used to give an error at compile time
+for such patterns. However, because there are cases where this can be useful,
+such patterns are now accepted, but if any repetition of the subpattern
+does in fact match no characters, the loop is forcibly broken. <p>
+By default,
+the quantifiers are "greedy", that is, they match as much as possible (up
+to the maximum number of permitted times), without causing the rest of
+the pattern to fail. The classic example of where this gives problems is
+in trying to match comments in C programs. These appear between /* and */
+and within the comment, individual * and / characters may appear. An attempt
+to match C comments by applying the pattern <p>
+ /\*.*\*/<br>
+ <p>
+to the string <p>
+ /* first comment */ not comment /* second comment */<br>
+ <p>
+fails, because it matches the entire string owing to the greediness of
+the .* item. <p>
+However, if a quantifier is followed by a question mark, it
+ceases to be greedy, and instead matches the minimum number of times possible,
+so the pattern <p>
+ /\*.*?\*/<br>
+ <p>
+does the right thing with the C comments. The meaning of the various quantifiers
+is not otherwise changed, just the preferred number of matches. Do not confuse
+this use of question mark with its use as a quantifier in its own right.
+Because it has two uses, it can sometimes appear doubled, as in <p>
+ \d??\d<br>
+ <p>
+which matches one digit by preference, but can match two if that is the
+only way the rest of the pattern matches. <p>
+If the PCRE_UNGREEDY option is
+set (an option which is not available in Perl), the quantifiers are not
+greedy by default, but individual ones can be made greedy by following
+them with a question mark. In other words, it inverts the default behaviour.
+<p>
+When a parenthesized subpattern is quantified with a minimum repeat count
+that is greater than 1 or with a limited maximum, more memory is required
+for the compiled pattern, in proportion to the size of the minimum or maximum.
+<p>
+If a pattern starts with .* or .{0,} and the PCRE_DOTALL option (equivalent
+to Perl&rsquo;s /s) is set, thus allowing the . to match newlines, the pattern
+is implicitly anchored, because whatever follows will be tried against
+every character position in the subject string, so there is no point in
+retrying the overall match at any position after the first. PCRE normally
+treats such a pattern as though it were preceded by \A. <p>
+In cases where it
+is known that the subject string contains no newlines, it is worth setting
+PCRE_DOTALL in order to obtain this optimization, or alternatively using
+^ to indicate anchoring explicitly. <p>
+However, there is one situation where
+the optimization cannot be used. When .* is inside capturing parentheses
+that are the subject of a backreference elsewhere in the pattern, a match
+at the start may fail, and a later one succeed. Consider, for example: <p>
+
+ (.*)abc\1<br>
+ <p>
+If the subject is "xyz123abc123" the match point is the fourth character.
+For this reason, such a pattern is not implicitly anchored. <p>
+When a capturing
+subpattern is repeated, the value captured is the substring that matched
+the final iteration. For example, after <p>
+ (tweedle[dume]{3}\s*)+<br>
+ <p>
+has matched "tweedledum tweedledee" the value of the captured substring
+is "tweedledee". However, if there are nested capturing subpatterns, the
+corresponding captured values may have been set in previous iterations.
+For example, after <p>
+ /(a|(b))+/<br>
+ <p>
+matches "aba" the value of the second captured substring is "b".
+<h2><a name='sect17' href='#toc17'>Atomic
+Grouping and Possessive Quantifiers</a></h2>
+ <p>
+With both maximizing and minimizing
+repetition, failure of what follows normally causes the repeated item to
+be re-evaluated to see if a different number of repeats allows the rest
+of the pattern to match. Sometimes it is useful to prevent this, either
+to change the nature of the match, or to cause it fail earlier than it
+otherwise might, when the author of the pattern knows there is no point
+in carrying on. <p>
+Consider, for example, the pattern \d+foo when applied to
+the subject line <p>
+ 123456bar<br>
+ <p>
+After matching all 6 digits and then failing to match "foo", the normal
+action of the matcher is to try again with only 5 digits matching the \d+
+item, and then with 4, and so on, before ultimately failing. "Atomic grouping"
+(a term taken from Jeffrey Friedl&rsquo;s book) provides the means for specifying
+that once a subpattern has matched, it is not to be re-evaluated in this
+way. <p>
+If we use atomic grouping for the previous example, the matcher would
+give up immediately on failing to match "foo" the first time. The notation
+is a kind of special parenthesis, starting with (?&gt; as in this example:
+<p>
+ (?&gt;\d+)foo<br>
+ <p>
+This kind of parenthesis "locks up" the part of the pattern it contains
+once it has matched, and a failure further into the pattern is prevented
+from backtracking into it. Backtracking past it to previous items, however,
+works as normal. <p>
+An alternative description is that a subpattern of this
+type matches the string of characters that an identical standalone pattern
+would match, if anchored at the current point in the subject string. <p>
+Atomic
+grouping subpatterns are not capturing subpatterns. Simple cases such as
+the above example can be thought of as a maximizing repeat that must swallow
+everything it can. So, while both \d+ and \d+? are prepared to adjust the
+number of digits they match in order to make the rest of the pattern match,
+(?&gt;\d+) can only match an entire sequence of digits. <p>
+Atomic groups in general
+can of course contain arbitrarily complicated subpatterns, and can be nested.
+However, when the subpattern for an atomic group is just a single repeated
+item, as in the example above, a simpler notation, called a "possessive
+quantifier" can be used. This consists of an additional + character following
+a quantifier. Using this notation, the previous example can be rewritten
+as <p>
+ \d++foo<br>
+ <p>
+Possessive quantifiers are always greedy; the setting of the PCRE_UNGREEDY
+option is ignored. They are a convenient notation for the simpler forms
+of atomic group. However, there is no difference in the meaning or processing
+of a possessive quantifier and the equivalent atomic group. <p>
+The possessive
+quantifier syntax is an extension to the Perl syntax. It originates in Sun&rsquo;s
+Java package. <p>
+When a pattern contains an unlimited repeat inside a subpattern
+that can itself be repeated an unlimited number of times, the use of an
+atomic group is the only way to avoid some failing matches taking a very
+long time indeed. The pattern <p>
+ (\D+|&lt;\d+&gt;)*[!?]<br>
+ <p>
+matches an unlimited number of substrings that either consist of non-digits,
+or digits enclosed in &lt;&gt;, followed by either ! or ?. When it matches, it runs
+quickly. However, if it is applied to <p>
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<br>
+ <p>
+it takes a long time before reporting failure. This is because the string
+can be divided between the internal \D+ repeat and the external * repeat
+in a large number of ways, and all have to be tried. (The example uses [!?]
+rather than a single character at the end, because both PCRE and Perl have
+an optimization that allows for fast failure when a single character is
+used. They remember the last single character that is required for a match,
+and fail early if it is not present in the string.) If the pattern is changed
+so that it uses an atomic group, like this: <p>
+ ((?&gt;\D+)|&lt;\d+&gt;)*[!?]<br>
+ <p>
+sequences of non-digits cannot be broken, and failure happens quickly.
+
+<h2><a name='sect18' href='#toc18'>Back References</a></h2>
+ <p>
+Outside a character class, a backslash followed by a
+digit greater than 0 (and possibly further digits) is a back reference
+to a capturing subpattern earlier (that is, to its left) in the pattern,
+provided there have been that many previous capturing left parentheses.
+<p>
+However, if the decimal number following the backslash is less than 10,
+it is always taken as a back reference, and causes an error only if there
+are not that many capturing left parentheses in the entire pattern. In other
+words, the parentheses that are referenced need not be to the left of the
+reference for numbers less than 10. See the subsection entitled "Non-printing
+characters" above for further details of the handling of digits following
+a backslash. <p>
+A back reference matches whatever actually matched the capturing
+subpattern in the current subject string, rather than anything matching
+the subpattern itself (see "Subpatterns as subroutines" below for a
+way of doing that). So the pattern <p>
+ (sens|respons)e and \1ibility<br>
+ <p>
+matches "sense and sensibility" and "response and responsibility", but
+not "sense and responsibility". If caseful matching is in force at the time
+of the back reference, the case of letters is relevant. For example, <p>
+ ((?i)rah)\s+\1<br>
+ <p>
+matches "rah rah" and "RAH RAH", but not "RAH rah", even though the original
+capturing subpattern is matched caselessly. <p>
+Back references to named subpatterns
+use the Python syntax (?P=name). We could rewrite the above example as follows:
+<p>
+ (?&lt;p1&gt;(?i)rah)\s+(?P=p1)<br>
+ <p>
+There may be more than one back reference to the same subpattern. If a
+subpattern has not actually been used in a particular match, any back references
+to it always fail. For example, the pattern <p>
+ (a|(bc))\2<br>
+ <p>
+always fails if it starts to match "a" rather than "bc". Because there
+may be many capturing parentheses in a pattern, all digits following the
+backslash are taken as part of a potential back reference number. If the
+pattern continues with a digit character, some delimiter must be used to
+terminate the back reference. If the PCRE_EXTENDED option is set, this can
+be whitespace. Otherwise an empty comment (see "Comments" below) can
+be used. <p>
+A back reference that occurs inside the parentheses to which it
+refers fails when the subpattern is first used, so, for example, (a\1) never
+matches. However, such references can be useful inside repeated subpatterns.
+For example, the pattern <p>
+ (a|b\1)+<br>
+ <p>
+matches any number of "a"s and also "aba", "ababbaa" etc. At each iteration
+of the subpattern, the back reference matches the character string corresponding
+to the previous iteration. In order for this to work, the pattern must be
+such that the first iteration does not need to match the back reference.
+This can be done using alternation, as in the example above, or by a quantifier
+with a minimum of zero.
+<h2><a name='sect19' href='#toc19'>Assertions</a></h2>
+ <p>
+An assertion is a test on the characters
+following or preceding the current matching point that does not actually
+consume any characters. The simple assertions coded as \b, \B, \A, \G, \Z, \z,
+^ and $ are described above. <p>
+More complicated assertions are coded as
+subpatterns. There are two kinds: those that look ahead of the current position
+in the subject string, and those that look behind it. An assertion subpattern
+is matched in the normal way, except that it does not cause the current
+matching position to be changed. <p>
+Assertion subpatterns are not capturing
+subpatterns, and may not be repeated, because it makes no sense to assert
+the same thing several times. If any kind of assertion contains capturing
+subpatterns within it, these are counted for the purposes of numbering
+the capturing subpatterns in the whole pattern. However, substring capturing
+is carried out only for positive assertions, because it does not make sense
+for negative assertions.
+<h3><a name='sect20' href='#toc20'>Lookahead assertions</a></h3>
+ <p>
+Lookahead assertions start
+with (?= for positive assertions and (?! for negative assertions. For example,
+<p>
+ \w+(?=;)<br>
+ <p>
+matches a word followed by a semicolon, but does not include the semicolon
+in the match, and <p>
+ foo(?!bar)<br>
+ <p>
+matches any occurrence of "foo" that is not followed by "bar". Note that
+the apparently similar pattern <p>
+ (?!foo)bar<br>
+ <p>
+does not find an occurrence of "bar" that is preceded by something other
+than "foo"; it finds any occurrence of "bar" whatsoever, because the assertion
+(?!foo) is always true when the next three characters are "bar". A lookbehind
+assertion is needed to achieve the other effect. <p>
+If you want to force a
+matching failure at some point in a pattern, the most convenient way to
+do it is with (?!) because an empty string always matches, so an assertion
+that requires there not to be an empty string must always fail.
+<h3><a name='sect21' href='#toc21'>Lookbehind
+assertions</a></h3>
+ <p>
+Lookbehind assertions start with (?&lt;= for positive assertions
+and (?&lt;! for negative assertions. For example, <p>
+ (?&lt;!foo)bar<br>
+ <p>
+does find an occurrence of "bar" that is not preceded by "foo". The contents
+of a lookbehind assertion are restricted such that all the strings it matches
+must have a fixed length. However, if there are several alternatives, they
+do not all have to have the same fixed length. Thus <p>
+ (?&lt;=bullock|donkey)<br>
+ <p>
+is permitted, but <p>
+ (?&lt;!dogs?|cats?)<br>
+ <p>
+causes an error at compile time. Branches that match different length strings
+are permitted only at the top level of a lookbehind assertion. This is an
+extension compared with Perl (at least for 5.8), which requires all branches
+to match the same length of string. An assertion such as <p>
+ (?&lt;=ab(c|de))<br>
+ <p>
+is not permitted, because its single top-level branch can match two different
+lengths, but it is acceptable if rewritten to use two top-level branches:
+<p>
+ (?&lt;=abc|abde)<br>
+ <p>
+The implementation of lookbehind assertions is, for each alternative,
+to temporarily move the current position back by the fixed width and then
+try to match. If there are insufficient characters before the current position,
+the match is deemed to fail. <p>
+PCRE does not allow the \C escape (which matches
+a single byte in UTF-8 mode) to appear in lookbehind assertions, because
+it makes it impossible to calculate the length of the lookbehind. The \X
+escape, which can match different numbers of bytes, is also not permitted.
+<p>
+Atomic groups can be used in conjunction with lookbehind assertions to
+specify efficient matching at the end of the subject string. Consider a
+simple pattern such as <p>
+ abcd$<br>
+ <p>
+when applied to a long string that does not match. Because matching proceeds
+from left to right, PCRE will look for each "a" in the subject and then
+see if what follows matches the rest of the pattern. If the pattern is specified
+as <p>
+ ^.*abcd$<br>
+ <p>
+the initial .* matches the entire string at first, but when this fails
+(because there is no following "a"), it backtracks to match all but the
+last character, then all but the last two characters, and so on. Once again
+the search for "a" covers the entire string, from right to left, so we
+are no better off. However, if the pattern is written as <p>
+ ^(?&gt;.*)(?&lt;=abcd)<br>
+ <p>
+or, equivalently, using the possessive quantifier syntax, <p>
+ ^.*+(?&lt;=abcd)<br>
+ <p>
+there can be no backtracking for the .* item; it can match only the entire
+string. The subsequent lookbehind assertion does a single test on the last
+four characters. If it fails, the match fails immediately. For long strings,
+this approach makes a significant difference to the processing time.
+<h3><a name='sect22' href='#toc22'>Using
+multiple assertions</a></h3>
+ <p>
+Several assertions (of any sort) may occur in succession.
+For example, <p>
+ (?&lt;=\d{3})(?&lt;!999)foo<br>
+ <p>
+matches "foo" preceded by three digits that are not "999". Notice that
+each of the assertions is applied independently at the same point in the
+subject string. First there is a check that the previous three characters
+are all digits, and then there is a check that the same three characters
+are not "999". This pattern does <i>not</i> match "foo" preceded by six characters,
+the first of which are digits and the last three of which are not "999".
+For example, it doesn&rsquo;t match "123abcfoo". A pattern to do that is <p>
+ (?&lt;=\d{3}...)(?&lt;!999)foo<br>
+ <p>
+This time the first assertion looks at the preceding six characters, checking
+that the first three are digits, and then the second assertion checks that
+the preceding three characters are not "999". <p>
+Assertions can be nested in
+any combination. For example, <p>
+ (?&lt;=(?&lt;!foo)bar)baz<br>
+ <p>
+matches an occurrence of "baz" that is preceded by "bar" which in turn
+is not preceded by "foo", while <p>
+ (?&lt;=\d{3}(?!999)...)foo<br>
+ <p>
+is another pattern that matches "foo" preceded by three digits and any
+three characters that are not "999".
+<h2><a name='sect23' href='#toc23'>Conditional Subpatterns</a></h2>
+ <p>
+It is possible
+to cause the matching process to obey a subpattern conditionally or to
+choose between two alternative subpatterns, depending on the result of
+an assertion, or whether a previous capturing subpattern matched or not.
+The two possible forms of conditional subpattern are <p>
+ (?(condition)yes-pattern)<br>
+ (?(condition)yes-pattern|no-pattern)<br>
+ <p>
+If the condition is satisfied, the yes-pattern is used; otherwise the no-pattern
+(if present) is used. If there are more than two alternatives in the subpattern,
+a compile-time error occurs. <p>
+There are three kinds of condition. If the text
+between the parentheses consists of a sequence of digits, the condition
+is satisfied if the capturing subpattern of that number has previously
+matched. The number must be greater than zero. Consider the following pattern,
+which contains non-significant white space to make it more readable (assume
+the PCRE_EXTENDED option) and to divide it into three parts for ease of
+discussion: <p>
+ ( \( )? [^()]+ (?(1) \) )<br>
+ <p>
+The first part matches an optional opening parenthesis, and if that character
+is present, sets it as the first captured substring. The second part matches
+one or more characters that are not parentheses. The third part is a conditional
+subpattern that tests whether the first set of parentheses matched or not.
+If they did, that is, if subject started with an opening parenthesis, the
+condition is true, and so the yes-pattern is executed and a closing parenthesis
+is required. Otherwise, since no-pattern is not present, the subpattern matches
+nothing. In other words, this pattern matches a sequence of non-parentheses,
+optionally enclosed in parentheses. <p>
+If the condition is the string (R),
+it is satisfied if a recursive call to the pattern or subpattern has been
+made. At "top level", the condition is false. This is a PCRE extension. Recursive
+patterns are described in the next section. <p>
+If the condition is not a sequence
+of digits or (R), it must be an assertion. This may be a positive or negative
+lookahead or lookbehind assertion. Consider this pattern, again containing
+non-significant white space, and with the two alternatives on the second
+line: <p>
+ (?(?=[^a-z]*[a-z])<br>
+ \d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} )<br>
+ <p>
+The condition is a positive lookahead assertion that matches an optional
+sequence of non-letters followed by a letter. In other words, it tests for
+the presence of at least one letter in the subject. If a letter is found,
+the subject is matched against the first alternative; otherwise it is matched
+against the second. This pattern matches strings in one of the two forms
+dd-aaa-dd or dd-dd-dd, where aaa are letters and dd are digits.
+<h2><a name='sect24' href='#toc24'>Comments</a></h2>
+
+<p>
+The sequence (?# marks the start of a comment that continues up to the
+next closing parenthesis. Nested parentheses are not permitted. The characters
+that make up a comment play no part in the pattern matching at all. <p>
+If the
+PCRE_EXTENDED option is set, an unescaped # character outside a character
+class introduces a comment that continues up to the next newline character
+in the pattern.
+<h2><a name='sect25' href='#toc25'>Recursive Patterns</a></h2>
+ <p>
+Consider the problem of matching a
+string in parentheses, allowing for unlimited nested parentheses. Without
+the use of recursion, the best that can be done is to use a pattern that
+matches up to some fixed depth of nesting. It is not possible to handle
+an arbitrary nesting depth. Perl provides a facility that allows regular
+expressions to recurse (amongst other things). It does this by interpolating
+Perl code in the expression at run time, and the code can refer to the
+expression itself. A Perl pattern to solve the parentheses problem can be
+created like this: <p>
+ $re = qr{\( (?: (?&gt;[^()]+) | (?p{$re}) )* \)}x;<br>
+ <p>
+The (?p{...}) item interpolates Perl code at run time, and in this case refers
+recursively to the pattern in which it appears. Obviously, PCRE cannot support
+the interpolation of Perl code. Instead, it supports some special syntax
+for recursion of the entire pattern, and also for individual subpattern
+recursion. <p>
+The special item that consists of (? followed by a number greater
+than zero and a closing parenthesis is a recursive call of the subpattern
+of the given number, provided that it occurs inside that subpattern. (If
+not, it is a "subroutine" call, which is described in the next section.)
+The special item (?R) is a recursive call of the entire regular expression.
+<p>
+For example, this PCRE pattern solves the nested parentheses problem (assume
+the PCRE_EXTENDED option is set so that white space is ignored): <p>
+ \( (
+(?&gt;[^()]+) | (?R) )* \)<br>
+ <p>
+First it matches an opening parenthesis. Then it matches any number of
+substrings which can either be a sequence of non-parentheses, or a recursive
+match of the pattern itself (that is a correctly parenthesized substring).
+Finally there is a closing parenthesis. <p>
+If this were part of a larger pattern,
+you would not want to recurse the entire pattern, so instead you could
+use this: <p>
+ ( \( ( (?&gt;[^()]+) | (?1) )* \) )<br>
+ <p>
+We have put the pattern into parentheses, and caused the recursion to
+refer to them instead of the whole pattern. In a larger pattern, keeping
+track of parenthesis numbers can be tricky. It may be more convenient to
+use named parentheses instead. For this, PCRE uses (?P&gt;name), which is an
+extension to the Python syntax that PCRE uses for named parentheses (Perl
+does not provide named parentheses). We could rewrite the above example
+as follows: <p>
+ (?P&lt;pn&gt; \( ( (?&gt;[^()]+) | (?P&gt;pn) )* \) )<br>
+ <p>
+This particular example pattern contains nested unlimited repeats, and
+so the use of atomic grouping for matching strings of non-parentheses is
+important when applying the pattern to strings that do not match. For example,
+when this pattern is applied to <p>
+ (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()<br>
+ <p>
+it yields "no match" quickly. However, if atomic grouping is not used,
+the match runs for a very long time indeed because there are so many different
+ways the + and * repeats can carve up the subject, and all have to be tested
+before failure can be reported. <p>
+At the end of a match, the values set for
+any capturing subpatterns are those from the outermost level of the recursion
+at which the subpattern value is set. If you want to obtain intermediate
+values, a callout function can be used (see the next section and the <b>pcrecallout</b>
+ documentation). If the pattern above is matched against <p>
+ (ab(cd)ef)<br>
+ <p>
+the value for the capturing parentheses is "ef", which is the last value
+taken on at the top level. If additional parentheses are added, giving <p>
+
+ \( ( ( (?&gt;[^()]+) | (?R) )* ) \)<br>
+ ^ ^<br>
+ ^ ^<br>
+ <p>
+the string they capture is "ab(cd)ef", the contents of the top level parentheses.
+If there are more than 15 capturing parentheses in a pattern, PCRE has
+to obtain extra memory to store data during a recursion, which it does
+by using <b>pcre_malloc</b>, freeing it via <b>pcre_free</b> afterwards. If no memory
+can be obtained, the match fails with the PCRE_ERROR_NOMEMORY error. <p>
+Do
+not confuse the (?R) item with the condition (R), which tests for recursion.
+Consider this pattern, which matches text in angle brackets, allowing for
+arbitrary nesting. Only digits are allowed in nested brackets (that is,
+when recursing), whereas any characters are permitted at the outer level.
+<p>
+ &lt; (?: (?(R) \d++ | [^&lt;&gt;]*+) | (?R)) * &gt;<br>
+ <p>
+In this pattern, (?(R) is the start of a conditional subpattern, with
+two different alternatives for the recursive and non-recursive cases. The
+(?R) item is the actual recursive call.
+<h2><a name='sect26' href='#toc26'>Subpatterns As Subroutines</a></h2>
+ <p>
+If
+the syntax for a recursive subpattern reference (either by number or by
+name) is used outside the parentheses to which it refers, it operates like
+a subroutine in a programming language. An earlier example pointed out that
+the pattern <p>
+ (sens|respons)e and \1ibility<br>
+ <p>
+matches "sense and sensibility" and "response and responsibility", but
+not "sense and responsibility". If instead the pattern <p>
+ (sens|respons)e
+and (?1)ibility<br>
+ <p>
+is used, it does match "sense and responsibility" as well as the other
+two strings. Such references must, however, follow the subpattern to which
+they refer.
+<h2><a name='sect27' href='#toc27'>Callouts</a></h2>
+ <p>
+Perl has a feature whereby using the sequence (?{...})
+causes arbitrary Perl code to be obeyed in the middle of matching a regular
+expression. This makes it possible, amongst other things, to extract different
+substrings that match the same pair of parentheses when there is a repetition.
+<p>
+PCRE provides a similar feature, but of course it cannot obey arbitrary
+Perl code. The feature is called "callout". The caller of PCRE provides an
+external function by putting its entry point in the global variable <i>pcre_callout</i>.
+By default, this variable contains NULL, which disables all calling out.
+<p>
+Within a regular expression, (?C) indicates the points at which the external
+function is to be called. If you want to identify different callout points,
+you can put a number less than 256 after the letter C. The default value
+is zero. For example, this pattern has two callout points: <p>
+ (?C1)dabc(?C2)def<br>
+ <p>
+If the PCRE_AUTO_CALLOUT flag is passed to <b>pcre_compile()</b>, callouts are
+automatically installed before each item in the pattern. They are all numbered
+255. <p>
+During matching, when PCRE reaches a callout point (and <i>pcre_callout</i>
+is set), the external function is called. It is provided with the number
+of the callout, the position in the pattern, and, optionally, one item
+of data originally supplied by the caller of <b>pcre_exec()</b>. The callout function
+may cause matching to proceed, to backtrack, or to fail altogether. A complete
+description of the interface to the callout function is given in the <b>pcrecallout</b>
+ documentation. <p>
+ Last updated: 09 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Pcre Regular Expression Details</a></li>
+<li><a name='toc2' href='#sect2'>Backslash</a></li>
+<ul>
+<li><a name='toc3' href='#sect3'>Non-printing characters</a></li>
+<li><a name='toc4' href='#sect4'>Generic character types</a></li>
+<li><a name='toc5' href='#sect5'>Unicode character properties</a></li>
+<li><a name='toc6' href='#sect6'>Simple assertions</a></li>
+</ul>
+<li><a name='toc7' href='#sect7'>Circumflex and Dollar</a></li>
+<li><a name='toc8' href='#sect8'>Full Stop (period, Dot)</a></li>
+<li><a name='toc9' href='#sect9'>Matching a Single Byte</a></li>
+<li><a name='toc10' href='#sect10'>Square Brackets and Character Classes</a></li>
+<li><a name='toc11' href='#sect11'>Posix Character Classes</a></li>
+<li><a name='toc12' href='#sect12'>Vertical Bar</a></li>
+<li><a name='toc13' href='#sect13'>Internal Option Setting</a></li>
+<li><a name='toc14' href='#sect14'>Subpatterns</a></li>
+<li><a name='toc15' href='#sect15'>Named Subpatterns</a></li>
+<li><a name='toc16' href='#sect16'>Repetition</a></li>
+<li><a name='toc17' href='#sect17'>Atomic Grouping and Possessive Quantifiers</a></li>
+<li><a name='toc18' href='#sect18'>Back References</a></li>
+<li><a name='toc19' href='#sect19'>Assertions</a></li>
+<ul>
+<li><a name='toc20' href='#sect20'>Lookahead assertions</a></li>
+<li><a name='toc21' href='#sect21'>Lookbehind assertions</a></li>
+<li><a name='toc22' href='#sect22'>Using multiple assertions</a></li>
+</ul>
+<li><a name='toc23' href='#sect23'>Conditional Subpatterns</a></li>
+<li><a name='toc24' href='#sect24'>Comments</a></li>
+<li><a name='toc25' href='#sect25'>Recursive Patterns</a></li>
+<li><a name='toc26' href='#sect26'>Subpatterns As Subroutines</a></li>
+<li><a name='toc27' href='#sect27'>Callouts</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcreperform.3.html b/spamfilter/Utilities/PCRE/man/html/pcreperform.3.html
new file mode 100644
index 0000000..a4fea50
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcreperform.3.html
@@ -0,0 +1,86 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Pcre Performance</a></h2>
+ <p>
+Certain items
+that may appear in regular expression patterns are more efficient than
+others. It is more efficient to use a character class like [aeiou] than
+a set of alternatives such as (a|e|i|o|u). In general, the simplest construction
+that provides the required behaviour is usually the most efficient. Jeffrey
+Friedl&rsquo;s book contains a lot of useful general discussion about optimizing
+regular expressions for efficient performance. This document contains a
+few observations about PCRE. <p>
+Using Unicode character properties (the \p,
+\P, and \X escapes) is slow, because PCRE has to scan a structure that contains
+data for over fifteen thousand characters whenever it needs a character&rsquo;s
+property. If you can find an alternative pattern that does not use character
+properties, it will probably be faster. <p>
+When a pattern begins with .* not
+in parentheses, or in parentheses that are not the subject of a backreference,
+and the PCRE_DOTALL option is set, the pattern is implicitly anchored by
+PCRE, since it can match only at the start of a subject string. However,
+if PCRE_DOTALL is not set, PCRE cannot make this optimization, because
+the . metacharacter does not then match a newline, and if the subject string
+contains newlines, the pattern may match from the character immediately
+following one of them instead of from the very start. For example, the pattern
+<p>
+ .*second<br>
+ <p>
+matches the subject "first\nand second" (where \n stands for a newline character),
+with the match starting at the seventh character. In order to do this, PCRE
+has to retry the match starting after every newline in the subject. <p>
+If you
+are using such a pattern with subject strings that do not contain newlines,
+the best performance is obtained by setting PCRE_DOTALL, or starting the
+pattern with ^.* to indicate explicit anchoring. That saves PCRE from having
+to scan along the subject looking for a newline to restart at. <p>
+Beware of
+patterns that contain nested indefinite repeats. These can take a long time
+to run when applied to a string that does not match. Consider the pattern
+fragment <p>
+ (a+)*<br>
+ <p>
+This can match "aaaa" in 33 different ways, and this number increases
+very rapidly as the string gets longer. (The * repeat can match 0, 1, 2,
+3, or 4 times, and for each of those cases other than 0, the + repeats
+can match different numbers of times.) When the remainder of the pattern
+is such that the entire match is going to fail, PCRE has in principle to
+try every possible variation, and this can take an extremely long time.
+<p>
+An optimization catches some of the more simple cases such as <p>
+ (a+)*b<br>
+ <p>
+where a literal character follows. Before embarking on the standard matching
+procedure, PCRE checks that there is a "b" later in the subject string,
+and if there is not, it fails the match immediately. However, when there
+is no following literal this optimization cannot be used. You can see the
+difference by comparing the behaviour of <p>
+ (a+)*\d<br>
+ <p>
+with the pattern above. The former gives a failure almost instantly when
+applied to a whole line of "a" characters, whereas the latter takes an
+appreciable time with strings longer than about 20 characters. <p>
+In many cases,
+the solution to this kind of performance issue is to use an atomic group
+or a possessive quantifier. <p>
+ Last updated: 09 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Pcre Performance</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcreposix.3.html b/spamfilter/Utilities/PCRE/man/html/pcreposix.3.html
new file mode 100644
index 0000000..0e7cafd
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcreposix.3.html
@@ -0,0 +1,187 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions.
+<h2><a name='sect1' href='#toc1'>Synopsis of Posix API</a></h2>
+ <p>
+<b>#include
+&lt;pcreposix.h&gt;</b> <p>
+<font size='-1'></font>
+ <br>
+<b>int regcomp(regex_t *<i>preg</i>, const char *<i>pattern</i>,</b> <b>int <i>cflags</i>);</b> <p>
+<br>
+<b>int regexec(regex_t *<i>preg</i>, const char *<i>string</i>,</b> <b>size_t <i>nmatch</i>, regmatch_t
+<i>pmatch</i>[], int <i>eflags</i>);</b> <p>
+<br>
+<b>size_t regerror(int <i>errcode</i>, const regex_t *<i>preg</i>,</b> <b>char *<i>errbuf</i>, size_t
+<i>errbuf_size</i>);</b> <p>
+<br>
+<b>void regfree(regex_t *<i>preg</i>);</b>
+<h2><a name='sect2' href='#toc2'>Description</a></h2>
+ <p>
+This set of functions provides
+a POSIX-style API to the PCRE regular expression package. See the <b>pcreapi</b>
+ documentation for a description of PCRE&rsquo;s native API, which contains additional
+functionality. <p>
+The functions described here are just wrapper functions that
+ultimately call the PCRE native API. Their prototypes are defined in the
+<b>pcreposix.h</b> header file, and on Unix systems the library itself is called
+<b>pcreposix.a</b>, so can be accessed by adding <b>-lpcreposix</b> to the command for
+linking an application that uses them. Because the POSIX functions call
+the native ones, it is also necessary to add <b>-lpcre</b>. <p>
+I have implemented only
+those option bits that can be reasonably mapped to PCRE native options.
+In addition, the options REG_EXTENDED and REG_NOSUB are defined with the
+value zero. They have no effect, but since programs that are written to
+the POSIX interface often use them, this makes it easier to slot in PCRE
+as a replacement library. Other POSIX options are not even defined. <p>
+When
+PCRE is called via these functions, it is only the API that is POSIX-like
+in style. The syntax and semantics of the regular expressions themselves
+are still those of Perl, subject to the setting of various PCRE options,
+as described below. "POSIX-like in style" means that the API approximates
+to the POSIX definition; it is not fully POSIX-compatible, and in multi-byte
+encoding domains it is probably even less compatible. <p>
+The header for these
+functions is supplied as <b>pcreposix.h</b> to avoid any potential clash with other
+POSIX libraries. It can, of course, be renamed or aliased as <b>regex.h</b>, which
+is the "correct" name. It provides two structure types, <i>regex_t</i> for compiled
+internal forms, and <i>regmatch_t</i> for returning captured substrings. It also
+defines some constants whose names start with "REG_"; these are used for
+setting options and identifying error codes. <p>
+
+<h2><a name='sect3' href='#toc3'>Compiling a Pattern</a></h2>
+ <p>
+The function
+<b>regcomp()</b> is called to compile a pattern into an internal form. The pattern
+is a C string terminated by a binary zero, and is passed in the argument
+<i>pattern</i>. The <i>preg</i> argument is a pointer to a <b>regex_t</b> structure that is used
+as a base for storing information about the compiled expression. <p>
+The argument
+<i>cflags</i> is either zero, or contains one or more of the bits defined by the
+following macros: <p>
+ REG_ICASE<br>
+ <p>
+The PCRE_CASELESS option is set when the expression is passed for compilation
+to the native function. <p>
+ REG_NEWLINE<br>
+ <p>
+The PCRE_MULTILINE option is set when the expression is passed for compilation
+to the native function. Note that this does <i>not</i> mimic the defined POSIX
+behaviour for REG_NEWLINE (see the following section). <p>
+In the absence of
+these flags, no options are passed to the native function. This means the
+the regex is compiled with PCRE default semantics. In particular, the way
+it handles newline characters in the subject string is the Perl way, not
+the POSIX way. Note that setting PCRE_MULTILINE has only <i>some</i> of the effects
+specified for REG_NEWLINE. It does not affect the way newlines are matched
+by . (they aren&rsquo;t) or by a negative class such as [^a] (they are). <p>
+The yield
+of <b>regcomp()</b> is zero on success, and non-zero otherwise. The <i>preg</i> structure
+is filled in on success, and one member of the structure is public: <i>re_nsub</i>
+contains the number of capturing subpatterns in the regular expression.
+Various error codes are defined in the header file.
+<h2><a name='sect4' href='#toc4'>Matching Newline Characters</a></h2>
+
+<p>
+This area is not simple, because POSIX and Perl take different views of
+things. It is not possible to get PCRE to obey POSIX semantics, but then
+PCRE was never intended to be a POSIX engine. The following table lists
+the different possibilities for matching newline characters in PCRE: <p>
+
+ Default Change with<br>
+ <p>
+ . matches newline no PCRE_DOTALL<br>
+ newline matches [^a] yes not changeable<br>
+ $ matches \n at end yes PCRE_DOLLARENDONLY<br>
+ $ matches \n in middle no PCRE_MULTILINE<br>
+ ^ matches \n in middle no PCRE_MULTILINE<br>
+ <p>
+This is the equivalent table for POSIX: <p>
+ Default
+ Change with<br>
+ <p>
+ . matches newline yes REG_NEWLINE<br>
+ newline matches [^a] yes REG_NEWLINE<br>
+ $ matches \n at end no REG_NEWLINE<br>
+ $ matches \n in middle no REG_NEWLINE<br>
+ ^ matches \n in middle no REG_NEWLINE<br>
+ <p>
+PCRE&rsquo;s behaviour is the same as Perl&rsquo;s, except that there is no equivalent
+for PCRE_DOLLAR_ENDONLY in Perl. In both PCRE and Perl, there is no way
+to stop newline from matching [^a]. <p>
+The default POSIX newline handling can
+be obtained by setting PCRE_DOTALL and PCRE_DOLLAR_ENDONLY, but there is
+no way to make PCRE behave exactly as for the REG_NEWLINE action.
+<h2><a name='sect5' href='#toc5'>Matching
+a Pattern</a></h2>
+ <p>
+The function <b>regexec()</b> is called to match a compiled pattern
+<i>preg</i> against a given <i>string</i>, which is terminated by a zero byte, subject
+to the options in <i>eflags</i>. These can be: <p>
+ REG_NOTBOL<br>
+ <p>
+The PCRE_NOTBOL option is set when calling the underlying PCRE matching
+function. <p>
+ REG_NOTEOL<br>
+ <p>
+The PCRE_NOTEOL option is set when calling the underlying PCRE matching
+function. <p>
+The portion of the string that was matched, and also any captured
+substrings, are returned via the <i>pmatch</i> argument, which points to an array
+of <i>nmatch</i> structures of type <i>regmatch_t</i>, containing the members <i>rm_so</i> and
+<i>rm_eo</i>. These contain the offset to the first character of each substring
+and the offset to the first character after the end of each substring,
+respectively. The 0th element of the vector relates to the entire portion
+of <i>string</i> that was matched; subsequent elements relate to the capturing
+subpatterns of the regular expression. Unused entries in the array have
+both structure members set to -1. <p>
+A successful match yields a zero return;
+various error codes are defined in the header file, of which REG_NOMATCH
+is the "expected" failure code.
+<h2><a name='sect6' href='#toc6'>Error Messages</a></h2>
+ <p>
+The <b>regerror()</b> function
+maps a non-zero errorcode from either <b>regcomp()</b> or <b>regexec()</b> to a printable
+message. If <i>preg</i> is not NULL, the error should have arisen from the use
+of that structure. A message terminated by a binary zero is placed in <i>errbuf</i>.
+The length of the message, including the zero, is limited to <i>errbuf_size</i>.
+The yield of the function is the size of buffer needed to hold the whole
+message.
+<h2><a name='sect7' href='#toc7'>Memory Usage</a></h2>
+ <p>
+Compiling a regular expression causes memory to
+be allocated and associated with the <i>preg</i> structure. The function <b>regfree()</b>
+frees all such memory, after which <i>preg</i> may no longer be used as a compiled
+expression.
+<h2><a name='sect8' href='#toc8'>Author</a></h2>
+ <p>
+Philip Hazel &lt;ph10@cam.ac.uk&gt; <br>
+University Computing Service, <br>
+Cambridge CB2 3QG, England. <p>
+ Last updated: 07 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis of Posix API</a></li>
+<li><a name='toc2' href='#sect2'>Description</a></li>
+<li><a name='toc3' href='#sect3'>Compiling a Pattern</a></li>
+<li><a name='toc4' href='#sect4'>Matching Newline Characters</a></li>
+<li><a name='toc5' href='#sect5'>Matching a Pattern</a></li>
+<li><a name='toc6' href='#sect6'>Error Messages</a></li>
+<li><a name='toc7' href='#sect7'>Memory Usage</a></li>
+<li><a name='toc8' href='#sect8'>Author</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcresample.3.html b/spamfilter/Utilities/PCRE/man/html/pcresample.3.html
new file mode 100644
index 0000000..6fad4a9
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcresample.3.html
@@ -0,0 +1,72 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRE(3) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+PCRE - Perl-compatible regular expressions
+<h2><a name='sect1' href='#toc1'>Pcre Sample Program</a></h2>
+ <p>
+A simple,
+complete demonstration program, to get you started with using PCRE, is
+supplied in the file <i>pcredemo.c</i> in the PCRE distribution. <p>
+The program compiles
+the regular expression that is its first argument, and matches it against
+the subject string in its second argument. No PCRE options are set, and
+default character tables are used. If matching succeeds, the program outputs
+the portion of the subject that matched, together with the contents of
+any captured substrings. <p>
+If the -g option is given on the command line, the
+program then goes on to check for further matches of the same regular expression
+in the same subject string. The logic is a little bit tricky because of
+the possibility of matching an empty string. Comments in the code explain
+what is going on. <p>
+If PCRE is installed in the standard include and library
+directories for your system, you should be able to compile the demonstration
+program using this command: <p>
+ gcc -o pcredemo pcredemo.c -lpcre<br>
+ <p>
+If PCRE is installed elsewhere, you may need to add additional options
+to the command line. For example, on a Unix-like system that has PCRE installed
+in <i>/usr/local</i>, you can compile the demonstration program using a command
+like this: <p>
+ gcc -o pcredemo -I/usr/local/include pcredemo.c \<br>
+ -L/usr/local/lib -lpcre<br>
+ <p>
+Once you have compiled the demonstration program, you can run simple tests
+like this: <p>
+ ./pcredemo &rsquo;cat|dog&rsquo; &rsquo;the cat sat on the mat&rsquo;<br>
+ ./pcredemo -g &rsquo;cat|dog&rsquo; &rsquo;the dog sat on the cat&rsquo;<br>
+ <p>
+Note that there is a much more comprehensive test program, called <b>pcretest</b>,
+ which supports many more facilities for testing regular expressions and
+the PCRE library. The <b>pcredemo</b> program is provided as a simple coding example.
+<p>
+On some operating systems (e.g. Solaris), when PCRE is not installed in the
+standard library directory, you may get an error like this when you try
+to run <b>pcredemo</b>: <p>
+ ld.so.1: a.out: fatal: libpcre.so.0: open failed: No such
+file or directory<br>
+ <p>
+This is caused by the way shared library support works on those systems.
+You need to add <p>
+ -R/usr/local/lib<br>
+ <p>
+(for example) to the compile command to get round this problem. <p>
+ Last updated:
+09 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Pcre Sample Program</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/man/html/pcretest.1.html b/spamfilter/Utilities/PCRE/man/html/pcretest.1.html
new file mode 100644
index 0000000..e96293e
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/man/html/pcretest.1.html
@@ -0,0 +1,433 @@
+<!-- manual page source format generated by PolyglotMan v3.2, -->
+<!-- available at http://polyglotman.sourceforge.net/ -->
+
+<html>
+<head>
+<title>PCRETEST(1) manual page</title>
+</head>
+<body bgcolor='white'>
+<a href='#toc'>Table of Contents</a><p>
+
+<h2><a name='sect0' href='#toc0'>Name</a></h2>
+pcretest - a program for testing Perl-compatible regular expressions.
+
+<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>
+ <p>
+<b>pcretest "[-C] [-d] [-i] [-m] [-o osize] [-p] [-t] [source]"</b> <b>[destination]</b>
+<p>
+<b>pcretest</b> was written as a test program for the PCRE regular expression
+library itself, but it can also be used for experimenting with regular
+expressions. This document describes the features of the test program; for
+details of the regular expressions themselves, see the <b>pcrepattern</b> documentation.
+For details of the PCRE library function calls and their options, see the
+ <b>pcreapi</b> documentation.
+<h2><a name='sect2' href='#toc2'>Options</a></h2>
+
+<dl>
+
+<dt><b>-C</b> </dt>
+<dd>Output the version number of the PCRE
+library, and all available information about the optional features that
+are included, and then exit. </dd>
+
+<dt><b>-d</b> </dt>
+<dd>Behave as if each regex had the <b>/D</b> (debug)
+modifier; the internal form is output after compilation. </dd>
+
+<dt><b>-i</b> </dt>
+<dd>Behave as if
+each regex had the <b>/I</b> modifier; information about the compiled pattern
+is given after compilation. </dd>
+
+<dt><b>-m</b> </dt>
+<dd>Output the size of each compiled pattern after
+it has been compiled. This is equivalent to adding <b>/M</b> to each regular expression.
+For compatibility with earlier versions of pcretest, <b>-s</b> is a synonym for
+<b>-m</b>. </dd>
+
+<dt><b>-o</b> <i>osize</i> </dt>
+<dd>Set the number of elements in the output vector that is used
+when calling <b>pcre_exec()</b> to be <i>osize</i>. The default value is 45, which is
+enough for 14 capturing subexpressions. The vector size can be changed for
+individual matching calls by including \O in the data line (see below). </dd>
+
+<dt><b>-p</b>
+</dt>
+<dd>Behave as if each regex has <b>/P</b> modifier; the POSIX wrapper API is used
+to call PCRE. None of the other options has any effect when <b>-p</b> is set. </dd>
+
+<dt><b>-t</b> </dt>
+<dd>Run
+each compile, study, and match many times with a timer, and output resulting
+time per compile or match (in milliseconds). Do not set <b>-m</b> with <b>-t</b>, because
+you will then get the size output a zillion times, and the timing will
+be distorted. </dd>
+</dl>
+
+<h2><a name='sect3' href='#toc3'>Description</a></h2>
+ <p>
+If <b>pcretest</b> is given two filename arguments,
+it reads from the first and writes to the second. If it is given only one
+filename argument, it reads from that file and writes to stdout. Otherwise,
+it reads from stdin and writes to stdout, and prompts for each line of
+input, using "re&gt;" to prompt for regular expressions, and "data&gt;" to prompt
+for data lines. <p>
+The program handles any number of sets of input on a single
+input file. Each set starts with a regular expression, and continues with
+any number of data lines to be matched against the pattern. <p>
+Each data line
+is matched separately and independently. If you want to do multiple-line
+matches, you have to use the \n escape sequence in a single line of input
+to encode the newline characters. The maximum length of data line is 30,000
+characters. <p>
+An empty line signals the end of the data lines, at which point
+a new regular expression is read. The regular expressions are given enclosed
+in any non-alphanumeric delimiters other than backslash, for example <p>
+ /(a|bc)x+yz/<br>
+ <p>
+White space before the initial delimiter is ignored. A regular expression
+may be continued over several input lines, in which case the newline characters
+are included within it. It is possible to include the delimiter within the
+pattern by escaping it, for example <p>
+ /abc\/def/<br>
+ <p>
+If you do so, the escape and the delimiter form part of the pattern, but
+since delimiters are always non-alphanumeric, this does not affect its interpretation.
+If the terminating delimiter is immediately followed by a backslash, for
+example, <p>
+ /abc/\<br>
+ <p>
+then a backslash is added to the end of the pattern. This is done to provide
+a way of testing the error condition that arises if a pattern finishes
+with a backslash, because <p>
+ /abc\/<br>
+ <p>
+is interpreted as the first line of a pattern that starts with "abc/",
+causing pcretest to read the next line as a continuation of the regular
+expression.
+<h2><a name='sect4' href='#toc4'>Pattern Modifiers</a></h2>
+ <p>
+A pattern may be followed by any number
+of modifiers, which are mostly single characters. Following Perl usage,
+these are referred to below as, for example, "the <b>/i</b> modifier", even though
+the delimiter of the pattern need not always be a slash, and no slash is
+used when writing modifiers. Whitespace may appear between the final pattern
+delimiter and the first modifier, and between the modifiers themselves.
+<p>
+The <b>/i</b>, <b>/m</b>, <b>/s</b>, and <b>/x</b> modifiers set the PCRE_CASELESS, PCRE_MULTILINE,
+PCRE_DOTALL, or PCRE_EXTENDED options, respectively, when <b>pcre_compile()</b>
+is called. These four modifier letters have the same effect as they do in
+Perl. For example: <p>
+ /caseless/i<br>
+ <p>
+The following table shows additional modifiers for setting PCRE options
+that do not correspond to anything in Perl: <p>
+ <b>/A</b> PCRE_ANCHORED<br>
+ <b>/C</b> PCRE_AUTO_CALLOUT<br>
+ <b>/E</b> PCRE_DOLLAR_ENDONLY<br>
+ <b>/N</b> PCRE_NO_AUTO_CAPTURE<br>
+ <b>/U</b> PCRE_UNGREEDY<br>
+ <b>/X</b> PCRE_EXTRA<br>
+ <p>
+Searching for all possible matches within each subject string can be requested
+by the <b>/g</b> or <b>/G</b> modifier. After finding a match, PCRE is called again to
+search the remainder of the subject string. The difference between <b>/g</b> and
+<b>/G</b> is that the former uses the <i>startoffset</i> argument to <b>pcre_exec()</b> to start
+searching at a new point within the entire string (which is in effect what
+Perl does), whereas the latter passes over a shortened substring. This makes
+a difference to the matching process if the pattern begins with a lookbehind
+assertion (including \b or \B). <p>
+If any call to <b>pcre_exec()</b> in a <b>/g</b> or <b>/G</b> sequence
+matches an empty string, the next call is done with the PCRE_NOTEMPTY and
+PCRE_ANCHORED flags set in order to search for another, non-empty, match
+at the same point. If this second match fails, the start offset is advanced
+by one, and the normal match is retried. This imitates the way Perl handles
+such cases when using the <b>/g</b> modifier or the <b>split()</b> function. <p>
+There are
+yet more modifiers for controlling the way <b>pcretest</b> operates. <p>
+The <b>/+</b> modifier
+requests that as well as outputting the substring that matched the entire
+pattern, pcretest should in addition output the remainder of the subject
+string. This is useful for tests where the subject contains multiple copies
+of the same substring. <p>
+The <b>/L</b> modifier must be followed directly by the
+name of a locale, for example, <p>
+ /pattern/Lfr_FR<br>
+ <p>
+For this reason, it must be the last modifier. The given locale is set,
+<b>pcre_maketables()</b> is called to build a set of character tables for the
+locale, and this is then passed to <b>pcre_compile()</b> when compiling the regular
+expression. Without an <b>/L</b> modifier, NULL is passed as the tables pointer;
+that is, <b>/L</b> applies only to the expression on which it appears. <p>
+The <b>/I</b> modifier
+requests that <b>pcretest</b> output information about the compiled pattern (whether
+it is anchored, has a fixed first character, and so on). It does this by
+calling <b>pcre_fullinfo()</b> after compiling a pattern. If the pattern is studied,
+the results of that are also output. <p>
+The <b>/D</b> modifier is a PCRE debugging
+feature, which also assumes <b>/I</b>. It causes the internal form of compiled
+regular expressions to be output after compilation. If the pattern was studied,
+the information returned is also output. <p>
+The <b>/F</b> modifier causes <b>pcretest</b>
+to flip the byte order of the fields in the compiled pattern that contain
+2-byte and 4-byte numbers. This facility is for testing the feature in PCRE
+that allows it to execute patterns that were compiled on a host with a
+different endianness. This feature is not available when the POSIX interface
+to PCRE is being used, that is, when the <b>/P</b> pattern modifier is specified.
+See also the section about saving and reloading compiled patterns below.
+<p>
+The <b>/S</b> modifier causes <b>pcre_study()</b> to be called after the expression has
+been compiled, and the results used when the expression is matched. <p>
+The
+<b>/M</b> modifier causes the size of memory block used to hold the compiled pattern
+to be output. <p>
+The <b>/P</b> modifier causes <b>pcretest</b> to call PCRE via the POSIX
+wrapper API rather than its native API. When this is done, all other modifiers
+except <b>/i</b>, <b>/m</b>, and <b>/+</b> are ignored. REG_ICASE is set if <b>/i</b> is present, and
+REG_NEWLINE is set if <b>/m</b> is present. The wrapper functions force PCRE_DOLLAR_ENDONLY
+always, and PCRE_DOTALL unless REG_NEWLINE is set. <p>
+The <b>/8</b> modifier causes
+<b>pcretest</b> to call PCRE with the PCRE_UTF8 option set. This turns on support
+for UTF-8 character handling in PCRE, provided that it was compiled with
+this support enabled. This modifier also causes any non-printing characters
+in output strings to be printed using the \x{hh...} notation if they are valid
+UTF-8 sequences. <p>
+If the <b>/?</b> modifier is used with <b>/8</b>, it causes <b>pcretest</b> to
+call <b>pcre_compile()</b> with the PCRE_NO_UTF8_CHECK option, to suppress the
+checking of the string for UTF-8 validity.
+<h2><a name='sect5' href='#toc5'>Data Lines</a></h2>
+ <p>
+Before each data
+line is passed to <b>pcre_exec()</b>, leading and trailing whitespace is removed,
+and it is then scanned for \ escapes. Some of these are pretty esoteric features,
+intended for checking out some of the more complicated features of PCRE.
+If you are just testing "ordinary" regular expressions, you probably don&rsquo;t
+need any of these. The following escapes are recognized: <p>
+ \a alarm
+(= BEL)<br>
+ \b backspace<br>
+ \e escape<br>
+ \f formfeed<br>
+ \n newline<br>
+ \r carriage return<br>
+ \t tab<br>
+ \v vertical tab<br>
+ \nnn octal character (up to 3 octal digits)<br>
+ \xhh hexadecimal character (up to 2 hex digits)<br>
+ \x{hh...} hexadecimal character, any number of digits<br>
+ in UTF-8 mode<br>
+ \A pass the PCRE_ANCHORED option to <b>pcre_exec()</b><br>
+ \B pass the PCRE_NOTBOL option to <b>pcre_exec()</b><br>
+ \Cdd call pcre_copy_substring() for substring dd<br>
+ after a successful match (number less than 32)<br>
+ \Cname call pcre_copy_named_substring() for substring<br>
+ "name" after a successful match (name termin-<br>
+ ated by next non alphanumeric character)<br>
+ \C+ show the current captured substrings at callout<br>
+ time<br>
+ \C- do not supply a callout function<br>
+ \C!n return 1 instead of 0 when callout number n is<br>
+ reached<br>
+ \C!n!m return 1 instead of 0 when callout number n is<br>
+ reached for the nth time<br>
+ \C*n pass the number n (may be negative) as callout<br>
+ data; this is used as the callout return value<br>
+ \Gdd call pcre_get_substring() for substring dd<br>
+ after a successful match (number less than 32)<br>
+ \Gname call pcre_get_named_substring() for substring<br>
+ "name" after a successful match (name termin-<br>
+ ated by next non-alphanumeric character)<br>
+ \L call pcre_get_substringlist() after a<br>
+ successful match<br>
+ \M discover the minimum MATCH_LIMIT setting<br>
+ \N pass the PCRE_NOTEMPTY option to <b>pcre_exec()</b><br>
+ \Odd set the size of the output vector passed to<br>
+ <b>pcre_exec()</b> to dd (any number of digits)<br>
+ \P pass the PCRE_PARTIAL option to <b>pcre_exec()</b><br>
+ \S output details of memory get/free calls during matching<br>
+ \Z pass the PCRE_NOTEOL option to <b>pcre_exec()</b><br>
+ \? pass the PCRE_NO_UTF8_CHECK option to<br>
+ <b>pcre_exec()</b><br>
+ \&gt;dd start the match at offset dd (any number of digits);<br>
+ this sets the <i>startoffset</i> argument for <b>pcre_exec()</b><br>
+ <p>
+A backslash followed by anything else just escapes the anything else. If
+the very last character is a backslash, it is ignored. This gives a way
+of passing an empty line as data, since a real empty line terminates the
+data input. <p>
+If \M is present, <b>pcretest</b> calls <b>pcre_exec()</b> several times, with
+different values in the <i>match_limit</i> field of the <b>pcre_extra</b> data structure,
+until it finds the minimum number that is needed for <b>pcre_exec()</b> to complete.
+This number is a measure of the amount of recursion and backtracking that
+takes place, and checking it out can be instructive. For most simple matches,
+the number is quite small, but for patterns with very large numbers of
+matching possibilities, it can become large very quickly with increasing
+length of subject string. <p>
+When \O is used, the value specified may be higher
+or lower than the size set by the <b>-O</b> command line option (or defaulted to
+45); \O applies only to the call of <b>pcre_exec()</b> for the line in which it
+appears. <p>
+If the <b>/P</b> modifier was present on the pattern, causing the POSIX
+wrapper API to be used, only \B and \Z have any effect, causing REG_NOTBOL
+and REG_NOTEOL to be passed to <b>regexec()</b> respectively. <p>
+The use of \x{hh...}
+to represent UTF-8 characters is not dependent on the use of the <b>/8</b> modifier
+on the pattern. It is recognized always. There may be any number of hexadecimal
+digits inside the braces. The result is from one to six bytes, encoded according
+to the UTF-8 rules.
+<h2><a name='sect6' href='#toc6'>Output from Pcretest</a></h2>
+ <p>
+When a match succeeds, pcretest
+outputs the list of captured substrings that <b>pcre_exec()</b> returns, starting
+with number 0 for the string that matched the whole pattern. Otherwise,
+it outputs "No match" or "Partial match" when <b>pcre_exec()</b> returns PCRE_ERROR_NOMATCH
+or PCRE_ERROR_PARTIAL, respectively, and otherwise the PCRE negative error
+number. Here is an example of an interactive pcretest run. <p>
+ $ pcretest<br>
+ PCRE version 5.00 07-Sep-2004<br>
+ <p>
+ re&gt; /^abc(\d+)/<br>
+ data&gt; abc123<br>
+ 0: abc123<br>
+ 1: 123<br>
+ data&gt; xyz<br>
+ No match<br>
+ <p>
+If the strings contain any non-printing characters, they are output as
+\0x escapes, or as \x{...} escapes if the <b>/8</b> modifier was present on the pattern.
+If the pattern has the <b>/+</b> modifier, the output for substring 0 is followed
+by the the rest of the subject string, identified by "0+" like this: <p>
+
+ re&gt; /cat/+<br>
+ data&gt; cataract<br>
+ 0: cat<br>
+ 0+ aract<br>
+ <p>
+If the pattern has the <b>/g</b> or <b>/G</b> modifier, the results of successive matching
+attempts are output in sequence, like this: <p>
+ re&gt; /\Bi(\w\w)/g<br>
+ data&gt; Mississippi<br>
+ 0: iss<br>
+ 1: ss<br>
+ 0: iss<br>
+ 1: ss<br>
+ 0: ipp<br>
+ 1: pp<br>
+ <p>
+"No match" is output only if the first match attempt fails. <p>
+If any of the
+sequences <b>\C</b>, <b>\G</b>, or <b>\L</b> are present in a data line that is successfully matched,
+the substrings extracted by the convenience functions are output with C,
+G, or L after the string number instead of a colon. This is in addition
+to the normal full list. The string length (that is, the return from the
+extraction function) is given in parentheses after each string for <b>\C</b> and
+<b>\G</b>. <p>
+Note that while patterns can be continued over several lines (a plain
+"&gt;" prompt is used for continuations), data lines may not. However newlines
+can be included in data by means of the \n escape.
+<h2><a name='sect7' href='#toc7'>Callouts</a></h2>
+ <p>
+If the pattern
+contains any callout requests, <b>pcretest</b>&rsquo;s callout function is called during
+matching. By default, it displays the callout number, the start and current
+positions in the text at the callout time, and the next pattern item to
+be tested. For example, the output <p>
+ ---&gt;pqrabcdef<br>
+ 0 ^ ^ \d<br>
+ <p>
+indicates that callout number 0 occurred for a match attempt starting
+at the fourth character of the subject string, when the pointer was at
+the seventh character of the data, and when the next pattern item was \d.
+Just one circumflex is output if the start and current positions are the
+same. <p>
+Callouts numbered 255 are assumed to be automatic callouts, inserted
+as a result of the <b>/C</b> pattern modifier. In this case, instead of showing
+the callout number, the offset in the pattern, preceded by a plus, is output.
+For example: <p>
+ re&gt; /\d?[A-E]\*/C<br>
+ data&gt; E*<br>
+ ---&gt;E*<br>
+ +0 ^ \d?<br>
+ +3 ^ [A-E]<br>
+ +8 ^^ \*<br>
+ +10 ^ ^<br>
+ 0: E*<br>
+ <p>
+The callout function in <b>pcretest</b> returns zero (carry on matching) by default,
+but you can use an \C item in a data line (as described above) to change
+this. <p>
+Inserting callouts can be helpful when using <b>pcretest</b> to check complicated
+regular expressions. For further information about callouts, see the <b>pcrecallout</b>
+ documentation.
+<h2><a name='sect8' href='#toc8'>Saving and Reloading Compiled Patterns</a></h2>
+ <p>
+The facilities
+described in this section are not available when the POSIX inteface to
+PCRE is being used, that is, when the <b>/P</b> pattern modifier is specified.
+<p>
+When the POSIX interface is not in use, you can cause <b>pcretest</b> to write
+a compiled pattern to a file, by following the modifiers with &gt; and a file
+name. For example: <p>
+ /pattern/im &gt;/some/file<br>
+ <p>
+See the <b>pcreprecompile</b> documentation for a discussion about saving and
+re-using compiled patterns. <p>
+The data that is written is binary. The first
+eight bytes are the length of the compiled pattern data followed by the
+length of the optional study data, each written as four bytes in big-endian
+order (most significant byte first). If there is no study data (either the
+pattern was not studied, or studying did not return any data), the second
+length is zero. The lengths are followed by an exact copy of the compiled
+pattern. If there is additional study data, this follows immediately after
+the compiled pattern. After writing the file, <b>pcretest</b> expects to read a
+new pattern. <p>
+A saved pattern can be reloaded into <b>pcretest</b> by specifing
+&lt; and a file name instead of a pattern. The name of the file must not contain
+a &lt; character, as otherwise <b>pcretest</b> will interpret the line as a pattern
+delimited by &lt; characters. For example: <p>
+ re&gt; &lt;/some/file<br>
+ Compiled regex loaded from /some/file<br>
+ No study data<br>
+ <p>
+When the pattern has been loaded, <b>pcretest</b> proceeds to read data lines
+in the usual way. <p>
+You can copy a file written by <b>pcretest</b> to a different
+host and reload it there, even if the new host has opposite endianness
+to the one on which the pattern was compiled. For example, you can compile
+on an i86 machine and run on a SPARC machine. <p>
+File names for saving and
+reloading can be absolute or relative, but note that the shell facility
+of expanding a file name that starts with a tilde (~) is not available.
+<p>
+The ability to save and reload files in <b>pcretest</b> is intended for testing
+and experimentation. It is not intended for production use because only
+a single pattern can be written to a file. Furthermore, there is no facility
+for supplying custom character tables for use with a reloaded pattern. If
+the original pattern was compiled with custom tables, an attempt to match
+a subject string using a reloaded pattern is likely to cause <b>pcretest</b> to
+crash. Finally, if you attempt to load a file that is not in the correct
+format, the result is undefined.
+<h2><a name='sect9' href='#toc9'>Author</a></h2>
+ <p>
+Philip Hazel &lt;ph10@cam.ac.uk&gt; <br>
+University Computing Service, <br>
+Cambridge CB2 3QG, England. <p>
+ Last updated: 10 September 2004 <br>
+Copyright (c) 1997-2004 University of Cambridge. <p>
+
+<hr><p>
+<a name='toc'><b>Table of Contents</b></a><p>
+<ul>
+<li><a name='toc0' href='#sect0'>Name</a></li>
+<li><a name='toc1' href='#sect1'>Synopsis</a></li>
+<li><a name='toc2' href='#sect2'>Options</a></li>
+<li><a name='toc3' href='#sect3'>Description</a></li>
+<li><a name='toc4' href='#sect4'>Pattern Modifiers</a></li>
+<li><a name='toc5' href='#sect5'>Data Lines</a></li>
+<li><a name='toc6' href='#sect6'>Output from Pcretest</a></li>
+<li><a name='toc7' href='#sect7'>Callouts</a></li>
+<li><a name='toc8' href='#sect8'>Saving and Reloading Compiled Patterns</a></li>
+<li><a name='toc9' href='#sect9'>Author</a></li>
+</ul>
+</body>
+</html>
diff --git a/spamfilter/Utilities/PCRE/manifest/pcre-5.0-doc.mft b/spamfilter/Utilities/PCRE/manifest/pcre-5.0-doc.mft
new file mode 100644
index 0000000..a2ac5c9
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/manifest/pcre-5.0-doc.mft
@@ -0,0 +1,58 @@
+man/html/pcre.3.html
+man/html/pcre_compile.3.html
+man/html/pcre_config.3.html
+man/html/pcre_copy_named_substring.3.html
+man/html/pcre_copy_substring.3.html
+man/html/pcre_exec.3.html
+man/html/pcre_free_substring.3.html
+man/html/pcre_free_substring_list.3.html
+man/html/pcre_fullinfo.3.html
+man/html/pcre_get_named_substring.3.html
+man/html/pcre_get_stringnumber.3.html
+man/html/pcre_get_substring.3.html
+man/html/pcre_get_substring_list.3.html
+man/html/pcre_info.3.html
+man/html/pcre_maketables.3.html
+man/html/pcre_study.3.html
+man/html/pcre_version.3.html
+man/html/pcreapi.3.html
+man/html/pcrebuild.3.html
+man/html/pcrecallout.3.html
+man/html/pcrecompat.3.html
+man/html/pcregrep.1.html
+man/html/pcrepattern.3.html
+man/html/pcreperform.3.html
+man/html/pcreposix.3.html
+man/html/pcresample.3.html
+man/html/pcretest.1.html
+man/man1/pcregrep.1
+man/man1/pcretest.1
+man/man3/pcre.3
+man/man3/pcre_compile.3
+man/man3/pcre_config.3
+man/man3/pcre_copy_named_substring.3
+man/man3/pcre_copy_substring.3
+man/man3/pcre_exec.3
+man/man3/pcre_free_substring.3
+man/man3/pcre_free_substring_list.3
+man/man3/pcre_fullinfo.3
+man/man3/pcre_get_named_substring.3
+man/man3/pcre_get_stringnumber.3
+man/man3/pcre_get_substring.3
+man/man3/pcre_get_substring_list.3
+man/man3/pcre_info.3
+man/man3/pcre_maketables.3
+man/man3/pcre_study.3
+man/man3/pcre_version.3
+man/man3/pcreapi.3
+man/man3/pcrebuild.3
+man/man3/pcrecallout.3
+man/man3/pcrecompat.3
+man/man3/pcrepattern.3
+man/man3/pcreperform.3
+man/man3/pcreposix.3
+man/man3/pcresample.3
+man/pdf/pcre-man.pdf
+man/ps/pcre-man.ps.gz
+manifest/pcre-5.0-doc.mft
+manifest/pcre-5.0-doc.ver
diff --git a/spamfilter/Utilities/PCRE/manifest/pcre-5.0-doc.ver b/spamfilter/Utilities/PCRE/manifest/pcre-5.0-doc.ver
new file mode 100644
index 0000000..6a4adaa
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/manifest/pcre-5.0-doc.ver
@@ -0,0 +1,2 @@
+Pcre 5.0: Documentation
+Pcre: Perl-compatible regular-expression library
diff --git a/spamfilter/Utilities/PCRE/manifest/pcre-5.0-lib.mft b/spamfilter/Utilities/PCRE/manifest/pcre-5.0-lib.mft
new file mode 100644
index 0000000..aabb128
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/manifest/pcre-5.0-lib.mft
@@ -0,0 +1,12 @@
+include/pcre.h
+include/pcreposix.h
+lib/libpcre-bcc.lib
+lib/libpcre.def
+lib/libpcre.dll.a
+lib/libpcre.lib
+lib/libpcreposix-bcc.lib
+lib/libpcreposix.def
+lib/libpcreposix.dll.a
+lib/libpcreposix.lib
+manifest/pcre-5.0-lib.mft
+manifest/pcre-5.0-lib.ver
diff --git a/spamfilter/Utilities/PCRE/manifest/pcre-5.0-lib.ver b/spamfilter/Utilities/PCRE/manifest/pcre-5.0-lib.ver
new file mode 100644
index 0000000..288da85
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/manifest/pcre-5.0-lib.ver
@@ -0,0 +1,2 @@
+Pcre 5.0: Developer files
+Pcre: Perl-compatible regular-expression library
diff --git a/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.3 b/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.3
new file mode 100644
index 0000000..b3c269b
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.3
@@ -0,0 +1,73 @@
+.TH PCRE 3
+.SH NAME
+pcre_subst - Perl-compatible regular expression subsitution.
+.SH SYNOPSIS
+.B #include <pcre.h>
+.br
+.B #include <pcre_subst.h>
+.PP
+.SM
+.br
+int pcre_exec(const pcre *\fIcode\fR, const pcre_extra *\fIextra\fR,
+.ti +5n
+const char *\fIsubject\fR, int \fIlength\fR, int \fIstartoffset\fR,
+.ti +5n
+int \fIoptions\fR, char *\fIreplacement\fR);
+
+
+
+.SH DESCRIPTION
+\fBpcre_subst\fR is a convenience routine that calls \fIpcre_exec\fR,
+and returns a freshly allocated string based on the \fIsubject\fR with
+the \fIreplacement\fR action applied. Unlike \fIsubject\fR, whics is
+passed as a byte array with a length, \fIreplacement\fR is expected to
+be a zero terminated string (most users will just pass \fIstrlen(subject)\fR
+as the \fIlength\fR).
+
+.br
+If no match is found, pcre_subst returns NULL. The returned string is zero
+terminated (note that \fIsubject\fR doesn't have to be). For information
+on the \fIcode\fR, \fIextra\fR, \fIsubject\fR, \fIlength\fR,
+\fIstartoffset\fR and \fIoptions\fR parameters, please see \fBpcre(3)\fR.
+
+.SH REPLACEMENT STRING
+The replacement string supports a subset of the PERL replacement string.
+In particular, \\1 style escapes are not supported (actually, only the
+$1 style is handled).
+
+.SH EXAMPLE
+.Bd -literal -compact
+#include <stdio.h>
+#include <pcre.h>
+#include "pcre_subst.h"
+
+int
+main()
+{
+ char *pat = "quick\\\\s(\\\\w+)\\\\s(fox)";
+ char *rep = "$1ish $2";
+ char *str = "The quick brown foxy";
+ char *newstr;
+ const char *err;
+ int erroff;
+ pcre_extra *extra;
+ pcre *p = pcre_compile(pat, 0, &err, &erroff, NULL);
+ if (p == NULL) {
+ fprintf(stderr, "%s at %d\\n", err, erroff);
+ exit(1);
+ }
+ extra = pcre_study(p, 0, &err);
+ if (err != NULL)
+ fprintf(stderr, "Study %s: %s\\n", pat, err);
+ newstr = pcre_subst(ppat, extra, str, strlen(str),
+ 0, 0, rep);
+ if (newstr) {
+ printf("New string: %s\\n", newstr);
+ pcre_free(newstr);
+ };
+ return 0;
+}
+.Ed
+
+.SH SEE ALSO
+pcre(3)
diff --git a/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.c b/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.c
new file mode 100644
index 0000000..3f34f97
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.c
@@ -0,0 +1,191 @@
+/*************************************************
+* PCRE string replacement *
+*************************************************/
+
+/*
+PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+pcre_subst is a wrapper around pcre_exec designed to make it easier to
+perform PERL style replacements with PCRE.
+
+Written by: Bert Driehuis <driehuis@playbeing.org>
+
+ Copyright (c) 2000 Bert Driehuis
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <pcre.h>
+#include "pcre_subst.h"
+
+#define MAXCAPTURE 50
+
+#ifdef DEBUG_PCRE_SUBST
+static void
+dumpstr(const char *str, int len, int start, int end)
+{
+ int i;
+ for (i = 0; i < strlen(str); i++) {
+ if (i >= start && i < end)
+ putchar(str[i]);
+ else
+ putchar('-');
+ }
+ putchar('\n');
+}
+
+static void
+dumpmatch(const char *str, int len, const char *rep, int nmat, const int *ovec)
+{
+ int i;
+ printf("%s Input\n", str);
+ printf("nmat=%d", nmat);
+ for (i = 0; i < nmat * 2; i++)
+ printf(" %d", ovec[i]);
+ printf("\n");
+ for (i = 0; i < nmat * 2; i += 2)
+ dumpstr(str, len, ovec[i], ovec[i+1]);
+ printf("\n");
+}
+#endif
+
+static int
+findreplen(const char *rep, int nmat, const int *replen)
+{
+ int len = 0;
+ int val;
+ char *cp = (char *)rep;
+ while(*cp) {
+ if (*cp == '$' && isdigit(cp[1])) {
+ val = strtoul(&cp[1], &cp, 10);
+ if (val && val <= nmat + 1)
+ len += replen[val -1];
+ else
+ fprintf(stderr, "repl %d out of range\n", val);
+ } else {
+ cp++;
+ len++;
+ }
+ }
+ return len;
+}
+
+static void
+doreplace(char *out, const char *rep, int nmat, int *replen, const char **repstr)
+{
+ int val;
+ char *cp = (char *)rep;
+ while(*cp) {
+ if (*cp == '$' && isdigit(cp[1])) {
+ val = strtoul(&cp[1], &cp, 10);
+ if (val && val <= nmat + 1) {
+ strncpy(out, repstr[val - 1], replen[val - 1]);
+ out += replen[val -1];
+ }
+ } else {
+ *out++ = *cp++;
+ }
+ }
+}
+
+static char *
+edit(const char *str, int len, const char *rep, int nmat, const int *ovec)
+{
+ int i, slen, rlen;
+ const int *mvec = ovec;
+ char *res, *cp;
+ int replen[MAXCAPTURE];
+ const char *repstr[MAXCAPTURE];
+ nmat--;
+ ovec += 2;
+ for (i = 0; i < nmat; i++) {
+ replen[i] = ovec[i * 2 + 1] - ovec[i * 2];
+ repstr[i] = &str[ovec[i * 2]];
+#ifdef DEBUG_PCRE_SUBST
+ printf(">>>%d %d %.*s\n", i, replen[i], replen[i], repstr[i]);
+#endif
+ }
+ slen = len;
+ len -= mvec[1] - mvec[0];
+ len += rlen = findreplen(rep, nmat, replen);
+#ifdef DEBUG_PCRE_SUBST
+ printf("resulting length %d (srclen=%d)\n", len, slen);
+#endif
+ cp = res = pcre_malloc(len + 1);
+ if (mvec[0] > 0) {
+ strncpy(cp, str, mvec[0]);
+ cp += mvec[0];
+ }
+ doreplace(cp, rep, nmat, replen, repstr);
+ cp += rlen;
+ if (mvec[1] < slen)
+ strcpy(cp, &str[mvec[1]]);
+ res[len] = 0;
+ return res;
+}
+
+char *
+pcre_subst(const pcre *ppat, const pcre_extra *extra, const char *str, int len,
+ int offset, int options, const char *rep)
+{
+ int nmat;
+ int ovec[MAXCAPTURE * 3];
+ nmat = pcre_exec(ppat, extra, str, len, offset, options,
+ ovec, sizeof(ovec));
+#ifdef DEBUG_PCRE_SUBST
+ dumpmatch(str, len, rep, nmat, ovec);
+#endif
+ if (nmat <= 0)
+ return NULL;
+ return(edit(str, len, rep, nmat, ovec));
+}
+
+#ifdef DEBUG_BUILD
+int
+main()
+{
+ char *pat = "quick\\s(\\w+)\\s(fox)";
+ char *rep = "$1ish $2";
+ char *str = "The quick brown foxy";
+ char *newstr;
+ const char *err;
+ int erroffset;
+ pcre_extra *extra;
+ pcre *ppat = pcre_compile(pat, 0, &err, &erroffset, NULL);
+ if (ppat == NULL) {
+ fprintf(stderr, "%s at %d\n", err, erroffset);
+ exit(1);
+ }
+ extra = pcre_study(ppat, 0, &err);
+ if (err != NULL)
+ fprintf(stderr, "Study %s failed: %s\n", pat, err);
+ newstr = pcre_subst(ppat, extra, str, strlen(str), 0, 0, rep);
+ if (newstr) {
+ printf("Newstr\t%s\n", newstr);
+ pcre_free(newstr);
+ } else {
+ printf("No match\n");
+ }
+ return 0;
+}
+#endif
diff --git a/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.h b/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.h
new file mode 100644
index 0000000..e4f4c44
--- /dev/null
+++ b/spamfilter/Utilities/PCRE/pcre_subst/pcre_subst.h
@@ -0,0 +1,35 @@
+/*************************************************
+* PCRE string replacement *
+*************************************************/
+
+/*
+PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+pcre_subst is a wrapper around pcre_exec designed to make it easier to
+perform PERL style replacements with PCRE.
+
+Written by: Bert Driehuis <driehuis@playbeing.org>
+
+ Copyright (c) 2000 Bert Driehuis
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+*/
+
+char *pcre_subst(const pcre *, const pcre_extra *, const char *, int, int, int, const char *);
diff --git a/spamfilter/Utilities/UnicoWS/license.txt b/spamfilter/Utilities/UnicoWS/license.txt
new file mode 100644
index 0000000..d2cd6d2
--- /dev/null
+++ b/spamfilter/Utilities/UnicoWS/license.txt
@@ -0,0 +1,66 @@
+END-USER LICENSE AGREEMENT FOR MICROSOFT SOFTWARE:
+Microsoft Layer for Unicode on Windows 95, 98, and Me Systems,
+Version No. 1.1.3790.0
+
+IMPORTANT—READ CAREFULLY: This End-User License Agreement (“EULA”) is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation for the Microsoft software product identified above, which includes computer software and may include associated media, printed materials, “online” or electronic documentation, and Internet-based services (“Product”). An amendment or addendum to this EULA may accompany the Product. YOU AGREE TO BE BOUND BY THE TERMS OF THIS EULA BY INSTALLING, COPYING, OR OTHERWISE USING THE PRODUCT. IF YOU DO NOT AGREE, DO NOT INSTALL, COPY, OR USE THE PRODUCT; YOU MAY RETURN IT TO YOUR PLACE OF PURCHASE FOR A FULL REFUND, IF APPLICABLE.
+SOFTWARE PRODUCT LICENSE
+
+1. GRANTS OF LICENSE. Microsoft grants you the rights described in this EULA provided that you comply with all terms and conditions of this EULA.
+ 1.1 General License Grant. You may install and use an unlimited number of copies of the Product on computers, including workstations, terminals or other digital electronic devices residing on your premises ("Computers") to design, develop, and test your software application(s) ("Licensee Products") for use with any version or edition of Microsoft Windows 95, Windows 98, Windows NT 4.0, Windows 2000 operating system products and/or any version or edition of any Microsoft operating system product that is a successor to the foregoing and/or any Microsoft product suite that contains any of the foregoing (each a "Microsoft Operating System).
+1.2 Documentation. You may make and use an unlimited number of copies of any documentation, provided that such copies shall be used only for personal purposes and are not to be republished or distributed (either in hard copy or electronic form) beyond your premises.
+1.3 Storage/Network Use. You may also store or install a copy of the Product on a storage device, such as a network server, used only to install or run the Product on computers used by a licensed end user in accordance with Section 1.1. A single license for the Product may not be shared or used concurrently by multiple end users.
+2. ADDITIONAL LICENSE RIGHTS -- REDISTRIBUTABLE CODE. In addition to the rights granted in Section 1, certain portions of the Product, as described in this Section 2, are provided to you with additional license rights. These additional license rights are conditioned upon your compliance with the distribution requirements and license restrictions described in Section 3.
+2.1 Sample Code. Microsoft grants you the right to use and modify the source code version of those portions of the Product identified as “Samples” in REDIST.TXT or elsewhere in the Product (“Sample Code”) for the sole purposes of designing, developing, and testing your product(s), and to reproduce and distribute the Sample Code, along with any modifications thereof, in object and/or source code form. For applicable redistribution requirements for Sample Code, see Section 3.1 below.
+2.2 Redistributable Code—General. Microsoft grants you a nonexclusive, royalty-free right to reproduce and distribute the object code form of any portion of the Product listed in REDIST.TXT (“Redistributable Code”). For general redistribution requirements for Redistributable Code, see Section 3.1, below.
+3. LICENSE RESTRICTIONS -- DISTRIBUTION REQUIREMENTS. If you choose to exercise your rights under Section 2, any redistribution by you is subject to your compliance with the following terms.
+3.1 If you are authorized and choose to redistribute Sample Code, or Redistributable Code (collectively, the “Redistributables”) as described in Section 2, you agree: (i) except as otherwise noted in Section 2.1 (Sample Code), to distribute the Redistributables only in object code form and in conjunction with and as a part of the Licensee Products developed by you that adds significant and primary functionality to the Redistributables; (ii) that the Redistributables only operate in conjunction with Microsoft Windows platforms; (iii) to distribute the Licensee Product containing the Redistributables pursuant to an end user license agreement (which may be “break-the-seal”, “click-wrap” or signed), with terms no less protective than those contained in this EULA; (iv) not to use Microsoft’s name, logo, or trademarks to market the Licensee Product; (v) to display your own valid copyright notice which shall be sufficient to protect Microsoft’s copyright in the Product; (vi) not to remove or obscure any copyright, trademark or patent notices that appear on the Product as delivered to you; (vii) to indemnify, hold harmless, and defend Microsoft from and against any claims or lawsuits, including attorney’s fees, that arise or result from the use or distribution of the Licensee Product; (viii) otherwise comply with the terms of this EULA; and (ix) agree that Microsoft reserves all rights not expressly granted.
+You also agree not to permit further distribution of the Redistributables by your end users except you may permit further redistribution of the Redistributables by your distributors to your end-user customers if your distributors only distribute the Redistributables in conjunction with, and as part of, the Licensee Product and you and your distributors comply with all other terms of this EULA.
+3.2 If you use the Redistributables, then in addition to your compliance with the applicable distribution requirements described for the Redistributables, the following also applies. Your license rights to the Redistributables are conditioned upon your (i) not incorporating Identified Product into or combining Identified Product with the Redistributables or a derivative work thereof; (ii) not distributing Identified Product in conjunction with the Redistributables or a derivative work thereof; and (iii) not using Identified Product in the development of a derivative work of the Redistributables. “Identified Product” means Product which is licensed pursuant to terms that directly or indirectly (A) create, or purport to create, obligations for Microsoft with respect to the Redistributables or derivative work thereof or (B) grant, or purport to grant, to any third party any rights or immunities under Microsoft’s intellectual property or proprietary rights in the Redistributables or derivative work thereof. Identified Product includes, without limitation, any Product that requires as a condition of its use, modification and/or distribution, that any other Product incorporated into, derived from or distributed with such Product must also be (1) disclosed or distributed in source code form; (2) licensed for the purpose of making derivative works; or (3) redistributable at no charge.
+4. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS
+5. RESERVATION OF RIGHTS. Microsoft reserves all rights not expressly granted to you in this EULA.
+6. UPGRADES. To use a Product identified as an upgrade, you must first be licensed for the product identified by Microsoft as eligible for the upgrade. After upgrading, you may no longer use the product that formed the basis for your upgrade eligibility.You may use the resulting upgraded product only in accordance with the terms of this EULA. If the Product is an upgrade of a component of a package of software programs that you licensed as a single product, the Product may be used and transferred only as part of that single product package and may not be separated for use by more than one end user.
+7. DOWNGRADES. Instead of installing and using the Product, you may install and use one copy of an earlier version of the Product, provided that you completely remove such earlier version and install the original Product within a reasonable time. Your use of such earlier version shall be governed by this EULA, and your rights to use such earlier version shall terminate when you install the original Product.
+8. LIMITATIONS ON REVERSE ENGINEERING, DECOMPILATION, AND DISASSEMBLY. You may not reverse engineer, decompile, or disassemble the Product, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation.
+9. RENTAL. You may not rent, lease or lend the Product.
+10. TRADEMARKS. This EULA does not grant you any rights in connection with any trademarks or service marks of Microsoft.
+11. NOT FOR RESALE SOFTWARE. If the Product is labeled “Not For Resale” or “NFR,” then you may not resell, or otherwise transfer for value, the Product.
+12. ACADEMIC EDITION SOFTWARE. To use Product identified as “Academic Edition” or “AE,” you must be a “Qualified Educational User.” For qualification-related questions, please contact the Microsoft Sales Information Center/One Microsoft Way/Redmond, WA 98052-6399 or the Microsoft subsidiary serving your country.
+13. CONSENT TO USE OF DATA. You agree that Microsoft and its affiliates may collect and use technical information gathered as part of the product support services provided to you, if any, related to the Product. Microsoft may use this information solely to improve our products or to provide customized services or technologies to you and will not disclose this information in a form that personally identifies you.
+14. LINKS TO THIRD PARTY SITES. You may link to third party sites through the use of the Product. The third party sites are not under the control of Microsoft, and Microsoft is not responsible for the contents of any third party sites, any links contained in third party sites, or any changes or updates to third party sites. Microsoft is not responsible for webcasting or any other form of transmission received from any third party sites. Microsoft is providing these links to third party sites to you only as a convenience, and the inclusion of any link does not imply an endorsement by Microsoft of the third party site.
+15. U.S. GOVERNMENT LICENSE RIGHTS. All Product provided to the U.S. Government pursuant to solicitations issued on or after December 1, 1995 is provided with the commercial license rights and restrictions described elsewhere herein. All Product provided to the U.S. Government pursuant to solicitations issued prior to December 1, 1995 is provided with “Restricted Rights” as provided for in FAR, 48 CFR 52.227-14 (JUNE 1987) or DFAR, 48 CFR 252.227-7013 (OCT 1988), as applicable.
+16. EXPORT RESTRICTIONS. You acknowledge that the Product is subject to U.S. export jurisdiction. You agree to comply with all applicable international and national laws that apply to the Product, including the U.S. Export Administration Regulations, as well as end-user, end-use, and destination restrictions issued by U.S. and other governments. For additional information see <http://www.microsoft.com/exporting/>.
+17. ADDITIONAL SOFTWARE/SERVICES. This EULA applies to updates, supplements, add-on components, or Internet-based services components, of the Product that Microsoft may provide to you or make available to you after the date you obtain your initial copy of the Product, unless we provide other terms along with the update, supplement, add-on component, or Internet-based services component. Microsoft reserves the right to discontinue any Internet-based services provided to you or made available to you through the use of the Product.
+18. SOFTWARE TRANSFER. The initial user of the Product may make a one-time permanent transfer of this EULA and Product to another end user. This transfer must include all of the Product (including all component parts, the media and printed materials, any upgrades, this EULA, and, if applicable, the Certificate of Authenticity). The transfer may not be an indirect transfer, such as a consignment. Prior to the transfer, the end user receiving the Software must agree to all the EULA terms.
+19. TERMINATION. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the Product and all of its component parts.
+20. APPLICABLE LAW. If you acquired this Product in the United States, this EULA is governed by the laws of the State of Washington. If you acquired this Product in Canada, unless expressly prohibited by local law, this EULA is governed by the laws in force in the Province of Ontario, Canada; and, in respect of any dispute which may arise hereunder, you consent to the jurisdiction of the federal and provincial courts sitting in Toronto, Ontario. If this Product was acquired outside the United States, then local law may apply.
+21. The Product is protected by copyright and other intellectual property laws and treaties. Microsoft or its suppliers own the title, copyright, and other intellectual property rights in the Product. The Product is licensed, not sold.
+
+22. LIMITED WARRANTY FOR PRODUCT ACQUIRED IN THE US AND CANADA.
+Except with respect to the Redistributables, which are provided “AS IS,” without warranty of any kind, Microsoft warrants that the Product will perform substantially in accordance with the accompanying materials for a period of ninety days from the date of receipt.
+If an implied warranty or condition is created by your state/jurisdiction and federal or state/provincial law prohibits disclaimer of it, you also have an implied warranty or condition, BUT ONLY AS TO DEFECTS DISCOVERED DURING THE PERIOD OF THIS LIMITED WARRANTY (NINETY DAYS). AS TO ANY DEFECTS DISCOVERED AFTER THE NINETY (90) DAY PERIOD, THERE IS NO WARRANTY OR CONDITION OF ANY KIND. Some states/jurisdictions do not allow limitations on how long an implied warranty or condition lasts, so the above limitation may not apply to you.
+Any supplements or updates to the Product, including without limitation, any (if any) service packs or hot fixes provided to you after the expiration of the ninety day Limited Warranty period are not covered by any warranty or condition, express, implied or statutory.
+LIMITATION ON REMEDIES; NO CONSEQUENTIAL OR OTHER DAMAGES. Your exclusive remedy for any breach of this Limited Warranty is as set forth below. Except for any refund elected by Microsoft, YOU ARE NOT ENTITLED TO ANY DAMAGES, INCLUDING BUT NOT LIMITED TO CONSEQUENTIAL DAMAGES, if the Product does not meet Microsoft’s Limited Warranty, and, to the maximum extent allowed by applicable law, even if any remedy fails of its essential purpose. The terms of Section 25 below (“Exclusion of Incidental, Consequential and Certain Other Damages”) are also incorporated into this Limited Warranty. Some states/jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so the above limitation or exclusion may not apply to you. This Limited Warranty gives you specific legal rights. You may have others which vary from state/jurisdiction to state/jurisdiction. YOUR EXCLUSIVE REMEDY. Microsoft’s and its suppliers’ entire liability and your exclusive remedy shall be, at Microsoft’s option from time to time exercised subject to applicable law, (a) return of the price paid (if any) for the Product, or (b) repair or replacement of the Product, that does not meet this Limited Warranty and that is returned to Microsoft with a copy of your receipt. You will receive the remedy elected by Microsoft without charge, except that you are responsible for any expenses you may incur (e.g. cost of shipping the Product to Microsoft). This Limited Warranty is void if failure of the Product has resulted from accident, abuse, misapplication, abnormal use or a virus. Any replacement Product will be warranted for the remainder of the original warranty period or thirty (30) days, whichever is longer. Outside the United States or Canada, neither these remedies nor any product support services offered by Microsoft are available without proof of purchase from an authorized international source. To exercise your remedy, contact: Microsoft, Attn. Microsoft Sales Information Center/One Microsoft Way/Redmond, WA 98052-6399, or the Microsoft subsidiary serving your country.
+
+LIMITED WARRANTY FOR PRODUCT ACQUIRED OUTSIDE THE US OR CANADA.
+FOR THE LIMITED WARRANTIES AND SPECIAL PROVISIONS PERTAINING TO YOUR PARTICULAR JURISDICTION, PLEASE REFER TO YOUR WARRANTY BOOKLET INCLUDED WITH THIS PACKAGE OR PROVIDED WITH THE SOFTWARE PRODUCT PRINTED MATERIALS.
+
+23. DISCLAIMER OF WARRANTIES. The Limited Warranty that appears above is the only express warranty made to you and is provided in lieu of any other express warranties (if any) created by any documentation, packaging, or other communications. Except for the Limited Warranty and to the maximum extent permitted by applicable law, Microsoft and its suppliers provide the Product and support services (if any) AS IS AND WITH ALL FAULTS, and hereby disclaim all other warranties and conditions, either express, implied or statutory, including, but not limited to, any (if any) implied warranties, duties or conditions of merchantability, of fitness for a particular purpose, of reliability or availability, of accuracy or completeness of responses, of results, of workmanlike effort, of lack of viruses, and of lack of negligence, all with regard to the Product, and the provision of or failure to provide support or other services, information, software, and related content through the Product or otherwise arising out of the use of the Product. ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT WITH REGARD TO THE PRODUCT.
+24. EXCLUSION OF INCIDENTAL, CONSEQUENTIAL AND CERTAIN OTHER DAMAGES. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL MICROSOFT OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, PUNITIVE, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, BUT NOT LIMITED TO, DAMAGES FOR LOSS OF PROFITS OR CONFIDENTIAL OR OTHER INFORMATION, FOR BUSINESS INTERRUPTION, FOR PERSONAL INJURY, FOR LOSS OF PRIVACY, FOR FAILURE TO MEET ANY DUTY INCLUDING OF GOOD FAITH OR OF REASONABLE CARE, FOR NEGLIGENCE, AND FOR ANY OTHER PECUNIARY OR OTHER LOSS WHATSOEVER) ARISING OUT OF OR IN ANY WAY RELATED TO THE USE OF OR INABILITY TO USE THE PRODUCT, THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT OR OTHER SERVICES, INFORMATON, SOFTWARE, AND RELATED CONTENT THROUGH THE PRODUCT OR OTHERWISE ARISING OUT OF THE USE OF THE PRODUCT, OR OTHERWISE UNDER OR IN CONNECTION WITH ANY PROVISION OF THIS EULA, EVEN IN THE EVENT OF THE FAULT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, BREACH OF CONTRACT OR BREACH OF WARRANTY OF MICROSOFT OR ANY SUPPLIER, AND EVEN IF MICROSOFT OR ANY SUPPLIER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+25. LIMITATION OF LIABILITY AND REMEDIES. Notwithstanding any damages that you might incur for any reason whatsoever (including, without limitation, all damages referenced above and all direct or general damages), the entire liability of Microsoft and any of its suppliers under any provision of this EULA and your exclusive remedy for all of the foregoing (except for any remedy of repair or replacement elected by Microsoft with respect to any breach of the Limited Warranty) shall be limited to the greater of the amount actually paid by you for the Product or U.S.$5.00. The foregoing limitations, exclusions and disclaimers (including Sections 22, 23, and 24 above) shall apply to the maximum extent permitted by applicable law, even if any remedy fails its essential purpose.
+26. ENTIRE AGREEMENT. This EULA (including any addendum or amendment to this EULA which is included with the Product) are the entire agreement between you and Microsoft relating to the Product and the support services (if any) and they supersede all prior or contemporaneous oral or written communications, proposals and representations with respect to the Product or any other subject matter covered by this EULA. To the extent the terms of any Microsoft policies or programs for support services conflict with the terms of this EULA, the terms of this EULA shall control.
+
+Si vous avez acquis votre produit Microsoft au CANADA, la garantie limitée suivante vous concerne :
+GARANTIE LIMITÉE
+Sauf pur celles du “Redistributables,” qui sont fournies “comme telles,” Microsoft garantit que le Produit fonctionnera conformément aux documents inclus pendant une période de 90 jours suivant la date de réception.
+Si une garantie ou condition implicite est créée par votre État ou votre territoire et qu’une loi fédérale ou provinciale ou État en interdit le déni, vous jouissez également d’une garantie ou condition implicite, MAIS UNIQUEMENT POUR LES DÉFAUTS DÉCOUVERTS DURANT LA PÉRIODE DE LA PRÉSENTE GARANTIE LIMITÉE (QUATRE-VINGT-DIX JOURS). IL N’Y A AUCUNE GARANTIE OU CONDITION DE QUELQUE NATURE QUE CE SOIT QUANT AUX DÉFAUTS DÉCOUVERTS APRÈS CETTE PÉRIODE DE QUATRE-VINGT-DIX JOURS. Certains États ou territoires ne permettent pas de limiter la durée d’une garantie ou condition implicite de sorte que la limitation ci­dessus peut ne pas s’appliquer à vous.
+Tous les suppléments ou toutes les mises à jour relatifs au Produit, notamment, les ensembles de services ou les réparations à chaud (le cas échéant) qui vous sont fournis après l’expiration de la période de quatre-vingt-dix jours de la garantie limitée ne sont pas couverts par quelque garantie ou condition que ce soit, expresse ou implicite.
+LIMITATION DES RECOURS; ABSENCE DE DOMMAGES INDIRECTS OU AUTRES. Votre recours exclusif pour toute violation de la présente garantie limitée est décrit ci­après. Sauf pour tout remboursement au choix de Microsoft, si le Produit ne respecte pas la garantie limitée de Microsoft et, dans la mesure maximale permise par les lois applicables, même si tout recours n’atteint pas son but essentiel, VOUS N’AVEZ DROIT À AUCUNS DOMMAGES, NOTAMMENT DES DOMMAGES INDIRECTS. Les modalités de la clause «Exclusion des dommages accessoires, indirects et de certains autres dommages » sont également intégrées à la présente garantie limitée. Certains États ou territoires ne permettent pas l’exclusion ou la limitation des dommages indirects ou accessoires de sorte que la limitation ou l’exclusion ci­dessus peut ne pas s’appliquer à vous. La présente garantie limitée vous donne des droits légaux spécifiques. Vous pouvez avoir d’autres droits qui peuvent varier d’un territoire ou d’un État à un autre. VOTRE RECOURS EXCLUSIF. L’obligation intégrale de Microsoft et de ses fournisseurs et votre recours exclusif seront, selon le choix de Microsoft de temps à autre sous réserve de toute loi applicable, a) le remboursement du prix payé, le cas échéant, pour le Produit ou b) la réparation ou le remplacement du Produit qui ne respecte pas la présente garantie limitée et qui est retourné à Microsoft avec une copie de votre reçu. Vous recevrez la compensation choisie par Microsoft, sans frais, sauf que vous êtes responsable des dépenses que vous pourriez engager (p. ex., les frais d’envoi du Produit à Microsoft). La présente garantie limitée est nulle si la défectuosité du Produit est causée par un accident, un usage abusif, une mauvaise application, un usage anormal ou un virus. Tout Produit de remplacement sera garanti pour le reste de la période de garantie initiale ou pendant trente (30) jours, selon la plus longue entre ces deux périodes. À l’extérieur des États-Unis ou du Canada, ces recours ou l’un quelconque des services de soutien technique offerts par Microsoft ne sont pas disponibles sans preuve d’achat d’une source internationale autorisée. Pour exercer votre recours, vous devez communiquer avec Microsoft et vous adresser au Microsoft Sales Information Center/One Microsoft Way/Redmond, WA 98052-6399, ou à la filiale de Microsoft de votre pays.
+
+DÉNI DE GARANTIES. La garantie limitée mentionnée ci-dessus constitue la seule garantie expresse qui vous est donnée et remplace toutes autres garanties expresses (s’il en est) mentionnées dans un document ou sur un emballage. Sauf en ce qui a trait à la garantie limitée et dans la mesure maximale permise par les lois applicables, le Produit et les services de soutien technique (le cas échéant) sont fournis TELS QUELS ET AVEC TOUS LES DÉFAUTS par Microsoft et ses fournisseurs, lesquels par les présentes dénient toutes autres garanties et conditions expresses, implicites ou en vertu de la loi, notamment (le cas échéant) les garanties, devoirs ou conditions implicites de qualité marchande, d’adaptation à un usage particulier, d’exactitude ou d’exhaustivité des réponses, des résultats, des efforts déployés selon les règles de l’art, d’absence de virus et de négligence, le tout à l’égard du Produit et de la prestation des services de soutien technique ou de l’omission d’une telle prestation. PAR AILLEURS, IL N’Y A AUCUNE GARANTIE OU CONDITION QUANT AU TITRE DE PROPRIÉTÉ, À LA JOUISSANCE OU LA POSSESSION PAISIBLE, À LA CONCORDANCE À UNE DESCRIPTION NI QUANT À UNE ABSENCE DE CONTREFAÇON CONCERNANT LE PRODUIT.
+EXCLUSION DES DOMMAGES ACCESSOIRES, INDIRECTS ET DE CERTAINS AUTRES DOMMAGES. DANS LA MESURE MAXIMALE PERMISE PAR LES LOIS APPLICABLES, EN AUCUN CAS MICROSOFT OU SES FOURNISSEURS NE SERONT RESPONSABLES DES DOMMAGES SPÉCIAUX, CONSÉCUTIFS, ACCESSOIRES OU INDIRECTS DE QUELQUE NATURE QUE CE SOIT (NOTAMMENT, LES DOMMAGES À L’ÉGARD DU MANQUE À GAGNER OU DE LA DIVULGATION DE RENSEIGNEMENTS CONFIDENTIELS OU AUTRES, DE LA PERTE D’EXPLOITATION, DE BLESSURES CORPORELLES, DE LA VIOLATION DE LA VIE PRIVÉE, DE L’OMISSION DE REMPLIR TOUT DEVOIR, Y COMPRIS D’AGIR DE BONNE FOI OU D’EXERCER UN SOIN RAISONNABLE, DE LA NÉGLIGENCE ET DE TOUTE AUTRE PERTE PÉCUNIAIRE OU AUTRE PERTE DE QUELQUE NATURE QUE CE SOIT) SE RAPPORTANT DE QUELQUE MANIÈRE QUE CE SOIT À L’UTILISATION DU PRODUIT OU À L’INCAPACITÉ DE S’EN SERVIR, À LA PRESTATION OU À L’OMISSION D’UNE TELLE PRESTATION DE SERVICES DE SOUTIEN TECHNIQUE OU AUTREMENT AUX TERMES DE TOUTE DISPOSITION DU PRÉSENT EULA OU RELATIVEMENT À UNE TELLE DISPOSITION, MÊME EN CAS DE FAUTE, DE DÉLIT CIVIL (Y COMPRIS LA NÉGLIGENCE), DE RESPONSABILITÉ STRICTE, DE VIOLATION DE CONTRAT OU DE VIOLATION DE GARANTIE DE MICROSOFT OU DE TOUT FOURNISSEUR ET MÊME SI MICROSOFT OU TOUT FOURNISSEUR A ÉTÉ AVISÉ DE LA POSSIBILITÉ DE TELS DOMMAGES.
+LIMITATION DE RESPONSABILITÉ ET RECOURS. Malgré les dommages que vous puissiez subir pour quelque motif que ce soit (notamment, tous les dommages susmentionnés et tous les dommages directs ou généraux), l’obligation intégrale de Microsoft et de l’un ou l’autre de ses fournisseurs aux termes de toute disposition du présent EULA et votre recours exclusif à l’égard de tout ce qui précède (sauf en ce qui concerne tout recours de réparation ou de remplacement choisi par Microsoft à l’égard de tout manquement à la garantie limitée) se limite au plus élevé entre les montants suivants : le montant que vous avez réellement payé pour le Produit ou 5,00 $US. Les limites, exclusions et dénis qui précèdent (y compris les clauses ci-dessus), s’appliquent dans la mesure maximale permise par les lois applicables, même si tout recours n’atteint pas son but essentiel.
+La présente Convention est régie par les lois de la province d’Ontario, Canada. Chacune des parties à la présente reconnaît irrévocablement la compétence des tribunaux de la province d’Ontario et consent à instituer tout litige qui pourrait découler de la présente auprès des tribunaux situés dans le district judiciaire de York, province d’Ontario.
+Au cas où vous auriez des questions concernant cette licence ou que vous désiriez vous mettre en rapport avec Microsoft pour quelque raison que ce soit, veuillez contacter la succursale Microsoft desservant votre pays, dont l’adresse est fournie dans ce produit, ou écrivez à : Microsoft Sales Information Center, One Microsoft Way, Redmond, Washington 98052-6399.
+
+
+
diff --git a/spamfilter/Utilities/UnicoWS/redist.txt b/spamfilter/Utilities/UnicoWS/redist.txt
new file mode 100644
index 0000000..e2f66a2
--- /dev/null
+++ b/spamfilter/Utilities/UnicoWS/redist.txt
@@ -0,0 +1,12 @@
+===============================================
+Microsoft Layer for Unicode on Windows 95/98/ME
+===============================================
+
+In addition to the rights granted in Section 1 of the Agreement ("Agreement"), with
+respect to UNICOWS.DLL, you have the following non-exclusive, royalty free
+rights subject to the Distribution Requirements detailed in Sections 1 and 3 of
+the Agreement:
+
+(1) You may distribute UNICOWS.DLL with the following: Windows 95, Windows 98,
+Windows 98 Second Edition, Windows Millennium, Windows NT4, Windows 2000, Windows XP,
+and Windows Server 2003.
diff --git a/spamfilter/Utilities/UnicoWS/unicows.dll b/spamfilter/Utilities/UnicoWS/unicows.dll
new file mode 100644
index 0000000..d992808
--- /dev/null
+++ b/spamfilter/Utilities/UnicoWS/unicows.dll
Binary files differ
diff --git a/spamfilter/Utilities/UnicoWS/unicows.lib b/spamfilter/Utilities/UnicoWS/unicows.lib
new file mode 100644
index 0000000..124ec84
--- /dev/null
+++ b/spamfilter/Utilities/UnicoWS/unicows.lib
Binary files differ
diff --git a/spamfilter/Utilities/UnicoWS/unicows.pdb b/spamfilter/Utilities/UnicoWS/unicows.pdb
new file mode 100644
index 0000000..4fb0f58
--- /dev/null
+++ b/spamfilter/Utilities/UnicoWS/unicows.pdb
Binary files differ
diff --git a/spamfilter/common.h b/spamfilter/common.h
new file mode 100644
index 0000000..0cf6cd5
--- /dev/null
+++ b/spamfilter/common.h
@@ -0,0 +1,284 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_COMMON_H__
+#define SF_COMMON_H__ 1
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+// -- Includes: CRT (C Runtime Library)
+//#include <tchar.h> // generic Ansi/Unicode functions
+#include <wchar.h> // unicode functions
+#include <stdio.h> // for mir_snprintf() etc.
+#include <process.h> // for forkthread()
+#include <time.h> // for time()
+
+// -- Includes: WinAPI
+#define _WIN32_WINNT 0x0501
+//#define WINVER 0x0502
+#define _WIN32_IE 0x0500 // for SHFileOperation
+
+#include <windows.h>
+#include <commctrl.h>
+#include <shlwapi.h>
+#include <shlobj.h>
+
+
+// -- Include: MirandaAPI
+//#define NOWIN2K
+#include <win2k.h>
+#include <newpluginapi.h>
+#include <m_clist.h>
+#include <m_clui.h>
+#include <m_database.h>
+#include <m_langpack.h>
+#include <m_options.h>
+#include <m_skin.h>
+#include <m_button.h>
+#include <m_protosvc.h>
+#include <m_protocols.h>
+#include <m_protomod.h>
+#include <m_plugins.h>
+#include <m_ignore.h>
+#include <m_contacts.h>
+#include <m_utils.h>
+#include <m_url.h>
+#include <m_file.h>
+#include <m_message.h>
+#include <m_popup.h>
+//#define VARIABLES_NOHELPER
+#include <m_variables.h>
+#include <m_icolib.h>
+#include <m_clc.h>
+#include <m_fontservice.h>
+#include <m_netlib.h>
+#include <m_updater.h>
+#include <m_folders.h>
+#include "m_trigger.h"
+#include <m_icq.h>
+#include <m_cluiframes.h>
+
+// -- Includes: Header
+#include "m_spamfilter.h"
+#include "forkthread.h"
+#include "path.h"
+#include "stringlist.h"
+#include "string.h"
+#include "regexp.h"
+#include "utilities.h"
+#include "spamdefinitions.h"
+#include "spamcheckdata.h"
+#include "popup.h"
+#include "logfile.h"
+#include "contact.h"
+#include "dlg.h"
+#include "options.h"
+#include "services.h"
+#include "spamcheck.h"
+#include "variables.h"
+#include "folders.h"
+#include "trigger.h"
+#include "extraimg.h"
+#include "menuitems.h"
+#include "version.h"
+#include "resource.h"
+#include <m_system.h>
+struct MM_INTERFACE memoryManagerInterface;
+struct UTF8_INTERFACE utfi;
+
+
+
+// -- Includes: Utilities
+#if defined(_DEBUG)
+ // Memory leak and invalid access detection
+ //#define _CRTDBG_MAP_ALLOC
+ #include <crtdbg.h>
+ //#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
+
+ // Check database setting type (m_database.h)
+ //#define DBCHECKSETTINGS
+#else
+ // Prevent C Runtime Library (CRT)
+ /*
+ #pragma comment(linker,"/ENTRY:DllMain")
+ #pragma comment(linker,"/NODEFAULTLIB")
+ */
+#endif
+
+// Debug Functions
+#include "Utilities/DebugHelper.h"
+
+
+// -- Variables: Plugin
+HINSTANCE hInstance;
+PLUGINLINK* pluginLink;
+
+
+// -- Variables: Window Handles
+HWND hwndSpamFilterOpt;
+HWND hwndAdvertismentFilter;
+HWND hwndRobotFilter;
+HWND hwndDislikedMessagesFilter;
+HWND hwndSpamDefinitionsInfo;
+HWND hwndSpammersInfo;
+
+// -- Variables: Message Types List
+CRITICAL_SECTION csMsgTypes;
+unsigned int uMsgTypesCount; // protected by csMsgTypes
+MESSAGETYPEDESC* pamtdMsgTypes; // protected by csMsgTypes
+
+// -- Defines: General
+#define DEFAULT_LOGFILE TranslateT("Spam.xml")
+
+#define DEFAULT_ADVERTISMENT_SOUNDFILE _T("filtered1.wav") // Sound files must not be translated and must be lower case
+#define DEFAULT_DISLIKEDMESSAGE_SOUNDFILE _T("filtered2.wav")
+#define DEFAULT_ROBOT_SOUNDFILE _T("filtered3.wav")
+
+#define SOUNDS_SUBDIRECTORY _T("Sounds") // Dirs must have no backslash at the end!
+#define DOCS_SUBDIRECTORY _T("Docs")
+#define LOGS_SUBDIRECTORY _T("Logs")
+#define DATA_SUBDIRECTORY _T("Spam Filter")
+
+#define RANDOM_ANSWER_LENGTH 8
+
+#define MAX_INT_LENGTH 15 // or just 10
+
+#define MS_CONTACTS_SEND "ContactsTransfer/SendContacts"
+#define MS_CONTACTS_RECEIVE "ContactsTransfer/ReceiveContacts"
+
+
+// -- Defines: Default Settings
+#define DEFAULT_SETTING_BEHAVIOUR 1 // 1 = Mark message read or 2 = Delete message
+#define DEFAULT_SETTING_POPUP FALSE
+#define DEFAULT_SETTING_LOGGING FALSE
+#define DEFAULT_SETTING_AUTOADDSPAMMERS TRUE
+#define DEFAULT_SETTING_ALLOWEDSPAMMESSAGES 3 // 0 to x
+#define DEFAULT_SETTING_HIDEFROMCLIST TRUE
+#define DEFAULT_SETTING_MOVETOGROUP FALSE
+#define DEFAULT_SETTING_MOVETOGROUPNAME TranslateT("Spammers")
+#define DEFAULT_SETTING_SETSPAMMERFLAGS 0 // flags can be SCASF_NO_REMOVE_HISTORY, SCASF_NO_DENY_AUTHREQUESTS, SCASF_NO_NOTIFY
+
+#define DEFAULT_SETTING_ADVERTISMENTFILTER TRUE
+#define DEFAULT_SETTING_ADVERTISMENT_DEFAULTBADWORDS TRUE
+#define DEFAULT_SETTING_ADVERTISMENT_FORMATING TRUE
+#define DEFAULT_SETTING_ADVERTISMENT_URL FALSE
+#define DEFAULT_SETTING_ADVERTISMENT_PHONENUMBERS FALSE
+#define DEFAULT_SETTING_ADVERTISMENT_CAPITALIZED FALSE
+#define DEFAULT_SETTING_ADVERTISMENT_EMPTY TRUE
+#define DEFAULT_SETTING_ADVERTISMENT_DEFAULTSPAMMERS TRUE
+#define DEFAULT_SETTING_ADVERTISMENT_MULTIPLEMESSAGES TRUE
+#define DEFAULT_SETTING_ADVERTISMENT_TEASERMESSAGES TRUE
+#define DEFAULT_SETTING_ADVERTISMENT_ALLOWEDBADWORDS 3 // 0 to x
+#define DEFAULT_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS 0 // 0 to x
+#define DEFAULT_SETTING_ADVERTISMENT_ALLOWEDFORMATEDWORDS 1 // 0 to x
+
+#define DEFAULT_SETTING_DISLIKEDMESSAGEFILTER FALSE
+#define DEFAULT_SETTING_DISLIKEDMESSAGE_HOAXTEXTS TRUE
+#define DEFAULT_SETTING_DISLIKEDMESSAGE_MULTIPLEMESSAGES TRUE
+#define DEFAULT_SETTING_DISLIKEDMESSAGE_NOTIFYSENDER FALSE
+#define DEFAULT_SETTING_DISLIKEDMESSAGE_ADDTOHISTORY TRUE
+#define DEFAULT_SETTING_DISLIKEDMESSAGE_RETURNMESSAGE TranslateT("Hello %sender%,\r\nI don't like such messages. Please don't send them to me anymore.\r\nThanks.\r\n\r\n(Automated message)")
+
+#define DEFAULT_SETTING_ROBOTFILTER FALSE
+#define DEFAULT_SETTING_ROBOT_INSTRUCTION TranslateT("Hello %sender%,\r\nPlease reply \"%securitycode%\" if you want to contact me. If you don't reply correctly your messages will be ignored. Sorry for the inconvenience.\r\nThis spam check guarantees that you are a real person.\r\nYou have %triesleft% tries left.\r\nThanks in advance!\r\n\r\n(Automated message)")
+#define DEFAULT_SETTING_ROBOT_CONFIRMATION TranslateT("Thank you for your help!\r\nAll your previous messages were delivered. New messages do now reach me directly without an additional spam check.\r\n\r\n(Automated message)")
+#define DEFAULT_SETTING_ROBOT_ADDTOHISTORY TRUE
+#define DEFAULT_SETTING_ROBOT_HIDEUSERS TRUE
+
+// -- Defines: Database
+#define DB_MODULE_NAME "SpamFilter"
+#define DB_MODULE_NAME_PRESPAMMERS "SpamFilterAutoIgnore"
+#define DB_MODULE_NAME_MSGTYPES "SpamFilterMsgTypes"
+
+#define DB_SETTING_FIRSTRUNDONE "FirstRunDone"
+
+#define DB_SETTING_POPUP "PopupActivated"
+#define DB_SETTING_BEHAVIOUR "FilterBehaviour"
+#define DB_SETTING_AUTOADDSPAMMERS "AutoIgnoreSpammers"
+#define DB_SETTING_ALLOWEDSPAMMESSAGES "AllowedSpamMsgs"
+#define DB_SETTING_MOVETOGROUP "MoveToGroup"
+#define DB_SETTING_HIDEFROMCLIST "HideFromCList"
+#define DB_SETTING_MOVETOGROUPNAME "MoveToGroupName"
+#define DB_SETTING_LOGGING "LoggingActivated"
+#define DB_SETTING_LOGFILE "LogFile"
+
+#define DB_SETTING_ADVERTISMENTFILTER "AFActivated"
+#define DB_SETTING_ADVERTISMENT_DEFAULTBADWORDS "AFDefaultBadWordsCheck"
+#define DB_SETTING_ADVERTISMENT_CUSTOMIZEDBADWORDS "AFCustomizedBadWordsCheck"
+#define DB_SETTING_ADVERTISMENT_CUSTOMIZEDGOODWORDS "AFGoodWordsCheck"
+#define DB_SETTING_ADVERTISMENT_FORMATING "AFDifferentFormatingCheck"
+#define DB_SETTING_ADVERTISMENT_URL "AFMsgContainsUrlCheck"
+#define DB_SETTING_ADVERTISMENT_PHONENUMBERS "AFMsgContainsPhoneNumbersCheck"
+#define DB_SETTING_ADVERTISMENT_CAPITALIZED "AFCapitalizedMsgsCheck"
+#define DB_SETTING_ADVERTISMENT_EMPTY "AFEmptyMsgsCheck"
+#define DB_SETTING_ADVERTISMENT_DEFAULTSPAMMERS "AFDefaultSpammersCheck"
+#define DB_SETTING_ADVERTISMENT_MULTIPLEMESSAGES "AFMultipleMsgsCheck"
+#define DB_SETTING_ADVERTISMENT_LASTMESSAGE "AFLastFilteredMsg"
+#define DB_SETTING_ADVERTISMENT_TEASERMESSAGES "AFTeaserMsgCheck"
+#define DB_SETTING_ADVERTISMENT_ALLOWEDBADWORDS "AFAllowedBadWords"
+#define DB_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS "AFAllowedGoodWords"
+#define DB_SETTING_ADVERTISMENT_ALLOWEDFORMATEDWORDS "AFAllowedFormatedWords"
+#define DB_SETTING_ADVERTISMENT_DIALOGPOSITION "AFConfigureDialog_"
+
+#define DB_SETTING_DISLIKEDMESSAGEFILTER "DFActivated"
+#define DB_SETTING_DISLIKEDMESSAGE_CUSTOMIZEDWORDS "DFIndicatingWordsCheck"
+#define DB_SETTING_DISLIKEDMESSAGE_HOAXTEXTS "DFHoaxTextsCheck"
+#define DB_SETTING_DISLIKEDMESSAGE_MULTIPLEMESSAGES "DFMultipleMsgsCheck"
+#define DB_SETTING_DISLIKEDMESSAGE_LASTMESSAGE "DFLastFilteredMsg"
+#define DB_SETTING_DISLIKEDMESSAGE_LASTSENDER "DFLastFilteredSender"
+#define DB_SETTING_DISLIKEDMESSAGE_NOTIFYSENDER "DFNotifySender"
+#define DB_SETTING_DISLIKEDMESSAGE_RETURNMESSAGE "DFNotifySenderMsg"
+#define DB_SETTING_DISLIKEDMESSAGE_ADDTOHISTORY "DFAddReplyToHistory"
+#define DB_SETTING_DISLIKEDMESSAGE_DIALOGPOSITION "DFConfigureDialog_"
+
+#define DB_SETTING_ROBOTFILTER "RFActivated"
+#define DB_SETTING_ROBOT_INSTRUCTION "RFInstructionMsg"
+#define DB_SETTING_ROBOT_CONFIRMATION "RFConfirmationMsg"
+#define DB_SETTING_ROBOT_ADDTOHISTORY "RFAddMsgsToHistory"
+#define DB_SETTING_ROBOT_DIALOGPOSITION "RFConfigureDialog_"
+#define DB_SETTING_ROBOT_ANSWER "RFCorrectAnswer"
+#define DB_SETTING_ROBOT_HIDEUSERS "RFHideUntilConfirmed"
+
+#define DB_SETTING_SPAMDEF_DIALOGPOSITION "SpamDefinitionsDlg_"
+#define DB_SETTING_SPAMMERS_DIALOGPOSITION "SpammersDlg_"
+
+// Per contact settings
+#define DB_SETTING_ROBOT_TESTPASSED "RFTestPassed"
+#define DB_SETTING_ISSPAMMER "IsSpammer"
+
+// Sounds
+#define DB_SOUND_ADVERTISMENT_SETTING DB_MODULE_NAME"_AdvertismentFiltered"
+#define DB_SOUND_DISLIKEDMESSAGE_SETTING DB_MODULE_NAME"_DislikedMessageFiltered"
+#define DB_SOUND_ROBOT_SETTING DB_MODULE_NAME"_RobotFiltered"
+
+// Icons
+#define DB_ICON_SPAMLAYER_SETTING DB_MODULE_NAME"_SpamLayer"
+#define DB_ICON_SPAMMANUALLY_SETTING DB_MODULE_NAME"_SpamManually"
+#define DB_ICON_ISSPAMMER_SETTING DB_MODULE_NAME"_IsSpammer"
+
+// Colors
+#define DB_COLOR_POPUPBACKGROUND_SETTING DB_MODULE_NAME"_PopupBackground"
+#define DB_COLOR_POPUPTEXT_SETTING DB_MODULE_NAME"_PopupText"
+
+
+#endif // SF_COMMON_H__ \ No newline at end of file
diff --git a/spamfilter/contact.c b/spamfilter/contact.c
new file mode 100644
index 0000000..6891b9c
--- /dev/null
+++ b/spamfilter/contact.c
@@ -0,0 +1,818 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+
+// -----------------------------------------
+
+
+WORD IncreasePreSpammerCount(SPAMCHECKDATA* pscd)
+{
+ WCHAR* pszUnique;
+ char* pszUniqueUtf8;
+ WORD sentSpam;
+
+ // Get UTF-8 encoded unique str
+ pszUnique = SCD_GetContactUniqueStr(pscd, FALSE);
+ if (!pszUnique) return FALSE;
+ pszUniqueUtf8 = mir_utf8encodeW(pszUnique);
+ mir_free(pszUnique);
+ if (!pszUniqueUtf8) return FALSE;
+
+ // Inc count
+ sentSpam = (WORD)DBGetContactSettingWord(NULL, DB_MODULE_NAME_PRESPAMMERS, pszUniqueUtf8, 0);
+ sentSpam++;
+ DBWriteContactSettingWord(NULL, DB_MODULE_NAME_PRESPAMMERS, pszUniqueUtf8, sentSpam);
+
+ mir_free(pszUniqueUtf8);
+
+ // Update "Auto-Ignore" button if options are opened
+ if (IsWindow(hwndSpamFilterOpt))
+ PostMessage(hwndSpamFilterOpt, SFM_CHECK_AUTOIGNORE, 0, 0);
+
+ return sentSpam;
+}
+
+WORD GetPreSpammerCount(SPAMCHECKDATA* pscd)
+{
+ WCHAR* pszUnique;
+ char* pszUniqueUtf8;
+ WORD sentSpam;
+
+ // Get UTF-8 encoded unique str
+ pszUnique = SCD_GetContactUniqueStr(pscd, FALSE);
+ if (!pszUnique) return 0;
+ pszUniqueUtf8 = mir_utf8encodeW(pszUnique);
+ mir_free(pszUnique);
+ if (!pszUniqueUtf8) return 0;
+
+ sentSpam = (WORD)DBGetContactSettingWord(NULL, DB_MODULE_NAME_PRESPAMMERS, pszUniqueUtf8, 0);
+ mir_free(pszUniqueUtf8);
+ return sentSpam;
+}
+
+BOOL RemovePreSpammerCount(SPAMCHECKDATA* pscd)
+{
+ WCHAR* pszUnique;
+ char* pszUniqueUtf8;
+ BOOL bReturn;
+
+ // Get UTF-8 encoded unique str
+ pszUnique = SCD_GetContactUniqueStr(pscd, FALSE);
+ if (!pszUnique) return FALSE;
+ pszUniqueUtf8 = mir_utf8encodeW(pszUnique);
+ mir_free(pszUnique);
+ if (!pszUniqueUtf8) return FALSE;
+
+ bReturn = (DBDeleteContactSetting(NULL, DB_MODULE_NAME_PRESPAMMERS, pszUniqueUtf8) == 0);
+
+ // Update "Auto-Ignore" button if options are opened
+ if (IsWindow(hwndSpamFilterOpt))
+ PostMessage(hwndSpamFilterOpt, SFM_CHECK_AUTOIGNORE, 0, 0);
+
+ mir_free(pszUniqueUtf8);
+ return bReturn;
+}
+
+
+int MarkAllContactEventsUnRead(HANDLE hContact)
+{
+ int iReturn;
+ HANDLE* hDbEvents = NULL;
+ HANDLE* hDbEventsBuf;
+ int iEventCount;
+ HANDLE hDbEvent;
+ int i;
+ DBEVENTINFO dbei;
+
+ iReturn = 0;
+
+ // Loop through all events in the contact chain
+ iEventCount = 0;
+ for (hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRST, (WPARAM)hContact, 0); hDbEvent; hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hDbEvent, 0))
+ {
+ // Store all events in memory
+ hDbEventsBuf = (HANDLE*)mir_realloc(hDbEvents, (iEventCount+1)*sizeof(HANDLE));
+ if (!hDbEventsBuf) break;
+ hDbEvents = hDbEventsBuf;
+ hDbEvents[iEventCount] = hDbEvent;
+ iEventCount++;
+ BOX2("cnt:%i, hndl:%i", iEventCount, hDbEvent);
+ }
+
+ // Add all old events back to the contact
+ for (i=0; i<iEventCount; i++)
+ {
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvents[i], 0);
+ if (dbei.cbBlob <= 0) continue;
+ dbei.pBlob = (PBYTE)mir_alloc(dbei.cbBlob);
+ if (!dbei.pBlob) continue;
+
+ if (CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvents[i], (LPARAM)&dbei) == 0)
+ {
+ switch (dbei.eventType)
+ {
+ case EVENTTYPE_AUTHREQUEST:
+ case EVENTTYPE_ADDED:
+ case EVENTTYPE_CONTACTS:
+ case EVENTTYPE_MESSAGE:
+ case EVENTTYPE_URL:
+ case EVENTTYPE_FILE:
+ {
+ if (!(dbei.flags&DBEF_SENT))
+ dbei.flags ^= DBEF_READ;
+
+ CallServiceSync(MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)hDbEvents[i]);
+
+ // Re-add event
+ BOX2("handle evnts: %i hdl:%i", i, hDbEvents[i]);
+ if (CallService(MS_DB_EVENT_DELETE, (WPARAM)hContact, (LPARAM)hDbEvents[i]) == 0)
+ if ((HANDLE)CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei) != NULL)
+ iReturn++;
+ break;
+ }
+ }
+ }
+
+ mir_free(dbei.pBlob);
+ }
+
+ // Authorization requests are stored under the main contact
+ // Loop through all events in the main chain
+ iEventCount = 0;
+ for (hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRST, (WPARAM)NULL, 0); hDbEvent; hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hDbEvent, 0))
+ {
+ // Store all events in memory
+ hDbEventsBuf = (HANDLE*)mir_realloc(hDbEvents, (iEventCount+1)*sizeof(HANDLE));
+ if (!hDbEventsBuf) break;
+ hDbEvents = hDbEventsBuf;
+ hDbEvents[iEventCount] = hDbEvent;
+ iEventCount++;
+ }
+
+ // Add all old events back to the contact
+ for (i=0; i<iEventCount; i++)
+ {
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvents[i], 0);
+ if (dbei.cbBlob <= 0) continue;
+ dbei.pBlob = (PBYTE)mir_alloc(dbei.cbBlob);
+ if (!dbei.pBlob) continue;
+
+ if (CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvents[i], (LPARAM)&dbei) == 0)
+ {
+ if ((dbei.eventType == EVENTTYPE_AUTHREQUEST) || (dbei.eventType == EVENTTYPE_ADDED))
+ {
+ // DB event corrupt check
+ if (dbei.cbBlob >= (sizeof(DWORD)+sizeof(HANDLE)))
+ {
+ if (*((PHANDLE)(dbei.pBlob+sizeof(DWORD))) == hContact)
+ {
+ if (!(dbei.flags&DBEF_SENT))
+ dbei.flags ^= DBEF_READ;
+
+ CallServiceSync(MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)hDbEvents[i]);
+
+ // Re-add event
+ if (CallService(MS_DB_EVENT_DELETE, (WPARAM)hContact, (LPARAM)hDbEvents[i]) == 0) // if successful
+ if ((HANDLE)CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei) != NULL)
+ iReturn++;
+ }
+ }
+ }
+ }
+
+ mir_free(dbei.pBlob);
+ }
+
+ if (hDbEvents) mir_free(hDbEvents);
+ return iReturn;
+}
+
+BOOL SendContactAutomatedMessage(HANDLE hContact, WCHAR* pszMessage, BOOL bAddToHistory)
+{
+ BOOL bReturn;
+ WCHAR* sendBuffer;
+ int bufSize;
+ char* pszProto;
+ int iSendRes;
+
+ if (!hContact || !pszMessage || (pszMessage[0] == _T('\0'))) return FALSE;
+ pszProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (!pszProto) return FALSE;
+
+ // Limit the message to max possible length
+ {
+ int iMaxLen = CallProtoService(pszProto, PS_GETCAPS, (WPARAM)PFLAG_MAXLENOFMESSAGE, (LPARAM)hContact);
+ if (iMaxLen != CALLSERVICE_NOTFOUND)
+ if ((int)lstrlen(pszMessage) > iMaxLen)
+ pszMessage[iMaxLen] = _T('\0');
+ }
+
+ // Construct message text
+ #if defined(UNICODE)
+ {
+ char* buf;
+ mir_utf8decode((char*)pszMessage,&sendBuffer);
+ if (!sendBuffer) return FALSE;
+ bufSize = lstrlenW(sendBuffer)+1;
+ bufSize *= sizeof(WCHAR)+1;
+ buf = (char*)mir_realloc(sendBuffer, bufSize);
+ if (!buf) {
+ mir_free(sendBuffer);
+ return FALSE;
+ }
+ }
+ #else
+ sendBuffer = pszMessage;
+ bufSize = lstrlen(pszMessage)+1;
+ #endif
+
+ // Send the message
+ #if defined(UNICODE)
+ if (pszProto) {
+ char szService[MAXMODULELABELLENGTH+10];
+ mir_snprintf(szService, ARRAYSIZE(szService), "%s%sW", pszProto, PSS_MESSAGE);
+ bReturn = ServiceExists(szService);
+ } else {
+ bReturn = FALSE;
+ }
+ iSendRes = CallContactService(hContact, bReturn?PSS_MESSAGE"W":PSS_MESSAGE, (WPARAM)PREF_UNICODE, (LPARAM)sendBuffer);
+ #else
+ iSendRes = CallContactService(hContact, PSS_MESSAGE, (WPARAM)0, (LPARAM)sendBuffer);
+ #endif
+ bReturn = ((iSendRes != CALLSERVICE_NOTFOUND) && (iSendRes != 0));
+
+ // Add the message to history
+ if (bReturn && bAddToHistory)
+ {
+ DBEVENTINFO dbei;
+
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.timestamp = time(NULL);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = DBEF_SENT;
+ dbei.szModule = pszProto;
+ dbei.cbBlob = bufSize; // Blob without terminating zero, but adding it though to avoid problems
+ dbei.pBlob = (PBYTE)sendBuffer;
+
+ CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei);
+ }
+
+ #if defined(UNICODE)
+ mir_free(sendBuffer);
+ #endif
+
+ return bReturn;
+}
+
+
+BOOL CreateContactListGroup(WCHAR* pszGroupName)
+{
+ int i;
+ char str[MAX_INT_LENGTH+1];
+ WCHAR name[256];
+ DBVARIANT dbv;
+
+ if (!pszGroupName) return FALSE;
+
+ for (i=0; ;i++)
+ {
+ mir_snprintf(str, ARRAYSIZE(str), "%i", i);
+ if (DBGetContactSettingTString(NULL, "CListGroups", str, &dbv) != 0)
+ break;
+ if (dbv.ptszVal[0] && !StrCmpI(dbv.ptszVal+1, pszGroupName)) {
+ DBFreeVariant(&dbv);
+ return FALSE;
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ name[0] = 1|GROUPF_EXPANDED;
+ mir_sntprintf(&name[1], ARRAYSIZE(name)-1, _T("%s"), pszGroupName);
+ DBWriteContactSettingTString(NULL, "CListGroups", str, name);
+ CallService(MS_CLUI_GROUPADDED, (WPARAM)i+1, 0);
+ return TRUE;
+}
+
+
+BOOL RemoveContactListGroup(WCHAR* pszGroupName)
+{
+ int i;
+ char str[MAX_INT_LENGTH+1];
+ DBVARIANT dbv;
+
+ if (!pszGroupName) return FALSE;
+ for (i=0; ;i++)
+ {
+ mir_snprintf(str, ARRAYSIZE(str), "%i", i);
+ if (DBGetContactSettingTString(NULL, "CListGroups", str, &dbv) != 0)
+ return FALSE;
+ if (dbv.ptszVal[0] && !StrCmpI(dbv.ptszVal+1, pszGroupName)) {
+ DBFreeVariant(&dbv);
+ break;
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ // Bring up confirm window
+ return (CallService(MS_CLIST_GROUPDELETE, (WPARAM)i+1, 0) == 0);
+}
+
+
+BOOL RemoveContactFromIgnoreList(HANDLE hContact)
+{
+ // Un-Ignore all events for the contact
+ BOOL bReturn = (CallService(MS_IGNORE_UNIGNORE, (WPARAM)hContact, IGNOREEVENT_ALL) == 0);
+
+ // Remove group association
+ DBDeleteContactSetting(hContact, "CList", "Group");
+
+ // Restore NotOnList state
+ if (!DBGetContactSettingByte(hContact, DB_MODULE_NAME, "WasOnList", 0))
+ DBWriteContactSettingByte(hContact, "CList", "NotOnList", 1);
+ DBDeleteContactSetting(hContact, DB_MODULE_NAME, "WasOnList");
+
+ // Unhide the contact
+ DBDeleteContactSetting(hContact, "CList", "Hidden");
+
+ // Re-Accept typing notifications
+ DBWriteContactSettingByte(hContact, "SRMM", "SupportTyping", 1);
+
+ // Restore not to appear as offline to the contact (back to normal mode)
+ CallContactService(hContact, PSS_SETAPPARENTMODE, (WPARAM)0, 0);
+
+ // Rename contact (to default name)
+ DBDeleteContactSetting(hContact, "CList", "MyHandle");
+ // Tell clist to refresh it's cache
+ CallService(MS_CLIST_INVALIDATEDISPLAYNAME, (WPARAM)hContact, 0);
+
+ // NotifySpammerStateChanged will be called automatically on settings change
+ DBWriteContactSettingByte(hContact, DB_MODULE_NAME, DB_SETTING_ISSPAMMER, (BYTE)FALSE);
+
+ return bReturn;
+}
+
+
+BOOL AddContactToIgnoreList(HANDLE hContact, BOOL bKeepHistory, BOOL bDenyAuthRequests, BOOL bNoLog)
+{
+ BOOL bReturn;
+ DBVARIANT dbv;
+
+ // NotifySpammerStateChanged will be called automatically on settings change
+ DBWriteContactSettingByte(hContact, DB_MODULE_NAME, DB_SETTING_ISSPAMMER, (BYTE)TRUE);
+
+ // Ignore all events for the contact
+ bReturn = (CallService(MS_IGNORE_IGNORE, (WPARAM)hContact, IGNOREEVENT_ALL) == 0);
+
+ if (DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ {
+ // Hide the contact
+ // (Hide it before removing NotOnList setting, so that no YouWereAdded notification is sent)
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_HIDEFROMCLIST, (BYTE)DEFAULT_SETTING_HIDEFROMCLIST))
+ DBWriteContactSettingByte(hContact, "CList", "Hidden", 1);
+ else
+ DBDeleteContactSetting(hContact, "CList", "Hidden");
+
+ // Add permanently to list (after hide!!!)
+ DBDeleteContactSetting(hContact, "CList", "NotOnList");
+
+ // Remove association to a contactlist group (change to spammers)
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_MOVETOGROUP, (BYTE)DEFAULT_SETTING_MOVETOGROUP) &&
+ DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_MOVETOGROUPNAME, &dbv) == 0)
+ {
+ CreateContactListGroup(dbv.ptszVal);
+ DBWriteContactSettingTString(hContact, "CList", "Group", dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else {
+ DBDeleteContactSetting(hContact, "CList", "Group");
+ }
+ } else {
+ DBWriteContactSettingByte(hContact, DB_MODULE_NAME, "WasOnList", 1);
+ }
+
+ // Do not send typing notifications
+ DBWriteContactSettingByte(hContact, "SRMM", "SupportTyping", 0);
+
+ // Appear as offline to the contact
+ CallContactService(hContact, PSS_SETAPPARENTMODE, (WPARAM)ID_STATUS_OFFLINE, 0);
+
+ // Rename contact
+ {
+ CONTACTINFO ci;
+
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.hContact = hContact;
+ #if defined(UNICODE)
+ ci.dwFlag = CNF_UNIQUEID|CNF_UNICODE;
+ #else
+ ci.dwFlag = CNF_UNIQUEID;
+ #endif
+
+ if (CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci) == 0)
+ {
+ WCHAR* pszUniqueID = NULL;
+
+ if (ci.type == CNFT_ASCIIZ) {
+ pszUniqueID = ci.pszVal; // mir_alloc used by MS_CONTACT_GETCONTACTINFO
+ } else if (ci.type == CNFT_DWORD) {
+ pszUniqueID = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszUniqueID)
+ mir_sntprintf(pszUniqueID, (MAX_INT_LENGTH+1), _T("%u"), ci.dVal);
+ } else if (ci.type == CNFT_WORD) {
+ pszUniqueID = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszUniqueID)
+ mir_sntprintf(pszUniqueID, (MAX_INT_LENGTH+1), _T("%u"), ci.wVal);
+ } else if (ci.type == CNFT_BYTE) {
+ pszUniqueID = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszUniqueID)
+ mir_sntprintf(pszUniqueID, (MAX_INT_LENGTH+1), _T("%u"), ci.bVal);
+ }
+
+ if (pszUniqueID) {
+ DBWriteContactSettingTString(hContact, "CList", "MyHandle", pszUniqueID);
+ mir_free(pszUniqueID);
+ }
+ }
+/*
+ pszSuffix = TranslateT(" (Spammer)");
+ if (pszUniqueID && pszSuffix)
+ {
+ TCHAR* pszNewName = (TCHAR*)mir_alloc((lstrlen(pszUniqueID)+lstrlen(pszSuffix)+1)*sizeof(TCHAR));
+
+ if (pszNewName)
+ {
+ mir_sntprintf(pszNewName, lstrlen(pszUniqueID)+lstrlen(pszSuffix)+1, _T("%s%s"), pszUniqueID, pszSuffix);
+
+ DBWriteContactSettingTString(hContact, "CList", "MyHandle", pszNewName);
+
+ // Tell CList to refresh it's cache
+ CallService(MS_CLIST_INVALIDATEDISPLAYNAME, (WPARAM)hContact, 0);
+
+ mir_free(pszNewName);
+ }
+ }
+*/
+ }
+
+ // Remove History (or mark read to get rid of pending "new message" notifications)
+ {
+ HANDLE hDbEvent, hDbEventBuf;
+
+ STRINGLIST* pslRecognition = NULL;
+ DBEVENTINFO dbei;
+ PBYTE pLogText;
+ WCHAR* pszUserName;
+ BOOL bDoWriteLog;
+
+ bDoWriteLog = !bNoLog && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_LOGGING, DEFAULT_SETTING_LOGGING);
+ if (bDoWriteLog)
+ {
+ pslRecognition = SLNewList();
+ SLAddItem(pslRecognition, TranslateT("Contact set as spammer"));
+ }
+
+ // ** History of the contact **
+ // hDbEvent gets buffered to allow deleting while enumerating (handle is offset)
+ hDbEventBuf = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0);
+ hDbEvent = hDbEventBuf;
+
+ while (hDbEvent)
+ {
+ hDbEventBuf = (HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hDbEvent, 0);
+
+ // Now handle hDbEvent
+ {
+ if (bDoWriteLog || bDenyAuthRequests)
+ {
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0);
+ if (dbei.cbBlob <= 0)
+ {
+ dbei.pBlob = (PBYTE)mir_alloc(dbei.cbBlob+sizeof(WCHAR)); // adding terminating zeros (security)
+ if (!dbei.pBlob)
+ {
+ // Ensure terminating zero
+ ZeroMemory(dbei.pBlob+dbei.cbBlob, sizeof(WCHAR));
+
+ if (CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei) == 0) // if successful
+ {
+ if (!(dbei.flags&DBEF_SENT) && !(dbei.flags&DBEF_READ))
+ {
+ switch (dbei.eventType)
+ {
+ case EVENTTYPE_MESSAGE:
+ {
+ if (bDoWriteLog)
+ {
+ if (!CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)dbei.szModule, (LPARAM)SFMT_MESSAGE))
+ {
+ #if defined(UNICODE)
+ // Checking if Unicode text available
+ if (dbei.cbBlob > lstrlenA((char*)dbei.pBlob))
+ pLogText = dbei.pBlob+(lstrlenA((char*)dbei.pBlob)+1)*sizeof(char);
+ else
+ pLogText = (PBYTE)mir_utf8encodeW((WCHAR*)dbei.pBlob);
+ #else
+ pLogText = dbei.pBlob;
+ #endif
+
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+ WriteToLogFile(dbei.szModule, SFMT_MESSAGE, pszUserName, (WCHAR*)pLogText, pslRecognition, bKeepHistory ? SFF_MARKREAD : SFF_DELETE);
+
+ #if defined(UNICODE)
+ if (pLogText)
+ if (dbei.cbBlob <= lstrlenA((char*)dbei.pBlob))
+ mir_free(pLogText);
+ #endif
+ }
+ }
+ break;
+ }
+
+ case EVENTTYPE_URL:
+ {
+ if (bDoWriteLog)
+ {
+ if (!CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)dbei.szModule, (LPARAM)SFMT_URL))
+ {
+ // DB event corrupt check
+ if (dbei.cbBlob >= 2*sizeof(char))
+ {
+ pLogText = dbei.pBlob+(lstrlenA((char*)dbei.pBlob)+1)*sizeof(char);
+
+ #if defined(UNICODE)
+ pLogText = (PBYTE)mir_utf8encodeW((WCHAR*)pLogText);
+ #endif
+
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+
+ WriteToLogFile(dbei.szModule, SFMT_URL, pszUserName, (WCHAR*)pLogText, pslRecognition, bKeepHistory ? SFF_MARKREAD : SFF_DELETE);
+
+ #if defined(UNICODE)
+ if (pLogText) mir_free(pLogText);
+ #endif
+ }
+ }
+ }
+ break;
+ }
+
+ case EVENTTYPE_FILE:
+ {
+ if (bDoWriteLog)
+ {
+ if (!CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)dbei.szModule, (LPARAM)SFMT_FILE))
+ {
+ // DB event corrupt check
+ if (dbei.cbBlob >= (sizeof(DWORD)+3*sizeof(char)))
+ {
+ pLogText = dbei.pBlob+sizeof(DWORD); // hTransfer, filename(s)
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // description
+
+ #if defined(UNICODE)
+ pLogText = (PBYTE)mir_utf8encodeW((WCHAR*)pLogText);
+ #endif
+
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+
+ WriteToLogFile(dbei.szModule, SFMT_FILE, pszUserName, (WCHAR*)pLogText, pslRecognition, bKeepHistory ? SFF_MARKREAD : SFF_DELETE);
+
+ #if defined(UNICODE)
+ if (pLogText) mir_free(pLogText);
+ #endif
+ }
+ }
+ }
+ break;
+ }
+
+ case EVENTTYPE_CONTACTS:
+ {
+ if (bDoWriteLog)
+ {
+ if (!CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)dbei.szModule, (LPARAM)SFMT_CONTACTS))
+ {
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+
+ WriteToLogFile(dbei.szModule, SFMT_CONTACTS, pszUserName, NULL, pslRecognition, bKeepHistory ? SFF_MARKREAD : SFF_DELETE);
+ }
+ }
+ break;
+ }
+
+ case EVENTTYPE_AUTHREQUEST:
+ {
+ if (bDoWriteLog)
+ {
+ if (!CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)dbei.szModule, (LPARAM)SFMT_MESSAGE))
+ {
+ // DB event corrupt check
+ if (dbei.cbBlob >= (sizeof(DWORD)+sizeof(HANDLE)+5*sizeof(char)))
+ {
+ pLogText = dbei.pBlob+sizeof(DWORD)+sizeof(HANDLE); // uin, hcontact, nick
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // first
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // last
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // email
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // reason
+
+ #if defined(UNICODE)
+ pLogText = (PBYTE)mir_utf8encodeW((WCHAR*)pLogText);
+ #endif
+
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+
+ WriteToLogFile(dbei.szModule, SFMT_AUTHREQUEST, pszUserName, (WCHAR*)pLogText, pslRecognition, bKeepHistory ? SFF_MARKREAD : SFF_DELETE);
+
+ #if defined(UNICODE)
+ if (pLogText) mir_free(pLogText);
+ #endif
+ }
+ }
+ }
+
+ // Deny authorisation
+ if (bDenyAuthRequests)
+ CallProtoService(dbei.szModule, PS_AUTHDENY, (WPARAM)hDbEvent, (LPARAM)NULL); // without reason
+
+ break;
+ }
+
+ case EVENTTYPE_ADDED:
+ {
+ if (bDoWriteLog)
+ {
+ if (!CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)dbei.szModule, (LPARAM)SFMT_MESSAGE))
+ {
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+
+ WriteToLogFile(dbei.szModule, SFMT_ADDED, pszUserName, NULL, pslRecognition, bKeepHistory ? SFF_MARKREAD : SFF_DELETE);
+ }
+ }
+ break;
+ }
+
+ } // switch
+ }
+
+ mir_free(dbei.pBlob);
+ }
+ }
+ }
+ }
+
+ CallServiceSync(MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)hDbEvent);
+
+ if (bKeepHistory)
+ CallService(MS_DB_EVENT_MARKREAD, (WPARAM)hContact, (LPARAM)hDbEvent);
+ else
+ CallService(MS_DB_EVENT_DELETE, (WPARAM)hContact, (LPARAM)hDbEvent);
+ }
+
+ hDbEvent = hDbEventBuf;
+ }
+
+ return TRUE;
+
+ // ** Main contact **
+ // Authorization Requests and Added Notifications are stored under the main contact
+ // hDbEvent gets buffered to allow deleting while enumerating (handle is offset)
+ hDbEventBuf = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0);
+ hDbEvent = hDbEventBuf;
+
+ while (hDbEvent)
+ {
+ hDbEventBuf = (HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hDbEvent, 0);
+
+ // Now handle hDbEvent
+ {
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0);
+ if (dbei.cbBlob < 0)
+ {
+ dbei.pBlob = (PBYTE)mir_alloc(dbei.cbBlob+sizeof(WCHAR));
+ if (dbei.pBlob)
+ {
+ // Ensure terminating zero
+ ZeroMemory(dbei.pBlob+dbei.cbBlob, sizeof(WCHAR));
+
+ if (CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei) == 0)
+ {
+ if ((dbei.eventType == EVENTTYPE_AUTHREQUEST) || (dbei.eventType == EVENTTYPE_ADDED))
+ {
+ // DB event corrupt check
+ if (dbei.cbBlob >= (sizeof(DWORD)+sizeof(HANDLE)))
+ {
+ if (*((PHANDLE)(dbei.pBlob+sizeof(DWORD))) == hContact)
+ {
+ if (bDoWriteLog || bDenyAuthRequests)
+ {
+ if (!(dbei.flags&DBEF_SENT) && !(dbei.flags&DBEF_READ))
+ {
+ switch (dbei.eventType)
+ {
+ case EVENTTYPE_AUTHREQUEST:
+ {
+ if (bDoWriteLog)
+ {
+ if (!CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)dbei.szModule, (LPARAM)SFMT_AUTHREQUEST))
+ {
+ // DB event corrupt check
+ if (dbei.cbBlob >= (sizeof(DWORD)+sizeof(HANDLE)+5*sizeof(char)))
+ {
+ pLogText = dbei.pBlob+sizeof(DWORD)+sizeof(HANDLE); // uin, hcontact, nick
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // first
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // last
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // email
+ pLogText = pLogText+(lstrlenA((char*)pLogText)+1)*sizeof(char); // reason
+
+ #if defined(UNICODE)
+ pLogText = (PBYTE)mir_utf8encodeW((WCHAR*)pLogText);
+ #endif
+
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+
+ WriteToLogFile(dbei.szModule, SFMT_AUTHREQUEST, pszUserName, (WCHAR*)pLogText, pslRecognition, bKeepHistory ? SFF_MARKREAD : SFF_DELETE);
+
+ #if defined(UNICODE)
+ if (pLogText) mir_free(pLogText);
+ #endif
+ }
+ }
+ }
+
+ // Deny authorisation
+ if (bDenyAuthRequests)
+ CallProtoService(dbei.szModule, PS_AUTHDENY, (WPARAM)hDbEvent, (LPARAM)NULL); // without reason
+
+ break;
+ }
+
+ case EVENTTYPE_ADDED:
+ {
+ if (bDoWriteLog)
+ {
+ if (!CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)dbei.szModule, (LPARAM)SFMT_ADDED))
+ {
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+
+ WriteToLogFile(dbei.szModule, SFMT_ADDED, pszUserName, NULL, pslRecognition, bKeepHistory ? SFF_MARKREAD : SFF_DELETE);
+ }
+ }
+ break;
+ }
+ } // switch
+ }
+ }
+
+ CallServiceSync(MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)hDbEvent);
+
+ if (bKeepHistory)
+ CallService(MS_DB_EVENT_MARKREAD, (WPARAM)hContact, (LPARAM)hDbEvent);
+ else
+ CallService(MS_DB_EVENT_DELETE, (WPARAM)hContact, (LPARAM)hDbEvent); }
+ }
+ }
+
+ }
+
+ mir_free(dbei.pBlob);
+ }
+ }
+
+ }
+
+ hDbEvent = hDbEventBuf;
+ }
+ }
+
+ return bReturn;
+}
diff --git a/spamfilter/contact.h b/spamfilter/contact.h
new file mode 100644
index 0000000..80a3e8e
--- /dev/null
+++ b/spamfilter/contact.h
@@ -0,0 +1,41 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_CONTACT_H__
+#define SF_CONTACT_H__ 1
+
+
+// -- Prototypes
+WORD IncreasePreSpammerCount(SPAMCHECKDATA* pscd);
+WORD GetPreSpammerCount(SPAMCHECKDATA* pscd);
+BOOL RemovePreSpammerCount(SPAMCHECKDATA* pscd);
+
+int MarkAllContactEventsUnRead(HANDLE hContact);
+BOOL SendContactAutomatedMessage(HANDLE hContact, WCHAR* pszMessage, BOOL bAddToHistory);
+
+BOOL RemoveContactListGroup(WCHAR* pszGroupName);
+BOOL CreateContactListGroup(WCHAR* pszGroupName);
+
+BOOL RemoveContactFromIgnoreList(HANDLE hContact);
+BOOL AddContactToIgnoreList(HANDLE hContact, BOOL bKeepHistory, BOOL bDenyAuthRequests, BOOL bNoLog);
+
+
+#endif // SF_CONTACT_H__
diff --git a/spamfilter/dlg.c b/spamfilter/dlg.c
new file mode 100644
index 0000000..83dd86d
--- /dev/null
+++ b/spamfilter/dlg.c
@@ -0,0 +1,385 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -- Defines
+#define PROP_FILEEDIT_ASSO_BTN _T("FileEditBrowse_AssociatedButton")
+
+
+// -----------------------------------------
+
+
+/*
+BOOL ResizeWindow(HWND hWnd, int cx, int cy, BOOL bRepaint)
+{
+ RECT rc;
+
+ if (GetWindowRect(hWnd, &rc))
+ return SetWindowPos(hWnd, NULL, 0, 0, (rc.right-rc.left)+cx, (rc.bottom-rc.top)+cy, SWP_NOSIZE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOACTIVATE|(bRepaint?0:SWP_NOREDRAW));
+ else
+ return FALSE;
+}
+*/
+
+
+int GetCtrlTextExtent(HWND hwndCtrl, const WCHAR* pszText, BOOL bAddAveChar)
+{
+ HDC hDC;
+ HFONT hFont;
+ SIZE size;
+ HGDIOBJ hOldObj;
+
+ if (!hwndCtrl || !pszText) return 0;
+
+ hFont = (HFONT)SendMessage(hwndCtrl, WM_GETFONT, 0, 0);
+
+ hDC = GetDC(hwndCtrl);
+ if (!hDC) return 0;
+
+ // If non-system font used select it first
+ if (hFont)
+ hOldObj = SelectObject(hDC, hFont);
+ else
+ hOldObj = NULL;
+
+ if (!GetTextExtentPoint32(hDC, pszText, lstrlen(pszText), &size))
+ size.cx = 0;
+
+ if (hFont)
+ SelectObject(hDC, hOldObj);
+
+ ReleaseDC(hwndCtrl, hDC);
+
+ if (bAddAveChar)
+ {
+ TEXTMETRIC tm;
+ if (GetTextMetrics(hDC, &tm))
+ size.cx += tm.tmAveCharWidth;
+ }
+
+ return size.cx;
+}
+
+
+
+BOOL ShowDlgItem(HWND hwndDlg, int iIDCtrl, BOOL bShow)
+{
+ HWND hwndCtrl = GetDlgItem(hwndDlg, iIDCtrl);
+ if (!hwndCtrl) return FALSE;
+
+ // Avoid flickering
+ if (bShow && IsWindowVisible(hwndCtrl))
+ return TRUE;
+
+ return ShowWindow(hwndCtrl, (bShow ? SW_SHOW : SW_HIDE));
+}
+
+
+
+BOOL EnableDlgItem(HWND hwndDlg, int iIDCtrl, BOOL bEnable)
+{
+ HWND hwndCtrl = GetDlgItem(hwndDlg, iIDCtrl);
+ if (!hwndCtrl) return FALSE;
+
+ // Avoid flickering
+ if (IsWindowEnabled(hwndCtrl) == bEnable)
+ return (bEnable == FALSE);
+
+ return EnableWindow(hwndCtrl, bEnable);
+}
+
+/*
+BOOL SetDlgItemPrintf(HWND hwndDlg, int iIDCtrl, unsigned int cbArgMaxLen, ...)
+{
+ HWND hwndCtrl;
+ BOOL bReturn;
+ int iMaxLen;
+ TCHAR* pszFmt;
+ TCHAR* pszOut;
+ va_list arglist;
+
+ hwndCtrl = GetDlgItem(hwndDlg, iIDCtrl);
+ if (!hwndCtrl) return FALSE;
+
+ // Prepare fmt buffer
+ iMaxLen = GetWindowTextLength(hwndCtrl);
+ pszFmt = (TCHAR*)mir_alloc((iMaxLen+1)*sizeof(TCHAR));
+ if (!pszFmt) return FALSE;
+
+ // Get fmt text
+ if (GetWindowText(hwndCtrl, pszFmt, iMaxLen+1) == 0)
+ {
+ mir_free(pszFmt);
+ return FALSE;
+ }
+
+ // Prepare out buffer
+ if (cbArgMaxLen > 0) iMaxLen = iMaxLen + cbArgMaxLen;
+ pszOut = (TCHAR*)mir_alloc((iMaxLen+1)*sizeof(TCHAR));
+
+ if (!pszOut)
+ {
+ mir_free(pszFmt);
+ return FALSE;
+ }
+
+ // Generate out text
+ va_start(arglist, cbArgMaxLen);
+ mir_vsnprintf(pszOut, iMaxLen+1, pszFmt, arglist);
+ va_end(arglist);
+ mir_free(pszFmt);
+
+ bReturn = SetWindowText(hwndCtrl, pszOut);
+
+ mir_free(pszOut);
+ return bReturn;
+}
+*/
+
+BOOL SetSpinCtrlRange(HWND hwndSpin, int iRangeMin, int iRangeMax, int iDefault, BOOL bForceDefault)
+{
+ HWND hwndDlg, hwndEdit;
+ int iIDEdit;
+ int iOldValue, iNewValue;
+ int iOldRangeMin, iOldRangeMax;
+ BOOL bIsEnabled;
+
+ hwndDlg = GetParent(hwndSpin);
+ if (!hwndDlg) return FALSE;
+
+ hwndEdit = (HWND)SendMessage(hwndSpin, UDM_GETBUDDY, 0 , 0);
+ if (!hwndEdit || (GetParent(hwndEdit) != hwndDlg))
+ return FALSE;
+
+ iIDEdit = GetDlgCtrlID(hwndEdit);
+
+ iOldValue = (int)GetDlgItemInt(hwndDlg, iIDEdit, NULL, TRUE);
+
+ //if (iRangeMin < UD_MINVAL) iRangeMin = UD_MINVAL;
+ //if (iRangeMax > UD_MAXVAL) iRangeMax = UD_MAXVAL;
+
+ // Get data
+ if (iRangeMax < iRangeMin) iRangeMax = iRangeMin;
+ bIsEnabled = (iRangeMin != iRangeMax);
+ SendMessage(hwndSpin, UDM_GETRANGE32, (WPARAM)&iOldRangeMin, (LPARAM)&iOldRangeMax);
+
+ // Set default if necessary
+ if (bForceDefault
+ || ((iOldValue==iOldRangeMax) && (iDefault > iOldRangeMax))
+ || ((iOldValue==iOldRangeMin) && (iDefault < iOldRangeMin))
+ || (bIsEnabled && !IsWindowEnabled(hwndSpin) && !IsWindowEnabled(hwndEdit)) )
+ iNewValue = iDefault;
+ else
+ iNewValue = iOldValue;
+
+ if (iNewValue < iRangeMin) iNewValue = iRangeMin;
+ if (iNewValue > iRangeMax) iNewValue = iRangeMax;
+
+ // Set edit
+ if (iNewValue != iOldValue)
+ SetDlgItemInt(hwndDlg, iIDEdit, iNewValue, TRUE);
+
+ // Set spin
+ if ((iRangeMin != iOldRangeMin) || iRangeMax != iOldRangeMax)
+ {
+ WCHAR szRangeMin[MAX_INT_LENGTH];
+ WCHAR szRangeMax[MAX_INT_LENGTH];
+
+ mir_sntprintf(szRangeMin, ARRAYSIZE(szRangeMin), _T("%i"), iRangeMin);
+ mir_sntprintf(szRangeMax, ARRAYSIZE(szRangeMax), _T("%i"), iRangeMax);
+
+ SendMessage(hwndSpin, UDM_SETRANGE32, (WPARAM)iRangeMin, (LPARAM)iRangeMax);
+ PostMessage(hwndEdit, EM_SETLIMITTEXT, (WPARAM)max(lstrlen(szRangeMin), lstrlen(szRangeMax)), 0);
+ }
+
+ if (iNewValue != iOldValue)
+ SendMessage(hwndSpin, UDM_SETPOS32, 0, (LPARAM)iNewValue);
+
+ // Avoiding flicker
+ if (IsWindowEnabled(hwndSpin) != bIsEnabled) EnableWindow(hwndSpin, bIsEnabled);
+ if (IsWindowEnabled(hwndEdit) != bIsEnabled) EnableWindow(hwndEdit, bIsEnabled);
+
+ return bIsEnabled;
+}
+
+
+LRESULT CALLBACK PathEditSubclassProc(HWND hwndEdit, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lResult = CallWindowProc((WNDPROC)GetWindowLongPtr(hwndEdit, GWLP_USERDATA), hwndEdit, uMsg, wParam, lParam);
+
+ switch(uMsg)
+ {
+ case WM_NCCALCSIZE:
+ {
+ if ((BOOL)wParam == TRUE)
+ {
+ // Resizing the client rect of the edit control
+ // and moving the button in the space.
+
+ HWND hwndButton = (HWND)GetProp(hwndEdit, PROP_FILEEDIT_ASSO_BTN);
+ NCCALCSIZE_PARAMS* pnccsp = (NCCALCSIZE_PARAMS*)lParam;
+ RECT rc;
+
+ if (hwndButton)
+ if (GetWindowRect(hwndButton, &rc))
+ {
+ rc.right -= rc.left;
+ rc.bottom = pnccsp->rgrc[0].bottom-pnccsp->rgrc[0].top;
+ pnccsp->rgrc[0].right -= (rc.right+1);
+ rc.left = pnccsp->rgrc[0].right+1;
+ rc.top = pnccsp->rgrc[0].top;
+
+ MoveWindow(hwndButton, rc.left, rc.top, rc.right, rc.bottom, TRUE);
+ }
+ }
+
+ break;
+ }
+
+ case WM_NCHITTEST:
+ {
+ // When the mouse is hovering the button
+ // HTTRANSPARENT needs to be returned
+ // else no mouse messages will be sent to the button
+
+ if (lResult == HTNOWHERE)
+ {
+ HWND hwndButton = (HWND)GetProp(hwndEdit, PROP_FILEEDIT_ASSO_BTN);
+ RECT rc;
+ POINT pt;
+
+ if (hwndButton)
+ if (GetWindowRect(hwndButton, &rc))
+ {
+ POINTSTOPOINT(pt, lParam);
+
+ if (PtInRect(&rc, pt))
+ lResult = HTTRANSPARENT;
+ }
+ }
+
+ break;
+ }
+
+ case WM_DROPFILES:
+ {
+ HDROP hDrop = (HDROP)wParam;
+ WCHAR szFileName[MAX_PATH];
+
+ DragQueryFile(hDrop, 0, szFileName, ARRAYSIZE(szFileName));
+
+ // Insert the text and Mark it
+ SetWindowText(hwndEdit, szFileName);
+ DragFinish(hDrop);
+
+ // Set focus to file editbox (SetFocus is not allowed in Dialogs)
+ if (GetParent(hwndEdit))
+ PostMessage(GetParent(hwndEdit), WM_NEXTDLGCTL, (WPARAM)hwndEdit, (LPARAM)TRUE);
+ break;
+ }
+
+ case WM_DESTROY: // before the destroy
+ {
+ // Remove assoicated browse button
+ RemoveProp(hwndEdit, PROP_FILEEDIT_ASSO_BTN);
+ break;
+ }
+
+ case WM_NCDESTROY: // after the destroy
+ {
+ // Uninit auto complete
+ if (GetProcAddress(GetModuleHandle(_T("SHLWAPI")), "SHAutoComplete"))
+ CoUninitialize();
+ break;
+ }
+
+ case WM_CHAR:
+ {
+ // Only allow valid path chars to be entered
+ if (PathGetCharType((WCHAR)wParam)&GCT_INVALID)
+ return FALSE;
+ else
+ break;
+ }
+ }
+
+ return lResult;
+}
+
+
+BOOL MakePathEditCtrl(HWND hwndEdit, HWND hwndButton)
+{
+ if (!hwndEdit || !hwndButton) return FALSE;
+
+ // Don't make a new mask if there is already one available
+ if((WNDPROC)GetWindowLongPtr(hwndEdit, GWLP_WNDPROC) == PathEditSubclassProc)
+ return FALSE;
+
+ // Remember associated button control
+ if (!SetProp(hwndEdit, PROP_FILEEDIT_ASSO_BTN, (HANDLE)hwndButton))
+ {
+ ShowWindow(hwndButton, SW_HIDE);
+ return FALSE;
+ }
+
+ // Ansi/OEM codepage converting
+ #if !defined(UNICODE)
+ // Allow later converting to OEM encoding for some functions (see docs)
+ SetWindowLongPtr(hwndEdit, GWL_STYLE, ES_OEMCONVERT|GetWindowLongPtr(hwndEdit, GWL_STYLE));
+ #endif
+
+ // Limit text
+ PostMessage(hwndEdit, EM_SETLIMITTEXT, (WPARAM)MAX_PATH, 0);
+
+ // Subclass
+ SetWindowLongPtr(hwndEdit, GWLP_USERDATA, SetWindowLongPtr(hwndEdit, GWLP_WNDPROC, (LONG_PTR)PathEditSubclassProc));
+
+ // Init auto complete
+ {
+ HRESULT (STDAPICALLTYPE *MySHAutoComplete)(HWND, DWORD);
+ *(FARPROC*)&MySHAutoComplete = GetProcAddress(GetModuleHandle(_T("SHLWAPI")), "SHAutoComplete");
+
+ if (MySHAutoComplete) {
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ MySHAutoComplete(hwndEdit, SHACF_FILESYSTEM|SHACF_FILESYS_ONLY|SHACF_USETAB);
+ } else OutputDebugString(_T("Spam Filter: SHAutoComplete in SHLWAPI.DLL not available.\r\n"));
+ }
+
+ // Accept drag & drop
+ DragAcceptFiles(hwndEdit, TRUE);
+
+ {
+ RECT rcEdit;
+ RECT rcButton;
+
+ if (GetClientRect(hwndEdit, &rcEdit))
+ if (GetClientRect(hwndButton, &rcButton))
+ {
+ // Send WM_NCCALCSIZE message to edit control and resize it to fit button
+ SetWindowPos(hwndEdit, NULL, 0, 0, (rcEdit.right-rcEdit.left), (rcButton.bottom-rcButton.top)+2*GetSystemMetrics(SM_CYBORDER), SWP_NOMOVE|SWP_NOZORDER|SWP_DRAWFRAME);
+ }
+ }
+
+ return TRUE;
+} \ No newline at end of file
diff --git a/spamfilter/dlg.h b/spamfilter/dlg.h
new file mode 100644
index 0000000..82a26fc
--- /dev/null
+++ b/spamfilter/dlg.h
@@ -0,0 +1,38 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_DLG_H__
+#define SF_DLG_H__ 1
+
+
+// -- Prototypes
+BOOL ResizeWindow(HWND hWnd, int cx, int cy, BOOL bRepaint);
+
+BOOL ShowDlgItem(HWND hwndDlg, int iIDCtrl, BOOL bShow);
+BOOL EnableDlgItem(HWND hwndDlg, int iIDCtrl, BOOL bEnable);
+BOOL SetDlgItemPrintf(HWND hwndDlg, int iIDCtrl, unsigned int cbArgMaxLen, ...);
+BOOL SetSpinCtrlRange(HWND hwndSpin, int iRangeMin, int iRangeMax, int iDefault, BOOL bForceDefault);
+
+int GetCtrlTextExtent(HWND hwndCtrl, const WCHAR* pszText, BOOL bAddAveChar);
+BOOL MakePathEditCtrl(HWND hwndEdit, HWND hwndButton);
+
+
+#endif // SF_DLG_H__ \ No newline at end of file
diff --git a/spamfilter/extraimg.c b/spamfilter/extraimg.c
new file mode 100644
index 0000000..59cd79e
--- /dev/null
+++ b/spamfilter/extraimg.c
@@ -0,0 +1,118 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -- Variables: Events/Services
+HANDLE hHookExtraListRebuild = NULL;
+HANDLE hHookExtraImageApply = NULL;
+HANDLE hHookDbSettingChanged = NULL;
+
+// -- Variables
+HANDLE hSpammerImage;
+
+
+// -----------------------------------------
+
+
+void SetSpammerIcon(HANDLE hContact, BOOL bShow)
+{
+ IconExtraColumn iec;
+
+ ZeroMemory(&iec, sizeof(iec));
+ iec.cbSize = sizeof(iec);
+ iec.ColumnType = EXTRA_ICON_ADV2;
+ iec.hImage = bShow?hSpammerImage:INVALID_HANDLE_VALUE;
+
+ CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+}
+
+
+static int ExtraImageApply(WPARAM wParam, LPARAM lParam)
+{
+ // Assign extra icon to contact
+ if (DBGetContactSettingByte((HANDLE)wParam, DB_MODULE_NAME, DB_SETTING_ISSPAMMER, (BYTE)FALSE))
+ SetSpammerIcon((HANDLE)wParam, TRUE);
+
+ return 0;
+}
+
+
+static int ExtraImageListRebuild(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact;
+
+ // Add spam icon to image list of clist extra images
+ hSpammerImage = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)SkinGetIcon(DB_ICON_ISSPAMMER_SETTING, IDI_DEFAULT, FALSE), 0);
+
+ // Update spammer icons for all contacts in list
+ for (hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); hContact; hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ ExtraImageApply(wParam, lParam);
+
+ return 0;
+}
+
+
+static int DbSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ DBCONTACTWRITESETTING* pdcws = (DBCONTACTWRITESETTING*)lParam;
+
+ // Watch DB_SETTING_ISSPAMMER to be in sync with spammer icon
+ if (StrCmpA(pdcws->szModule, DB_MODULE_NAME) == 0)
+ if (StrCmpA(pdcws->szSetting, DB_SETTING_ISSPAMMER) == 0)
+ {
+ BOOL bIsSpammer = (pdcws->value.type==DBVT_DELETED) ? FALSE : (pdcws->value.bVal?TRUE:FALSE);
+ SetSpammerIcon((HANDLE)wParam, bIsSpammer);
+ NotifySpammerStateChanged((HANDLE)wParam, bIsSpammer);
+
+ if (IsWindow(hwndSpammersInfo))
+ PostMessage(hwndSpammersInfo, SFM_REFRESH_SPAMMERS, 0, 0);
+ }
+
+ return 0;
+}
+
+
+// ------------------------------------
+
+
+void InitExtraImg(void)
+{
+ // Enable CList extra image support (in clist clones)
+ if (ServiceExists(MS_CLIST_EXTRA_SET_ICON) && ServiceExists(MS_CLIST_EXTRA_ADD_ICON))
+ {
+ SkinAddNewIcon(DB_ICON_ISSPAMMER_SETTING, TranslateT("Spam Filter"), TranslateT("Spammer Indicator"), IDI_DEFAULT, FALSE);
+
+ hHookExtraListRebuild = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, ExtraImageListRebuild);
+ hHookExtraImageApply = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, ExtraImageApply);
+ hHookDbSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DbSettingChanged);
+ }
+}
+
+
+void UninitExtraImg(void)
+{
+ if (hHookExtraListRebuild) UnhookEvent(hHookExtraListRebuild);
+ if (hHookExtraImageApply) UnhookEvent(hHookExtraImageApply);
+ if (hHookDbSettingChanged) UnhookEvent(hHookDbSettingChanged);
+} \ No newline at end of file
diff --git a/spamfilter/extraimg.h b/spamfilter/extraimg.h
new file mode 100644
index 0000000..cd66c94
--- /dev/null
+++ b/spamfilter/extraimg.h
@@ -0,0 +1,30 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_EXTRAIMG_H__
+#define SF_EXTRAIMG_H__ 1
+
+// -- Prototypes
+void InitExtraImg(void);
+void UninitExtraImg(void);
+
+
+#endif // SF_EXTRAIMG_H__
diff --git a/spamfilter/folders.c b/spamfilter/folders.c
new file mode 100644
index 0000000..182a2d1
--- /dev/null
+++ b/spamfilter/folders.c
@@ -0,0 +1,101 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -- Variables
+HANDLE hDefinitionsFolder = NULL;
+HANDLE hCustomFolder = NULL;
+
+// -----------------------------------------
+
+
+BOOL GetDefinitionsPath(WCHAR* pszReturn, BOOL bCustomFile, WCHAR* pszAppend)
+{
+ // pszReturn muss MAX_PATH groß sein
+ BOOL bReturn = FALSE;
+
+ if ((bCustomFile && hCustomFolder) || (!bCustomFile && hDefinitionsFolder))
+ {
+ FOLDERSGETDATA fgd;
+
+ if (!pszReturn) return FALSE;
+ ZeroMemory(&fgd, sizeof(fgd));
+ fgd.cbSize = sizeof(fgd);
+ fgd.nMaxPathSize = MAX_PATH;
+ fgd.szPathT = pszReturn;
+
+ if (CallService(MS_FOLDERS_GET_PATH, (WPARAM)(bCustomFile?hCustomFolder:hDefinitionsFolder), (LPARAM)&fgd) == 0)
+ {
+ if (pszAppend) PathAppend(pszReturn, pszAppend);
+ bReturn = TRUE;
+ }
+ }
+
+ // Custom Folder Plugin not installed (using standard folder)
+ if (!bReturn)
+ bReturn = PConstructLocalPath(pszReturn, bCustomFile?CSIDL_EXT_MIRANDAPROFILE:CSIDL_EXT_EXECUTABLE, DATA_SUBDIRECTORY, pszAppend, NULL);
+
+ return bReturn;
+}
+
+
+BOOL IsCustomFoldersAvailable(void)
+{
+ return (hCustomFolder || hDefinitionsFolder);
+}
+
+
+// ------------------------------------
+
+
+void InitFolders(void)
+{
+ // Enable Custom Folder Plugin support (needs new version!)
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH) && ServiceExists(MS_FOLDERS_GET_PATH))
+ {
+ FOLDERSDATA fd;
+
+ ZeroMemory(&fd, sizeof(fd));
+ fd.cbSize = sizeof(fd);
+ #if defined(UNICODE)
+ fd.flags = FF_UNICODE;
+ #endif
+ mir_snprintf(fd.szSection, ARRAYSIZE(fd.szSection), "%s", Translate("Spam Filter"));
+
+ // Definitions Folder
+ mir_snprintf(fd.szName, ARRAYSIZE(fd.szName), "%s", Translate("Spam Definitions"));
+ fd.szFormatT = _T("%miranda_path%\\")DATA_SUBDIRECTORY; // readonly allocated
+ hDefinitionsFolder = (HANDLE)CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM)&fd);
+
+ // Custom Definitions Folder
+ mir_snprintf(fd.szName, ARRAYSIZE(fd.szName), "%s", Translate("Customized Words"));
+ fd.szFormatT = _T("%profile_path%\\")DATA_SUBDIRECTORY; // readonly allocated
+ hCustomFolder = (HANDLE)CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM)&fd);
+ }
+}
+
+
+void UninitFolders(void)
+{
+} \ No newline at end of file
diff --git a/spamfilter/folders.h b/spamfilter/folders.h
new file mode 100644
index 0000000..d874911
--- /dev/null
+++ b/spamfilter/folders.h
@@ -0,0 +1,33 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_FOLDERS_H__
+#define SF_FOLDERS_H__ 1
+
+// -- Prototypes
+BOOL GetDefinitionsPath(WCHAR* pszReturn, BOOL bCustomPath, WCHAR* pszAppend);
+BOOL IsCustomFoldersAvailable(void);
+
+void InitFolders(void);
+void UninitFolders(void);
+
+
+#endif // SF_FOLDERS_H__
diff --git a/spamfilter/forkthread.c b/spamfilter/forkthread.c
new file mode 100644
index 0000000..2613f62
--- /dev/null
+++ b/spamfilter/forkthread.c
@@ -0,0 +1,112 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2006 Miranda ICQ/IM project,
+all portions of this code file are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "common.h"
+
+struct FORK_ARG {
+ HANDLE hEvent;
+ void (__cdecl *threadcode)(void*);
+ unsigned (__stdcall *threadcodeex)(void*);
+ void *arg;
+};
+
+void __cdecl forkthread_r(void * arg)
+{
+ struct FORK_ARG * fa = (struct FORK_ARG *) arg;
+ void (*callercode)(void*)=fa->threadcode;
+ void * cookie=fa->arg;
+ CallService(MS_SYSTEM_THREAD_PUSH,0,0);
+ if (fa->hEvent) SetEvent(fa->hEvent);
+ __try {
+ callercode(cookie);
+ } __finally {
+ CallService(MS_SYSTEM_THREAD_POP,0,0);
+ }
+ return;
+}
+
+
+unsigned long forkthread (
+ void (__cdecl *threadcode)(void*),
+ unsigned long stacksize,
+ void *arg
+)
+{
+ unsigned long rc;
+ struct FORK_ARG fa;
+ fa.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
+ fa.threadcode=threadcode;
+ fa.arg=arg;
+ rc=_beginthread(forkthread_r,stacksize,&fa);
+ if (fa.hEvent) {
+ if ((unsigned long)-1L != rc) {
+ WaitForSingleObject(fa.hEvent,INFINITE);
+ } //if
+ CloseHandle(fa.hEvent);
+ } // if
+ return rc;
+}
+
+
+/*
+unsigned __stdcall forkthreadex_r(void * arg)
+{
+ struct FORK_ARG *fa=(struct FORK_ARG *)arg;
+ unsigned (__stdcall * threadcode) (void *)=fa->threadcodeex;
+ void *cookie=fa->arg;
+ unsigned long rc;
+
+ CallService(MS_SYSTEM_THREAD_PUSH,0,0);
+ if (fa->hEvent) SetEvent(fa->hEvent);
+ __try {
+ rc=threadcode(cookie);
+ } __finally {
+ CallService(MS_SYSTEM_THREAD_POP,0,0);
+ }
+ return rc;
+}
+
+unsigned long forkthreadex(
+ void *sec,
+ unsigned stacksize,
+ unsigned (__stdcall *threadcode)(void*),
+ void *arg,
+ unsigned cf,
+ unsigned *thraddr
+)
+{
+ unsigned long rc;
+ struct FORK_ARG fa;
+ fa.threadcodeex=threadcode;
+ fa.arg=arg;
+ fa.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
+ rc=_beginthreadex(sec,stacksize,forkthreadex_r,(void *)&fa,0,thraddr);
+ if (fa.hEvent) {
+ if (rc) {
+ WaitForSingleObject(fa.hEvent,INFINITE);
+ } // if
+ CloseHandle(fa.hEvent);
+ }
+ return rc;
+}
+*/ \ No newline at end of file
diff --git a/spamfilter/forkthread.h b/spamfilter/forkthread.h
new file mode 100644
index 0000000..cbf32ad
--- /dev/null
+++ b/spamfilter/forkthread.h
@@ -0,0 +1,64 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2006 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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.
+*/
+
+/*
+
+Purpose:
+
+ A safe version of _beginthread()
+
+Description:
+
+ A new thread is created and the source thread is paused until
+ internal code to call MS_SYSTEM_THREAD_PUSH is made in the context
+ if the new thread.
+
+ The source thread is then released and then the user supplied
+ code is called, when that function returns -- MS_SYSTEM_THREAD_POP
+ is called and then the thread returns.
+
+ This insures that Miranda will not exit whilst new threads
+ are trying to be born; and the unwind wait stack will ensure
+ that Miranda will wait for all created threads to return as well.
+
+Cavets:
+
+ The function must be reimplemented across MT plugins, since thread
+ creation depends on CRT which can not be shared.
+
+*/
+unsigned long forkthread (
+ void (__cdecl *threadcode)(void*),
+ unsigned long stacksize,
+ void *arg
+);
+
+unsigned long forkthreadex(
+ void *sec,
+ unsigned stacksize,
+ unsigned (__stdcall *threadcode)(void*),
+ void *arg,
+ unsigned cf,
+ unsigned *thraddr
+);
+
diff --git a/spamfilter/logfile.c b/spamfilter/logfile.c
new file mode 100644
index 0000000..7712119
--- /dev/null
+++ b/spamfilter/logfile.c
@@ -0,0 +1,478 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+
+// -----------------------------------------
+
+
+BOOL WriteToLogFile(const char* pszMsgTypeSection, const char* pszMsgTypeName, const WCHAR* pszUserName, const WCHAR* pszMsgContent, STRINGLIST* pslRecognition, DWORD dwResult)
+{
+ BOOL bReturn;
+
+ HANDLE hFile;
+ DWORD dwWritten; // For Windows 95/98/ME compatibility for WriteFile (parameter needed)
+
+ STRINGLIST* pslVariablesFromTo;
+ WCHAR szLogFile[MAX_PATH];
+
+ int iBuf;
+ WCHAR* pszBuf;
+
+ // Get file to log to
+ {
+ DBVARIANT dbv;
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_LOGFILE, &dbv) == 0)
+ {
+ mir_sntprintf(szLogFile, ARRAYSIZE(szLogFile), _T("%s"), dbv.pszVal);
+ DBFreeVariant(&dbv);
+ PMakePathUsable(szLogFile);
+ } else {
+ szLogFile[0] = _T('\0');
+ }
+ }
+
+ // Make sure the directory exists: SILENT!
+ PPreparePathForWrite(NULL, szLogFile, FALSE, TRUE);
+
+ // Open file
+ hFile = CreateFile(szLogFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ bReturn = (hFile != INVALID_HANDLE_VALUE);
+ if (!bReturn)
+ {
+ WCHAR* pszLastError = GetLastErrorDescription(GetLastError());
+
+ ShowInfoMessage(NIIF_ERROR, TranslateT("Spam Filter Error"), TranslateT("The log file \"%s\"\r\ncan not be written.\r\n\r\nProblem:\r\n%s\r\nPlease check the settings."), lstrlen(szLogFile)+lstrlen(pszLastError), (lstrlen(PathFindFileName(szLogFile))>0)?PathFindFileName(szLogFile):szLogFile, pszLastError?pszLastError:_T(""));
+ if (pszLastError) mir_free(pszLastError);
+
+ return FALSE;
+ }
+
+ // Variables list (From-To)
+ pslVariablesFromTo = SLNewList();
+
+ // %log_time%
+ iBuf = GetTimeFormat(GetThreadLocale(), 0, NULL, NULL, NULL, 0); // or: LOCALE_USER_DEFAULT
+ pszBuf = (WCHAR*)mir_alloc((iBuf+1)*sizeof(WCHAR));
+ GetTimeFormat(GetThreadLocale(), 0, NULL, NULL, pszBuf, iBuf); // or: LOCALE_USER_DEFAULT
+ SLAddItemPair(pslVariablesFromTo, _T("%log_time%"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // %log_date%
+ iBuf = GetDateFormat(GetThreadLocale(), DATE_SHORTDATE, NULL, NULL, NULL, 0); // or: LOCALE_USER_DEFAULT
+ pszBuf = (WCHAR*)mir_alloc((iBuf+1)*sizeof(WCHAR));
+ GetDateFormat(GetThreadLocale(), DATE_SHORTDATE, NULL, NULL, pszBuf, iBuf); // or: LOCALE_USER_DEFAULT
+ SLAddItemPair(pslVariablesFromTo, _T("%log_date%"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // %log_user%
+ SLAddItemPair(pslVariablesFromTo, _T("%log_user%"), pszUserName);
+
+ // %log_type%
+ EnterCriticalSection(&csMsgTypes); // thread safety
+ iBuf = GetMsgTypeID(pszMsgTypeSection, pszMsgTypeName);
+ pszBuf = (iBuf >= 0) ? pamtdMsgTypes[iBuf].ptszDescription : NULL;
+ SLAddItemPair(pslVariablesFromTo, _T("%log_type%"), pszBuf);
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+
+ // %log_recognition%
+ pszBuf = SLConvertToString(pslRecognition, TranslateT(", "), FALSE, FALSE, NULL);
+ SLAddItemPair(pslVariablesFromTo, _T("%log_recognition%"), pszBuf);
+ if (pszBuf) SLFreeReturn(pszBuf);
+
+ // %log_result%
+ if (dwResult&SFF_MARKREAD)
+ pszBuf = TranslateT("Message marked read");
+ else if (dwResult&SFF_IGNORE)
+ pszBuf = TranslateT("Message deleted");
+ else
+ pszBuf = TranslateT("User ignored");
+ SLAddItemPair(pslVariablesFromTo, _T("%log_result%"), pszBuf);
+
+ // %log_message%
+ SLAddItemPair(pslVariablesFromTo, _T("%log_message%"), pszMsgContent);
+
+ // ##### XML format #####
+ if (PathMatchSpec(szLogFile, _T("*.xml")))
+ {
+ HANDLE hXSLFile;
+ WCHAR szXSLFile[MAX_PATH];
+
+ STRINGLIST* pslSpecialCharsFromTo;
+ char* pszUtf8;
+ WCHAR* pszBuf2;
+
+ // Create XSL file name
+ mir_sntprintf(szXSLFile, ARRAYSIZE(szXSLFile), _T("%s"), szLogFile);
+ PathRenameExtension(szXSLFile, _T(".xsl"));
+
+ // Construct XML special char replace filter
+ pslSpecialCharsFromTo = SLNewList();
+ SLAddItemPair(pslSpecialCharsFromTo, _T("&"), _T("&amp;"));
+ SLAddItemPair(pslSpecialCharsFromTo, _T("<"), _T("&lt;"));
+ SLAddItemPair(pslSpecialCharsFromTo, _T(">"), _T("&gt;"));
+ SLAddItemPair(pslSpecialCharsFromTo, _T("'"), _T("&apos;"));
+ SLAddItemPair(pslSpecialCharsFromTo, _T("\""), _T("&quot;"));
+ SLAddItemPair(pslSpecialCharsFromTo, _T(" "), _T("&nbsp;&nbsp;"));// Only one single space is allowed (multiple must be replaced)
+
+ // Replace special chars in replacement variables
+ for (iBuf=SL_MIN_POS; iBuf<=SLGetMaxPos(pslVariablesFromTo); iBuf+=2)
+ {
+ pszBuf = ReplaceSubStringWithStringMultiple(SLGetItem(pslVariablesFromTo, iBuf), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLChangeItem(pslVariablesFromTo, iBuf, pszBuf, FALSE);
+ if (pszBuf) mir_free(pszBuf);
+ }
+
+ // Write XSL stylesheet for XML (XSL-File)
+ hXSLFile = CreateFile(szXSLFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hXSLFile != INVALID_HANDLE_VALUE) // If file does not already exist
+ {
+ STRINGLIST* pslXSLFromTo = SLNewList();
+
+ // Translate file contents
+ // ", "
+ pszBuf = ReplaceSubStringWithStringMultiple(TranslateT(", "), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLAddItemPair(pslXSLFromTo, _T(", "), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // "Received Spam"
+ pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Received Spam"), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLAddItemPair(pslXSLFromTo, _T("Received Spam"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // "Logged since"
+ pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Logged since"), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLAddItemPair(pslXSLFromTo, _T("Logged since"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // "Date/Time:"
+ pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Date/Time:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLAddItemPair(pslXSLFromTo, _T("Date/Time:"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // "User:"
+ pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("User:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLAddItemPair(pslXSLFromTo, _T("User:"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // Type:
+ pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Type:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLAddItemPair(pslXSLFromTo, _T("Type:"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // "Recognition:"
+ pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Recognition:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLAddItemPair(pslXSLFromTo, _T("Recognition:"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // "Result:"
+ pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Result:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL);
+ SLAddItemPair(pslXSLFromTo, _T("Result:"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ mir_utf8decode(LockResource(LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_XSLLAYOUTFILETEMPLATE), _T("TEXT")))),&pszBuf2);
+ if (pszBuf2)
+ {
+ pszBuf = ReplaceSubStringWithStringMultiple(pszBuf2, pslXSLFromTo, TRUE, FALSE, NULL);
+ mir_free(pszBuf2);
+ if (pszBuf)
+ {
+ pszUtf8 = mir_utf8encodeW(pszBuf);
+ mir_free(pszBuf);
+ if (pszUtf8)
+ {
+ WriteFile(hXSLFile, (PBYTE)pszUtf8, lstrlenA(pszUtf8)*sizeof(char), &dwWritten, NULL);
+ //SetEndOfFile(hXSLFile);
+ mir_free(pszUtf8);
+ }
+ }
+ }
+
+ SLFreeList(pslXSLFromTo);
+ CloseHandle(hXSLFile);
+ }
+
+ // Write XML header/footer (XML-File)
+ if (GetFileSize(hFile, NULL) == 0) // If file is empty
+ {
+ // Replace spacial variables in header
+ // %log_xslfile%
+ SLAddItemPair(pslVariablesFromTo, _T("%log_xslfile%"), PathFindFileName(szXSLFile));
+
+ mir_utf8decode(LockResource(LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_XMLFILETEMPLATE), _T("TEXT")))),&pszBuf2);
+ if (pszBuf2)
+ {
+ pszBuf = ReplaceSubStringWithStringMultiple(pszBuf2, pslVariablesFromTo, TRUE, FALSE, NULL);
+ mir_free(pszBuf2);
+ if (pszBuf)
+ {
+ pszUtf8 = mir_utf8encodeW(pszBuf);
+ mir_free(pszBuf);
+ if (pszUtf8)
+ {
+ WriteFile(hFile, (PBYTE)pszUtf8, lstrlenA(pszUtf8)*sizeof(char), &dwWritten, NULL);
+ //SetEndOfFile(hFile);
+ mir_free(pszUtf8);
+ }
+ }
+ }
+ }
+
+ // Inject new XML item (XML-File)
+ mir_utf8decode(LockResource(LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_XMLITEMTEMPLATE), _T("TEXT")))),&pszBuf2);
+ if (pszBuf2)
+ {
+ pszBuf = ReplaceSubStringWithStringMultiple(pszBuf2, pslVariablesFromTo, TRUE, FALSE, NULL);
+ mir_free(pszBuf2);
+ if (pszBuf)
+ {
+ pszUtf8 = mir_utf8encodeW(pszBuf);
+ mir_free(pszBuf);
+ if (pszUtf8)
+ {
+ SetFilePointer(hFile, -12, NULL, FILE_END);
+ bReturn = WriteFile(hFile, (PBYTE)pszUtf8, lstrlenA(pszUtf8)*sizeof(char), &dwWritten, NULL);
+ //SetEndOfFile(hFile);
+ mir_free(pszUtf8);
+ }
+ }
+ }
+
+ SLFreeList(pslSpecialCharsFromTo);
+
+
+ // ##### Character Separated Values (CSV) format #####
+ } else if (PathMatchSpec(szLogFile, _T("*.csv"))) {
+
+ BOOL bIsUnicodeFile;
+
+ // Replace special chars in replacement variables
+ for (iBuf=SL_MIN_POS; iBuf<=SLGetMaxPos(pslVariablesFromTo); iBuf+=2)
+ {
+ pszBuf = ReplaceSubStringWithString(SLGetItem(pslVariablesFromTo, iBuf), _T("\""), _T("\"\""), TRUE, FALSE, NULL);
+ SLChangeItem(pslVariablesFromTo, iBuf, pszBuf, FALSE);
+ if (pszBuf) mir_free(pszBuf);
+ }
+
+ // Unicode and CSV header (Byte-order Mark)
+ if (GetFileSize(hFile, NULL) == 0) // If file is empty
+ {
+ #if defined(UNICODE)
+ // Write Unicode BOM UTF-16LE (Notepad)
+ // FF FE = UTF-16, little-endian
+ // -> indicating Unicode text file
+ if (IsWinVer2000Plus()) { // Notepad doesn't support Unicode on Win9x/ME -> no viewer available
+ WCHAR chBOM = 0xFFFE;
+ bIsUnicodeFile = WriteFile(hFile, (PBYTE)&chBOM, sizeof(WCHAR), &dwWritten, NULL);
+ } else {
+ bIsUnicodeFile = FALSE;
+ }
+ #else
+ bIsUnicodeFile = FALSE;
+ #endif
+
+ pszBuf = TranslateT("\"Date\";\"Time\";\"User\";\"Type\";\"Recognition\";\"Result\";\"Message\"\r\n");
+ if (pszBuf)
+ {
+ #if defined(UNICODE)
+
+ if (!bIsUnicodeFile)
+ {
+ WCHAR* pszAnsi;
+ mir_utf8decode((char*)pszBuf,&pszAnsi);
+ if (pszAnsi)
+ {
+ bReturn = WriteFile(hFile, (PBYTE)pszAnsi, lstrlenW(pszAnsi)*sizeof(char), &dwWritten, NULL);
+ mir_free(pszAnsi);
+ }
+
+ } else {
+ bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlenW(pszBuf)*sizeof(WCHAR), &dwWritten, NULL);
+ }
+
+ #else
+
+ if (bIsUnicodeFile)
+ {
+ WCHAR* pszUnicode = mir_utf8encodeW(pszBuf);
+ if (pszUnicode)
+ {
+ bReturn = WriteFile(hFile, (PBYTE)pszUnicode, lstrlenW(pszUnicode)*sizeof(WCHAR), &dwWritten, NULL);
+ mir_free(pszUnicode);
+ }
+
+ } else {
+ bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlenA(pszBuf)*sizeof(char), &dwWritten, NULL);
+ }
+
+ #endif
+ }
+
+ } else {
+
+ WCHAR chBOM;
+
+ // Read BOM header (Is file UTF-16LE?)
+ if (ReadFile(hFile, (PBYTE)&chBOM, sizeof(WCHAR), &dwWritten, NULL))
+ bIsUnicodeFile = (chBOM == 0xFFFE);
+ else
+ bIsUnicodeFile = FALSE;
+ }
+
+ pszBuf = TranslateT("\"%log_date%\";\"%log_time%\";\"%log_user%\";\"%log_type%\";\"%log_recognition%\";\"%log_result%\";\"%log_message%\"\r\n");
+ pszBuf = ReplaceSubStringWithStringMultiple(pszBuf, pslVariablesFromTo, TRUE, FALSE, NULL);
+ if (pszBuf)
+ {
+ SetFilePointer(hFile, 0, NULL, FILE_END);
+
+ // Append Unicode/ANSI CSV item
+ #if defined(UNICODE)
+
+ if (!bIsUnicodeFile)
+ {
+ WCHAR* pszAnsi;
+ mir_utf8decode((char*)pszBuf,&pszAnsi);
+ if (pszAnsi)
+ {
+ bReturn = WriteFile(hFile, (PBYTE)pszAnsi, lstrlenW(pszAnsi)*sizeof(char), &dwWritten, NULL);
+ mir_free(pszAnsi);
+ }
+
+ } else {
+ bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlen(pszBuf)*sizeof(WCHAR), &dwWritten, NULL);
+ }
+
+ #else
+
+ if (bIsUnicodeFile)
+ {
+ WCHAR* pszUnicode = mir_utf8encodeW(pszBuf);
+ if (pszUnicode)
+ {
+ bReturn = WriteFile(hFile, (PBYTE)pszUnicode, lstrlenW(pszUnicode)*sizeof(WCHAR), &dwWritten, NULL);
+ mir_free(pszUnicode);
+ }
+
+ } else {
+ bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlen(pszBuf)*sizeof(WCHAR), &dwWritten, NULL);
+ }
+
+ #endif
+
+ //SetEndOfFile(hFile);
+ mir_free(pszBuf);
+ }
+
+
+ // ##### Plain-Text format (TXT/LOG) #####
+ } else {
+
+ BOOL bIsUnicodeFile;
+
+ // Unicode header (Byte-order Mark)
+ if (GetFileSize(hFile, NULL) == 0) // If file is empty
+ {
+ #if defined(UNICODE)
+ // Write Unicode BOM UTF-16LE (Notepad)
+ // FF FE = UTF-16, little-endian
+ // -> indicating Unicode text file
+ if (IsWinVer2000Plus()) {// Notepad doesn't support Unicode on Win9x/ME -> no viewer available
+ WCHAR chBOM = 0xFFFE;
+ bIsUnicodeFile = WriteFile(hFile, (PBYTE)&chBOM, sizeof(WCHAR), &dwWritten, NULL);
+ } else {
+ bIsUnicodeFile = FALSE;
+ }
+ #else
+ bIsUnicodeFile = FALSE;
+ #endif
+
+ } else {
+
+ WCHAR chBOM;
+ bIsUnicodeFile = FALSE;
+
+ // Read BOM header (Is file UTF-16LE?)
+ if (ReadFile(hFile, (PBYTE)&chBOM, sizeof(WCHAR), &dwWritten, NULL)) {
+ bIsUnicodeFile = (chBOM == 0xFFFE);
+ } else {
+ bIsUnicodeFile = FALSE;
+ }
+ }
+
+ pszBuf = TranslateT("[%log_date% %log_time%, User: %log_user%, Type: %log_type%]\r\nRecognition: %log_recognition%\t\r\nResult: %log_result%\r\n%log_message%\r\n\r\n");
+ pszBuf = ReplaceSubStringWithStringMultiple(pszBuf, pslVariablesFromTo, TRUE, FALSE, NULL);
+ if (pszBuf)
+ {
+ SetFilePointer(hFile, 0, NULL, FILE_END);
+
+ // Append Unicode/ANSI log item
+ #if defined(UNICODE)
+
+ if (!bIsUnicodeFile)
+ {
+ WCHAR* pszAnsi;
+ mir_utf8decode((char*)pszBuf,&pszAnsi);
+ if (pszAnsi)
+ {
+ bReturn = WriteFile(hFile, (PBYTE)pszAnsi, lstrlenW(pszAnsi)*sizeof(char), &dwWritten, NULL);
+ mir_free(pszAnsi);
+ }
+
+ } else {
+ bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlen(pszBuf)*sizeof(WCHAR), &dwWritten, NULL);
+ }
+
+ #else
+
+ if (bIsUnicodeFile)
+ {
+ WCHAR* pszUnicode = mir_utf8encodeW(pszBuf);
+ if (pszUnicode)
+ {
+ bReturn = WriteFile(hFile, (PBYTE)pszUnicode, lstrlenW(pszUnicode)*sizeof(WCHAR), &dwWritten, NULL);
+ mir_free(pszUnicode);
+ }
+
+ } else {
+ bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlen(pszBuf)*sizeof(TCHAR), &dwWritten, NULL);
+ }
+
+ #endif
+
+ //SetEndOfFile(hFile);
+ mir_free(pszBuf);
+ }
+
+
+ } // ##### format end #####
+
+ CloseHandle(hFile);
+ SLFreeList(pslVariablesFromTo);
+
+ // Enable "show log" button if log was created while window open
+ if (IsWindow(hwndSpamFilterOpt))
+ PostMessage(hwndSpamFilterOpt, SFM_VALIDATE_LOGFILENAME, 0, 0);
+
+ return bReturn;
+} \ No newline at end of file
diff --git a/spamfilter/logfile.h b/spamfilter/logfile.h
new file mode 100644
index 0000000..265b534
--- /dev/null
+++ b/spamfilter/logfile.h
@@ -0,0 +1,28 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_LOGFILE_H__
+#define SF_LOGFILE_H__ 1
+
+// -- Prototypes
+BOOL WriteToLogFile(const char* pszMsgTypeSection, const char* pszMsgTypeName, const WCHAR* pszUserName, const WCHAR* pszMsgContent, STRINGLIST* pslRecognition, DWORD dwResult);
+
+#endif // SF_LOGFILE_H__
diff --git a/spamfilter/m_popup.h b/spamfilter/m_popup.h
new file mode 100644
index 0000000..7efad50
--- /dev/null
+++ b/spamfilter/m_popup.h
@@ -0,0 +1,338 @@
+/*
+===============================================================================
+ PopUp plugin
+Plugin Name: PopUp
+Plugin authors: Luca Santarelli aka hrk (hrk@users.sourceforge.net)
+ Victor Pavlychko aka zazoo (nullbie@gmail.com)
+===============================================================================
+The purpose of this plugin is to give developers a common "platform/interface"
+to show PopUps. It is born from the source code of NewStatusNotify, another
+plugin I've made.
+
+Remember that users *must* have this plugin enabled, or they won't get any
+popup. Write this in the requirements, do whatever you wish ;-)... but tell
+them!
+===============================================================================
+*/
+
+#ifndef M_POPUP_H
+#define M_POPUP_H
+
+/*
+NOTE! Since Popup 1.0.1.2 there is a main meun group called "PopUps" where I
+have put a "Enable/Disable" item. You can add your own "enable/disable" items
+by adding these lines before you call MS_CLIST_ADDMAINMENUITEM:
+mi.pszPopUpName = Translate("PopUps");
+mi.position = 0; //You don't need it and it's better if you put it to zero.
+*/
+
+#define MAX_CONTACTNAME 2048
+#define MAX_SECONDLINE 2048
+
+// This is the basic data you'll need to fill and pass to the service function.
+typedef struct
+{
+ HANDLE lchContact; // Handle to the contact, can be NULL (main contact).
+ HICON lchIcon; // Handle to a icon to be shown. Cannot be NULL.
+ union
+ {
+ char lptzContactName[MAX_CONTACTNAME]; // This is the contact name or the first line in the plugin. Cannot be NULL.
+ char lpzContactName[MAX_CONTACTNAME];
+ };
+ union
+ {
+ char lptzText[MAX_SECONDLINE]; // This is the second line text. Users can choose to hide it. Cannot be NULL.
+ char lpzText[MAX_SECONDLINE];
+ };
+ COLORREF colorBack; // COLORREF to be used for the background. Can be NULL, default will be used.
+ COLORREF colorText; // COLORREF to be used for the text. Can be NULL, default will be used.
+ WNDPROC PluginWindowProc; // Read below. Can be NULL; default will be used.
+ void * PluginData; // Read below. Can be NULL.
+} POPUPDATA, * LPPOPUPDATA;
+
+// Extended popup data
+typedef struct
+{
+ HANDLE lchContact;
+ HICON lchIcon;
+ union
+ {
+ char lptzContactName[MAX_CONTACTNAME];
+ char lpzContactName[MAX_CONTACTNAME];
+ };
+ union
+ {
+ char lptzText[MAX_SECONDLINE];
+ char lpzText[MAX_SECONDLINE];
+ };
+ COLORREF colorBack;
+ COLORREF colorText;
+ WNDPROC PluginWindowProc;
+ void * PluginData;
+ int iSeconds; // Custom delay time in seconds. -1 means "forever", 0 means "default time".
+ char cZero[16]; // Some unused bytes which may come useful in the future.
+} POPUPDATAEX, *LPPOPUPDATAEX;
+
+// Unicode version of POPUPDATAEX
+typedef struct
+{
+ HANDLE lchContact;
+ HICON lchIcon;
+ union
+ {
+ WCHAR lptzContactName[MAX_CONTACTNAME];
+ WCHAR lpwzContactName[MAX_CONTACTNAME];
+ };
+ union
+ {
+ WCHAR lptzText[MAX_CONTACTNAME];
+ WCHAR lpwzText[MAX_CONTACTNAME];
+ };
+ COLORREF colorBack;
+ COLORREF colorText;
+ WNDPROC PluginWindowProc;
+ void * PluginData;
+ int iSeconds;
+ char cZero[16];
+} POPUPDATAW, *LPPOPUPDATAW;
+
+#if defined(_UNICODE) || defined(UNICODE)
+ typedef POPUPDATAW POPUPDATAT;
+ typedef LPPOPUPDATAW LPPOPUPDATAT;
+#else
+ typedef POPUPDATAEX POPUPDATAT;
+ typedef LPPOPUPDATAEX LPPOPUPDATAT;
+#endif
+
+/* PopUp/AddPopup
+Creates, adds and shows a popup, given a (valid) POPUPDATA structure pointer.
+
+wParam = (WPARAM)(*POPUPDATA)PopUpDataAddress
+lParam = 0
+
+Returns: > 0 on success, 0 if creation went bad, -1 if the PopUpData contained unacceptable values.
+NOTE: it returns -1 if the PopUpData was not valid, if there were already too many popups, if the module was disabled.
+Otherwise, it can return anything else...
+
+Popup Plus 2.0.4.0+
+You may pass additional creation flags via lParam:
+ APF_RETURN_HWND ....... function returns handle to newly created popup window (however this calls are a bit slower)
+ APF_CUSTOM_POPUP ...... new popup is created in hidden state and doesn't obey to popup queue rules.
+ you may control it via UM_* messages and custom window procedure
+*/
+#define APF_RETURN_HWND 0x1
+#define APF_CUSTOM_POPUP 0x2
+
+#define MS_POPUP_ADDPOPUP "PopUp/AddPopUp"
+static int __inline PUAddPopUp(POPUPDATA* ppdp) {
+ return CallService(MS_POPUP_ADDPOPUP, (WPARAM)ppdp,0);
+}
+
+#define MS_POPUP_ADDPOPUPEX "PopUp/AddPopUpEx"
+static int __inline PUAddPopUpEx(POPUPDATAEX* ppdp) {
+ return CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)ppdp,0);
+}
+
+#define MS_POPUP_ADDPOPUPW "PopUp/AddPopUpW"
+static int __inline PUAddPopUpW(POPUPDATAW* ppdp) {
+ return CallService(MS_POPUP_ADDPOPUPW, (WPARAM)ppdp,0);
+}
+
+#if defined(_UNICODE) || defined(UNICODE)
+ #define MS_POPUP_ADDPOPUPT MS_POPUP_ADDPOPUPW
+ #define PUAddPopUpT PUAddPopUpW
+#else
+ #define MS_POPUP_ADDPOPUPT MS_POPUP_ADDPOPUPEX
+ #define PUAddPopUpT PUAddPopUpEx
+#endif
+
+
+/* PopUp/GetContact
+Returns the handle to the contact associated to the specified PopUpWindow.
+
+wParam = (WPARAM)(HWND)hPopUpWindow
+lParam = 0;
+
+Returns: the HANDLE of the contact. Can return NULL, meaning it's the main contact. -1 means failure.
+*/
+#define MS_POPUP_GETCONTACT "PopUp/GetContact"
+static HANDLE __inline PUGetContact(HWND hPopUpWindow) {
+ return (HANDLE)CallService(MS_POPUP_GETCONTACT, (WPARAM)hPopUpWindow,0);
+}
+
+/* PopUp/GetPluginData
+Returns custom plugin date associated with popup
+
+wParam = (WPARAM)(HWND)hPopUpWindow
+lParam = (LPARAM)(PLUGINDATA*)PluginDataAddress;
+
+Returns: the address of the PLUGINDATA structure. Can return NULL, meaning nothing was given. -1 means failure.
+
+IMPORTANT NOTE: it doesn't seem to work if you do:
+CallService(..., (LPARAM)aPointerToAStruct);
+and then use that struct.
+Do this, instead:
+aPointerToStruct = CallService(..., (LPARAM)aPointerToAStruct);
+and it will work. Just look at the example I've written above (PopUpDlgProc).
+
+*/
+#define MS_POPUP_GETPLUGINDATA "PopUp/GetPluginData"
+static void __inline * PUGetPluginData(HWND hPopUpWindow) {
+ long * uselessPointer = NULL;
+ return (void*)CallService(MS_POPUP_GETPLUGINDATA,(WPARAM)hPopUpWindow,(LPARAM)uselessPointer);
+}
+
+/* PopUp/IsSecondLineShown
+Checks if second line is enable
+
+wParam = 0
+lParam = 0
+
+Returns: 0 if the user has chosen not to have the second line, 1 if he choose to have the second line.
+*/
+#define MS_POPUP_ISSECONDLINESHOWN "PopUp/IsSecondLineShown"
+static BOOL __inline PUIsSecondLineShown() {
+ return (BOOL)CallService(MS_POPUP_ISSECONDLINESHOWN,0,0);
+}
+
+/* PopUp/Query
+
+Requests an action or an answer from PopUp module.
+
+wParam = (WPARAM)wpQuery
+
+returns 0 on success, -1 on error, 1 on stupid calls ;-)
+*/
+
+#define PUQS_ENABLEPOPUPS 1 // returns 0 if state was changed, 1 if state wasn't changed
+#define PUQS_DISABLEPOPUPS 2 // " "
+#define PUQS_GETSTATUS 3 //Returns 1 (TRUE) if popups are enabled, 0 (FALSE) if popups are disabled.
+
+#define MS_POPUP_QUERY "PopUp/Query"
+
+/* UM_FREEPLUGINDATA
+Process this message if you have allocated your own memory. (i.e.: POPUPDATA.PluginData != NULL)
+
+wParam = 0
+lParam = 0
+*/
+#define UM_FREEPLUGINDATA (WM_USER + 0x0200)
+
+/* UM_DESTROYPOPUP
+Send this message when you want to destroy the popup, or use the function below.
+
+wParam = 0
+lParam = 0
+*/
+#define UM_DESTROYPOPUP (WM_USER + 0x0201)
+static int __inline PUDeletePopUp(HWND hWndPopUp) {
+ return (int)SendMessage(hWndPopUp, UM_DESTROYPOPUP,0,0);
+}
+
+/* UM_INITPOPUP
+This message is sent to the PopUp when its creation has been finished, so POPUPDATA (and thus your PluginData) is reachable.
+Catch it if you needed to catch WM_CREATE or WM_INITDIALOG, which you'll never ever get in your entire popup-life.
+Return value: if you process this message, return 0. If you don't process it, return 0. Do whatever you like ;-)
+
+wParam = (WPARAM)(HWND)hPopUpWindow (this is useless, you get message inside your popup window)
+lParam = 0
+*/
+#define UM_INITPOPUP (WM_USER + 0x0202)
+
+/* PopUp/Changetext
+Changes the text displayed in the second line of the popup.
+
+wParam = (WPARAM)(HWND)hPopUpWindow
+lParam = (LPARAM)(char*)lpzNewText
+
+returns: > 0 for success, -1 for failure, 0 if the failure is due to second line not being shown. (but you could call
+PUIsSecondLineShown() before changing the text...)
+*/
+#define MS_POPUP_CHANGETEXT "PopUp/Changetext"
+static int __inline PUChangeText(HWND hWndPopUp, LPCTSTR lpzNewText) {
+ return (int)CallService(MS_POPUP_CHANGETEXT, (WPARAM)hWndPopUp, (LPARAM)lpzNewText);
+}
+
+#define MS_POPUP_CHANGETEXTW "PopUp/ChangetextW"
+static int __inline PUChangeTextW(HWND hWndPopUp, LPCWSTR lpwzNewText) {
+ return (int)CallService(MS_POPUP_CHANGETEXTW, (WPARAM)hWndPopUp, (LPARAM)lpwzNewText);
+}
+
+#if defined(_UNICODE) || defined(UNICODE)
+ #define MS_POPUP_CHANGETEXTT MS_POPUP_CHANGETEXTW
+ #define PUChangeTextT PUChangeTextW
+#else
+ #define MS_POPUP_CHANGETEXTT MS_POPUP_CHANGETEXT
+ #define PUChangeTextT PUChangeText
+#endif
+
+/* PopUp/Change
+Changes the entire popup
+
+wParam = (WPARAM)(HWND)hPopUpWindow
+lParam = (LPARAM)(POPUPDATAEX*)newData
+*/
+#define MS_POPUP_CHANGE "PopUp/Change"
+static int __inline PUChange(HWND hWndPopUp, POPUPDATAEX *newData) {
+ return (int)CallService(MS_POPUP_CHANGE, (WPARAM)hWndPopUp, (LPARAM)newData);
+}
+
+#define MS_POPUP_CHANGEW "PopUp/ChangeW"
+static int __inline PUChangeW(HWND hWndPopUp, POPUPDATAW *newData) {
+ return (int)CallService(MS_POPUP_CHANGE, (WPARAM)hWndPopUp, (LPARAM)newData);
+}
+
+#if defined(_UNICODE) || defined(UNICODE)
+ #define MS_POPUP_CHANGET MS_POPUP_CHANGEW
+ #define PUChangeT PUChangeW
+#else
+ #define MS_POPUP_CHANGET MS_POPUP_CHANGE
+ #define PUChangeT PUChange
+#endif
+
+/* UM_CHANGEPOPUP
+This message is triggered by Change/ChangeText services. You also may post it directly :)
+
+wParam = Modification type
+lParam = value of type defined by wParam
+*/
+
+#define CPT_TEXT 1 // lParam = (char *)text
+#define CPT_TEXTW 2 // lParam = (WCHAR *)text
+#define CPT_TITLE 3 // lParam = (char *)title
+#define CPT_TITLEW 4 // lParam = (WCHAR *)title
+#define CPT_DATA 5 // lParam = (POPUPDATA *)data
+#define CPT_DATAEX 6 // lParam = (POPUPDATAEX *)data
+#define CPT_DATAW 7 // lParam = (POPUPDATAW *)data
+
+#define UM_CHANGEPOPUP (WM_USER + 0x0203)
+
+#if defined(_UNICODE) || defined(UNICODE)
+ #define CPT_TEXTT CPT_TEXTW
+ #define CPT_TITLET CPT_TITLEW
+ #define CPT_DATAT CPT_DATAW
+#else
+ #define CPT_TEXTT CPT_TEXT
+ #define CPT_TITLET CPT_TITLE
+ #define CPT_DATAT CPT_DATA
+#endif
+
+/* PopUp/ShowMessage
+This is mainly for developers.
+Shows a warning message in a PopUp. It's useful if you need a "MessageBox" like function, but you don't want a modal
+window (which will interfere with a DialogProcedure. MessageBox steals focus and control, this one not.
+
+wParam = (char *)lpzMessage
+lParam = 0;
+
+Returns: 0 if the popup was shown, -1 in case of failure.
+*/
+#define SM_WARNING 0x01 //Triangle icon.
+#define SM_NOTIFY 0x02 //Exclamation mark icon.
+#define MS_POPUP_SHOWMESSAGE "PopUp/ShowMessage"
+
+static int __inline PUShowMessage(char *lpzText, BYTE kind) {
+ return (int)CallService(MS_POPUP_SHOWMESSAGE, (WPARAM)lpzText,(LPARAM)kind);
+}
+
+#endif // __m_popup_h__
diff --git a/spamfilter/m_spamfilter.h b/spamfilter/m_spamfilter.h
new file mode 100644
index 0000000..a8b48db
--- /dev/null
+++ b/spamfilter/m_spamfilter.h
@@ -0,0 +1,826 @@
+/*
+
+"Spam Filter"-Plugin for
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_SPAMFILTER_H__
+#define M_SPAMFILTER_H__ 1
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+// Spam Filter Version: 2.5.2.3
+// See "spamcheck.c" out of the source code for an example on how to use the spam checking services.
+
+// The functionality and the API of Spam Filter is quite complex
+// and not easy to describe or explain.
+// If you have any question or extension whishes please send me an e-mail:
+// hrathh at users.sourceforge.net
+
+// Notes:
+// * All services are fully thread-safe, you can call everything from any thread
+// (If there are problems with it it is a bug :-) )
+// * Unicode parameters can also be passed to the ANSI version of the plugin
+// and the other way round. The strings get converted as they are needed
+// using automatic two-way translation.
+// Unicode support was added in v2.5.0.0
+
+
+// -------------------------------------------
+// -- Getting handles ------------------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_GETHANDLE "SpamFilter/GetHandle"
+
+// Description:
+// -------------
+// Gets a specified icon/window handle.
+
+// Parameters:
+// -------------
+// wParam = (UINT)uHandleType (see below)
+// lParam = 0
+
+// Possible values for wParam:
+#define SFHT_HWND_PLUGIN_OPTIONS 2 // HWND of the "Spam Filter" options dialog (if it is loaded; else NULL)
+#define SFHT_HICON_SPAM 3 // HICON of the spam icon (small): The returned HICON must be DestroyIcon()ed.
+
+// Added in v2.0.2.0:
+#define SFHT_HICON_SPAM_LARGE 4 // HICON of the spam icon (large): The returned HICON must be DestroyIcon()ed.
+#define SFHT_HICON_SPAM_LAYER 5 // HICON of the spam layer icon (small): The returned HICON must be DestroyIcon()ed.
+
+// Added in v2.1.0.0:
+#define SFHT_HWND_ADVERTISMENT_FILTER 6 // HWND of the "Advertisment Filter" settings dialog (if it is opened; else NULL)
+#define SFHT_HWND_ROBOT_FILTER 7 // HWND of the "Robot Filter" settings dialog (if it is opened; else NULL)
+#define SFHT_HWND_DISLIKEDMESSAGES_FILTER 8 // HWND of the "Disliked Messages Filter" settings dialog (if it is opened; else NULL)
+
+// Added in v2.5.0.0:
+#define SFHT_HWND_SPAMDEFINITIONS_INFO 9 // HWND of the "Spam Definitions" info dialog (if it is opened; else NULL)
+#define SFHT_HWND_SPAMMERS_INFO 10 // HWND of the Spammers info dialog (if it is opened; else NULL)
+
+#define SFHT_BOOL_IS_UNICODE 0 // No handle, returns TRUE if Spam Filter is installed as Unicode version, FALSE otherwise
+ // (You probably never need to call this since Spam Filter performs automatic two-way translation
+ // when a service is called)
+
+
+// Note: Icons are taken directly from resources, they are not skinned by user
+
+// Return Values:
+// --------------
+// Returns the specified handle value: HMODULE, HWND, ICON
+//
+// Note:
+// If a handle is not available (or if it was removed) the function will
+// return NULL.
+// When you are using this service you need to check always for the
+// NULL return value.
+
+
+
+// -------------------------------------------
+// -- Activating/Deactivating the filters ----
+// -------------------------------------------
+
+#define MS_SPAMFILTER_CHANGEFILTERACTIVATION "SpamFilter/ChangeFilterActivation"
+
+// Description:
+// -------------
+// Provides the possibility to enable or disable the spam filters.
+// Also updates the checkboxes in the spam filter options if the option dialog is opened.
+
+// Parameters:
+// -------------
+// wParam = (UINT)uFilterType (For possible values see below)
+// lParam = (BOOL)bNewState (new activation state of filter)
+
+// Possible filter type values (dwFilterType):
+#define SFT_ADVERTISMENT_FILTER 1
+#define SFT_DISLIKEDMESSAGES_FILTER 2
+#define SFT_ROBOT_FILTER 3
+
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -------------------------------------------
+// -- Showing configure dialogs --------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_SHOWFILTERDIALOG "SpamFilter/ShowFilterDialog"
+
+// Description:
+// -------------
+// Shows the configure dialog of the specified filter.
+
+// Parameters:
+// -------------
+// wParam = (HWND)hwndParent (Can be NULL)
+// lParam = (UINT)uFilterType (For possible values see above, SFT_*)
+// -> since 2.0.2.0: If this is zero then the options dialog will be opened pointing to the Spam Filter options.
+// Note:
+// If hwndParent is not NULL the function will not return
+// until the configure dialog is closed.
+
+// Since 2.0.2.0:
+#define SFTEX_OPTIONS_PAGE 0
+
+// Since 2.5.0.0:
+// Combine the following flag with the lParam/uFilterType parameter
+// to show the corresponding Spam Definitions info dialog instead
+// of the settings dialog:
+#define SFTEXF_SPAMDEFINITIONS_INFO 0x2000
+// Note: SFTEXF_SPAMDEFINITIONS_INFO can't be combined with SFT_ROBOT_FILTER
+
+// Combine the following flag with the lParam/uFilterType of 0 parameter
+// to show the Spammers dialog instead of the settings dialog:
+#define SFTEXF_SPAMMERS_INFO 0x4000
+// Note: SFTEXF_SPAMMERS_INFO can't be combined with SFT_*
+
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+// Since v2.1.0.0:
+// If the specified settings window is already opened it will be
+// brought to front (reactivated) instead (returns success though).
+// This might be changed however in the future.
+
+// Changed behaviour since v2.1.0.0:
+// If hwndParent was not set then the function returned the handle to the new window.
+// Now the function returns 0 to indicate success instead.
+
+
+// -------------------------------------------
+// -- Adding message types -------------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_REGISTERMESSAGETYPE "SpamFilter/MessageTypes/Register"
+
+// Description:
+// -------------
+// Adds a new message type which can be checked for spam.
+// Associated event: ME_SPAMFILTER_PREREGISTERMESSAGETYPE
+
+// Parameters:
+// -------------
+// wParam = 0
+// lParam = (MESSAGETYPEDESC*)mtd (see below)
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// Already registered message types are:
+// (With pszSection set to the installed network protocol names, e.g. "ICQ")
+#define SFMT_AUTHREQUEST "Auth"
+#define SFMT_ADDED "Added" // since v2.1.0.0
+#define SFMT_MESSAGE "Msg"
+#define SFMT_URL "URL"
+#define SFMT_FILE "File"
+#define SFMT_CONTACTS "Contacts"
+
+// MESSAGETYPEDESC structure:
+typedef struct {
+ int cbSize; // Set to sizeof(MESSAGETYPEDESC), in bytes
+ char* pszSection; // Section of message name for database e.g. protocol name (Can not be NULL)
+ // -> should be short, no-spaces, english and untranslated
+ union {
+ char* pszSectionDescription; // Readable name of section e.g. protocol name (can be NULL)
+ WCHAR* ptszSectionDescription; // Ignored if section existed previously
+ WCHAR* pwszSectionDescription;
+ };
+
+ HICON hSectionIcon; // Icon to show for section e.g. protocol icon (can be NULL)
+ // -> Can be destroyed after service returns
+ // Ignored if section existed previously
+ char* pszName; // Name to refer to message type when spam checking and in db (Can not be NULL)
+ // -> should be short, no-spaces, english and untranslated
+
+ union {
+ char* pszDescription; // Description for options dialog (can be NULL)
+ WCHAR* ptszDescription;
+ WCHAR* pwszDescription;
+ };
+
+ HICON hIcon; // EITHER: icon handle representing the message type OR: one of mirandas skin constants with a negative sign, e.g (HICON)-SKINICON_EVENT_MESSAGE, same as for LoadSkinnedIcon() -> can be NULL and can also be a normal icon handle (HICON)
+ // -> Can be destroyed after service returns
+ DWORD dwFlags; // Flags for the message type (for possible flags see below)
+
+ // Added in v2.5.0.0:
+ int iSectionPosition; // Approx position number for the section in the options list, lower numbers are nearer to the top (Can be 0 to be sorted alphabetically)
+ // Ignored if section existed previously (or if pszSection is NULL)
+ int iPosition; // Approx position number for the item in the options list, lower numbers are nearer to the top (Can be 0 to be sorted alphabetically)
+} MESSAGETYPEDESC;
+
+
+// Possible MESSAGETYPEDESC flags:
+#define MTDF_DEFAULTDISABLED 0x00000001 // Is disabled by default
+#define MTDF_HIDDEN 0x00000002 // Does not get shown on the options page
+
+// Since v2.5.0.0
+#define MTDF_UNICODE 0x00000004 // pszSectionDescription and pszDescription are Unicode strings
+
+
+// MESSAGETYPEDESC struct was enhanced in v2.5.0.0:
+#define MESSAGETYPEDESC_V2000_SIZE (((int)((LPBYTE)(&((MESSAGETYPEDESC*)0)->dwFlags) - ((LPBYTE)((MESSAGETYPEDESC*)0)))) + sizeof(((MESSAGETYPEDESC*)0)->dwFlags))
+
+
+#ifndef SPAMFILTER_NOHELPER
+static int __inline SFRegisterMessageType(const char* section, const WCHAR* sectionDesc, HICON sectionIcon, const char* name, const WCHAR* desc, HICON icon, int pos, DWORD flags)
+{
+ MESSAGETYPEDESC mtd;
+ mtd.cbSize = sizeof(MESSAGETYPEDESC);
+ mtd.dwFlags = flags|MTDF_UNICODE;
+ mtd.pszSection = (char*)section;
+ mtd.ptszSectionDescription = (WCHAR*)sectionDesc;
+ mtd.hSectionIcon = sectionIcon;
+ mtd.iSectionPosition = 0; // not really needed
+ mtd.pszName = (char*)name;
+ mtd.ptszDescription = (WCHAR*)desc;
+ mtd.hIcon = icon;
+ mtd.iPosition = pos;
+ return CallService(MS_SPAMFILTER_REGISTERMESSAGETYPE, 0, (LPARAM)&mtd);
+}
+#endif
+
+// -------------------------------------------
+// -- Modify new added message type (Event) --
+// -------------------------------------------
+
+#define ME_SPAMFILTER_PREREGISTERMESSAGETYPE "SpamFilter/MessageTypes/PreRegister"
+
+// Description:
+// -------------
+// Gets fired before a new message type is added.
+//
+// This is meant for asking other plugins if the specified default data is ok.
+// Other plugins (especially protocols) can disable a message type if either sending spam
+// is not possible for those protocols or if spam checking would cause unwanted side-effects.
+
+// Note: Works as it does now since v2.1.1.0
+
+// Warning: Most elements of the MESSAGETYPEDESC structure can also be NULL.
+// However, the structure was checked for errors before this events gets called.
+
+// Parameters:
+// -----------------
+// wParam = 0 (same wParam as used for MS_SPAMFILTER_REGISTERMESSAGETYPE)
+// lParam = (MESSAGETYPEDESC*)mtd (see above, contents can be modified)
+
+// Return Values:
+// -----------------
+// Returning 0 on this event will accept the message type to be added.
+// Returning 1 will prevent the the message type to be added.
+
+
+// -------------------------------------------
+// -- Uninstalling a message type ------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_REMOVEMESSAGETYPE "SpamFilter/MessageTypes/Remove"
+
+// Description:
+// -------------
+// Uninstalls a specifed message type. It removes the database setting related to the given message type.
+// Should only be used for uninstalling purposes (e.g in uninstall function of PluginUninstaller)
+
+// Parameters:
+// -------------
+// wParam = (char*)pszMsgTypeName (Message type name; You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before)
+// lParam = (char*)pszMsgTypeSection (The section of the message type string; You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before)
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -------------------------------------------
+// -- Message type activation ----------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_ISMESSAGETYPEACTIVATED "SpamFilter/MessageTypes/IsActivated"
+
+// Description:
+// -------------
+// Returns if a specified message type is activated or not.
+
+// Parameters:
+// -------------
+// wParam = (char*)pszMsgTypeName (Message type name; You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before)
+// lParam = (char*)pszMsgTypeSection (The section of the message type string; You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before)
+
+// Return Values:
+// --------------
+// Returns TRUE if the message type is activated and FALSE otherwise.
+
+
+// -------------------------------------------
+// -- Reset Auto-ignore counters -------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_RESETAUTOIGNORE "SpamFilter/AutoIgnore/Reset"
+
+// Description:
+// -------------
+// Resets the list in which the total number of spam sent by
+// each user is kept.
+// The list is necessary to determine when a particular user reached the
+// allowed spam messages limit.
+
+// Parameters:
+// -------------
+// wParam = (BOOL)bOnlyCount (if TRUE the service will only return the user count without doing reset)
+// lParam = 0
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -----------------------------------------
+// -- Spam Checking ------------------------
+// -----------------------------------------
+
+#define MS_SPAMFILTER_ADVERTISMENTCHECK "SpamFilter/AdvertismentCheck"
+#define MS_SPAMFILTER_DISLIKEDMESSAGESCHECK "SpamFilter/DislikedMessagesCheck"
+#define MS_SPAMFILTER_ROBOTCHECK "SpamFilter/RobotCheck"
+
+// Description:
+// -------------
+// These services provide the possibility to check a specified text for contained spam.
+// The function checks for everything that is activated in the plugin's options.
+
+// Note: These functions work as they do now since v2.1.0.0
+
+// Parameters:
+// -------------
+// wParam = (SPAMCHECKDATA*)scd
+// lParam = 0
+
+// SPAMCHECKDATA structure:
+typedef struct {
+ int cbSize; // Set to sizeof(SPAMCHECKDATA)
+
+ char* pszMsgTypeSection; // The section of the message type string: You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before
+ char* pszMsgTypeName; // Description of the message: You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before
+
+ // Pointer to the text of a message which is checked for spam. Can not be NULL.
+ // (To use Unicode, you need to set the flag SCDF_UNICODE)
+ union {
+ char* pszMsgText; // ANSI
+ WCHAR* ptszMsgText; // Unicode or ANSI
+ WCHAR* pwszMsgText; // Unicode
+ };
+
+ DWORD dwFlags; // Flags for the spam checking. (For more details see below.)
+
+ union {
+ HANDLE hContact; // Handle to the sending contact (use pszUserName instead if no hContact is available)
+ char* pszUserName; // Name of the user, e.g. nick (set SCDF_NO_CONTACT in the flags to use this parameter)
+ WCHAR* ptszUserName; // -"- (Unicode or ANSI)
+ WCHAR* pwszUserName; // -"- (Unicode)
+ // -> UserName should be as unique as possible (To use Unicode you need to specify SCDF_UNICODE)
+ };
+} SPAMCHECKDATA;
+
+
+// Possible flags in the SPAMCHECKDATA structure:
+#define SCDF_NO_NOTIFY 0x00000001 // Don't show a popup, play sound or log to file if the message is spam.
+#define SCDF_NO_CANCEL 0x00000002 // Do check even if user pressed control key to avoid the spam check manually
+#define SCDF_NO_AUTOIGNORE 0x00000004 // Do not use the AutoIgnore feature for this check
+
+#define SCDF_NO_CONTACT 0x00000008 // Use pszUserName instead of hContact of union
+
+// Since v2.5.0.0:
+#define SCDF_UNICODE 0x00000010 // Specify this flag if associated message text and/or user name is Unicode
+
+
+// Return Values:
+// --------------
+// Possible return flags:
+#define SFF_MARKREAD 0x00000010 // Flag
+#define SFF_DELETE 0x00000020 // Flag
+#define SFF_IGNORE 0x00000040 // Flag
+#define SFF_SENDMSG_NOTIFY 0x00000100 // Flag, Only MS_SPAMFILTER_DISLIKEDMESSAGESCHECK
+#define SFF_SENDMSG_INSTRUCTION 0x00000200 // Flag, Only MS_SPAMFILTER_ROBOTCHECK
+#define SFF_SENDMSG_CONFIRMATION 0x00000400 // Flag, Only MS_SPAMFILTER_ROBOTCHECK
+#define SFF_TESTPASSED 0x00000080 // Flag, Only MS_SPAMFILTER_ROBOTCHECK
+#define SFF_ISSPAM 0x00000002 // Flag
+#define SFF_ISNORMAL 0x00000001 // Flag
+
+// Since 2.5.2.0:
+#define SFF_HIDE 0x00001000 // Flag, Only MS_SPAMFILTER_ROBOTCHECK
+
+
+// -------------------------------------------
+// -- Getting additional Check Info ----------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_GETSPAMCHECKINFO "SpamFilter/GetSpamCheckInfo"
+
+// Description:
+// -------------
+// Gets data needed for the processing of spam messages.
+
+// Parameters:
+// -------------
+// wParam = (UINT)uDataType (For possible values see below)
+// lParam = depends on wParam (see below)
+
+// Possible values for lParam:
+
+// TRUE/FALSE: if the specified filter is enabled
+#define SFSCI_ACTIVATION_FILTER 1 // lParam = uFilterType (For possible values see above)
+
+// Strings: Get the message texts specified by the user
+// ANSI
+#define SFSCI_MSGTEXT_NOTIFYA 2 // lParam = (SPAMCHECKDATA*)scd, scd->hContact (or scd->pszUserName) and scd->pszMsgText (and scd->cbSize) should be filled in.
+#define SFSCI_MSGTEXT_INSTRUCTIONA 3 // lParam: see SFSCI_MSGTEXT_NOTIFYA
+#define SFSCI_MSGTEXT_CONFIRMATIONA 4 // lParam: see SFSCI_MSGTEXT_NOTIFYA
+
+// Unicode (since v2.5.0.0)
+#define SFSCI_MSGTEXT_NOTIFYW 6 // lParam = (SPAMCHECKDATA*)scd, scd->hContact (or scd->pwszUserName) and scd->pwszMsgText (and scd->cbSize) should be filled in.
+#define SFSCI_MSGTEXT_INSTRUCTIONW 7 // lParam: see SFSCI_MSGTEXT_NOTIFYW
+#define SFSCI_MSGTEXT_CONFIRMATIONW 8 // lParam: see SFSCI_MSGTEXT_NOTIFYW
+
+// ANSI/Unicode
+#if defined(_UNICODE) || defined(UNICODE)
+ #define SFSCI_MSGTEXT_NOTIFY SFSCI_MSGTEXT_NOTIFYW
+ #define SFSCI_MSGTEXT_INSTRUCTION SFSCI_MSGTEXT_INSTRUCTIONW
+ #define SFSCI_MSGTEXT_CONFIRMATION SFSCI_MSGTEXT_CONFIRMATIONW
+#else
+ #define SFSCI_MSGTEXT_NOTIFY SFSCI_MSGTEXT_NOTIFYA
+ #define SFSCI_MSGTEXT_INSTRUCTION SFSCI_MSGTEXT_INSTRUCTIONA
+ #define SFSCI_MSGTEXT_CONFIRMATION SFSCI_MSGTEXT_CONFIRMATIONA
+#endif
+
+// TRUE/FALSE: if the above message text should be added to history.
+#define SFSCI_ADD_TO_HISTORY 5 // lParam = SFSCI_MSGTEXT_NOTIFY(A/W), SFSCI_MSGTEXT_INSTRUCTION(A/W) or SFSCI_MSGTEXT_CONFIRMATION(A/W)
+
+
+// Return Values:
+// --------------
+// SFSCI_ACTIVATION_FILTER -> (BOOL)TRUE/FALSE
+// SFSCI_DELAY_TIME_REPLY -> (BOOL)TRUE/FALSE
+
+// SFSCI_MSGTEXT_NOTIFYA -> (char*) free with miranda_sys_free() of "m_system.h", (all %vars% in the text are already resolved)
+// SFSCI_MSGTEXT_INSTRUCTIONA -> see SFSCI_MSGTEXT_NOTIFYA
+// SFSCI_MSGTEXT_CONFIRMATIONA -> see SFSCI_MSGTEXT_NOTIFYA
+
+// SFSCI_MSGTEXT_NOTIFYW -> (WCHAR*) free with miranda_sys_free() of "m_system.h", (all %vars% in the text are already resolved)
+// SFSCI_MSGTEXT_INSTRUCTIONW -> see SFSCI_MSGTEXT_NOTIFYW
+// SFSCI_MSGTEXT_CONFIRMATIONW -> see SFSCI_MSGTEXT_NOTIFYW
+
+// SFSCI_ADD_TO_HISTORY -> (BOOL)TRUE/FALSE
+
+// returns NULL on error
+
+
+// -------------------------------------------
+// -- Confirm spam check (Event) -------------
+// -------------------------------------------
+
+#define ME_SPAMFILTER_OKTOSPAMCHECK "SpamFilter/OkToSpamCheck"
+
+// Description:
+// -------------
+// Gets fired before the spam check for a message starts.
+
+// Parameters:
+// -----------------
+// wParam = (SPAMCHECKDATA*)scd (Pointer to SPAMCHECKDATA struct)
+// lParam = (UINT)uFilterType (For possible values see above)
+
+// Return Values:
+// -----------------
+// Returning 0 on this event will accept spam checking for the message,
+// Returning 1 will prevent the spam check.
+
+
+// -------------------------------------------
+// -- Confirm spam detection (Event) ---------
+// -------------------------------------------
+
+#define ME_SPAMFILTER_OKTOSPAMDETECTION "SpamFilter/OkToSpamDetection"
+
+// Description:
+// -------------
+// Gets fired after a spam message is detected.
+
+// Parameters:
+// -----------------
+// wParam = (SPAMCHECKDATA*)scd (Pointer to SPAMCHECKDATA struct)
+// lParam = (UINT)uFilterType (For possible values see above)
+
+// Return Values:
+// -----------------
+// Returning 0 on this event will accept the detection
+// Returning 1 cause the message not to be seen as spam.
+
+// Note: If you only would like to get the final result hook
+// ME_SPAMFILTER_SPAMRECEIVED instead
+
+
+// -------------------------------------------
+// -- Spam message received (Event) ----------
+// -------------------------------------------
+
+#define ME_SPAMFILTER_SPAMRECEIVED "SpamFilter/SpamReceived"
+
+// Description:
+// -------------
+// Gets fired when a message is recognized as spam.
+
+// Parameters:
+// -----------------
+// wParam = (SPAMCHECKDATA*)scd (Pointer to SPAMCHECKDATA struct)
+// lParam = (UINT)uFilterType (For possible values see above)
+
+// Return Values:
+// -----------------
+// The return value should be 0.
+
+
+
+// -------------------------------------------
+// -- Spam Filter loaded ---------------------
+// -------------------------------------------
+
+#define ME_SPAMFILTER_MODULELOADED "SpamFilter/ModuleLoaded"
+
+// Description:
+// -------------
+// Gets fired when a all parts of the "Spam Filter" were loaded and available
+// to other plugins.
+// Past this event all functionality of the plugin is fully available.
+//
+// The event works quite similar as ME_SYSTEM_MODULESLOADED.
+// You should use ME_SYSTEM_MODULESLOADED in most cases instead. Please do so!
+//
+// (Service added in v2.0.1.0)
+
+// Parameters:
+// -----------------
+// wParam = 0
+// lParam = 0
+
+// Return Values:
+// -----------------
+// The return value should be 0.
+
+
+
+// -------------------------------------------
+// -- Helper: Duplicate SPAMCHECKDATA --------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_COPYSPAMCHECKDATA "SpamFilter/SpamCheckData/Copy"
+
+// Description:
+// -------------
+// Copys all contents of a SPAMCHECKDATA struct (especially strings) to another one
+// using Miranda's Memory Manager Interface (see m_system.h).
+// Use MS_SPAMFILTER_FREESPAMCHECKDATA to free the result when no longer needed.
+
+// Available since 2.5.2.0.
+
+// An entire copy of the SPAMCHECKDATA structure is made at pscd->cbSize.
+// All strings are duplicated.
+// The pscdTo->cbSize member is set to the current size of SPAMCHECKDATA
+// that SpamFilter uses.
+
+// Warning:
+// If you pass a structure that is bigger than the current sizeof(SPAMCHECKDATA)
+// then do not use pointers not contained in the current area because
+// they will not be duplicated (However, you could duplicate them by yourself).
+
+// Parameters:
+// -------------
+// wParam = (SPAMCHECKDATA*)pscdTo
+// lParam = (SPAMCHECKDATA*)pscdFrom
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+
+// -------------------------------------------
+// -- Helper: Free SPAMCHECKDATA -------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_FREESPAMCHECKDATA "SpamFilter/SpamCheckData/Free"
+
+// Description:
+// -------------
+// Frees the memory allocated by MS_SPAMFILTER_COPYSPAMCHECKDATA.
+// using Miranda's Memory Manager Interface (see m_system.h).
+
+// Available since 2.5.2.0.
+
+// Parameters:
+// -------------
+// wParam = (SPAMCHECKDATA*)pscd
+// lParam = 0
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on failure.
+
+
+
+// -------------------------------------------
+// -- Showing error messages -----------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_SHOWERROR "SpamFilter/ShowError"
+
+// Description:
+// -------------
+// Shows a Miranda try balloon tip, popup or message box
+// with the specified error message.
+
+// Parameters:
+// -------------
+// wParam = (UINT)uErrorType (For possible values see below)
+// lParam = 0
+
+// Possible values for wParam:
+#define SFSE_CRITICAL_ERROR 1 // Unspecified critical error occured related to the spam check.
+ // Only use this for situations that may never ever occur.
+ // Please do avoid this if possible.
+
+#define SFSE_SEND_FAILED 2 // Show this error when a sending of SFSCI_MSGTEXT_NOTIFY,
+ // SFSCI_MSGTEXT_INSTRUCTION, SFSCI_MSGTEXT_CONFIRMATION or
+ // SFSCI_MSGTEXT_NOTIFY failed (SFSCI_MSGTEXT_*)
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+
+// -------------------------------------------
+// -- Set contact as spammer (Contact) -------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_CONTACT_SETASSPAMMER "SpamFilter/Contact/SetAsSpammer"
+
+// Description:
+// -------------
+// This service adds a specified user to the spammer list (fully ignored).
+
+// Associated event: ME_SPAMFILTER_CONTACT_SPAMMERSTATECHANGED
+
+// Note: works same as MS_SPAMFILTER_CONTACT_SHOWSETASSPAMMERDIALOG
+// but does not prompt the user.
+
+// Available since v2.5.0.0
+
+// Parameters:
+// -------------
+// wParam = (HANDLE)hContact (Handle to a contact in database. Can't be NULL.)
+// lParam = (DWORD)dwFlags (flags about what should be done, see below)
+
+// Available Flags:
+#define SCASF_NO_NOTIFY 0x00000001 // Prevents logging and playing of sounds
+#define SCASF_USE_ROBOT_SOUND 0x00000002 // play robot sound instead of advertisment sound (only if SCASF_NO_NOTIFY is not set)
+#define SCASF_NO_REMOVE_HISTORY 0x00000004 // history is marked read instead of fully removed
+#define SCASF_NO_DENY_AUTHREQUESTS 0x00000008 // do not deny pending auth requests
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -------------------------------------------
+// -- Show mark spammer dialog (Contact) -----
+// -------------------------------------------
+
+#define MS_SPAMFILTER_CONTACT_SHOWSETASSPAMMERDIALOG "SpamFilter/Contact/ShowSetAsSpammerDlg"
+
+// Description:
+// -------------
+// This service adds a specified user to the spammer list.
+// Before it shows a dialog to let the user confirm the action.
+// For use for example as action for a button on the message dialog.
+
+// Note: This service performs the same tasks as MS_SPAMFILTER_SETCONTACTASSPAMMER
+// to execute the action the user chooses.
+
+// Associated event: ME_SPAMFILTER_CONTACT_SPAMMERSTATECHANGED
+
+// Available since v2.5.0.0
+
+// Parameters:
+// -------------
+// wParam = (HANDLE)hContact (Handle to a contact in database. Can't be NULL.)
+// lParam = (HWND)hwndParent (Handle to the parent window. Can be NULL.)
+
+// Return Values:
+// --------------
+// Returns FALSE if user clicked cancel on the dialog.
+// TRUE if the contact was handled as spammer.
+
+
+// Deprecated in v2.5.0.0:
+// The service MS_SPAMFILTER_SETSPAMMANUALLY has been deprecated.
+// New plugins should use MS_SPAMFILTER_CONTACT_SETASSPAMMERDLG instead.
+#define MS_SPAMFILTER_SETSPAMMANUALLY "SpamFilter/SetSpamManually"
+
+
+// Return Values:
+// --------------
+// Returns 0 on success, nozero otherwise.
+
+
+// -------------------------------------------
+// -- Test if contact is spammer (Contact) ---
+// -------------------------------------------
+
+#define MS_SPAMFILTER_CONTACT_ISSPAMMER "SpamFilter/Contact/IsSpammer"
+
+// Description:
+// -------------
+// This service tests if a specified contact is on the spammer list (fully ignored).
+
+// Available since v2.5.0.0
+
+// Parameters:
+// -------------
+// wParam = (HANDLE)hContact (Handle to a contact in database. Can't be NULL.)
+// lParam = 0
+
+// Return Values:
+// --------------
+// Returns TRUE when the contact is on the spammer list, FALSE otherwise.
+
+
+// -------------------------------------------
+// -- Unmarks a contact as spammer (Contact) -
+// -------------------------------------------
+
+#define MS_SPAMFILTER_CONTACT_UNSETSPAMMER "SpamFilter/Contact/UnSetSpammer"
+
+// Description:
+// -------------
+// This service restores a spammer to be again a normal contact (unignored).
+
+// Associated event: ME_SPAMFILTER_CONTACT_SPAMMERSTATECHANGED
+
+// Available since v2.5.0.0
+
+// Parameters:
+// -------------
+// wParam = (HANDLE)hContact (Handle to a contact in database. Can't be NULL.)
+// lParam = 0
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -------------------------------------------
+// -- Spammer state changed (Event) ----------
+// -------------------------------------------
+
+#define ME_SPAMFILTER_CONTACT_SPAMMERSTATECHANGED "SpamFilter/Contact/SpammerStateChanged"
+
+// Description:
+// -------------
+// Gets fired when a a contact gets marked as spammer or unmarked.
+
+// Parameters:
+// -----------------
+// wParam = (HANDLE)hContact
+// lParam = (BOOL)bIsSpammer (new state)
+
+// Return Values:
+// -----------------
+// The return value should be 0.
+
+
+#endif // M_SPAMFILTER_H \ No newline at end of file
diff --git a/spamfilter/m_spamfilter.inc b/spamfilter/m_spamfilter.inc
new file mode 100644
index 0000000..ac371ff
--- /dev/null
+++ b/spamfilter/m_spamfilter.inc
@@ -0,0 +1,757 @@
+{
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2003-2004 Heiko Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+}
+
+{$ifndef M_SPAMFILTER_H}
+{$define M_SPAMFILTER_H}
+
+const
+
+// Spam Filter Version: 2.5.2.3
+// See "spamcheck.c" out of the source code for an example on how to use the spam checking services.
+
+// The functionality and the API of Spam Filter is quite complex
+// and not easy to describe or explain.
+// If you have any question or extension whishes please send me an e-mail:
+// hrathh at users.sourceforge.net
+
+// Notes:
+// * All services are fully thread-safe, you can call everything from any thread
+// (If there are problems with it it is a bug :-) )
+// * Unicode parameters can also be passed to the ANSI version of the plugin
+// and the other way round. The strings get converted as they are needed
+// using automatic two-way translation.
+// Unicode support was added in v2.5.0.0.
+
+
+// -------------------------------------------
+// -- Getting handles ------------------------
+// -------------------------------------------
+
+MS_SPAMFILTER_GETHANDLE = 'SpamFilter/GetHandle';
+
+// Description:
+// -------------
+// Gets a specified icon/window handle.
+
+// Parameters:
+// -------------
+// wParam = (UINT)uHandleType (see below);
+// lParam = 0
+
+// Possible values for wParam:
+SFHT_HWND_PLUGIN_OPTIONS = 2; // HWND of the "Spam Filter" options dialog (if it is loaded; else NULL)
+SFHT_HICON_SPAM = 3; // HICON of the spam icon (small): needs to be destroyed! (DestroyIcon())
+
+// Added in v2.0.2.0:
+SFHT_HICON_SPAM_LARGE = 4; // HICON of the spam icon (large): needs to be destroyed! (DestroyIcon())
+SFHT_HICON_SPAM_LAYER = 5; // HICON of the spam layer icon (small): needs to be destroyed! (DestroyIcon())
+ // -> Icons are taken directly from resources, not skinned by user
+// Added in v2.0.3.0:
+SFHT_HWND_ADVERTISMENT_FILTER = 6; // HWND of the "Advertisment Filter" settings dialog (if it is opened; else NULL)
+SFHT_HWND_ROBOT_FILTER = 7; // HWND of the "Robot Filter" settings dialog (if it is opened; else NULL)
+SFHT_HWND_DISLIKEDMESSAGES_FILTER = 8; // HWND of the "Disliked Messages Filter" settings dialog (if it is opened; else NULL)
+
+// Added in v2.5.0.0:
+SFHT_HWND_SPAMDEFINITIONS_INFO = 9; // HWND of the "Spam Definitions" info dialog (if it is opened; else NULL)
+SFHT_HWND_SPAMMERS_INFO = 10; // HWND of the Spammers info dialog (if it is opened; else NULL)
+
+SFHT_BOOL_IS_UNICODE = 100; // No handle, returns TRUE if Spam Filter is installed as Unicode version, FALSE otherwise
+ // (You probably never need to call this since Spam Filter performs automatic two-way translation
+ // when a service is called)
+
+// Note: Icons are taken directly from resources, they are not skinned by user.
+
+
+// Return Values:
+// --------------
+// Returns the specified handle value: HINSTANCE, HWND, ICON
+//
+// Note:
+// If a handle is not available (or if it was removed) the function will
+// return NULL.
+// When you are using this service you need to check always for the
+// NULL return value.
+
+
+
+// -------------------------------------------
+// -- Activating/Deactivating the filters ----
+// -------------------------------------------
+
+MS_SPAMFILTER_CHANGEFILTERACTIVATION = 'SpamFilter/ChangeFilterActivation';
+
+// Description:
+// -------------
+// Provides the possibility to enable or disable the spam filters.
+// Also updates the checkboxes in the spam filter options if the option dialog is opened.
+
+// Parameters:
+// -------------
+// wParam = (UINT)uFilterType (For possible values see below)
+// lParam = (BOOL)bNewState (new activation state of filter)
+
+// Possible filter type values (uFilterType):
+SFT_ADVERTISMENT_FILTER = 1;
+SFT_DISLIKEDMESSAGES_FILTER = 2;
+SFT_ROBOT_FILTER = 3;
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -------------------------------------------
+// -- Showing configure dialogs --------------
+// -------------------------------------------
+
+MS_SPAMFILTER_SHOWFILTERDIALOG = 'SpamFilter/ShowFilterDialog';
+
+// Description:
+// -------------
+// Shows the configure dialog of the specified filter.
+
+// Parameters:
+// -------------
+// wParam = (HWND)hwndParent (Can be NULL)
+// lParam = (UINT)uFilterType (For possible values see above)
+// -> since 2.0.2.0: If this is zero then the options dialog will be opened pointing to the Spam Filter options.
+// Note:
+// If hwndParent is not NULL the function will not return
+// until the configure dialog is closed.
+// If hwndParent is NULL the Miranda window will be used as parent
+
+// Since 2.0.2.0:
+SFTEX_OPTIONS_PAGE = 0;
+
+// Since 2.5.0.0:
+// Combine the following flag with the lParam/uFilterType parameter
+// to show the corresponding Spam Definitions info dialog instead
+// of the settings dialog:
+SFTEXF_SPAMDEFINITIONS_INFO = $2000;
+// Note: SFTEXF_SPAMDEFINITIONS_INFO can't be combined with SFT_ROBOT_FILTER
+
+// Combine the following flag with the lParam/uFilterType of 0 parameter
+// to show the Spammers dialog instead of the settings dialog:
+SFTEXF_SPAMMERS_INFO = $4000;
+// Note: SFTEXF_SPAMMERS_INFO can't be combined with SFT_*
+
+
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+// Since v2.0.3.0:
+// If the specified settings window is already opened it will be
+// brought to front (reactivated) instead (returns success though).
+// This might be changed however in the future.
+
+// Changed behaviour in v2.0.3.0:
+// If hwndParent was not set then the function returned the handle to the new window.
+// Now the function returns 0 to indicate success instead.
+
+
+// -------------------------------------------
+// -- Adding message types -------------------
+// -------------------------------------------
+
+#define MS_SPAMFILTER_REGISTERMESSAGETYPE "SpamFilter/MessageTypes/Register"
+
+// Description:
+// -------------
+// Adds a new message type which can be checked for spam.
+// Associated event: ME_SPAMFILTER_PREREGISTERMESSAGETYPE
+
+// Parameters:
+// -------------
+// wParam = 0
+// lParam = (MESSAGETYPEDESC*)mtd (see below)
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// Already registered message types are:
+// (With pszSection set to the installed network protocol names, e.g. "ICQ")
+SFMT_AUTHREQUEST = 'Auth';
+SFMT_ADDED = 'Added'; // since v2.1.0.0
+SFMT_MESSAGE = 'Msg';
+SFMT_URL = 'URL';
+SFMT_FILE = 'File';
+SFMT_CONTACTS = 'Contacts';
+
+
+// MESSAGETYPEDESC structure:
+type
+
+ PMESSAGETYPEDESC = ^TMESSAGETYPEDESC;
+ TMESSAGETYPEDESC = record
+ cbSize: Integer; // Set to sizeof(MESSAGETYPEDESC)
+ pszSection: PChar; // Section of message name for database e.g. protocol name (Can not be NULL)
+ pszSectionDescription : PChar; // Readable name of section e.g. protocol name (can be NULL)
+ // Ignored if section existed previously
+ hSectionIcon: THandle; // Icon to show for section e.g. protocol icon (can be NULL)
+ // Ignored if section existed previously
+ pszName: PChar; // Name to refer to message type when spam checking and in db (Can not be NULL)
+ pszDescription: PChar; // Description for options dialog (can be NULL)
+ hIcon: THandle; // EITHER: icon handle representing the message type OR: one of mirandas skin constants with a negative sign, e.g (HICON)-SKINICON_EVENT_MESSAGE, same as for LoadSkinnedIcon() -> can be 0 and can also be a normal icon handle (HICON)
+ bDefaultStatus: BOOL; // Whether checking for this type should be activated or not by default
+ dwFlags: DWORD; // Flags for the message type (for possible flags see below)
+ iSectionPosition: Integer; // Approx position number for the section in the options list, lower numbers are nearer to the top (Can be 0 to be sorted alphabetically)
+ // Ignored if section existed previously (or if pszSection is NULL)
+ // Added in v2.1.1.0
+ iPosition: Integer; // Approx position number for the item in the options list, lower numbers are nearer to the top (Can be 0 to be sorted alphabetically)
+ // Added in v2.1.1.0
+ end;
+
+const
+
+// Possible MESSAGETYPEDESC flags:
+MTDF_DEFAULTDISABLED = $00000001; // Is disabled by default
+MTDF_HIDDEN = $00000002; // Does not get shown on the options page
+
+// Since v2.5.0.0
+MTDF_UNICODE = $00000004; // pszSectionDescription and pszDescription are Unicode strings
+
+
+// -------------------------------------------
+// -- Modify new added message type (Event) --
+// -------------------------------------------
+
+ME_SPAMFILTER_PREREGISTERMESSAGETYPE = 'SpamFilter/MessageTypes/PreRegister';
+
+// Description:
+// -------------
+// Gets fired before a new message type is added to be checked for spam.
+//
+// This is meant for asking other plugins if the specified default data is ok.
+// Other plugins (especially protocols) can disable a message type if either sending spam
+// is not possible for those protocols or if spam checking would cause unwanted side-effects.
+
+// Warning: Most elements of the MESSAGETYPEDESC structure can also be NULL.
+// However, the structure was checked for errors before this events gets called.
+
+// Parameters:
+// -----------------
+// wParam = 0 (same wParam as used for MS_SPAMFILTER_REGISTERMESSAGETYPE)
+// lParam = (MESSAGETYPEDESC*)mtd (see above, contents can be modified)
+
+// Return Values:
+// -----------------
+// Returning 0 on this event will accept the message type to be added.
+// Returning 1 will prevent the the message type to be added.
+
+// Note: Works as it does now since v2.1.1.0
+
+
+// -------------------------------------------
+// -- Uninstalling a message type ------------
+// -------------------------------------------
+
+MS_SPAMFILTER_REMOVEMESSAGETYPE = 'SpamFilter/RemoveMessageType';
+
+// Description:
+// -------------
+// Uninstalls a specifed message type. It removes the database setting related to the given message type.
+// Should only be used for uninstalling purposes (e.g in uninstall function of PluginUninstaller)
+
+// Parameters:
+// -------------
+// wParam = (char*)pszMsgTypeName (Message type name; You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before)
+// lParam = (char*)pszMsgTypeSection (The section of the message type string; You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before)
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error..
+
+
+// -------------------------------------------
+// -- Message type activation ----------------
+// -------------------------------------------
+
+MS_SPAMFILTER_ISMESSAGETYPEACTIVATED = 'SpamFilter/MessageTypes/IsActivated';
+
+// Description:
+// -------------
+// Returns if a specified message type is activated or not.
+
+// Parameters:
+// -------------
+// wParam = (char*)pszMsgTypeName (Message type name; You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before)
+// lParam = (char*)pszMsgTypeSection (The section of the message type string; You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before)
+
+// Return Values:
+// --------------
+// Returns TRUE if the message type is activated and FALSE otherwise.
+
+
+// -------------------------------------------
+// -- Reset AutoIgnore counters --------------
+// -------------------------------------------
+
+MS_SPAMFILTER_RESETAUTOIGNORE = 'SpamFilter/AutoIgnore/Reset';
+
+// Description:
+// -------------
+// Resets the list in which the total number of spam sent by
+// each user is kept.
+// The list is necessary to determine when a particular user reached the
+// allowed spam messages limit.
+
+// Parameters:
+// -------------
+// wParam = (BOOL)bOnlyCount (if TRUE the service will only return the user count without doing reset)
+// lParam = 0
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -----------------------------------------
+// -- Spam Checking ------------------------
+// -----------------------------------------
+
+MS_SPAMFILTER_ADVERTISMENTCHECK = 'SpamFilter/AdvertismentCheck';
+MS_SPAMFILTER_DISLIKEDMESSAGESCHECK = 'SpamFilter/DislikedMessagesCheck';
+MS_SPAMFILTER_ROBOTCHECK = 'SpamFilter/RobotCheck';
+
+// Description:
+// -------------
+// These services provide the possibility to check a specified text for contained spam.
+// The function checks for everything that is activated in the plugin's options.
+
+// Note: These functions work as they do now since v2.1.0.0
+
+// Parameters:
+// -------------
+// wParam = (SPAMCHECKDATA*)scd
+// lParam = 0
+
+// SPAMCHECKDATA structure:
+type
+
+ PSPAMCHECKDATA = ^TSPAMCHECKDATA;
+ TSPAMCHECKDATA = record
+ cbSize: Integer; // sizeof(SPAMCHECKDATA)
+ pszMsgTypeSection: PChar; // The section of the message type string: You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before
+ pszMsgTypeName: PChar; // Description of the message: You need to use MS_SPAMFILTER_REGISTERMESSAGETYPE before
+ pszMsgText: PChar; // Pointer to the text of a message which is checked for spam. Can be NULL.
+ dwFlags: DWORD; // Flags for the spam checking. (For more details see below.)
+ case LongInt of
+ 0: (hContact: HCONTACT); // Handle to the sending contact (use pszUserName instead if no hContact is available)
+ 1: (pszUserName: PChar); // Name of the user, e.g. nick (set SCDF_NO_CONTACT in the flags to use this parameter) -> should be as unique as possible
+ end;
+
+const
+
+// Possible flags in the SPAMCHECKDATA structure:
+SCDF_NO_NOTIFY = $00000001; // Don't show a popup, play sound or log to file if the message is spam.
+SCDF_NO_CANCEL = $00000002; // Do check even if user pressed control key to avoid the spam check manually
+SCDF_NO_AUTOIGNORE = $00000004; // Do not use the AutoIgnore feature for this check
+
+SCDF_NO_CONTACT = $00000008; // Use pszUserName instead of hContact of union
+
+// Since v2.5.0.0:
+#define SCDF_UNICODE = $00000010; // Specify this flag if associated message text and/or user name is Unicode
+
+
+// Return Values:
+// --------------
+// Possible return flags:
+SFF_MARKREAD = $00000010; // Flag
+SFF_DELETE = $00000020; // Flag
+SFF_IGNORE = $00000040; // Flag
+SFF_SENDMSG_NOTIFY = $00000100; // Flag, Only MS_SPAMFILTER_DISLIKEDMESSAGESCHECK
+SFF_SENDMSG_INSTRUCTION = $00000200; // Flag, Only MS_SPAMFILTER_ROBOTCHECK
+SFF_SENDMSG_CONFIRMATION = $00000400; // Flag, Only MS_SPAMFILTER_ROBOTCHECK
+SFF_TESTPASSED = $00000080; // Flag, Only MS_SPAMFILTER_ROBOTCHECK
+SFF_ISSPAM = $00000002; // Flag
+SFF_ISNORMAL = $00000001; // Flag
+
+// Since 2.5.2.0:
+SFF_HIDE = 0x00001000; // Flag, Only MS_SPAMFILTER_ROBOTCHECK
+
+
+// -------------------------------------------
+// -- Getting additional Check Info ----------
+// -------------------------------------------
+
+MS_SPAMFILTER_GETSPAMCHECKINFO = 'SpamFilter/GetSpamCheckInfo';
+
+// Description:
+// -------------
+// Gets data needed for the processing of spam mesages.
+
+// Parameters:
+// -------------
+// wParam = (DWORD)dwDataType (For possible values see below)
+// lParam = depends on wParam (see below)
+
+// Possible values for lParam:
+
+// TRUE/FALSE: if the specified filter is enabled
+SFSCI_ACTIVATION_FILTER = 1; // lParam = uFilterType (For possible values see above)
+
+// Strings: Get the message texts specified by the user
+// ANSI
+SFSCI_MSGTEXT_NOTIFYA = 2; // lParam = (SPAMCHECKDATA*)scd, scd->hContact (or scd->pszUserName) and scd->pszMsgText (and scd->cbSize) should be filled in.
+SFSCI_MSGTEXT_INSTRUCTIONA = 3; // lParam: see SFSCI_MSGTEXT_NOTIFYA
+SFSCI_MSGTEXT_CONFIRMATIONA = 4; // lParam: see SFSCI_MSGTEXT_NOTIFYA
+
+// Unicode (since v2.5.0.0)
+SFSCI_MSGTEXT_NOTIFYW = 6; // lParam = (SPAMCHECKDATA*)scd, scd->hContact (or scd->pwszUserName) and scd->pwszMsgText (and scd->cbSize) should be filled in.
+SFSCI_MSGTEXT_INSTRUCTIONW = 7; // lParam: see SFSCI_MSGTEXT_NOTIFYW
+SFSCI_MSGTEXT_CONFIRMATIONW = 8; // lParam: see SFSCI_MSGTEXT_NOTIFYW
+
+// TRUE/FALSE: if the above message text should be added to history.
+SFSCI_ADD_TO_HISTORY = 5; // lParam = SFSCI_MSGTEXT_NOTIFY(A/W), SFSCI_MSGTEXT_INSTRUCTION(A/W) or SFSCI_MSGTEXT_CONFIRMATION(A/W)
+
+
+// Return Values:
+// --------------
+// SFSCI_ACTIVATION_FILTER -> (BOOL)TRUE/FALSE
+// SFSCI_DELAY_TIME_REPLY -> (BOOL)TRUE/FALSE
+
+// SFSCI_MSGTEXT_NOTIFYA -> (char*) free with miranda_sys_free() of "m_system.h", (all %vars% in the text are already resolved)
+// SFSCI_MSGTEXT_INSTRUCTIONA -> see SFSCI_MSGTEXT_NOTIFYA
+// SFSCI_MSGTEXT_CONFIRMATIONA -> see SFSCI_MSGTEXT_NOTIFYA
+
+// SFSCI_MSGTEXT_NOTIFYW -> (WCHAR*) free with miranda_sys_free() of "m_system.h", (all %vars% in the text are already resolved)
+// SFSCI_MSGTEXT_INSTRUCTIONW -> see SFSCI_MSGTEXT_NOTIFYW
+// SFSCI_MSGTEXT_CONFIRMATIONW -> see SFSCI_MSGTEXT_NOTIFYW
+
+// SFSCI_ADD_TO_HISTORY -> (BOOL)TRUE/FALSE
+
+// returns NULL on error
+
+
+// -------------------------------------------
+// -- Confirm spam check (Event) -------------
+// -------------------------------------------
+
+ME_SPAMFILTER_OKTOSPAMCHECK = 'SpamFilter/OkToSpamCheck';
+
+// Description:
+// -------------
+// Gets fired before the spam check for a message starts.
+
+// Parameters:
+// -----------------
+// wParam = (SPAMCHECKDATA*)scd (Pointer to SPAMCHECKDATA struct)
+// lParam = (UINT)uFilterType (For possible values see above)
+
+// Return Values:
+// -----------------
+// Returning 0 on this event will accept spam checking for the message,
+// Returning 1 will prevent the spam check.
+
+
+// -------------------------------------------
+// -- Confirm spam detection (Event) ---------
+// -------------------------------------------
+
+ME_SPAMFILTER_OKTOSPAMDETECTION = 'SpamFilter/OkToSpamDetection';
+
+// Description:
+// -------------
+// Gets fired after a spam message is detected.
+
+// Parameters:
+// -----------------
+// wParam = (SPAMCHECKDATA*)scd (Pointer to SPAMCHECKDATA struct)
+// lParam = (UINT)uFilterType (For possible values see above)
+
+// Return Values:
+// -----------------
+// Returning 0 on this event will accept the spam detection
+// Returning 1 cause the message not to be seen as spam.
+
+// Note: If you only would like to get the final result hook
+// ME_SPAMFILTER_SPAMRECEIVED instead
+
+
+// -------------------------------------------
+// -- Spam message received (Event) ----------
+// -------------------------------------------
+
+ME_SPAMFILTER_SPAMRECEIVED = 'SpamFilter/SpamReceived';
+
+// Description:
+// -------------
+// Gets fired when a message if recognized as spam.
+
+// Parameters:
+// -----------------
+// wParam = (SPAMCHECKDATA*)scd (Pointer to SPAMCHECKDATA struct)
+// lParam = (UINT)uFilterType (For possible values see above)
+
+// Return Values:
+// -----------------
+// The return value should be 0.
+
+
+// -------------------------------------------
+// -- Spam Filter loaded (Event) -------------
+// -------------------------------------------
+
+ME_SPAMFILTER_MODULELOADED = 'SpamFilter/ModuleLoaded';
+
+// Description:
+// -------------
+// Gets fired when a all parts of the "Spam Filter" were loaded and available
+// to other plugins.
+// Past this event all functionality of the plugin is fully available.
+// The event works quite similar as ME_SYSTEM_MODULESLOADED.
+// You can use ME_SYSTEM_MODULESLOADED in most cases instead. Please do so!
+
+// Parameters:
+// -----------------
+// wParam = 0
+// lParam = 0
+
+// Return Values:
+// -----------------
+// The return value should be 0.
+
+
+
+// -------------------------------------------
+// -- Helper: Duplicate SPAMCHECKDATA --------
+// -------------------------------------------
+
+MS_SPAMFILTER_COPYSPAMCHECKDATA = 'SpamFilter/SpamCheckData/Copy';
+
+// Description:
+// -------------
+// Copys all contents of a SPAMCHECKDATA struct (especially strings) to another one
+// using Miranda's Memory Manager Interface (see m_system.h).
+// Use MS_SPAMFILTER_FREESPAMCHECKDATA to free the result when no longer needed.
+
+// Available since 2.5.2.0.
+
+// An entire copy of the SPAMCHECKDATA structure is made at pscd->cbSize.
+// All strings are duplicated.
+// The pscdTo->cbSize member is set to the current size of SPAMCHECKDATA
+// that SpamFilter uses.
+
+// Warning:
+// If you pass a structure that is bigger than the current sizeof(SPAMCHECKDATA)
+// then do not use pointers not contained in the current area because
+// they will not be duplicated (However, you could duplicate them by yourself).
+
+
+// Parameters:
+// -------------
+// wParam = (SPAMCHECKDATA*)pscdTo
+// lParam = (SPAMCHECKDATA*)pscdFrom
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+
+// -------------------------------------------
+// -- Helper: Free SPAMCHECKDATA -------------
+// -------------------------------------------
+
+MS_SPAMFILTER_FREESPAMCHECKDATA = 'SpamFilter/SpamCheckData/Free';
+
+// Description:
+// -------------
+// Frees the memory allocated by MS_SPAMFILTER_COPYSPAMCHECKDATA.
+// using Miranda's Memory Manager Interface (see m_system.h).
+
+// Available since 2.5.2.0.
+
+// Parameters:
+// -------------
+// wParam = (SPAMCHECKDATA*)pscd
+// lParam = 0
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on failure.
+
+
+
+// -------------------------------------------
+// -- Showing error messages -----------------
+// -------------------------------------------
+
+MS_SPAMFILTER_SHOWERROR = 'SpamFilter/ShowError';
+
+// Description:
+// -------------
+// Shows a Miranda try balloon tip, popup or message box
+// with the specified error message.
+
+// Parameters:
+// -------------
+// wParam = (UINT)uErrorType (For possible values see below)
+// lParam = 0
+
+// Possible values for wParam:
+SFSE_CRITICAL_ERROR = 1; // Unspecified critical error occured related to spam checking.
+ // Only use this for situations that may never ever occur.
+ // Please do avoid this if possible.
+
+SFSE_SEND_FAILED = 2; // Show this error when a sending of SFSCI_MSGTEXT_NOTIFY,
+ // SFSCI_MSGTEXT_INSTRUCTION, or SFSCI_MSGTEXT_CONFIRMATION failed
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+
+// -------------------------------------------
+// -- Set contact as spammer (Contact) -------
+// -------------------------------------------
+
+MS_SPAMFILTER_CONTACT_SETASSPAMMER = 'SpamFilter/Contact/SetAsSpammer';
+
+// Description:
+// -------------
+// This service adds a specified user to the spammer list (fully ignored).
+
+// Note: works same as MS_SPAMFILTER_CONTACT_SHOWSETASSPAMMERDIALOG
+// but does not prompt the user.
+
+// Available since v2.5.0.0
+
+// Parameters:
+// -------------
+// wParam = (HANDLE)hContact (Handle to a contact in database. Can't be NULL.)
+// lParam = (DWORD)dwFlags (flags about what should be done, see below)
+
+// Available Flags:
+SCASF_NO_NOTIFY = $00000001; // Prevents logging and playing of sounds
+SCASF_USE_ROBOT_SOUND = $00000002; // play robot sound instead of advertisment sound (only if SCASF_NO_NOTIFY is not set)
+SCASF_NO_REMOVE_HISTORY = $00000004; // history is marked read instead of fully removed
+SCASF_NO_DENY_AUTHREQUESTS = $00000008; // do not deny pending auth requests
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -------------------------------------------
+// -- Show mark spammer dialog (Contact) -----
+// -------------------------------------------
+
+MS_SPAMFILTER_CONTACT_SHOWSETASSPAMMERDIALOG = 'SpamFilter/Contact/ShowSetAsSpammerDialog';
+
+// Description:
+// -------------
+// This service adds a specified user to the spammer list.
+// Before it shows a dialog to let the user confirm the action.
+// For use for example as action for a button on the message dialog.
+
+// Note: This service performs the same tasks as MS_SPAMFILTER_SETCONTACTASSPAMMER
+// to execute the action the user chooses.
+
+// Available since v2.5.0.0
+
+// Parameters:
+// -------------
+// wParam = (HANDLE)hContact (Handle to a contact in database. Can't be NULL.)
+// lParam = (HWND)hwndParent (Handle to the parent window. Can be NULL.)
+
+// Return Values:
+// --------------
+// Returns FALSE if user clicked cancel on the dialog.
+// TRUE if the contact was handled as spammer.
+
+
+// Changed in v2.5.0.0:
+// The service MS_SPAMFILTER_SETSPAMMANUALLY has been deprecated.
+// New plugins should use MS_SPAMFILTER_CONTACT_SHOWSETASSPAMMERDIALOG instead.
+MS_SPAMFILTER_SETSPAMMANUALLY = 'SpamFilter/SetSpamManually';
+
+
+// -------------------------------------------
+// -- Test if contact is spammer (Contact) ---
+// -------------------------------------------
+
+MS_SPAMFILTER_CONTACT_ISSPAMMER = 'SpamFilter/Contact/IsSpammer';
+
+// Description:
+// -------------
+// This service tests if a specified contact is on the spammer list (fully ignored).
+
+// Available since v2.5.0.0
+
+// Parameters:
+// -------------
+// wParam = (HANDLE)hContact (Handle to a contact in database. Can't be NULL.)
+// lParam = 0
+
+// Return Values:
+// --------------
+// Returns TRUE when the contact is on the spammer list, FALSE otherwise.
+
+
+// -------------------------------------------
+// -- Unmarks a contact as spammer (Contact) -
+// -------------------------------------------
+
+MS_SPAMFILTER_CONTACT_UNSETSPAMMER = 'SpamFilter/Contact/UnSetSpammer';
+
+// Description:
+// -------------
+// This service restores a spammer to be again a normal contact (unignored).
+
+// Available since v2.5.0.0
+
+// Parameters:
+// -------------
+// wParam = (HANDLE)hContact (Handle to a contact in database. Can't be NULL.)
+// lParam = 0
+
+// Return Values:
+// --------------
+// Returns 0 on success, nonzero on error.
+
+
+// -------------------------------------------
+// -- Spammer state changed (Event) ----------
+// -------------------------------------------
+
+ME_SPAMFILTER_CONTACT_SPAMMERSTATECHANGED = 'SpamFilter/Contact/SpammerStateChanged';
+
+// Description:
+// -------------
+// Gets fired when a a contact gets marked as spammer or unmarked.
+
+// Parameters:
+// -----------------
+// wParam = (HANDLE)hContact
+// lParam = (BOOL)bIsSpammer (new state)
+
+// Return Values:
+// -----------------
+// The return value should be 0.
+
+
+{$endif} // M_SPAMFILTER_H \ No newline at end of file
diff --git a/spamfilter/m_trigger.h b/spamfilter/m_trigger.h
new file mode 100644
index 0000000..cfee738
--- /dev/null
+++ b/spamfilter/m_trigger.h
@@ -0,0 +1,348 @@
+/* Triggers */
+
+typedef struct {
+ int cbSize;
+ char *pszName; // used as identifier, must be unique
+ HINSTANCE hInstance; // only needed when options screen is available
+ DLGPROC pfnDlgProc; // only needed when options screen is available; the callback proc
+ char *pszTemplate; // only needed when options screen is available; must be WS_CHILD, Control parent and !visible are recommended
+} TRIGGERREGISTER;
+
+/* /Trigger/RegisterTrigger service
+Register your trigger at the TriggerPlugin
+ wParam = 0
+ lParam = (LPARAM)(TRIGGERREGISTER *)&tr
+If you try to register a trigger with a name that already exists, the service will fail.
+Returns 0 on success, any other value on failure.
+*/
+#define MS_TRIGGER_REGISTERTRIGGER "/Trigger/RegisterTrigger"
+
+/* Dialog messages
+These message will be send to the options dialog, if available.
+*/
+/* TM_ADDTRIGGER
+'OK' is pressed and a new trigger will be added. Save your settings using the given triggerID.
+ wParam = (WPARAM)(DWORD)triggerID
+ lParam = 0
+*/
+#define TM_ADDTRIGGER WM_USER+10
+/* TM_DELTRIGGER
+The trigger addociated with the given triggerID will be removed, cleanup your settings for this trigger.
+ wParam = (WPARAM)(DWORD)triggerID
+ lParam = 0
+*/
+#define TM_DELTRIGGER WM_USER+11
+
+#define DF_CONTACT 0x01 // the hContact member is valid
+#define DF_PROTO 0x02 // the szProto member is valid
+#define DF_STATUS 0x04 // the status member is valid
+#define DF_TEXT 0x08 // the szText member is valid
+#define DF_LPARAM 0x10 // reserverd, lParam is valid
+
+typedef struct {
+ int cbSize;
+ int dFlags; // DF_*
+ HANDLE hContact;
+ char *szProto;
+ int status;
+ char *szText;
+ LPARAM lParam;
+} TRIGGERDATA;
+
+typedef struct {
+ int cbSize;
+ DWORD triggerID;
+ char *pszSummary; // small summary to be shown in the options screen
+ TRIGGERDATA *td; // may be NULL, or only the dfFlags member set if triggerID is 0
+} SPECIFICTRIGGERINFO;
+
+/* TM_GETTRIGGERINFO
+(optional) Information for a specific trigger is requested.
+ wParam = (WPARAM)(DWORD)triggerID
+ lParam = (LPARAM)(SPECIFICTRIGGERINFO **)&sti
+Set *(SPECIFICTRIGGERINFO **)lParam to a (SPECIFICTRIGGERINFO *)&sti, and keep it in memory
+If triggerID is 0, you may only set the dFlags member of the TRIGGERDATA struct.
+*/
+#define TM_GETTRIGGERINFO WM_USER+12
+/* TM_ACTIONCHANGED
+(optional) Notification when the user changed the action in the options dialog.
+ wParam = 0
+ lParam = (SPECIFICACTIONINFO *)&sai
+*/
+#define TM_ACTIONCHANGED WM_USER+13
+
+#define TRG_PERFORM 0x01 // perform the actions associated to this trigger
+#define TRG_CLEANUP 0x02 // remove all related information after the call
+#define TRG_GETINFO 0x04 // retrieve the (SPECIFICATIONINFO *)&sai for this trigger (only if triggerID != 0)
+
+typedef struct {
+ int cbSize;
+ DWORD triggerID; // triggerID of the event to trigger or 0 for all
+ char *pszName; // name of trigger (may be NULL if triggerID isn't 0)
+ int flags; // flags (TRG_*)
+ SPECIFICTRIGGERINFO *sti; // may be NULL
+} REPORT_INFO;
+
+/* /Trigger/ReportAction
+Service to call when you trigger your event.
+ wParam = 0
+ lParam = (REPORT_INFO *)&ri
+If you're trigger does not have any options, set triggerID to 0 and TriggerPlugin will call all
+associated actions, but set the triggerName member in that case.
+*/
+#define MS_TRIGGER_REPORTACTION "/Trigger/ReportAction"
+
+
+
+
+/* Actions */
+
+typedef struct {
+ int cbSize;
+ char *pszName; // must be unique!
+ char *pszService; // called with wParam = flags, lParam = (SPECIFICATIONINFO *)&sai
+ HINSTANCE hInstance; // only needed when options screen is available
+ DLGPROC pfnDlgProc; // only needed when options screen is available; the callback proc
+ char *pszTemplate; // only needed when options screen is available; must be WS_CHILD, Control parent and !visible are recommended
+} ACTIONREGISTER;
+
+/* /Trigger/RegisterAction service
+Register your action at the TriggerPlugin
+ wParam = 0
+ lParam = (LPARAM)(ACTIONREGISTER *)&ar
+If you try to register an action with a name that already exists, the service will fail.
+Returns 0 on success, any other value on failure.
+*/
+#define MS_TRIGGER_REGISTERACTION "/Trigger/RegisterAction"
+
+/* Dialog messages
+These message will be send to the options dialog, if available.
+*/
+/* TM_ADDACTION
+'OK' is pressed and a new trigger will be added. Save your settings using the given actionID.
+ wParam = (WPARAM)(DWORD)actionID
+ lParam = 0
+*/
+#define TM_ADDACTION WM_USER+1
+/* TM_TRIGGERCHANGED
+(optional) Notification when the user changed the trigger in the options dialog.
+ wParam = 0
+ lParam = (SPECIFICTRIGGERINFO *)&sai
+*/
+#define TM_TRIGGERCHANGED WM_USER+2
+
+#define ACT_PERFORM 0x01 // perform your action
+#define ACT_CLEANUP 0x02 // cleanup associated settings
+#define ACT_GETINFO 0x04 // return (SPECIFICACTIONINFO *)&sai
+
+/* pszService
+The service called, specified in the ACTIONREGISTER
+ wParam = (WPARAM)ACT_* flags
+ lParam = (LPARAM)SPECIFICACTIONINFO *)&sai
+You can retrieve the actionID from the SPECIFICACTIONINFO. If ACT_PERFORM is set, you can retrive
+additional data given by the trigger plugin from the (TRIGGERDATA *)&td member (this can be NULL).
+Return 0 on success, any other on failure.
+*/
+typedef struct {
+ int cbSize;
+ DWORD actionID; // actionID of the info requested
+ char *pszSummary; // null terminating string containing a summary of the action, may be NULL
+ TRIGGERDATA *td; // may be NULL
+} SPECIFICACTIONINFO;
+
+
+
+/* Helpers */
+#define PREFIX_ACTIONID "aid"
+#define PREFIX_TRIGGERID "tid"
+
+typedef struct {
+ int cbSize;
+ char *prefix; // prefix, defaults are PREFIX_ACTIONID for actions, PREFIX_TRIGGERID for triggers
+ DWORD id; // action or trigger ID of the setting to remove
+ char *szModule; // module where the settings are stored
+ HANDLE hContact; // associated contact
+} REMOVETRIGGERSETTINGS;
+
+/* /Trigger/RemoveSettings Service
+ wParam = 0
+ lParam = (LPARAM)(REMOVETRIGGERSETTINGS *)&rts
+Removes settings from the database you wrote with the DBWriteAction* or DBWriteTrigger* helpers.
+Returns the number of settings removed or -1 on failure.
+*/
+#define MS_TRIGGER_REMOVESETTINGS "/Trigger/RemoveSettings"
+
+typedef struct {
+ int cbSize;
+ char *prefix; // prefix, defaults are PREFIX_ACTIONID for actions, PREFIX_TRIGGERID for triggers
+ DWORD id; // action or trigger ID of the previous setting
+ char *szName; // either the name of the trigger or the action, or NULL for all
+ char *szModule; // module where the settings are stored
+ HANDLE hContact; // assoicated contact
+} FINDTRIGGERSETTING;
+
+/* /Trigger/FindNextSetting Service
+ wParam = 0
+ lParam = (LPARAM)(FINDTRIGGERSETTING *)&fts
+Search for a setting in the database. Set id to 0 to start from the beginning, else search is done from id+1.
+Returns (DWORD)triggerID or actionID depending on prefix of the next setting in the database. Returns 0 if
+no more settings are found.
+*/
+#define MS_TRIGGER_FINDNEXTSETTING "/Trigger/FindNextSetting"
+
+
+
+/* Database helpers */
+static int __inline DBWriteActionSettingByte(DWORD actionID, HANDLE hContact,const char *szModule,const char *szSetting,BYTE val) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return DBWriteContactSettingByte(hContact, szModule, dbSetting, val);
+}
+
+static int __inline DBWriteActionSettingWord(DWORD actionID, HANDLE hContact,const char *szModule,const char *szSetting,WORD val) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return DBWriteContactSettingWord(hContact, szModule, dbSetting, val);
+}
+
+static int __inline DBWriteActionSettingDword(DWORD actionID, HANDLE hContact,const char *szModule,const char *szSetting,DWORD val) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return DBWriteContactSettingDword(hContact, szModule, dbSetting, val);
+}
+
+static int __inline DBWriteActionSettingString(DWORD actionID, HANDLE hContact,const char *szModule,const char *szSetting,const char *val) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return DBWriteContactSettingString(hContact, szModule, dbSetting, val);
+}
+
+static BYTE __inline DBGetActionSettingByte(DWORD actionID, HANDLE hContact, const char *szModule, const char *szSetting, BYTE errorValue) {
+
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return (BYTE)DBGetContactSettingByte(hContact, szModule, dbSetting, errorValue);
+}
+
+static WORD __inline DBGetActionSettingWord(DWORD actionID, HANDLE hContact, const char *szModule, const char *szSetting, WORD errorValue) {
+
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return (WORD)DBGetContactSettingWord(hContact, szModule, dbSetting, errorValue);
+}
+
+static DWORD __inline DBGetActionSettingDword(DWORD actionID, HANDLE hContact, const char *szModule, const char *szSetting, int errorValue) {
+
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return DBGetContactSettingDword(hContact, szModule, dbSetting, errorValue);
+}
+
+static int __inline DBGetActionSetting(DWORD actionID, HANDLE hContact, const char *szModule, const char *szSetting, DBVARIANT *dbv) {
+
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return DBGetContactSetting(hContact, szModule, dbSetting, dbv);
+}
+
+static int __inline DBDeleteActionSetting(DWORD actionID, HANDLE hContact,const char *szModule,const char *szSetting) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_ACTIONID, actionID, szSetting);
+ return DBDeleteContactSetting(hContact, szModule, dbSetting);
+}
+
+/* Triggers */
+
+static int __inline DBWriteTriggerSettingByte(DWORD triggerID, HANDLE hContact,const char *szModule,const char *szSetting,BYTE val) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return DBWriteContactSettingByte(hContact, szModule, dbSetting, val);
+}
+
+static int __inline DBWriteTriggerSettingWord(DWORD triggerID, HANDLE hContact,const char *szModule,const char *szSetting,WORD val) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return DBWriteContactSettingWord(hContact, szModule, dbSetting, val);
+}
+
+static int __inline DBWriteTriggerSettingDword(DWORD triggerID, HANDLE hContact,const char *szModule,const char *szSetting,DWORD val) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return DBWriteContactSettingDword(hContact, szModule, dbSetting, val);
+}
+
+static int __inline DBWriteTriggerSettingString(DWORD triggerID, HANDLE hContact,const char *szModule,const char *szSetting,const char *val) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return DBWriteContactSettingString(hContact, szModule, dbSetting, val);
+}
+
+static BYTE __inline DBGetTriggerSettingByte(DWORD triggerID, HANDLE hContact, const char *szModule, const char *szSetting, BYTE errorValue) {
+
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return (BYTE)DBGetContactSettingByte(hContact, szModule, dbSetting, errorValue);
+}
+
+static WORD __inline DBGetTriggerSettingWord(DWORD triggerID, HANDLE hContact, const char *szModule, const char *szSetting, WORD errorValue) {
+
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return (WORD)DBGetContactSettingWord(hContact, szModule, dbSetting, errorValue);
+}
+
+static DWORD __inline DBGetTriggerSettingDword(DWORD triggerID, HANDLE hContact, const char *szModule, const char *szSetting, int errorValue) {
+
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return DBGetContactSettingDword(hContact, szModule, dbSetting, errorValue);
+}
+
+static int __inline DBGetTriggerSetting(DWORD triggerID, HANDLE hContact, const char *szModule, const char *szSetting, DBVARIANT *dbv) {
+
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return DBGetContactSetting(hContact, szModule, dbSetting, dbv);
+}
+
+static int __inline DBDeleteTriggerSetting(DWORD triggerID, HANDLE hContact,const char *szModule,const char *szSetting) {
+
+ char dbSetting[128];
+
+ _snprintf(dbSetting, sizeof(dbSetting), "%s%u_%s", PREFIX_TRIGGERID, triggerID, szSetting);
+ return DBDeleteContactSetting(hContact, szModule, dbSetting);
+}
diff --git a/spamfilter/main.c b/spamfilter/main.c
new file mode 100644
index 0000000..88bb5de
--- /dev/null
+++ b/spamfilter/main.c
@@ -0,0 +1,595 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+
+
+
+
+// -- Plugin Info
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ "Spam Filter Mod (Unicode)",
+ PLUGIN_VERSION,
+ "Filters out advertisment messages, messages sent by robots and other disliked messages such as hoax messages.", // Gets translated automatically
+ "Heiko Herkenrath",
+ "", // PLUGIN_EMAIL will get set later
+ "© 2003-2006 Heiko Herkenrath",
+ "",
+ 1,
+ 0, // Does not replace anything built-in
+ { 0xecb1c450, 0xf71a, 0x425d, { 0xab, 0x63, 0xed, 0x17, 0x5a, 0x86, 0x23, 0x84 } }
+};
+
+// -- Variables: Events/Hooks
+HANDLE hHookModulesLoaded;
+
+// -----------------------------------------
+
+
+static void ThreadInstallation(void* arg)
+{
+ WCHAR* szFileName = 0;
+
+ if (Miranda_Terminated()) return;
+
+ // Install Files
+ {
+ // Spam Definitions
+ // Move "Spam Filter" subdirectory to Miranda IM folder.
+ // Since the Plugins directory should only contain DLLs and no additional data files
+ GetDefinitionsPath(szFileName, FALSE, NULL);
+ PInstallFile(DATA_SUBDIRECTORY, szFileName);
+
+ // Documentation
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, DOCS_SUBDIRECTORY, NULL, NULL);
+ PInstallFile(_T("SpamFilter-Readme.txt"), szFileName);
+ PInstallFile(_T("SpamFilter-License.txt"), szFileName);
+ PInstallFile(_T("SpamFilter-SDK.zip"), szFileName);
+ PInstallFile(_T("SpamFilter-Translation.txt"), szFileName); // normally in SDK
+ PInstallFile(_T("SpamFilter-Developer.txt"), szFileName); // normally in SDK
+ PInstallFile(_T("m_spamfilter.h"), szFileName); // normally in SDK
+ PInstallFile(_T("m_spamfilter.inc"), szFileName); // normally in SDK
+
+ PInstallFile(_T("SpamDefinitions-*.txt"), szFileName); // additional SpamDefs installed
+
+ PInstallFile(_T("PCRE-License.txt"), szFileName);
+
+ // Sounds
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, SOUNDS_SUBDIRECTORY, NULL, NULL);
+ PInstallFile(DEFAULT_ADVERTISMENT_SOUNDFILE, szFileName);
+ PInstallFile(DEFAULT_DISLIKEDMESSAGE_SOUNDFILE, szFileName);
+ PInstallFile(DEFAULT_ROBOT_SOUNDFILE, szFileName);
+
+ // DLLs (non-plugin)
+ PInstallDLLFile(_T("pcre.dll"), TRUE, TRUE);
+
+ // InstallScript.xml
+ PConstructLocalPath(szFileName, CSIDL_EXT_MODULE, NULL, _T("InstallScript.xml"), NULL);
+ DeleteFile(szFileName);
+ }
+
+ if (Miranda_Terminated()) return;
+
+ // Add default list items
+ {
+ STRINGLIST* pslWords = SLNewList();
+
+ // Create default bad words
+ if (!IsSpamDefinitionDataPresent(SDID_CUSTOM_BADWORDS))
+ {
+ SLAddItem(pslWords, TranslateT("Automated message"));
+ SetSpamDefinitionData(pslWords, SDID_CUSTOM_BADWORDS, NULL);
+
+ SLClearList(pslWords);
+ }
+
+ // Create default good words
+ if (!IsSpamDefinitionDataPresent(SDID_CUSTOM_GOODWORDS))
+ {
+ SLAddItem(pslWords, TranslateT("Miranda IM"));
+ SetSpamDefinitionData(pslWords, SDID_CUSTOM_GOODWORDS, NULL);
+
+ SLClearList(pslWords);
+ }
+
+ // Create default disliked message words
+ if (!IsSpamDefinitionDataPresent(SDID_CUSTOM_DISLIKEDWORDS))
+ {
+ SLAddItem(pslWords, TranslateT("Please forward"));
+ SetSpamDefinitionData(pslWords, SDID_CUSTOM_DISLIKEDWORDS, NULL);
+ }
+
+ SLFreeList(pslWords);
+ }
+
+ if (Miranda_Terminated()) return;
+
+ // On First Run
+ if (DBGetContactSettingDword(NULL, DB_MODULE_NAME, DB_SETTING_FIRSTRUNDONE, 0) != PLUGIN_VERSION) // no use of "<"!
+ {
+ // Import bad words v1.0.4.0 -> v2.0.0.0 (DB->FILE converter)
+ {
+ STRINGLIST* pslBadWords = NULL;
+ STRINGLIST* pslDefaultBadWords = NULL;
+ int i;
+ char szSetting[7+MAX_INT_LENGTH+1];
+ DBVARIANT dbv;
+
+ // Get old bad words out of database
+ for (i=0;; i++) // no break condition
+ {
+ mir_snprintf(szSetting, ARRAYSIZE(szSetting), "BadWord%i", i);
+
+ if (DBGetContactSettingTString(NULL, "SpamFilter", szSetting, &dbv) != 0)
+ break;
+
+ // Load default list when needed
+ if (!pslBadWords)
+ {
+ pslDefaultBadWords = SLNewList();
+ pslBadWords = SLNewList();
+
+ // Load default bad words
+ GetSpamDefinitionData(pslDefaultBadWords, SDID_BADWORDS);
+
+ // Load bad words already in file
+ GetSpamDefinitionData(pslBadWords, SDID_CUSTOM_BADWORDS);
+ }
+
+ if (!SLIsItem(pslDefaultBadWords, dbv.ptszVal, FALSE) && !SLIsItem(pslBadWords, dbv.ptszVal, FALSE))
+ SLAddItem(pslBadWords, dbv.ptszVal);
+
+ DBFreeVariant(&dbv);
+
+ // Delete old setting (important! else it will reimport every version change)
+ DBDeleteContactSetting(NULL, "SpamFilter", szSetting);
+ }
+
+ // Save old and new bad words
+ if (pslBadWords) {
+ SetSpamDefinitionData(pslBadWords, SDID_CUSTOM_BADWORDS, NULL);
+ SLFreeList(pslBadWords);
+ }
+ if (pslDefaultBadWords)
+ SLFreeList(pslDefaultBadWords);
+ }
+
+ if (Miranda_Terminated()) return;
+
+ // Import old spammers in database of v2.0.1.0 (DB->DB converter)
+ {
+ WCHAR* pszSuffix = _T(" (Spammer)"); // readonly allocated
+ WCHAR* pszSuffixT = TranslateTS(pszSuffix);
+ WCHAR* pszUserName;
+ HANDLE hContact;
+ BOOL bIsOldSpammer;
+
+ if (pszSuffix)
+ {
+ for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); hContact; hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0))
+ {
+ // Skip if not hidden
+ if (!DBGetContactSettingByte(hContact, "CList", "Hidden", 0))
+ continue;
+
+ // Skip if is already "new" spammer
+ if (CallService(MS_SPAMFILTER_CONTACT_ISSPAMMER, (WPARAM)hContact, 0))
+ continue;
+
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+
+ // Find suffix
+ bIsOldSpammer = FALSE;
+ if (lstrlen(pszUserName) > lstrlen(pszSuffix))
+ if (StrCmp(pszUserName+lstrlen(pszUserName)-lstrlen(pszSuffix), pszSuffix) == 0)
+ bIsOldSpammer = TRUE;
+ if (lstrlen(pszUserName) > lstrlen(pszSuffixT))
+ if (StrCmp(pszUserName+lstrlen(pszUserName)-lstrlen(pszSuffixT), pszSuffixT) == 0)
+ bIsOldSpammer = TRUE;
+
+ if (bIsOldSpammer)
+ CallService(MS_SPAMFILTER_CONTACT_SETASSPAMMER, (WPARAM)hContact, (LPARAM)SCASF_NO_NOTIFY|SCASF_NO_REMOVE_HISTORY|SCASF_NO_DENY_AUTHREQUESTS);
+ }
+ }
+
+ }
+
+ if (Miranda_Terminated()) return;
+
+ // Import Spam Definition Data previous to v2.5.0.0 (ANSI->UTF8 converter)
+ {
+ // own-badwords.sfw
+ PConstructLocalPath(szFileName, CSIDL_EXT_MIRANDAPROFILE, _T("Spam Filter"), _T("own-badwords.sfw"), NULL);
+ mir_utf8encodeW(szFileName); // current codepage
+
+ // own-goodwords.sfw
+ PConstructLocalPath(szFileName, CSIDL_EXT_MIRANDAPROFILE, _T("Spam Filter"), _T("own-goodwords.sfw"), NULL);
+ mir_utf8encodeW(szFileName); // current codepage
+
+ // own-dislikedwords.sfw
+ PConstructLocalPath(szFileName, CSIDL_EXT_MIRANDAPROFILE, _T("Spam Filter"), _T("own-dislikedwords.sfw"), NULL);
+ mir_utf8encodeW(szFileName); // current codepage
+
+ // BadWords\*.sfw
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\BadWords"), _T("Chinese (Traditional).sfw"), NULL);
+ mir_utf8encodeW(szFileName); // big5 codepage (Chinese) [PRC and Singapore have other codepages]
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\BadWords"), _T("Russian.sfw"), NULL);
+ mir_utf8encodeW(szFileName); // cp-1251 codepage (Russian)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\BadWords"), _T("English.sfw"), NULL);
+ mir_utf8encodeW(szFileName); // English, German, French, Portuguese codepage (European)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\BadWords"), _T("German.sfw"), NULL);
+ mir_utf8encodeW(szFileName); // English, German, French, Portuguese codepage (European)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\BadWords"), _T("French.sfw"), NULL);
+ mir_utf8encodeW(szFileName); // English, German, French, Portuguese codepage (European)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\BadWords"), _T("Portuguese (Brazil).sfw"), NULL);
+ mir_utf8encodeW(szFileName); // English, German, French, Portuguese codepage (European)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\BadWords"), _T("*.sfw"), NULL);
+ mir_utf8encodeW(szFileName); // customized reminder
+
+ // HoaxTexts\*.sft
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\HoaxTexts"), _T("Example.sft"), NULL);
+ mir_utf8encodeW(szFileName); // English, German, French, Portuguese codepage (European)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\HoaxTexts"), _T("Example.sft"), NULL);
+ mir_utf8encodeW(szFileName); // customized reminder
+
+ // Teasers\*.sft
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\Teasers"), _T("English.sft"), NULL);
+ mir_utf8encodeW(szFileName); // English, German, French, Portuguese codepage (European)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\Teasers"), _T("German.sft"), NULL);
+ mir_utf8encodeW(szFileName); // English, German, French, Portuguese codepage (European)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\Teasers"), _T("*.sft"), NULL);
+ mir_utf8encodeW(szFileName); // reminder customized
+
+ // Urls\*.sfc
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\Urls"), _T("*.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // url strings are all English
+
+ // PhoneNumbers\*.sfc
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\PhoneNumbers"), _T("Russian.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // cp-1251 codepage (Russian)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\PhoneNumbers"), _T("German.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // English, German, French, Portuguese codepage (European)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\PhoneNumbers"), _T("*.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // reminder customizd
+
+ // Spammers\*.sfc
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\Spammers"), _T("Russian.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // cp-1251 codepage (Russian)
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter\\Spammers"), _T("*.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // reminder customized
+
+ // format_text.sfc
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter"), _T("format_text.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // was English only
+
+ // format_numbers.sfc
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter"), _T("format_numbers.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // was English only
+
+ // separators.sfc
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, _T("Spam Filter"), _T("separators.sfc"), NULL);
+ mir_utf8encodeW(szFileName); // was English only
+ }
+
+ if (Miranda_Terminated()) return;
+
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)DEFAULT_SETTING_ADVERTISMENTFILTER) || DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOTFILTER, (BYTE)DEFAULT_SETTING_ROBOTFILTER))
+ {
+ CallService(MS_IGNORE_UNIGNORE, (WPARAM)NULL, (LPARAM)IGNOREEVENT_MESSAGE);
+ CallService(MS_IGNORE_UNIGNORE, (WPARAM)NULL, (LPARAM)IGNOREEVENT_URL);
+ CallService(MS_IGNORE_UNIGNORE, (WPARAM)NULL, (LPARAM)IGNOREEVENT_FILE);
+ CallService(MS_IGNORE_UNIGNORE, (WPARAM)NULL, (LPARAM)IGNOREEVENT_AUTHORIZATION);
+
+ CallService(MS_IGNORE_IGNORE, (WPARAM)NULL, (LPARAM)IGNOREEVENT_USERONLINE);
+
+ {
+ DBVARIANT dbv;
+
+ if (DBGetContactSetting(NULL, "SRMM", "UnknownTyping", &dbv) == 0) // If existant
+ {
+ if (dbv.type == DBVT_BYTE)
+ DBWriteContactSettingByte(NULL, "SRMM", "UnknownTyping", 0);
+ DBFreeVariant(&dbv);
+ }
+ }
+ }
+
+ // Disable "old"/other spam plugins (if installed) to avoid conflicts and user confusion
+ // (All features of them included in Spam Filter)
+ if (GetModuleHandle(_T("SpamBlock")))
+ DBWriteContactSettingByte(NULL, "PluginDisable", "spamblock.dll", 1);
+ if (GetModuleHandle(_T("SpamBlockPlus")))
+ DBWriteContactSettingByte(NULL, "PluginDisable", "spamblockplus.dll", 1);
+ if (GetModuleHandle(_T("spamreturner")))
+ DBWriteContactSettingByte(NULL, "PluginDisable", "spamreturner.dll", 1);
+ if (GetModuleHandle(_T("stopspam"))) {
+ DBWriteContactSettingByte(NULL, "PluginDisable", "stopspam.dll", 1);
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOTFILTER, (BYTE)TRUE);
+ }
+ if (GetModuleHandle(_T("ignorecontact")))
+ DBWriteContactSettingByte(NULL, "PluginDisable", "ignorecontact.dll", 1);
+
+ #if defined (UNICODE)
+ DBWriteContactSettingByte(NULL, "PluginDisable", "spamfilter.dll", 1);
+ #else
+ DBWriteContactSettingByte(NULL, "PluginDisable", "spamfilterw.dll", 1);
+ #endif
+
+ // FirstRunDone
+ DBWriteContactSettingDword(NULL, DB_MODULE_NAME, DB_SETTING_FIRSTRUNDONE, PLUGIN_VERSION);
+ }
+}
+
+
+__declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ if (mirandaVersion < NEEDED_MIRANDA_VERSION)
+ {
+ // No Translate() here! (langpack service not yet loaded, no need anyway)
+ MessageBoxEx(NULL, _T("The Spam Filter plugin can not be loaded. It requires Miranda IM ")_T(NEEDED_MIRANDA_VERSION_STR)_T(" or later."), _T("Spam Filter Plugin"), MB_OK|MB_ICONERROR|MB_SETFOREGROUND|MB_TOPMOST|MB_TASKMODAL, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
+ return NULL;
+ }
+
+ return &pluginInfo;
+}
+static const MUUID interfaces[] = {MIID_SERVICEMODE, MIID_LAST};
+__declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+
+ return TRUE;
+}
+
+
+
+static int AllModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ AssignAllContacts();
+
+ // Custom Profile Folder Plugin support (as early as possible!)
+ InitFolders();
+
+ // Definition file cache (after folder)
+ InitSpamDefinitions();
+
+ // Load regular expressions DLL
+ // Important: Not in Load() to allow other plugins to update/replace the DLL in their Load()
+ InitRegExp();
+
+ // Trigger Plugin support
+ InitTrigger();
+
+ // Add message types for filter
+ {
+ int i, iProtoCount = 0;
+ WCHAR szProtoNameBuf[256];
+ WCHAR* pszProtoName;
+ DWORD dwProtoCaps, dwProtoCaps4;
+ HICON hProtoIcon;
+ PROTOCOLDESCRIPTOR** appd = NULL;
+
+ if (CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&iProtoCount, (LPARAM)&appd) == 0)
+ {
+ for (i=0; i<iProtoCount; i++)
+ {
+ if (appd[i]->type != PROTOTYPE_PROTOCOL) continue;
+
+ // Get capabilities
+ dwProtoCaps = CallProtoService(appd[i]->szName, PS_GETCAPS, (WPARAM)PFLAGNUM_1, 0);
+ if ((int)dwProtoCaps == CALLSERVICE_NOTFOUND) continue;
+ dwProtoCaps4 = CallProtoService(appd[i]->szName, PS_GETCAPS, (WPARAM)PFLAGNUM_4, 0);
+
+ // Get icon
+ hProtoIcon = (HICON)CallProtoService(appd[i]->szName, PS_LOADICON, PLI_PROTOCOL|PLIF_SMALL, 0);
+ if ((int)hProtoIcon == CALLSERVICE_NOTFOUND) hProtoIcon = NULL;
+
+ // Get readable name
+ #if defined(UNICODE)
+ if (CallProtoService(appd[i]->szName, PS_GETNAME"W", ARRAYSIZE(szProtoNameBuf), (LPARAM)szProtoNameBuf) == 0) {
+ pszProtoName = szProtoNameBuf;
+ } else if (CallProtoService(appd[i]->szName, PS_GETNAME, ARRAYSIZE(szProtoNameBuf), (LPARAM)szProtoNameBuf) == 0) {
+ pszProtoName = (WCHAR*)mir_utf8encodeW(szProtoNameBuf);
+ } else {
+ pszProtoName = (WCHAR*)mir_utf8encodeW((WCHAR*)appd[i]->szName);
+ }
+ #else
+ if (CallProtoService(appd[i]->szName, PS_GETNAME, ARRAYSIZE(szProtoNameBuf), (LPARAM)szProtoNameBuf) == 0) {
+ pszProtoName = szProtoNameBuf;
+ } else {
+ pszProtoName = appd[i]->szName;
+ }
+ #endif
+
+ // Authorization Requests (flag supported and display module installed)
+ if (dwProtoCaps&PF1_AUTHREQ)
+ SFRegisterMessageType(appd[i]->szName, pszProtoName, hProtoIcon, SFMT_AUTHREQUEST, TranslateT("Authorization Request"), (HICON)-SKINICON_OTHER_MIRANDA, 100, ((dwProtoCaps4&PF4_FORCEAUTH) ? MTDF_DEFAULTDISABLED : 0));
+
+ // YouWereAdded Notification (flag supported and display module installed)
+ if (dwProtoCaps&PF1_ADDED)
+ SFRegisterMessageType(appd[i]->szName, pszProtoName, hProtoIcon, SFMT_ADDED, TranslateT("Added Notification"), (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(210), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED), 110, ((dwProtoCaps4&PF4_FORCEADDED) ? MTDF_DEFAULTDISABLED : 0));
+
+ // Special support for ICQ (workaround, quite hacky)
+ if (StrCmpA(appd[i]->szName, "ICQ") == 0)
+ {
+ // WebPager (display module installed: WebPager Plugin)
+ if (ServiceExists("WebPager/ReceiveWebPager"))
+ SFRegisterMessageType(appd[i]->szName, pszProtoName, hProtoIcon, "WebPager", TranslateT("WebPager"), GetModuleHandle(_T("WebPager")) ? (HICON)LoadImage(GetModuleHandle(_T("WebPager")), MAKEINTRESOURCE(101), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED) : NULL, 200, 0);
+
+ // EmailExpress (display module installed: EmailExpress Plugin)
+ if (ServiceExists("EmailExpress/ReceiveEmailExpress"))
+ SFRegisterMessageType(appd[i]->szName, pszProtoName, hProtoIcon, "EmailExpress", TranslateT("EmailExpress"), GetModuleHandle(_T("EmailExpress")) ? (HICON)LoadImage(GetModuleHandle(_T("EmailExpress")), MAKEINTRESOURCE(102), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED) : NULL, 210, 0);
+ }
+
+ // Instant Messages (flag supported and display module installed)
+ if ((dwProtoCaps&PF1_IMRECV) && ServiceExists(MS_MSG_SENDMESSAGE))
+ SFRegisterMessageType(appd[i]->szName, pszProtoName, hProtoIcon, SFMT_MESSAGE, TranslateT("Message"), (HICON)-SKINICON_EVENT_MESSAGE, 300, ((dwProtoCaps&PF1_IMSEND) ? 0 : MTDF_DEFAULTDISABLED));
+
+ // URLs (flag supported and display module installed)
+ if ((dwProtoCaps&PF1_URLRECV) && ServiceExists(MS_URL_SENDURL))
+ SFRegisterMessageType(appd[i]->szName, pszProtoName, hProtoIcon, SFMT_URL, TranslateT("URL"), (HICON)-SKINICON_EVENT_URL, 400, ((dwProtoCaps&PF1_URLSEND) ? 0 : MTDF_DEFAULTDISABLED));
+
+ // File Transfers (flag supported and display module installed)
+ if ((dwProtoCaps&PF1_FILERECV) && ServiceExists(MS_FILE_SENDFILE))
+ SFRegisterMessageType(appd[i]->szName, pszProtoName, hProtoIcon, SFMT_FILE, TranslateT("File"), (HICON)-SKINICON_EVENT_FILE, 500, ((dwProtoCaps&PF1_FILESEND) ? 0 : MTDF_DEFAULTDISABLED));
+
+ // Contact Transfers (flag supported and display module installed)
+ if ((dwProtoCaps&PF1_CONTACTRECV) && ServiceExists(MS_CONTACTS_SEND))
+ SFRegisterMessageType(appd[i]->szName, pszProtoName, hProtoIcon, SFMT_CONTACTS, TranslateT("Contacts"), GetModuleHandle(_T("contacts")) ? (HICON)LoadImage(GetModuleHandle(_T("contacts")), MAKEINTRESOURCE(109), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED) : NULL, 600, ((dwProtoCaps&PF1_CONTACTSEND) ? 0 : MTDF_DEFAULTDISABLED));
+
+
+ if (hProtoIcon) DestroyIcon(hProtoIcon); // must be destroyed
+
+ #if defined(UNICODE)
+ if (pszProtoName != szProtoNameBuf)
+ mir_free(pszProtoName);
+ #endif
+ }
+ }
+ }
+
+ // Options
+ InitOptions();
+
+ // Add sounds
+ SkinAddNewSoundBundled(DB_SOUND_ADVERTISMENT_SETTING, Translate("Spam Filter"), Translate("Advertisment Alert"), DEFAULT_ADVERTISMENT_SOUNDFILE);
+ SkinAddNewSoundBundled(DB_SOUND_DISLIKEDMESSAGE_SETTING, Translate("Spam Filter"), Translate("Disliked Message Alert"), DEFAULT_DISLIKEDMESSAGE_SOUNDFILE);
+ SkinAddNewSoundBundled(DB_SOUND_ROBOT_SETTING, Translate("Spam Filter"), Translate("Robot Alert"), DEFAULT_ROBOT_SOUNDFILE);
+
+ // Add known database modules (Support for DBEditor++)
+ if (ServiceExists("DBEditorpp/RegisterModule"))
+ {
+ const char* apszModuleNames[] = {DB_MODULE_NAME, DB_MODULE_NAME_MSGTYPES, DB_MODULE_NAME_PRESPAMMERS};
+ CallService("DBEditorpp/RegisterModule", (WPARAM)apszModuleNames, (LPARAM)ARRAYSIZE(apszModuleNames));
+ }
+
+ // Support for own variables in Variables-Plugin
+ InitVariables();
+
+ // Init CList extra image (for spammers)
+ InitExtraImg();
+
+ // Support for Popup/PopupPlus Plugin
+ if (IsPopupAvailable(FALSE))
+ {
+ BOOL bFilterActive = DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, DEFAULT_SETTING_ADVERTISMENTFILTER) || DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, DEFAULT_SETTING_DISLIKEDMESSAGEFILTER);
+ BOOL bPopupsActive = DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_POPUP, DEFAULT_SETTING_POPUP);
+
+ InitPopup();
+
+ // Init popup menu item
+ SetPopupMenuItem(bPopupsActive, !bFilterActive);
+
+ // Add colors
+ SkinAddNewColor(DB_COLOR_POPUPBACKGROUND_SETTING, Translate("Spam Filter"), Translate("Popup Background"), RGB(213,209,208), FALSE);
+ SkinAddNewColor(DB_COLOR_POPUPTEXT_SETTING, Translate("Spam Filter"), Translate("Popup Text"), RGB(193,0,38), FALSE);
+ }
+
+ // Init contact menu item
+ InitMenuItems();
+
+ // SpamFilter fully loaded
+ NotifyModuleLoaded();
+ return 0;
+}
+
+
+__declspec(dllexport) int Load(PLUGINLINK* link)
+{
+ pluginLink = (PLUGINLINK*)link;;
+
+ hwndSpamFilterOpt = NULL;
+ hwndAdvertismentFilter = NULL;
+ hwndRobotFilter = NULL;
+ hwndDislikedMessagesFilter = NULL;
+ hwndSpamDefinitionsInfo = NULL;
+ hwndSpammersInfo = NULL;
+
+ if (!ServiceExists(MS_DB_CONTACT_GETSETTING_STR))
+ {
+ // No Translate() here
+ MessageBoxEx(NULL, _T("The Spam Filter plugin requires database driver (db3x) 0.5.1.0 or later."), _T("Spam Filter Plugin"), MB_OK|MB_ICONERROR|MB_SETFOREGROUND|MB_TOPMOST|MB_TASKMODAL, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
+ return 1;
+ }
+
+ // Load Unicode version only for Unicode core
+ {
+ char szVer[128];
+ if (CallService(MS_SYSTEM_GETVERSIONTEXT, (WPARAM)ARRAYSIZE(szVer), (LPARAM)szVer) == 0)
+ if (!StrStrA(szVer, "Unicode")) {
+ OutputDebugString(_T("Spam Filter: No Unicode core, Unicode version not loaded.\r\n"));
+ return 1;
+ }
+ }
+
+ // Get Miranda's simple memory problems catcher
+
+ memset(&memoryManagerInterface,0,sizeof(memoryManagerInterface));
+ memoryManagerInterface.cbSize = sizeof(memoryManagerInterface);
+ CallService(MS_SYSTEM_GET_MMI, 0, (LPARAM)&memoryManagerInterface);
+ mir_getUTFI( &utfi );
+
+ // Init SpamFilter-API
+ InitServices();
+
+ // Establish the filter protocol
+ InitSpamCheck();
+
+ // Installation (making sure files are placed correctly)
+ forkthread(ThreadInstallation, 0, NULL);
+ hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, AllModulesLoaded);
+ if (!hHookModulesLoaded) CallService(MS_SPAMFILTER_SHOWERROR, SFSE_CRITICAL_ERROR, 0);
+
+ return 0;
+}
+
+
+__declspec(dllexport) int Unload(void)
+{
+ if (hHookModulesLoaded) UnhookEvent(hHookModulesLoaded);
+ UninitExtraImg();
+ UninitMenuItems();
+ UninitOptions();
+ UninitPopup();
+ UninitSpamCheck();
+ UninitVariables();
+ UninitServices();
+ UninitRegExp();
+ UninitSpamDefinitions();
+ UninitTrigger();
+ UninitFolders();
+ return 0;
+}
+
+
diff --git a/spamfilter/make.bat b/spamfilter/make.bat
new file mode 100644
index 0000000..ed03c06
--- /dev/null
+++ b/spamfilter/make.bat
@@ -0,0 +1,77 @@
+@echo off
+
+rem Note: This batch file does not work on all machines.
+
+echo Synchronizing m_spamfilter.h...
+echo ----------------------------------------------------------------------
+copy m_spamfilter.h Include\m_spamfilter.h /Y
+echo.
+if errorlevel 1 goto :failed
+
+echo Archiving previous release... (ignore errors)
+echo ----------------------------------------------------------------------
+del Release\*.mir > NUL
+rem del Release\*.dll > NUL
+del Release\SpamFilter-SDK.zip > NUL
+move Release\*.zip Release\Archive\ > NUL
+echo.
+
+echo Creating output directory... (ignore errors)
+echo ----------------------------------------------------------------------
+mkdir Release > NUL
+echo.
+
+rem echo Copiling: Win32 Release ...
+rem echo ----------------------------------------------------------------------
+rem cd "C:\Programme\Microsoft Visual Studio\VC98\Bin"
+rem nmake /f SpamFilter.mak CFG="SpamFilter - Win32 Release"
+rem echo.
+rem if errorlevel 1 goto :failed
+
+rem echo Copiling: Win32 Release Unicode ...
+rem echo ----------------------------------------------------------------------
+rem nmake /f SpamFilter.mak CFG="SpamFilter - Win32 Release Unicode"
+rem echo.
+rem if errorlevel 1 goto :failed
+
+rem for /F "tokens=1,2 delims= " %%i in (..\build.no) do call :pack %%i %%j
+remexit
+rem :pack
+
+rem Note: Using WinZip Commandline Add-On for zipping
+
+echo Creating SpamFilter.zip...
+echo ----------------------------------------------------------------------
+wzzip Release\SpamFilter-SDK.zip Docs\SpamFilter-Translation.txt Docs\SpamFilter-Developer.txt m_*.inc m_*.h Release\*.pdb
+echo.
+if errorlevel 1 goto :failed
+
+wzzip Release\SpamFilter.zip Docs\SpamFilter-Readme.txt Docs\SpamFilter-License.txt Docs\PCRE-License.txt Release\SpamFilter-SDK.zip Release\*.dll Utilities\Pcre\bin\pcre.dll
+wzzip /r /p Release\SpamFilter.zip Extensions\*.*
+del Release\SpamFilter-SDK.zip
+echo.
+if errorlevel 1 goto :failed
+
+echo Creating SpamFiltersrc.zip... (Source)
+echo ----------------------------------------------------------------------
+wzzip Release\SpamFilterSrc.zip *.dsp *.dsw *.mak *.dep *.h *.c *.rc *.txt *.inc *.bat build.no
+wzzip /r /P Release\SpamFilterSrc.zip Docs\*.* Extensions\*.* Resources\*.* Utilities\*.* Include\*.*
+echo.
+if errorlevel 1 goto :failed
+
+echo Creating SpamFilter.mir... (for Miranda Installer)
+echo ----------------------------------------------------------------------
+copy Release\SpamFilter.zip Release\SpamFilter.mir
+echo.
+
+goto :eof
+
+:failed
+echo ----------------------------------------------------------------------
+echo !!! ERROR!: release creation failed. !!!
+echo ----------------------------------------------------------------------
+echo.
+pause
+
+:eof
+exit
diff --git a/spamfilter/menuitems.c b/spamfilter/menuitems.c
new file mode 100644
index 0000000..ff0412e
--- /dev/null
+++ b/spamfilter/menuitems.c
@@ -0,0 +1,136 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -- Variables: Events/Hooks
+HANDLE hHookPreBuildContactMenu;
+HANDLE hHookIconsChanged;
+
+
+// -----------------------------------------
+
+
+// -- Contact Menu Items --
+
+void SetContactMenuItem_Mark(BOOL bShowItem)
+{
+ static HANDLE hContactMenuItemMark = NULL;
+ CLISTMENUITEM cmi;
+
+ ZeroMemory(&cmi, sizeof(cmi));
+ cmi.cbSize = sizeof(cmi);
+ cmi.flags = CMIF_NOTONLIST;
+ if (!bShowItem) cmi.flags |= CMIF_HIDDEN;
+ cmi.hIcon = SkinGetIcon(DB_ICON_SPAMMANUALLY_SETTING, IDI_DEFAULT, FALSE);
+
+ if (hContactMenuItemMark)
+ {
+ cmi.flags |= CMIM_FLAGS|CMIM_ICON;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hContactMenuItemMark, (LPARAM)&cmi);
+
+ } else {
+
+ cmi.pszName = Translate("&Mark as spammer");
+ cmi.position = -2050000001; // "Add user to contact list"/"Add permanently to list" is -2050000000
+ cmi.pszService = MS_SPAMFILTER_CONTACT_SHOWSETASSPAMMERDIALOG;
+
+ hContactMenuItemMark = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&cmi);
+ }
+}
+
+
+void SetContactMenuItem_Unmark(BOOL bShowItem)
+{
+ static HANDLE hContactMenuItemUnmark = NULL;
+ CLISTMENUITEM cmi;
+
+ ZeroMemory(&cmi, sizeof(cmi));
+ cmi.cbSize = sizeof(cmi);
+ cmi.flags = CMIF_NOTOFFLIST;
+ if (!bShowItem) cmi.flags |= CMIF_HIDDEN;
+ cmi.hIcon = SkinGetIcon(DB_ICON_SPAMMANUALLY_SETTING, IDI_DEFAULT, FALSE);
+
+ if (hContactMenuItemUnmark)
+ {
+ cmi.flags |= CMIM_FLAGS|CMIM_ICON;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hContactMenuItemUnmark, (LPARAM)&cmi);
+
+ } else {
+
+ cmi.pszName = Translate("&Unmark spammer");
+ cmi.position = -2050000001; // "Add user to contact list"/"Add permanently to list" is -2050000000
+ cmi.pszService = MS_SPAMFILTER_CONTACT_UNSETSPAMMER;
+
+ hContactMenuItemUnmark = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&cmi);
+ }
+}
+
+
+static int IconsChanged(WPARAM wParam, LPARAM lParam)
+{
+ // Update menu item
+ SetContactMenuItem_Mark(TRUE);
+ SetContactMenuItem_Unmark(FALSE);
+ return 0;
+}
+
+
+static int PreBuildContactMenu(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ char* pszProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+
+ if (hContact && pszProto)
+ if (!DBGetContactSettingByte(hContact, pszProto, "ChatRoom", 0))
+ {
+ BOOL bNotOnList = DBGetContactSettingByte(hContact, "CList", "NotOnList", 0);
+ BOOL bIsSpammer = DBGetContactSettingByte((HANDLE)wParam, DB_MODULE_NAME, DB_SETTING_ISSPAMMER, (BYTE)FALSE);
+
+ SetContactMenuItem_Mark(bNotOnList && !bIsSpammer);
+ SetContactMenuItem_Unmark(bIsSpammer && !IsWindow(hwndSpammersInfo));
+ }
+
+ return 0;
+}
+
+
+// -----------------------------------------
+
+
+void InitMenuItems(void)
+{
+ if (ServiceExists(MS_SKIN2_GETICON))
+ hHookIconsChanged = HookEvent(ME_SKIN2_ICONSCHANGED, IconsChanged);
+
+ hHookPreBuildContactMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu);
+
+ SetContactMenuItem_Mark(TRUE);
+ SetContactMenuItem_Unmark(FALSE);
+}
+
+void UninitMenuItems(void)
+{
+ if (hHookPreBuildContactMenu) UnhookEvent(hHookPreBuildContactMenu);
+ if (hHookIconsChanged) UnhookEvent(hHookIconsChanged);
+} \ No newline at end of file
diff --git a/spamfilter/menuitems.h b/spamfilter/menuitems.h
new file mode 100644
index 0000000..c116a13
--- /dev/null
+++ b/spamfilter/menuitems.h
@@ -0,0 +1,30 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_MENUITEMS_H__
+#define SF_MENUITEMS_H__ 1
+
+
+// -- Prototypes
+void InitMenuItems(void);
+void UninitMenuItems(void);
+
+#endif // SF_MENUITEMS_H__ \ No newline at end of file
diff --git a/spamfilter/options.c b/spamfilter/options.c
new file mode 100644
index 0000000..62c2af3
--- /dev/null
+++ b/spamfilter/options.c
@@ -0,0 +1,3699 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -- Variables: Events/Services
+HANDLE hHookOptInit = NULL;
+HANDLE hHookDbSettingChangedOpt = NULL;
+
+// -- Defines: Self-defined dialog messages
+#define SFM_ENABLE_SUBCTRLS (WM_APP+121) // wParam=lParam=0
+#define SFM_REMOVE_SPAMDEFINITIONFILE (WM_APP+122) // wParam=(int)listIndex, lParam=0
+
+// -- Structs
+struct WORDSLISTBOXADDITEMPARAMS
+{
+ POINT ptPos;
+ WCHAR* pszTitle;
+ WCHAR* pszNewItem;
+};
+
+
+// -----------------------------------------
+
+
+BOOL CALLBACK DlgProcSpamFilterOpt(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ /*
+ #if defined(UNICODE)
+ case WM_NOTIFYFORMAT:
+ {
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)NFR_UNICODE);
+ return TRUE;
+ }
+ #endif
+ */
+
+ case WM_INITDIALOG:
+ {
+ hwndSpamFilterOpt = hwndDlg;
+
+ /*
+ #if defined(UNICODE)
+ // Convert DlgProc to Unicode (IsWindowUnicode returns true after this call -> see docs)
+ SetWindowLongPtrW(hwndDlg, DWLP_DLGPROC, (LONG_PTR)DlgProcSpamFilterOpt);
+ #endif
+ */
+
+ // Init treeview (TVS_CHECKBOXES style is too buggy [it behaves incorrect when space pressed on sections], so self-implementing checkboxes)
+ // The treeview does checkboxes exactly the same as it is done here
+ TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TREEVIEW_MESSAGETYPES), LoadSystemCheckboxImageList(), TVSIL_STATE);
+
+ // Translate dialog
+ TranslateDialogDefault(hwndDlg);
+
+ // Set font
+ {
+ HFONT hUnderlinedFont;
+ LOGFONT lf;
+
+ GetObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_CHECKBOX_ADVERTISMENTFILTER, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfUnderline = TRUE;
+ hUnderlinedFont = CreateFontIndirect(&lf);
+
+ SendDlgItemMessage(hwndDlg, IDC_CHECKBOX_ADVERTISMENTFILTER, WM_SETFONT, (WPARAM)hUnderlinedFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_CHECKBOX_DISLIKEDMESSAGEFILTER, WM_SETFONT, (WPARAM)hUnderlinedFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_CHECKBOX_ROBOTFILTER, WM_SETFONT, (WPARAM)hUnderlinedFont, 0);
+ }
+
+ // Init file name control
+ {
+ RECT rc;
+
+ // Set icon and resize button
+ SendDlgItemMessage(hwndDlg, IDC_BUTTON_LOGBROWSE, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)LoadSystemIcon(IDB_STD_SMALL_COLOR, STD_FILEOPEN));
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_BUTTON_LOGBROWSE), NULL, 0, 0, 20, 20, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+
+ // Transform into file edit browse combo control
+ MakePathEditCtrl(GetDlgItem(hwndDlg, IDC_EDITBOX_LOGFILE), GetDlgItem(hwndDlg, IDC_BUTTON_LOGBROWSE));
+
+ if (GetClientRect(GetDlgItem(hwndDlg, IDC_BUTTON_OPENLOG), &rc))
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_BUTTON_OPENLOG), NULL, 0, 0, (rc.right-rc.left), (rc.bottom-rc.top)+2*GetSystemMetrics(SM_CYBORDER), SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+
+ // Forwarding all file drag/drops of spam filter options to the file edit
+ // (child windows of childs seem not to receive that message...)
+ DragAcceptFiles(hwndDlg, (BOOL)SendMessage(GetParent(hwndDlg), PSM_ISEXPERT, 0, 0));
+ }
+
+ // Load log file from database
+ {
+ DBVARIANT dbv;
+
+ if(DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_LOGFILE, &dbv) == 0) // If existant
+ {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_LOGFILE, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+
+ } else {
+ // Nothing specified, display default log filename and path
+ WCHAR szLogFile[MAX_PATH];
+
+ PConstructLocalPath(szLogFile, CSIDL_EXT_EXECUTABLE, LOGS_SUBDIRECTORY, DEFAULT_LOGFILE, NULL);
+ PMakePathPretty(szLogFile, PC_ABSOLUTERELATIVE);
+
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_LOGFILE, szLogFile);
+ }
+ }
+
+ // Init checkboxes
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_ADVERTISMENTFILTER, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)DEFAULT_SETTING_ADVERTISMENTFILTER) ? BST_CHECKED : BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_DISLIKEDMESSAGEFILTER, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, (BYTE)DEFAULT_SETTING_DISLIKEDMESSAGEFILTER) ? BST_CHECKED : BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_ROBOTFILTER, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOTFILTER, (BYTE)DEFAULT_SETTING_ROBOTFILTER) ? BST_CHECKED : BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_POPUP, IsPopupAvailable(TRUE) ? DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_POPUP, (BYTE)(DEFAULT_SETTING_POPUP ? BST_CHECKED : BST_UNCHECKED)) : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_AUTOADDSPAMMERS, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_AUTOADDSPAMMERS, (BYTE)(DEFAULT_SETTING_AUTOADDSPAMMERS ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_LOGGING, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_LOGGING, (BYTE)DEFAULT_SETTING_LOGGING) ? BST_CHECKED : BST_UNCHECKED));
+
+ // Check/Uncheck radio buttons:
+ // Delete/Mark read?
+ CheckRadioButton(hwndDlg, IDC_RADIO_MARKREADMSG, IDC_RADIO_DELETEMSG, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_BEHAVIOUR, (BYTE)DEFAULT_SETTING_BEHAVIOUR) == 1) ? IDC_RADIO_MARKREADMSG : IDC_RADIO_DELETEMSG);
+
+ // Init sub control state
+ SendMessage(hwndDlg, SFM_ENABLE_SUBCTRLS, 0, 0);
+ SendMessage(hwndDlg, SFM_REBUILD_MSGTYPES, 0, 0);
+
+ SendMessage(hwndDlg, SFM_CHECK_AUTOIGNORE, 0, 0);
+ PostMessage(hwndDlg, SFM_VALIDATE_LOGFILENAME, 0, 0);
+ return TRUE;
+ }
+
+ case SFM_ENABLE_SUBCTRLS:
+ {
+ BOOL bStatus;
+
+ // IDC_CHECKBOX_ADVERTISMENTFILTER
+ BOOL bStatusRobotFilter = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_ROBOTFILTER) == BST_CHECKED);
+ BOOL bStatusChainFilter = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_DISLIKEDMESSAGEFILTER) == BST_CHECKED);
+ BOOL bStatusAdvertismentFilter = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_ADVERTISMENTFILTER) == BST_CHECKED);
+ bStatus = bStatusAdvertismentFilter || bStatusChainFilter || bStatusRobotFilter;
+
+ EnableDlgItem(hwndDlg, IDC_BUTTON_ADVERTISMENTFILTER, bStatusAdvertismentFilter);
+ EnableDlgItem(hwndDlg, IDC_BUTTON_DISLIKEDMESSAGEFILTER, bStatusChainFilter);
+ EnableDlgItem(hwndDlg, IDC_BUTTON_ROBOTFILTER, bStatusRobotFilter);
+
+ EnableDlgItem(hwndDlg, IDC_GROUP_BEHAVIOUR, bStatus);
+ EnableDlgItem(hwndDlg, IDC_RADIO_MARKREADMSG, bStatus);
+ EnableDlgItem(hwndDlg, IDC_RADIO_DELETEMSG, bStatus);
+
+ EnableDlgItem(hwndDlg, IDC_GROUP_IGNORE, bStatus);
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_AUTOADDSPAMMERS, bStatus);
+
+ EnableDlgItem(hwndDlg, IDC_GROUP_LOGGING, bStatus);
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_LOGGING, bStatus);
+
+ EnableDlgItem(hwndDlg, IDC_GROUP_MESSAGETYPES, bStatus);
+ EnableDlgItem(hwndDlg, IDC_TEXT_MESSAGETYPES, bStatus);
+ EnableDlgItem(hwndDlg, IDC_TREEVIEW_MESSAGETYPES, bStatus);
+
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_POPUP, IsPopupAvailable(TRUE) ? bStatus: FALSE);
+
+ // Auto Ignore
+ SetSpinCtrlRange(GetDlgItem(hwndDlg, IDC_SPIN_ALLOWEDSPAMMESSAGES), bStatusRobotFilter?1:0, 999, DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ALLOWEDSPAMMESSAGES, (DWORD)DEFAULT_SETTING_ALLOWEDSPAMMESSAGES), FALSE);
+ bStatus = (IsWindowEnabled(GetDlgItem(hwndDlg,IDC_CHECKBOX_AUTOADDSPAMMERS)) && (IsDlgButtonChecked(hwndDlg,IDC_CHECKBOX_AUTOADDSPAMMERS) == BST_CHECKED));
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDSPAMMESSAGES, bStatus);
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_ALLOWEDSPAMMESSAGES, bStatus);
+ EnableDlgItem(hwndDlg, IDC_SPIN_ALLOWEDSPAMMESSAGES, bStatus);
+
+ // Logging
+ bStatus = (IsWindowEnabled(GetDlgItem(hwndDlg,IDC_CHECKBOX_LOGGING)) && (IsDlgButtonChecked(hwndDlg,IDC_CHECKBOX_LOGGING) == BST_CHECKED));
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_LOGFILE, bStatus);
+ EnableDlgItem(hwndDlg, IDC_BUTTON_LOGBROWSE, bStatus);
+
+ return TRUE;
+ }
+
+ case SFM_REBUILD_MSGTYPES:
+ {
+ HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_TREEVIEW_MESSAGETYPES);
+ HIMAGELIST hImageList;
+
+ unsigned int u;
+ char* pszSetting;
+ TVINSERTSTRUCT tvis;
+ BOOL bUseSections;
+
+ HTREEITEM hItem;
+ MESSAGETYPEDESC* pmtd;
+ int iCmpResult;
+
+ // Clear treeview
+ TreeView_DeleteAllItems(hwndTreeView);
+
+ if (InSendMessage())
+ ReplyMessage(TRUE);
+
+ // Check if only one section is installed
+ // -> if only one section is installed it is not necessary to show the section
+ EnterCriticalSection(&csMsgTypes); // thread safety
+
+ bUseSections = !IsOnlyOneMsgTypeSectionRegistered();
+
+ // New image list
+ hImageList = TreeView_GetImageList(hwndTreeView, TVSIL_NORMAL);
+ if (hImageList) ImageList_Destroy(hImageList);
+ hImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON), (IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK, uMsgTypesCount, bUseSections?uMsgTypesCount:0); // includes msgtype and section images
+ TreeView_SetImageList(hwndTreeView, hImageList, TVSIL_NORMAL);
+
+ // Add items to treeview
+ if (pamtdMsgTypes)
+ {
+ for (u=0; u<uMsgTypesCount; u++)
+ {
+ if (pamtdMsgTypes[u].dwFlags&MTDF_HIDDEN) continue;
+
+ if (!bUseSections)
+ {
+ tvis.hParent = NULL;
+
+ } else {
+ // Find section if already added
+ tvis.hParent = NULL;
+ for (hItem=TreeView_GetRoot(hwndTreeView); hItem; hItem=TreeView_GetNextSibling(hwndTreeView, hItem))
+ {
+ pmtd = GetDataFromTreeViewItem(hwndTreeView, hItem);
+ if (pmtd)
+ if (StrCmpA(pamtdMsgTypes[u].pszSection, pmtd->pszSection) == 0)
+ {
+ tvis.hParent = hItem;
+ break;
+ }
+ }
+
+ // Section does not yet exist: add it first
+ if (!tvis.hParent)
+ {
+ tvis.item.mask = TVIF_TEXT|TVIF_STATE|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = pamtdMsgTypes[u].ptszSectionDescription;
+ tvis.item.stateMask = TVIS_BOLD|TVIS_EXPANDED|TVIS_STATEIMAGEMASK;
+ tvis.item.state = TVIS_BOLD|TVIS_EXPANDED|INDEXTOSTATEIMAGEMASK(0); // no checkbox for sections
+ tvis.item.lParam = (LPARAM)u;
+ tvis.item.iImage = ImageList_AddIcon(hImageList, (pamtdMsgTypes[u].hSectionIcon ? pamtdMsgTypes[u].hSectionIcon : LoadSkinnedIcon(SKINICON_OTHER_MIRANDA)) );
+ tvis.item.iSelectedImage = tvis.item.iImage; // else the wrong icon will be shown when selected
+
+ // Position new section using position numbers (lowest first)
+ tvis.hInsertAfter = TVI_FIRST;
+ for (hItem=TreeView_GetRoot(hwndTreeView); hItem; hItem=TreeView_GetNextSibling(hwndTreeView, hItem))
+ {
+ pmtd = GetDataFromTreeViewItem(hwndTreeView, hItem);
+ if (pmtd)
+ {
+ if (pamtdMsgTypes[u].iSectionPosition > pmtd->iSectionPosition)
+ {
+ tvis.hInsertAfter = hItem;
+
+ // Colliding numbers are sorted alphabetically
+ } else if (pamtdMsgTypes[u].iSectionPosition == pmtd->iSectionPosition) {
+
+ int iCmpResult;
+
+ iCmpResult = CompareString(GetThreadLocale(), 0, pamtdMsgTypes[u].ptszSectionDescription, -1, pmtd->ptszSectionDescription, -1); // or: LOCALE_USER_DEFAULT
+
+ if (iCmpResult != 0) iCmpResult -= 2; // see docs
+
+ if (iCmpResult >= 0)
+ tvis.hInsertAfter = hItem;
+
+ } else {
+ // iteration pos is larger than new item pos
+ // (or reached "Other" section)
+ // -> found out where to sort in
+ break;
+ }
+ }
+ }
+
+ tvis.hParent = TreeView_InsertItem(hwndTreeView, &tvis);
+ }
+ }
+
+ pszSetting = GetMsgTypeSettingName(pamtdMsgTypes[u].pszSection, pamtdMsgTypes[u].pszName);
+ if (!pszSetting) continue;
+
+ tvis.item.mask = TVIF_TEXT|TVIF_STATE|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvis.item.pszText = pamtdMsgTypes[u].ptszDescription;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(DBGetContactSettingByte(NULL, DB_MODULE_NAME_MSGTYPES, pszSetting, (BYTE)!(pamtdMsgTypes[u].dwFlags&MTDF_DEFAULTDISABLED))?2:1);
+ tvis.item.lParam = (LPARAM)u;
+
+ if ((int)pamtdMsgTypes[u].hIcon < 0)
+ tvis.item.iImage = ImageList_AddIcon(hImageList, LoadSkinnedIcon((-1)*(int)pamtdMsgTypes[u].hIcon));
+ else
+ tvis.item.iImage = ImageList_AddIcon(hImageList, pamtdMsgTypes[u].hIcon ? pamtdMsgTypes[u].hIcon : (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED));
+
+ tvis.item.iSelectedImage = tvis.item.iImage; // else the wrong icon will be shown when selected
+ mir_free(pszSetting);
+
+ // Position new item using position numbers (lowest first)
+ tvis.hInsertAfter = TVI_FIRST;
+ for (hItem=TreeView_GetChild(hwndTreeView, tvis.hParent); hItem; hItem=TreeView_GetNextSibling(hwndTreeView, hItem))
+ {
+ pmtd = GetDataFromTreeViewItem(hwndTreeView, hItem);
+ if (pmtd)
+ {
+ if (pamtdMsgTypes[u].iPosition > pmtd->iPosition)
+ {
+ tvis.hInsertAfter = hItem;
+
+ // Colliding numbers are sorted alphabetically
+ } else if (pamtdMsgTypes[u].iPosition == pmtd->iPosition)
+ {
+ iCmpResult = CompareString(GetThreadLocale(), 0, pamtdMsgTypes[u].ptszDescription, -1, pmtd->ptszDescription, -1); // or: LOCALE_USER_DEFAULT
+
+ if (iCmpResult != 0) iCmpResult -= 2; // see docs
+
+ if (iCmpResult >= 0)
+ tvis.hInsertAfter = hItem;
+
+ } else {
+ // Iteration pos is larger than new item pos
+ // -> found out where to sort in
+ break;
+ }
+ }
+ }
+
+ TreeView_InsertItem(hwndTreeView, &tvis);
+ }
+ }
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+
+ return TRUE;
+ }
+
+ case SFM_CHECK_AUTOIGNORE:
+ {
+ EnableDlgItem(hwndDlg, IDC_BUTTON_RESETAUTOIGNORE, (CallService(MS_SPAMFILTER_RESETAUTOIGNORE, (WPARAM)TRUE, 0) > 0));
+ return TRUE;
+ }
+
+ case SFM_VALIDATE_LOGFILENAME:
+ {
+ const WCHAR* aszFileExtensions[] = {_T(".txt"), _T(".log"), _T(".csv"), _T(".xml"), _T("."), _T(""), NULL};
+ const WCHAR* aszContentTypes[] = {_T("text/plain"), _T("text/xml"), _T("application/xml"), NULL};
+
+ WCHAR szLogFile[MAX_PATH];
+ DWORD dwPathFlags;
+ BOOL bIsValid;
+
+ GetDlgItemText(hwndDlg, IDC_EDITBOX_LOGFILE, szLogFile, ARRAYSIZE(szLogFile));
+ dwPathFlags = PMakePathUsable(szLogFile);
+
+ // Make sure the correct xml file extension is specified
+ if (PathMatchSpec(szLogFile, _T("*.xsl")))
+ PathRenameExtension(szLogFile, _T(".xml"));
+
+ bIsValid = PIsValidFile(szLogFile, aszFileExtensions, ARRAYSIZE(aszFileExtensions), aszContentTypes, ARRAYSIZE(aszContentTypes));
+
+ PMakePathPretty(szLogFile, dwPathFlags);
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_LOGFILE, szLogFile);
+
+ EnableDlgItem(hwndDlg, IDC_BUTTON_OPENLOG, bIsValid);
+ return TRUE;
+ }
+
+ case WM_DROPFILES:
+ {
+ // Forward do log edit (child windows of childs can not receive that message)
+ PostMessage(GetDlgItem(hwndDlg, IDC_EDITBOX_LOGFILE), uMsg, wParam, lParam);
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ {
+ HIMAGELIST hImgLst;
+ // Free font
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_CHECKBOX_ADVERTISMENTFILTER, WM_GETFONT, 0, 0));
+
+ // Destroy tree view imagelist since it does not get destroyed automatically (see msdn docs)
+ hImgLst = TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_TREEVIEW_MESSAGETYPES), TVSIL_STATE);
+ if (hImgLst) {
+ TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TREEVIEW_MESSAGETYPES), NULL, TVSIL_STATE); // Avoiding Access Violation in CommonControls DLL
+ ImageList_Destroy(hImgLst);
+ }
+ hImgLst = TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_TREEVIEW_MESSAGETYPES), TVSIL_NORMAL);
+ if (hImgLst) {
+ TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TREEVIEW_MESSAGETYPES), NULL, TVSIL_NORMAL); // Avoiding Access Violation in CommonControls DLL
+ ImageList_Destroy(hImgLst);
+ }
+
+ // Destroy open icon
+ DestroyIcon((HICON)SendDlgItemMessage(hwndDlg, IDC_BUTTON_LOGBROWSE, BM_GETIMAGE, (WPARAM)IMAGE_ICON, 0));
+
+ hwndSpamFilterOpt = NULL;
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_BUTTON_ADVERTISMENTFILTER:
+ {
+ CallService(MS_SPAMFILTER_SHOWFILTERDIALOG, (WPARAM)hwndDlg, (LPARAM)SFT_ADVERTISMENT_FILTER);
+
+ // Do not activate "Apply" button
+ return FALSE;
+ }
+
+ case IDC_BUTTON_DISLIKEDMESSAGEFILTER:
+ {
+ CallService(MS_SPAMFILTER_SHOWFILTERDIALOG, (WPARAM)hwndDlg, (LPARAM)SFT_DISLIKEDMESSAGES_FILTER);
+
+ // Do not activate "Apply" button
+ return FALSE;
+ }
+
+ case IDC_BUTTON_ROBOTFILTER:
+ {
+ CallService(MS_SPAMFILTER_SHOWFILTERDIALOG, (WPARAM)hwndDlg, (LPARAM)SFT_ROBOT_FILTER);
+
+ // Do not activate "Apply" button
+ return FALSE;
+ }
+
+ case IDC_BUTTON_LOGBROWSE:
+ {
+ WCHAR szLogFile[MAX_PATH];
+ WCHAR szInitialDir[MAX_PATH];
+ WCHAR szDefaultLogFile[MAX_PATH];
+ BOOL bDefaultDirUsed, bDefaultFileUsed;
+ BOOL bFileChanged;
+ DWORD dwPathFlags;
+ int iIndexAll, iIndexOther;
+ OPENFILENAME ofn;
+
+ // Set focus on editbox
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_LOGFILE), (LPARAM)TRUE);
+
+ // Get chosen log file
+ GetDlgItemText(hwndDlg, IDC_EDITBOX_LOGFILE, szLogFile, ARRAYSIZE(szLogFile));
+ dwPathFlags = PMakePathUsable(szLogFile);
+
+ // Construct default log file
+ PConstructLocalPath(szDefaultLogFile, CSIDL_EXT_EXECUTABLE, LOGS_SUBDIRECTORY, DEFAULT_LOGFILE, NULL);
+
+ // Check if default file gets used
+ if ((lstrlen(szLogFile) == 0) || (PathFindFileName(szLogFile) == szLogFile))
+ {
+ // Using default dir if nothing specified
+ mir_sntprintf(szLogFile, ARRAYSIZE(szLogFile), _T("%s"), szDefaultLogFile);
+ bDefaultDirUsed = bDefaultFileUsed = TRUE;
+ } else {
+ bDefaultDirUsed = bDefaultFileUsed = (StrCmpI(szLogFile, szDefaultLogFile) == 0);
+ }
+
+ // Using default file name when no file given
+ if ( (lstrlen(PathFindFileName(szLogFile)) == 0) || PathIsDirectory(szLogFile))
+ {
+ PathAppend(szLogFile, PathFindFileName(szDefaultLogFile));
+ bDefaultFileUsed = TRUE;
+ }
+
+ // Split file name
+ {
+ mir_sntprintf(szInitialDir, ARRAYSIZE(szInitialDir), _T("%s"), szLogFile);
+
+ // Get file name
+ mir_sntprintf(szLogFile, ARRAYSIZE(szLogFile), _T("%s"), PathFindFileName(szInitialDir));
+
+ // Get initial dir
+ PathRemoveFileSpec(szInitialDir);
+ }
+
+ // Initialize OPENFILENAME structure
+ ZeroMemory(&ofn, sizeof(ofn));
+ ofn.lStructSize = (IsWinVerNT() && !IsWinVer2000Plus()) ? OPENFILENAME_SIZE_VERSION_400 : sizeof(ofn); // backward compatibility for NT4
+ ofn.hwndOwner = hwndDlg;
+ ofn.Flags = OFN_ENABLESIZING|OFN_EXPLORER | OFN_LONGNAMES|OFN_HIDEREADONLY|OFN_NOREADONLYRETURN|OFN_OVERWRITEPROMPT; // also possible: OFN_DONTADDTORECENT, OFN_CREATEPROMPT
+ ofn.lpstrFile = szLogFile;
+ ofn.nMaxFile = ARRAYSIZE(szLogFile);
+ ofn.lpstrTitle = TranslateT("Select Log File");
+ ofn.lpstrDefExt = CharNext(PathFindExtension(DEFAULT_LOGFILE));
+
+ // Make sure the initial directory exists
+ if (SUCCEEDED(PPreparePathForWrite(hwndDlg, szInitialDir, !bDefaultDirUsed, FALSE)))
+ ofn.lpstrInitialDir = szInitialDir;
+
+ // File Filter
+ {
+ STRINGLIST* pslFilter = SLNewList();
+
+ SLAddItemPair(pslFilter, TranslateT("Log Files (*.log)"), _T("*.log"));
+ SLAddItemPair(pslFilter, TranslateT("Text Files (*.txt)"), _T("*.txt"));
+ SLAddItemPair(pslFilter, TranslateT("Comma Separated Values Files (*.csv)"), _T("*.csv"));
+ SLAddItemPair(pslFilter, TranslateT("XML Files (*.xml; *.xsl)"), _T("*.xml; *.xsl"));
+ iIndexAll = SLAddItemPair(pslFilter, TranslateT("All Logging Files (*.log; *.txt; *.csv; *.xml)"), _T("*.log; *.txt; *.csv; *.xml"));
+ iIndexOther = SLAddItemPair(pslFilter, TranslateT("All Files (*.*)"), _T("*.*"));
+
+ ofn.lpstrFilter = SLConvertToString(pslFilter, NULL, FALSE, TRUE, NULL);
+ SLFreeList(pslFilter);
+ }
+
+ // Default selected index
+ if ( PathMatchSpec(szLogFile, _T("*.log")) ||
+ PathMatchSpec(szLogFile, _T("*.txt")) ||
+ PathMatchSpec(szLogFile, _T("*.csv")) ||
+ PathMatchSpec(szLogFile, _T("*.xml")) )
+ {
+ // Show "all logging files" to show the user the possibilities
+ ofn.nFilterIndex = (iIndexAll/2)+1; // filter index is 1 based
+ } else {
+ // Select "All files"
+ ofn.nFilterIndex = (iIndexOther/2)+1; // filter index is 1 based
+ }
+
+ // Show browse file dialog
+ bFileChanged = GetSaveFileName(&ofn);
+ SLFreeReturn((WCHAR*)ofn.lpstrFilter);
+
+ if (bFileChanged)
+ {
+ PMakePathPretty(szLogFile, dwPathFlags);
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_LOGFILE, szLogFile);
+
+ // Re-Set focus on editbox
+ PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_LOGFILE), (LPARAM)TRUE);
+ PostMessage(hwndDlg, SFM_VALIDATE_LOGFILENAME, 0, 0);
+
+ } else {
+ PostMessage(hwndDlg, SFM_VALIDATE_LOGFILENAME, 0, 0);
+ // Do not activate "Apply" button
+ return FALSE;
+ }
+
+ break;
+ }
+
+ case IDC_BUTTON_OPENLOG:
+ {
+ WCHAR szLogFile[MAX_PATH];
+
+ GetDlgItemText(hwndDlg, IDC_EDITBOX_LOGFILE, szLogFile, ARRAYSIZE(szLogFile));
+ PMakePathUsable(szLogFile);
+
+ if ((int)ShellExecute(hwndDlg, _T("open"), szLogFile, NULL, NULL, SW_SHOWNORMAL) <= 32)
+ EnableDlgItem(hwndDlg, IDC_BUTTON_OPENLOG, FALSE);
+
+ // Do not activate "Apply" button
+ return FALSE;
+ }
+
+ case IDC_BUTTON_RESETAUTOIGNORE:
+ {
+ // Show message box before doing reset
+ if (MessageBoxEx(hwndDlg, TranslateT("Are you sure that the Auto Ignore counters shall really be reset?\r\n\r\nSpammers that are not already ignored will then not be remembered any longer."), TranslateT("Reset Auto Ignore"), MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON1, LANGIDFROMLCID(GetThreadLocale())) == IDYES)
+ EnableDlgItem(hwndDlg, IDC_BUTTON_RESETAUTOIGNORE, (CallService(MS_SPAMFILTER_RESETAUTOIGNORE, (WPARAM)FALSE, 0) != 0) );
+
+ // Do not activate "Apply" button
+ return FALSE;
+ }
+
+ case IDC_BUTTON_SHOWSPAMMERS:
+ {
+ CallService(MS_SPAMFILTER_SHOWFILTERDIALOG, (WPARAM)hwndDlg, (LPARAM)SFTEXF_SPAMMERS_INFO);
+
+ // Do not activate "Apply" button
+ return FALSE;
+ }
+
+ case IDC_CHECKBOX_AUTOADDSPAMMERS:
+ case IDC_CHECKBOX_LOGGING:
+ case IDC_CHECKBOX_ADVERTISMENTFILTER:
+ case IDC_CHECKBOX_DISLIKEDMESSAGEFILTER:
+ case IDC_CHECKBOX_ROBOTFILTER:
+ {
+ PostMessage(hwndDlg, SFM_ENABLE_SUBCTRLS, 0, 0);
+ break;
+ }
+
+ case IDC_EDITBOX_LOGFILE:
+ {
+ switch (HIWORD(wParam))
+ {
+ case EN_CHANGE:
+ {
+ EnableDlgItem(hwndDlg, IDC_BUTTON_OPENLOG, FALSE);
+ break;
+ }
+
+ case EN_KILLFOCUS:
+ {
+ PostMessage(hwndDlg, SFM_VALIDATE_LOGFILENAME, 0, 0);
+ break;
+ }
+ }
+
+ // fall through
+ }
+
+ case IDC_EDITBOX_ALLOWEDSPAMMESSAGES:
+ {
+ if (HIWORD(wParam) != EN_SETFOCUS)
+ {
+ // Do not activate "Apply" button
+ return FALSE;
+ }
+
+ break;
+ }
+ }
+
+ // Send message to activate "Apply" button...
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return FALSE;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR pnmhdr = (LPNMHDR)lParam;
+
+ switch (pnmhdr->idFrom)
+ {
+ case IDC_TREEVIEW_MESSAGETYPES:
+ {
+ switch (pnmhdr->code)
+ {
+ case TVN_KEYDOWN:
+ {
+ switch (((NMTVKEYDOWN*)lParam)->wVKey)
+ {
+ case VK_SPACE:
+ {
+ if (TreeView_GetSelection(pnmhdr->hwndFrom))
+ {
+ ToggleTreeViewItemState(pnmhdr->hwndFrom, TreeView_GetSelection(pnmhdr->hwndFrom));
+
+ // Send message to activate "Apply" button...
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ // Prevent default processing
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)FALSE);
+ return TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case NM_CLICK:
+ {
+ TVHITTESTINFO hti;
+
+ // Get click position
+ GetCursorPos(&hti.pt);
+ ScreenToClient(pnmhdr->hwndFrom, &hti.pt);
+
+ // Check if click was on state icon
+ if (TreeView_HitTest(pnmhdr->hwndFrom, &hti))
+ if (hti.flags&TVHT_ONITEMSTATEICON)
+ {
+ ToggleTreeViewItemState(pnmhdr->hwndFrom, hti.hItem);
+
+ // Send message to activate "Apply" button...
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ return TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case 0:
+ {
+ switch (pnmhdr->code)
+ {
+ case PSN_EXPERTCHANGED: // Expert mode checkbox checked/unchecked
+ {
+ BOOL bNewState = (BOOL)lParam;
+
+ // Accept files (when log file edit control is visible)
+ DragAcceptFiles(hwndDlg, bNewState);
+ return TRUE;
+ }
+
+ case PSN_APPLY: // When "Apply"/"OK" pressed in options dialog then save changes
+ {
+ BOOL bFilterState = ( (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_DISLIKEDMESSAGEFILTER) == BST_CHECKED) ||
+ (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_ADVERTISMENTFILTER) == BST_CHECKED) ||
+ (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_ROBOTFILTER) == BST_CHECKED) );
+
+ // Save filter state
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_ADVERTISMENTFILTER) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_DISLIKEDMESSAGEFILTER) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOTFILTER, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_ROBOTFILTER) == BST_CHECKED));
+
+ // Save popup state
+ if (IsPopupAvailable(TRUE))
+ {
+ BOOL bPopupState = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_POPUP) == BST_CHECKED);
+
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_POPUP, (BYTE)bPopupState);
+ SetPopupMenuItem(bPopupState, !bFilterState);
+ }
+
+ // Save Auto Ignore
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_AUTOADDSPAMMERS, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_AUTOADDSPAMMERS) == BST_CHECKED));
+ DBWriteContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ALLOWEDSPAMMESSAGES, (WORD)GetDlgItemInt(hwndDlg, IDC_EDITBOX_ALLOWEDSPAMMESSAGES, NULL, FALSE));
+
+ // Save behaviour
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_BEHAVIOUR, (BYTE)((IsDlgButtonChecked(hwndDlg, IDC_RADIO_MARKREADMSG) == BST_CHECKED) ? 1 : 2));
+
+ // Save log file
+ {
+ WCHAR szLogFile[MAX_PATH];
+
+ GetDlgItemText(hwndDlg, IDC_EDITBOX_LOGFILE, szLogFile, ARRAYSIZE(szLogFile));
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_LOGFILE, szLogFile);
+
+ // Save activation of logging
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_LOGGING, (BYTE)((lstrlen(szLogFile) > 0) ? (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_LOGGING) == BST_CHECKED) : FALSE));
+ }
+
+ // Save message types
+ {
+ HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_TREEVIEW_MESSAGETYPES);
+ MESSAGETYPEDESC* pmtd;
+ HTREEITEM hSectionItem;
+ HTREEITEM hItem;
+ char* pszSetting;
+
+ for (hSectionItem=TreeView_GetRoot(hwndTreeView); hSectionItem; hSectionItem=TreeView_GetNextSibling(hwndTreeView, hSectionItem))
+ for (hItem=TreeView_GetChild(hwndTreeView, hSectionItem); hItem; hItem=TreeView_GetNextSibling(hwndTreeView, hItem))
+ {
+ EnterCriticalSection(&csMsgTypes); // thread safety
+
+ pmtd = GetDataFromTreeViewItem(hwndTreeView, hItem);
+ if (pmtd)
+ {
+ // Save setting
+ pszSetting = GetMsgTypeSettingName(pmtd->pszSection, pmtd->pszName);
+ if (!pszSetting) continue;
+
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME_MSGTYPES, pszSetting, (BYTE)((TreeView_GetItemState(hwndTreeView, hItem, TVIS_STATEIMAGEMASK)&INDEXTOSTATEIMAGEMASK(2))?TRUE:FALSE));
+
+ mir_free(pszSetting);
+ }
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ }
+ }
+
+ return FALSE;
+ }
+
+ } // switch code
+
+ break;
+ }
+
+ } // switch idFrom
+
+ break;
+ }
+
+ }
+
+ return FALSE;
+}
+
+
+BOOL CALLBACK DlgProcConfigureAdvertismentFilter(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ hwndAdvertismentFilter = hwndDlg;
+
+ // Translate dialog
+ TranslateDialogDefault(hwndDlg);
+
+ // Make group headings bold
+ {
+ HFONT hBoldFont;
+ LOGFONT lf;
+
+ GetObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_GROUP_BADWORDS, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hBoldFont = CreateFontIndirect(&lf);
+
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_BADWORDS, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_OTHERCONTENTS, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_SPAMMERS, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_GOODWORDS, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT_WARNING, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ }
+
+ // Set icon
+ SendMessage(hwndDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)(HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED));
+
+ // Init checkboxes
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_DEFAULTBADWORDS, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DEFAULTBADWORDS, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_DEFAULTBADWORDS ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_FORMATING, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_FORMATING, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_FORMATING ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_URL, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_URL, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_URL)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_CAPITALIZED, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CAPITALIZED, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_CAPITALIZED ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_DEFAULTSPAMMERS, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DEFAULTSPAMMERS, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_DEFAULTSPAMMERS ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_MULTIPLEMESSAGES, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_MULTIPLEMESSAGES, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_MULTIPLEMESSAGES ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_TEASERMESSAGES, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_TEASERMESSAGES, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_TEASERMESSAGES ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_EMPTY, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_EMPTY, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_EMPTY ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_PHONENUMBERS, DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_PHONENUMBERS, (BYTE)(DEFAULT_SETTING_ADVERTISMENT_PHONENUMBERS ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED);
+
+ // Load default bad words count (for spins)
+ SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)SLNewList());
+ SendMessage(hwndDlg, SFM_RELOAD_DEFAULTBADWORDS, 0, 0);
+
+ // Init listboxes
+ {
+ STRINGLIST* pslWords = SLNewList();
+
+ // Bad words
+ GetSpamDefinitionData(pslWords, SDID_CUSTOM_BADWORDS);
+ WordsListBoxInit(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS, IDC_BUTTON_ADDBADWORD, IDC_BUTTON_DELETEBADWORD, pslWords, Translate("Add bad phrase/pattern"), Translate("Remove bad phrase/pattern"));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDBADWORDS, (SLGetItemCount(pslWords) > 0) ? (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CUSTOMIZEDBADWORDS, (BYTE)TRUE)?BST_CHECKED:BST_UNCHECKED) : BST_UNCHECKED);
+
+ SLClearList(pslWords);
+
+ // Good words
+ GetSpamDefinitionData(pslWords, SDID_CUSTOM_GOODWORDS);
+ WordsListBoxInit(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS, IDC_BUTTON_ADDGOODWORD, IDC_BUTTON_DELETEGOODWORD, pslWords, Translate("Add good phrase/pattern"), Translate("Remove good phrase/pattern"));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDGOODWORDS, (SLGetItemCount(pslWords) > 0) ? (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CUSTOMIZEDGOODWORDS, (BYTE)TRUE)?BST_CHECKED:BST_UNCHECKED) : BST_UNCHECKED);
+
+ SLFreeList(pslWords);
+ }
+
+ // Init editboxes and spins
+ {
+ BOOL bStatus = TRUE;
+ int iBadWordsCount = ( (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDBADWORDS) == BST_CHECKED) ? SendDlgItemMessage(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS, LB_GETCOUNT, 0, 0) : 0)
+ + ( (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_DEFAULTBADWORDS) == BST_CHECKED) ? SLGetItemCount((STRINGLIST*)GetWindowLongPtr(hwndDlg, DWLP_USER)) : 0)
+ - 1;
+
+ // Allowed bad words edit/spin
+ bStatus = SetSpinCtrlRange(GetDlgItem(hwndDlg, IDC_SPIN_ALLOWEDBADWORDS), 0, iBadWordsCount, DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDBADWORDS, (DWORD)DEFAULT_SETTING_ADVERTISMENT_ALLOWEDBADWORDS), TRUE);
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDBADWORDS, bStatus);
+
+ // Allowed formated words edit/spin
+ bStatus = SetSpinCtrlRange(GetDlgItem(hwndDlg, IDC_SPIN_ALLOWEDFORMATEDWORDS), 0, iBadWordsCount, DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDFORMATEDWORDS, (DWORD)DEFAULT_SETTING_ADVERTISMENT_ALLOWEDFORMATEDWORDS), TRUE);
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDFORMATEDWORDS, bStatus);
+
+ // Allowed good words edit/spin
+ SetSpinCtrlRange(GetDlgItem(hwndDlg, IDC_SPIN_ALLOWEDGOODWORDS), 0, (SendDlgItemMessage(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS, LB_GETCOUNT, 0, 0) - 1), DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS, (DWORD)DEFAULT_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS), TRUE);
+ }
+
+ // Check if all definition files do exist
+ if (SLGetItemCount((STRINGLIST*)GetWindowLongPtr(hwndDlg, DWLP_USER)) <= 0) {
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_DEFAULTBADWORDS, FALSE);
+ ShowDlgItem(hwndDlg, IDC_TEXT_WARNING, TRUE);
+ }
+ if (!IsSpamDefinitionDataPresent(SDID_URLTLDS)) {
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_URL, FALSE);
+ ShowDlgItem(hwndDlg, IDC_TEXT_WARNING, TRUE);
+ }
+ if (!IsSpamDefinitionDataPresent(SDID_SPAMMERS)) {
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_DEFAULTSPAMMERS, FALSE);
+ ShowDlgItem(hwndDlg, IDC_TEXT_WARNING, TRUE);
+ }
+ if (!IsSpamDefinitionDataPresent(SDID_PHONENUMBERS)) {
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_PHONENUMBERS, FALSE);
+ ShowDlgItem(hwndDlg, IDC_TEXT_WARNING, TRUE);
+ }
+ if (!IsSpamDefinitionDataPresent(SDID_TEASERTEXTS)) {
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_TEASERMESSAGES, FALSE);
+ ShowDlgItem(hwndDlg, IDC_TEXT_WARNING, TRUE);
+ }
+
+ // Simulate messages
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_CUSTOMIZEDBADWORDS, BN_CLICKED), (LPARAM)NULL);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_CUSTOMIZEDGOODWORDS, BN_CLICKED), (LPARAM)NULL);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_FORMATING, BN_CLICKED), (LPARAM)NULL);
+
+ // Restore dlg position (at the end, so the dialog does not get shown early)
+ Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DIALOGPOSITION);
+ // Make sure the dialog is not moved out of sight
+ SendMessage(hwndDlg, DM_REPOSITION, 0, 0);
+
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), (LPARAM)TRUE);
+ return FALSE;
+ }
+
+ case SFM_RELOAD_DEFAULTBADWORDS:
+ {
+ if (InSendMessage())
+ ReplyMessage(TRUE);
+
+ SLClearList((STRINGLIST*)GetWindowLongPtr(hwndDlg, DWLP_USER));
+ GetSpamDefinitionData((STRINGLIST*)GetWindowLongPtr(hwndDlg, DWLP_USER), SDID_BADWORDS);
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ {
+ // Save dlg position
+ Utils_SaveWindowPosition(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DIALOGPOSITION);
+
+ // Free default bad words
+ SLFreeList((STRINGLIST*)GetWindowLongPtr(hwndDlg, DWLP_USER));
+
+ // Free font
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_GROUP_BADWORDS, WM_GETFONT, 0, 0));
+
+ hwndAdvertismentFilter = NULL;
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_CHECKBOX_DEFAULTBADWORDS:
+ case IDC_CHECKBOX_CUSTOMIZEDBADWORDS:
+ {
+ int iListCount = SendDlgItemMessage(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS, LB_GETCOUNT, 0, 0);
+ BOOL bStatus = ((IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDBADWORDS) == BST_CHECKED)
+ || (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_DEFAULTBADWORDS) == BST_CHECKED));
+
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDBADWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDFORMATEDWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_FORMATING, bStatus);
+
+ bStatus = (bStatus && ((iListCount+SLGetItemCount((STRINGLIST*)GetWindowLongPtr(hwndDlg, DWLP_USER))) > 1));
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDBADWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_ALLOWEDBADWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_SPIN_ALLOWEDBADWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDFORMATEDWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_ALLOWEDFORMATEDWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_SPIN_ALLOWEDFORMATEDWORDS, bStatus);
+
+ // Customized bad words box checked -> Deactivate all customized bad words options
+ bStatus = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDBADWORDS) == BST_CHECKED);
+
+ EnableDlgItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_BUTTON_ADDBADWORD, bStatus);
+ EnableDlgItem(hwndDlg, IDC_BUTTON_DELETEBADWORD, (iListCount > 0) ? bStatus : FALSE);
+
+ // Set/Check allowed bad words value (Simulate changed edit box)
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_EDITBOX_ALLOWEDBADWORDS, EN_KILLFOCUS), 0);
+
+ return TRUE;
+ }
+
+ case IDC_CHECKBOX_CUSTOMIZEDGOODWORDS:
+ {
+ BOOL bStatus;
+ int iListCount = SendDlgItemMessage(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS, LB_GETCOUNT, 0, 0);
+
+ // Customized good words box checked -> Deactivate all good words options
+ bStatus = (IsDlgButtonChecked(hwndDlg,IDC_CHECKBOX_CUSTOMIZEDGOODWORDS) == BST_CHECKED);
+
+ EnableDlgItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDGOODWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_ALLOWEDGOODWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_SPIN_ALLOWEDGOODWORDS, bStatus);
+
+ // Activate Add/Delete buttons?
+ EnableDlgItem(hwndDlg, IDC_BUTTON_ADDGOODWORD, bStatus);
+ EnableDlgItem(hwndDlg, IDC_BUTTON_DELETEGOODWORD, (iListCount > 0) ? bStatus : FALSE);
+
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDGOODWORDS, bStatus);
+
+ bStatus = (bStatus && (iListCount > 1));
+ EnableDlgItem(hwndDlg, IDC_SPIN_ALLOWEDGOODWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_ALLOWEDGOODWORDS, bStatus);
+
+ return TRUE;
+ }
+
+ case IDC_CHECKBOX_FORMATING:
+ {
+ BOOL bStatus = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_FORMATING) == BST_CHECKED);
+
+ EnableDlgItem(hwndDlg, IDC_TEXT_ALLOWEDFORMATEDWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_ALLOWEDFORMATEDWORDS, bStatus);
+ EnableDlgItem(hwndDlg, IDC_SPIN_ALLOWEDFORMATEDWORDS, bStatus);
+
+ return TRUE;
+ }
+
+ case IDC_EDITBOX_ALLOWEDBADWORDS:
+ case IDC_EDITBOX_ALLOWEDFORMATEDWORDS:
+ {
+ switch(HIWORD(wParam))
+ {
+ case EN_KILLFOCUS:
+ {
+ int iRangeMax = ((IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDBADWORDS) == BST_CHECKED) ? SendDlgItemMessage(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS, LB_GETCOUNT, 0, 0) : 0)
+ + ((IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_DEFAULTBADWORDS) == BST_CHECKED) ? SLGetItemCount((STRINGLIST*)GetWindowLongPtr(hwndDlg, DWLP_USER)) : 0)
+ - 1;
+
+ // Check allowed bad words value
+ SetSpinCtrlRange(GetDlgItem(hwndDlg, IDC_SPIN_ALLOWEDBADWORDS), 0, iRangeMax, DEFAULT_SETTING_ADVERTISMENT_ALLOWEDBADWORDS, FALSE);
+
+ // Check allowed formated words value
+ SetSpinCtrlRange(GetDlgItem(hwndDlg, IDC_SPIN_ALLOWEDFORMATEDWORDS), 0, iRangeMax, DEFAULT_SETTING_ADVERTISMENT_ALLOWEDFORMATEDWORDS, FALSE);
+
+ return TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case IDC_EDITBOX_ALLOWEDGOODWORDS:
+ {
+ switch (HIWORD(wParam))
+ {
+ case EN_KILLFOCUS:
+ {
+ int iRangeMax = SendDlgItemMessage(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS, LB_GETCOUNT, 0, 0) - 1;
+ if (iRangeMax < 0) iRangeMax = 0;
+
+ // Check allowed good words value
+ SetSpinCtrlRange(GetDlgItem(hwndDlg, IDC_SPIN_ALLOWEDGOODWORDS), 0, iRangeMax, DEFAULT_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS, FALSE);
+
+ return TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case IDC_BUTTON_ADDBADWORD:
+ {
+ WCHAR* pszMsgTitle = TranslateT("Add bad phrase/pattern");
+ WCHAR* pszMsgTextInList = TranslateT("The entered bad phrase \"%s\" already exists in the list.\r\n\r\nThe filter doesn't differentiate between lower and upper case.\r\nTwo entries with the same phrase are not neccessary\nand will decrease the filter quality.\r\n\r\nShall the bad phrase be added anyway?");
+ WCHAR* pszMsgTextInOther = TranslateT("The entered bad phrase \"%s\" already exists in the default bad phrases lists.\r\n\r\nAllowing the same bad phrase in the customized bad phrases lists will\npull down the filter quality.\r\n\r\nShall the bad phrase be added anyway?");
+
+ WordsListBoxAddItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS, (STRINGLIST*)GetWindowLongPtr(hwndDlg, DWLP_USER), pszMsgTitle, pszMsgTextInList, pszMsgTextInOther, IDC_BUTTON_DELETEBADWORD);
+
+ // Set/Check allowed bad words value (Simulate changed edit box)
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_EDITBOX_ALLOWEDBADWORDS, EN_KILLFOCUS), 0);
+
+ // Simulate messages
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_CUSTOMIZEDBADWORDS, BN_CLICKED), 0);
+
+ return TRUE;
+ }
+
+ case IDC_BUTTON_DELETEBADWORD:
+ {
+ int iItemCount = WordsListBoxDeleteItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS, IDC_BUTTON_DELETEBADWORD);
+
+ // Deactivate list on zero entries
+ if (iItemCount == 0)
+ {
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDBADWORDS, BST_UNCHECKED);
+ SendMessage(hwndDlg, WM_COMMAND, IDC_CHECKBOX_CUSTOMIZEDBADWORDS, 0);
+ }
+
+ // Set/Check allowed bad words value (Simulate changed edit box)
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_EDITBOX_ALLOWEDBADWORDS, EN_KILLFOCUS), 0);
+
+ // Simulate messages
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_CUSTOMIZEDBADWORDS, BN_CLICKED), 0);
+
+ return TRUE;
+ }
+
+ case IDC_BUTTON_ADDGOODWORD:
+ {
+ WCHAR* pszMsgTitle = TranslateT("Add good phrase/pattern");
+ WCHAR* pszMsgTextInList = TranslateT("The entered good phrase \"%s\" already exists\r\nin the list.\r\n\r\nThe filter doesn't differentiate between lower and upper case.\r\nTwo entries with the same phrase are not neccessary\nand will decrease the filter quality.\r\n\r\nShall the good phrase be added anyway?");
+
+ WordsListBoxAddItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS, NULL, pszMsgTitle, pszMsgTextInList, NULL, IDC_BUTTON_DELETEGOODWORD);
+
+ // Set/Check allowed bad words value (Simulate changed edit box)
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_EDITBOX_ALLOWEDGOODWORDS, EN_KILLFOCUS), 0);
+
+ // Simulate messages
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_CUSTOMIZEDGOODWORDS, BN_CLICKED), 0);
+
+ return TRUE;
+ }
+
+ case IDC_BUTTON_DELETEGOODWORD:
+ {
+ int iItemCount = WordsListBoxDeleteItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS, IDC_BUTTON_DELETEGOODWORD);
+ int iRangeMax = iItemCount - 1;
+
+ // Deactivate list on zero entries
+ if (iItemCount == 0)
+ {
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDGOODWORDS, BST_UNCHECKED);
+ SendMessage(hwndDlg, WM_COMMAND, IDC_CHECKBOX_CUSTOMIZEDGOODWORDS, 0);
+ }
+
+ // Update allowed good words spin
+ SetSpinCtrlRange(GetDlgItem(hwndDlg, IDC_SPIN_ALLOWEDGOODWORDS), 0, iRangeMax, DEFAULT_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS, FALSE);
+
+ // Set/Check allowed bad words value (Simulate changed edit box)
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_EDITBOX_ALLOWEDGOODWORDS, EN_KILLFOCUS), 0);
+
+ // Simulate messages
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_CUSTOMIZEDGOODWORDS, BN_CLICKED), 0);
+
+ // Set focus on list box again (might have gotten lost on IDC_CHECKBOX_CUSTOMIZEDGOODWORDS)
+ PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS), (LPARAM)TRUE);
+ return TRUE;
+ }
+
+ case IDC_BUTTON_SPAMDEFINITIONS:
+ {
+ CallService(MS_SPAMFILTER_SHOWFILTERDIALOG, (WPARAM)NULL, (LPARAM)SFT_ADVERTISMENT_FILTER|SFTEXF_SPAMDEFINITIONS_INFO);
+ return TRUE;
+ }
+
+ case IDOK:
+ {
+ EnableDlgItem(hwndDlg, IDOK, FALSE);
+
+ // Save changes
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DEFAULTBADWORDS, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_DEFAULTBADWORDS) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_FORMATING, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_FORMATING) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_URL, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_URL) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CAPITALIZED, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CAPITALIZED) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DEFAULTSPAMMERS, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_DEFAULTSPAMMERS) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_MULTIPLEMESSAGES, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_MULTIPLEMESSAGES) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_TEASERMESSAGES, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_TEASERMESSAGES) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_EMPTY, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_EMPTY) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_PHONENUMBERS, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_PHONENUMBERS) == BST_CHECKED));
+
+ DBWriteContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDBADWORDS, (WORD)GetDlgItemInt(hwndDlg, IDC_EDITBOX_ALLOWEDBADWORDS, NULL, FALSE));
+ DBWriteContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDFORMATEDWORDS, (WORD)GetDlgItemInt(hwndDlg, IDC_EDITBOX_ALLOWEDFORMATEDWORDS, NULL, FALSE));
+ DBWriteContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS, (WORD)GetDlgItemInt(hwndDlg, IDC_EDITBOX_ALLOWEDGOODWORDS, NULL, FALSE));
+
+ // Save listbox contents
+ {
+ STRINGLIST* pslWords = SLNewList();
+
+ // Bad words
+ SLRetrieveFromListBoxCtrl(pslWords, GetDlgItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS));
+
+ SetSpamDefinitionData(pslWords, SDID_CUSTOM_BADWORDS, hwndDlg);
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CUSTOMIZEDBADWORDS, (BYTE)((SLGetItemCount(pslWords) > 0) ? (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDBADWORDS) == BST_CHECKED) : FALSE));
+
+ SLClearList(pslWords);
+
+ // Good words
+ SLRetrieveFromListBoxCtrl(pslWords, GetDlgItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS));
+
+ SetSpamDefinitionData(pslWords, SDID_CUSTOM_GOODWORDS, hwndDlg);
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CUSTOMIZEDGOODWORDS, (BYTE)((SLGetItemCount(pslWords) > 0) ? (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDGOODWORDS) == BST_CHECKED) : FALSE));
+
+ SLFreeList(pslWords);
+ }
+
+ // fall through
+ }
+
+ case IDCANCEL:
+ // WM_CLOSE is transformed into an IDCANCEL by dialog manager
+ {
+ // Different method of closing the window depending on if the window
+ // was created modal or not
+ if (!GetParent(hwndDlg))
+ DestroyWindow(hwndDlg);
+ else
+ EndDialog(hwndDlg, LOWORD(wParam));
+
+ return TRUE;
+ }
+
+ } // switch wParam
+
+ break;
+ }
+
+ default:
+ {
+ if (WordsListBoxDragDrop(hwndDlg, IDC_LISTBOX_CUSTOMIZEDBADWORDS, uMsg, lParam, FALSE)
+ || WordsListBoxDragDrop(hwndDlg, IDC_LISTBOX_CUSTOMIZEDGOODWORDS, uMsg, lParam, FALSE) )
+ return TRUE;
+ }
+
+ } // switch uMsg
+
+ return FALSE;
+}
+
+
+BOOL CALLBACK DlgProcConfigureDislikedMessagesFilter(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ hwndDislikedMessagesFilter = hwndDlg;
+
+ // Translate dialog
+ TranslateDialogDefault(hwndDlg);
+
+ // Make group headings bold
+ {
+ HFONT hBoldFont;
+ LOGFONT lf;
+
+ GetObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_GROUP_OTHERCONTENTS, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hBoldFont = CreateFontIndirect(&lf);
+
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_OTHERCONTENTS, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_WORDS, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_NOTIFYSENDER, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT_WARNING, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ }
+
+ // Load icons
+ SendMessage(hwndDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)(HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED));
+
+ // Set Data
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_HOAXTEXTS, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_HOAXTEXTS, (BYTE)(DEFAULT_SETTING_DISLIKEDMESSAGE_HOAXTEXTS ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_MULTIPLEMESSAGES, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_MULTIPLEMESSAGES, (BYTE)(DEFAULT_SETTING_DISLIKEDMESSAGE_MULTIPLEMESSAGES ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_NOTIFYSENDER, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_NOTIFYSENDER, (BYTE)(DEFAULT_SETTING_DISLIKEDMESSAGE_NOTIFYSENDER ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_ADDTOHISTORY, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_ADDTOHISTORY, (BYTE)(DEFAULT_SETTING_DISLIKEDMESSAGE_ADDTOHISTORY ? BST_CHECKED : BST_UNCHECKED)) ? BST_CHECKED : BST_UNCHECKED));
+
+ // Init listboxes
+ {
+ STRINGLIST* pslWords = SLNewList();
+
+ // Disliked words
+ GetSpamDefinitionData(pslWords, SDID_CUSTOM_DISLIKEDWORDS);
+ WordsListBoxInit(hwndDlg, IDC_LISTBOX_CUSTOMIZEDWORDS, IDC_BUTTON_ADDWORD, IDC_BUTTON_DELETEWORD, pslWords, Translate("Add indicating phrase/pattern"), Translate("Remove indicating phrase"));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDWORDS, (SLGetItemCount(pslWords) > 0) ? (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_CUSTOMIZEDWORDS, (BYTE)TRUE)?BST_CHECKED:BST_UNCHECKED) : BST_UNCHECKED);
+
+ SLFreeList(pslWords);
+ }
+
+ // Init editbox
+ {
+ DBVARIANT dbv;
+
+ // Notify text
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_RETURNMESSAGE, &dbv) == 0)
+ {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_NOTIFYSENDER, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_NOTIFYSENDER, DEFAULT_SETTING_DISLIKEDMESSAGE_RETURNMESSAGE);
+ }
+ }
+
+ if (!IsSpamDefinitionDataPresent(SDID_HOAXTEXTS)) {
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_HOAXTEXTS, FALSE);
+ ShowDlgItem(hwndDlg, IDC_TEXT_WARNING, TRUE);
+ }
+
+ // Show/Hide Variables Plugin help button
+ if (!MakeVariablesHelpButton(hwndDlg, IDC_BUTTON_NOTIFYSENDER_VARIABLES))
+ {
+ RECT rc;
+
+ if (GetWindowRect(GetDlgItem(hwndDlg, IDC_TEXT_NOTIFYSENDER), &rc))
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_TEXT_NOTIFYSENDER), NULL, 0, 0, (rc.right-rc.left)+25, (rc.bottom-rc.top), SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+ }
+
+ // Simulate messages
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_CUSTOMIZEDWORDS, BN_CLICKED), 0);
+ SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_CHECKBOX_NOTIFYSENDER, BN_CLICKED), 0);
+
+ // Restore dlg position (at the end, so the dialog does not get shown early)
+ Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_DIALOGPOSITION);
+ // Make sure the dialog is not moved out of sight
+ SendMessage(hwndDlg, DM_REPOSITION, 0, 0);
+
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), (LPARAM)TRUE);
+ return FALSE;
+ }
+
+ case WM_DESTROY:
+ {
+ // Save dlg position
+ Utils_SaveWindowPosition(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_DIALOGPOSITION);
+
+ // Free font
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_GROUP_OTHERCONTENTS, WM_GETFONT, 0, 0));
+
+ // Destroy help icon
+ DestroyIcon((HICON)SendDlgItemMessage(hwndDlg, IDC_BUTTON_NOTIFYSENDER_VARIABLES, BM_GETIMAGE, (WPARAM)IMAGE_ICON, 0));
+
+ hwndDislikedMessagesFilter = NULL;
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_CHECKBOX_NOTIFYSENDER:
+ {
+ BOOL bStatus = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_NOTIFYSENDER) == BST_CHECKED);
+
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_NOTIFYSENDER, bStatus);
+ EnableDlgItem(hwndDlg, IDC_TEXT_NOTIFYSENDER, bStatus);
+ EnableDlgItem(hwndDlg, IDC_CHECKBOX_ADDTOHISTORY, bStatus);
+
+ if (ServiceExists(MS_VARS_SHOWHELP) && ServiceExists(MS_VARS_FORMATSTRING))
+ EnableDlgItem(hwndDlg, IDC_BUTTON_NOTIFYSENDER_VARIABLES, bStatus);
+
+ // Avoid passing simulated messages to the system
+ if (!lParam) return TRUE;
+ break;
+ }
+
+ case IDC_CHECKBOX_CUSTOMIZEDWORDS:
+ {
+ BOOL bStatus = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDWORDS) == BST_CHECKED);
+ int iListCount = SendDlgItemMessage(hwndDlg, IDC_LISTBOX_CUSTOMIZEDWORDS, LB_GETCOUNT, 0, 0);
+
+ // Customized bad words box checked -> Deactivate all customized bad words options
+ EnableDlgItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDWORDS, bStatus);
+
+ // Deactivate Add/Delete buttons?
+ EnableDlgItem(hwndDlg, IDC_BUTTON_ADDWORD, bStatus);
+ EnableDlgItem(hwndDlg, IDC_BUTTON_DELETEWORD, (iListCount > 0) ? bStatus : FALSE);
+
+ // Avoid passing simulated messages to the system
+ if (!lParam) return TRUE;
+
+ break;
+ }
+
+ case IDC_BUTTON_NOTIFYSENDER_VARIABLES:
+ {
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_NOTIFYSENDER), (LPARAM)TRUE);
+
+ CallService(MS_VARS_SHOWHELP, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_NOTIFYSENDER), (LPARAM)NULL);
+ break;
+ }
+
+ case IDC_BUTTON_ADDWORD:
+ {
+ WCHAR* pszMsgTitle = TranslateT("Add indicating phrase/pattern");
+ WCHAR* pszMsgTextInList = TranslateT("The entered indicating phrase \"%s\" already exists in the list.\r\n\r\nThe filter doesn't differentiate between lower and upper case.\r\nTwo entries with the same phrase are not neccessary\nand will decrease the filter quality.\r\n\r\nShall the indicating phrase be added anyway?");
+
+ WordsListBoxAddItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDWORDS, NULL, pszMsgTitle, pszMsgTextInList, NULL, IDC_BUTTON_DELETEWORD);
+
+ break;
+ }
+
+ case IDC_BUTTON_DELETEWORD:
+ {
+ int iItemCount = WordsListBoxDeleteItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDWORDS, IDC_BUTTON_DELETEWORD);
+
+ // Deactivate list on zero entries
+ if (iItemCount == 0)
+ {
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDWORDS, BST_UNCHECKED);
+ SendMessage(hwndDlg, WM_COMMAND, IDC_CHECKBOX_CUSTOMIZEDWORDS, 0);
+ }
+
+ break;
+ }
+
+ case IDC_BUTTON_SPAMDEFINITIONS:
+ {
+ CallService(MS_SPAMFILTER_SHOWFILTERDIALOG, (WPARAM)NULL, (LPARAM)SFT_DISLIKEDMESSAGES_FILTER|SFTEXF_SPAMDEFINITIONS_INFO);
+ break;
+ }
+
+ case IDOK:
+ {
+ EnableDlgItem(hwndDlg, IDOK, FALSE);
+
+ // Save changes
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_MULTIPLEMESSAGES, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_MULTIPLEMESSAGES) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_HOAXTEXTS, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_HOAXTEXTS) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_ADDTOHISTORY, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_ADDTOHISTORY) == BST_CHECKED));
+
+ // Save texts
+ {
+ HWND hwndEdit = GetDlgItem(hwndDlg, IDC_EDITBOX_NOTIFYSENDER);
+ int iTextLen = GetWindowTextLength(hwndEdit);
+ WCHAR* pszText = (WCHAR*)mir_alloc((iTextLen+1)*sizeof(WCHAR));
+
+ // Get entered text
+ if (pszText)
+ {
+ if (GetWindowText(hwndEdit, pszText, iTextLen+1) == 0)
+ pszText[0] = _T('\0');
+
+ // Save text
+ if (lstrlen(pszText) > 0) {
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_RETURNMESSAGE, pszText);
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_NOTIFYSENDER, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_NOTIFYSENDER) == BST_CHECKED));
+ } else{
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_RETURNMESSAGE, DEFAULT_SETTING_DISLIKEDMESSAGE_RETURNMESSAGE);
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_NOTIFYSENDER, (BYTE)FALSE);
+ }
+
+ mir_free(pszText);
+ }
+ }
+
+ // Save listbox contents
+ {
+ STRINGLIST* pslWords = SLNewList();
+
+ // Chain words
+ SLRetrieveFromListBoxCtrl(pslWords, GetDlgItem(hwndDlg, IDC_LISTBOX_CUSTOMIZEDWORDS));
+
+ SetSpamDefinitionData(pslWords, SDID_CUSTOM_DISLIKEDWORDS, hwndDlg);
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_CUSTOMIZEDWORDS, (BYTE)((SLGetItemCount(pslWords) > 0) ? (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_CUSTOMIZEDWORDS) == BST_CHECKED) : FALSE));
+
+ SLFreeList(pslWords);
+ }
+
+ // fall through
+ }
+
+ case IDCANCEL:
+ // WM_CLOSE is transformed into an IDCANCEL by dialog manager
+ {
+ // Different method of closing the window depending on if the window
+ // was created modal or not
+ if (!GetParent(hwndDlg))
+ DestroyWindow(hwndDlg);
+ else
+ EndDialog(hwndDlg, LOWORD(wParam));
+
+ return TRUE;
+ }
+
+ } // switch wParam
+ break;
+ }
+
+ default:
+ {
+ if (WordsListBoxDragDrop(hwndDlg, IDC_LISTBOX_CUSTOMIZEDWORDS, uMsg, lParam, FALSE))
+ return TRUE;
+ }
+
+ }
+ return FALSE;
+}
+
+
+BOOL CALLBACK DlgProcConfigureRobotFilter(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ hwndRobotFilter = hwndDlg;
+
+ // Translate dialog
+ TranslateDialogDefault(hwndDlg);
+
+ // Make group headings bold
+ {
+ HFONT hBoldFont;
+ LOGFONT lf;
+
+ GetObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_GROUP_MESSAGETEXTS, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hBoldFont = CreateFontIndirect(&lf);
+
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_MESSAGETEXTS, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_GROUP_ANSWER, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ }
+
+ // Load icons
+ SendMessage(hwndDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)(HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED));
+
+ // Set Data
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_ADDTOHISTORY, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ADDTOHISTORY, (BYTE)DEFAULT_SETTING_ROBOT_ADDTOHISTORY) ? BST_CHECKED : BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_HIDE, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_HIDEUSERS, (BYTE)DEFAULT_SETTING_ROBOT_HIDEUSERS) ? BST_CHECKED : BST_UNCHECKED));
+
+ // Init editboxes
+ {
+ DBVARIANT dbv;
+
+ // Question
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_INSTRUCTION, &dbv) == 0) {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_INSTRUCTION, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_INSTRUCTION, DEFAULT_SETTING_ROBOT_INSTRUCTION);
+ }
+
+ // Congratulation
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_CONFIRMATION, &dbv) == 0) {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_CONFIRMATION, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_CONFIRMATION, DEFAULT_SETTING_ROBOT_CONFIRMATION);
+ }
+
+ // Answer
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ANSWER, &dbv) == 0) {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_ANSWER, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else {
+ WCHAR szRnd[RANDOM_ANSWER_LENGTH+1];
+ GetRandomString(szRnd, ARRAYSIZE(szRnd));
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ANSWER, szRnd);
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_ANSWER, szRnd);
+ }
+ }
+
+ // Show/Hide Variables Plugin help button (Question)
+ MakeVariablesHelpButton(hwndDlg, IDC_BUTTON_INSTRUCTION_VARIABLES);
+
+ // Show/Hide Variables Plugin help button (Confirmation)
+ if (!MakeVariablesHelpButton(hwndDlg, IDC_BUTTON_CONFIRMATION_VARIABLES))
+ {
+ RECT rc;
+ if (GetWindowRect(GetDlgItem(hwndDlg, IDC_TEXT_VARS), &rc))
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_TEXT_VARS), NULL, 0, 0, (rc.right-rc.left)+25, (rc.bottom-rc.top), SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+ }
+
+
+ // Restore dlg position (at the end, so the dialog does not get shown early)
+ Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_DIALOGPOSITION);
+ // Make sure the dialog is not moved out of sight
+ SendMessage(hwndDlg, DM_REPOSITION, 0, 0);
+
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), (LPARAM)TRUE);
+ return FALSE;
+ }
+
+ case WM_DESTROY:
+ {
+ // Save dlg position
+ Utils_SaveWindowPosition(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_DIALOGPOSITION);
+
+ // Free font
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_GROUP_MESSAGETEXTS, WM_GETFONT, 0, 0));
+
+ // Destroy help icons
+ DestroyIcon((HICON)SendDlgItemMessage(hwndDlg, IDC_BUTTON_INSTRUCTION_VARIABLES, BM_GETIMAGE, (WPARAM)IMAGE_ICON, 0));
+ DestroyIcon((HICON)SendDlgItemMessage(hwndDlg, IDC_BUTTON_CONFIRMATION_VARIABLES, BM_GETIMAGE, (WPARAM)IMAGE_ICON, 0));
+
+ hwndRobotFilter = NULL;
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_BUTTON_INSTRUCTION_VARIABLES:
+ {
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_INSTRUCTION), (LPARAM)TRUE);
+
+ CallService(MS_VARS_SHOWHELP, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_INSTRUCTION), (LPARAM)NULL);
+ break;
+ }
+
+ case IDC_BUTTON_CONFIRMATION_VARIABLES:
+ {
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_CONFIRMATION), (LPARAM)TRUE);
+
+ CallService(MS_VARS_SHOWHELP, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_CONFIRMATION), (LPARAM)NULL);
+ break;
+ }
+
+ case IDC_BUTTON_ANSWER:
+ {
+ WCHAR szRnd[RANDOM_ANSWER_LENGTH+1];
+
+ GetRandomString(szRnd, ARRAYSIZE(szRnd));
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_ANSWER, szRnd);
+ break;
+ }
+
+ case IDOK:
+ {
+ EnableDlgItem(hwndDlg, IDOK, FALSE);
+
+ // Save changes
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ADDTOHISTORY, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_ADDTOHISTORY) == BST_CHECKED));
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_HIDEUSERS, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_HIDE) == BST_CHECKED));
+
+ // Save texts
+ {
+ int iTextLen;
+ WCHAR* pszText;
+
+ // Instruction
+ iTextLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_EDITBOX_INSTRUCTION));
+ pszText = (WCHAR*)mir_alloc((iTextLen+1)*sizeof(WCHAR));
+ if (pszText)
+ {
+ if (GetWindowText(GetDlgItem(hwndDlg, IDC_EDITBOX_INSTRUCTION), pszText, iTextLen+1) == 0)
+ pszText[0] = _T('\0');
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_INSTRUCTION, (lstrlen(pszText)>0) ? pszText : DEFAULT_SETTING_ROBOT_INSTRUCTION);
+ mir_free(pszText);
+ }
+
+ // Confirmation
+ iTextLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_EDITBOX_CONFIRMATION));
+ pszText = (WCHAR*)mir_alloc((iTextLen+1)*sizeof(WCHAR));
+ if (pszText)
+ {
+ if (GetWindowText(GetDlgItem(hwndDlg, IDC_EDITBOX_CONFIRMATION), pszText, iTextLen+1) == 0)
+ pszText[0] = _T('\0');
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_CONFIRMATION, (lstrlen(pszText)>0) ? pszText : DEFAULT_SETTING_ROBOT_CONFIRMATION);
+ mir_free(pszText);
+ }
+
+ // Correct Answer
+ iTextLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_EDITBOX_ANSWER));
+ pszText = (WCHAR*)mir_alloc((iTextLen+1)*sizeof(WCHAR));
+ if (pszText)
+ {
+ if (GetWindowText(GetDlgItem(hwndDlg, IDC_EDITBOX_ANSWER), pszText, iTextLen+1) == 0)
+ pszText[0] = _T('\0');
+ if (lstrlen(pszText) > 0) {
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ANSWER, pszText);
+ } else {
+ WCHAR szRnd[RANDOM_ANSWER_LENGTH+1];
+ GetRandomString(szRnd, ARRAYSIZE(szRnd));
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ANSWER, szRnd);
+ }
+ mir_free(pszText);
+ }
+ }
+
+ // fall through
+ }
+
+ case IDCANCEL:
+ // WM_CLOSE is transformed into an IDCANCEL by dialog manager
+ {
+ // Different method of closing the window depending on if the window
+ // was created modal or not
+ if (!GetParent(hwndDlg))
+ DestroyWindow(hwndDlg);
+ else
+ EndDialog(hwndDlg, LOWORD(wParam));
+
+ return TRUE;
+ }
+
+ } // switch wParam
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+
+// -- Spam Definitions Info
+
+static BOOL CALLBACK EnumSpamDefinitions_InsertItem(UINT uSpamDefID, WCHAR* pszFilePath, WCHAR* pszName, SYSTEMTIME* pstDate, WPARAM wParam, LPARAM lParam)
+{
+ LVITEM lvi;
+
+ if (!lParam) return FALSE;
+ ZeroMemory(&lvi, sizeof(lvi));
+ lvi.mask = LVIF_TEXT|LVIF_PARAM;
+ lvi.pszText = pszName?TranslateTS(pszName):TranslateT("Not Available");
+ lvi.lParam = (LPARAM)mir_wstrdup(pszFilePath);
+
+ lvi.iItem = ListView_InsertItem((HWND)wParam, &lvi);
+ if (lvi.iItem >= 0)
+ {
+ // Version
+ if (pstDate)
+ {
+ WCHAR szDate[9];
+ if (GetDateFormat(GetThreadLocale(), 0, pstDate, _T("yy'.'MM'.'dd"), szDate, ARRAYSIZE(szDate)) != 0)
+ ListView_SetItemText((HWND)wParam, lvi.iItem, 1, szDate);
+ }
+
+ if (!IsWinVerXPPlus() || !ListView_IsGroupViewEnabled((HWND)wParam))
+ {
+ ListView_SetItemText((HWND)wParam, lvi.iItem, 2, (WCHAR*)lParam); // Type
+
+ } else {
+
+ if (ListView_HasGroup((HWND)wParam, uSpamDefID))
+ {
+ lvi.mask = LVIF_GROUPID;
+ lvi.iGroupId = uSpamDefID;
+
+ ListView_SetItem((HWND)wParam, &lvi);
+ // ListView_MoveItemToGroup not implemented in WinSDK
+
+ } else {
+
+ LVGROUP lvg;
+
+ ZeroMemory(&lvg, sizeof(lvg));
+ lvg.cbSize = sizeof(lvg);
+ lvg.mask = LVGF_HEADER|LVGF_GROUPID;
+ lvg.iGroupId = uSpamDefID;
+ #if defined(UNICODE)
+ lvg.pszHeader = (WCHAR*)lParam;
+ ListView_InsertGroup((HWND)wParam, lvi.iItem, &lvg);
+ #else
+ lvg.pszHeader = mir_utf8encodeW((WCHAR*)lParam);
+ ListView_InsertGroup((HWND)wParam, lvi.iItem, &lvg);
+ if (lvg.pszHeader) mir_free(lvg.pszHeader);
+ #endif
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+static int ResizeProcSpamDefinitionsInfo(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL *urc)
+{
+ switch(urc->wId)
+ {
+ case IDC_TEXT_DESCRIPTION:
+ return RD_ANCHORY_TOP|RD_ANCHORX_WIDTH;
+
+ case IDC_TAB_SPAMDEFINITIONS:
+ return RD_ANCHORY_HEIGHT|RD_ANCHORX_WIDTH;
+
+ case IDC_LISTVIEW_SPAMDEFINITIONS:
+ {
+ POINT pt = {0, 0};
+
+ // Keep exactly same position (resized later)
+ GetWindowRect(GetDlgItem(hwndDlg, urc->wId), &urc->rcItem);
+ if (ClientToScreen(hwndDlg, &pt))
+ OffsetRect(&urc->rcItem, -pt.x, -pt.y);
+
+ return RD_ANCHORY_CUSTOM|RD_ANCHORX_CUSTOM;
+ }
+
+ case IDC_URL_GETMORE:
+ return RD_ANCHORY_BOTTOM|RD_ANCHORX_WIDTH;
+
+ //case IDCANCEL:
+ //case IDC_SCROLLBAR_SIZEGRIP:
+ default:
+ return RD_ANCHORY_BOTTOM|RD_ANCHORX_RIGHT;
+ }
+}
+
+void FixListHeader(HWND hwndListView)
+{
+ // Resize header (does not fit fully using small border)
+ // at least on WinME
+ RECT rcList, rcHeader;
+ HWND hwndHeader = ListView_GetHeader(hwndListView);
+
+ if (GetClientRect(hwndListView, &rcList) && GetClientRect(hwndHeader, &rcHeader))
+ if ((rcHeader.right-rcHeader.left) != (rcList.right-rcList.left))
+ SetWindowPos(hwndHeader, NULL, 0, 0, rcList.right-rcList.left, rcHeader.bottom-rcHeader.top, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
+}
+
+BOOL CALLBACK DlgProcSpamDefinitionsInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ hwndSpamDefinitionsInfo = hwndDlg;
+
+ /*
+ #if defined(UNICODE)
+ // Convert DlgProc to Unicode (IsWindowUnicode returns true after this call -> see docs)
+ SetWindowLongPtrW(hwndDlg, DWLP_DLGPROC, (LONG_PTR)DlgProcSpamDefinitionsInfo);
+ #endif
+ */
+
+ // Translate dialog first
+ TranslateDialogDefault(hwndDlg);
+
+ // Set icon
+ SendMessage(hwndDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)(HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_SPAM_DEFINITIONS), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED));
+
+ // Make heading
+ {
+ HFONT hBoldFont;
+ LOGFONT lf;
+
+ GetObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hBoldFont = CreateFontIndirect(&lf);
+
+ SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ }
+
+ // Init tabs
+ {
+ HWND hwndTab = GetDlgItem(hwndDlg, IDC_TAB_SPAMDEFINITIONS);
+ TCITEM tci;
+
+ tci.mask = TCIF_TEXT|TCIF_PARAM;
+ tci.pszText = TranslateT("Advertisment Filter");
+ tci.lParam = SFT_ADVERTISMENT_FILTER;
+ TabCtrl_InsertItem(hwndTab, 0, &tci);
+
+ tci.pszText = TranslateT("Disliked Messages Filter");
+ tci.lParam = SFT_DISLIKEDMESSAGES_FILTER;
+ TabCtrl_InsertItem(hwndTab, 1, &tci);
+ }
+
+ // Init ListView
+ {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_LISTVIEW_SPAMDEFINITIONS);
+ LVCOLUMN lvc;
+ int iMinSizePacketCol;
+
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_LABELTIP|LVS_EX_FULLROWSELECT);
+
+ if (IsWinVerXPPlus())
+ ListView_EnableGroupView(hwndList, TRUE);
+
+ lvc.mask = LVCF_TEXT;
+
+ if (IsWinVerXPPlus() && ListView_IsGroupViewEnabled(hwndList))
+ {
+ // Name
+ lvc.pszText = TranslateT("Packet");
+ ListView_InsertColumn(hwndList, 0, &lvc);
+ iMinSizePacketCol = 200;
+
+ // Date
+ lvc.mask |= LVCF_FMT;
+ lvc.pszText = TranslateT("Version");
+ lvc.fmt = LVCFMT_RIGHT;
+ ListView_InsertColumn(hwndList, 1, &lvc);
+
+ } else {
+
+ // Name
+ lvc.pszText = TranslateT("Packet");
+ ListView_InsertColumn(hwndList, 0, &lvc);
+ iMinSizePacketCol = 120;
+
+ // Date
+ lvc.mask |= LVCF_FMT;
+ lvc.pszText = TranslateT("Version");
+ lvc.fmt = LVCFMT_RIGHT;
+ ListView_InsertColumn(hwndList, 1, &lvc);
+
+ // Type
+ lvc.mask ^= LVCF_FMT;
+ lvc.pszText = TranslateT("Type");
+ ListView_InsertColumn(hwndList, 2, &lvc);
+ }
+
+ // Insert info items
+ SendMessage(hwndDlg, SFM_REFRESH_SPAMDEFINITIONS, (WPARAM)TRUE, (LPARAM)lParam);
+
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER);
+ if (ListView_GetColumnWidth(hwndList, 0) < iMinSizePacketCol)
+ ListView_SetColumnWidth(hwndList, 0, iMinSizePacketCol);
+
+ ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE_USEHEADER);
+ if (ListView_GetColumnWidth(hwndList, 1) < 60)
+ ListView_SetColumnWidth(hwndList, 1, 60);
+
+ // Restore dlg position (at the end, so the dialog does not get shown early)
+ Utils_RestoreWindowPosition(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_SPAMDEF_DIALOGPOSITION);
+ // Make sure the dialog is not moved out of sight
+ SendMessage(hwndDlg, DM_REPOSITION, 0, 0);
+ SendMessage(hwndDlg, WM_SIZE, 0, 0); //simulate message
+
+ // Stretch last column to fit remaining space
+ if (!IsWinVerXPPlus() || !ListView_IsGroupViewEnabled(hwndList))
+ ListView_SetColumnWidth(hwndList, 2, LVSCW_AUTOSIZE_USEHEADER);
+ else
+ ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE_USEHEADER);
+
+ FixListHeader(hwndList);
+ }
+
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDCANCEL), (LPARAM)TRUE);
+ return FALSE;
+ }
+
+ case SFM_REFRESH_SPAMDEFINITIONS:
+ {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_LISTVIEW_SPAMDEFINITIONS);
+ DWORD dwFilterType;
+ int iSelectedIndex = ListView_GetSelectionMark(hwndList);
+
+ if ((DWORD)lParam == 0) {
+ dwFilterType = (DWORD)GetWindowLongPtr(hwndDlg, DWLP_USER);
+ } else {
+ dwFilterType = (DWORD)lParam;
+ if ((DWORD)SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)dwFilterType) == dwFilterType)
+ if (!(BOOL)wParam) // if not forced refresh
+ return TRUE;
+ }
+
+ ListView_DeleteAllItems(hwndList);
+ //ListView_RemoveAllGroups(); // not really needed, always the same groups
+
+ FixListHeader(hwndList);
+
+ if (InSendMessage())
+ ReplyMessage(TRUE);
+
+ // Change tab
+ {
+ HWND hwndTab = GetDlgItem(hwndDlg, IDC_TAB_SPAMDEFINITIONS);
+ int i;
+ TCITEM tci;
+
+ tci.mask = TCIF_PARAM;
+ for (i=TabCtrl_GetItemCount(hwndTab)-1; i>=0; i--)
+ {
+ if (TabCtrl_GetItem(hwndTab, i, &tci))
+ if ((DWORD)tci.lParam == dwFilterType)
+ {
+ if (TabCtrl_GetCurSel(hwndTab) != i)
+ TabCtrl_SetCurSel(hwndTab, i);
+ break;
+ }
+
+ if (i==0) TabCtrl_SetCurSel(hwndTab, -1);
+ }
+ FixListHeader(hwndList);
+ }
+
+ // Insert info items
+ switch (dwFilterType)
+ {
+ case SFT_ADVERTISMENT_FILTER:
+ {
+ SetWindowText(hwndDlg, TranslateT("Spam Definitions: Advertisment Filter"));
+
+ EnumSpamDefinitions(SDID_BADWORDS, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("Default Bad Words"));
+ EnumSpamDefinitions(SDID_SPAMMERS, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("Known Spammers"));
+ EnumSpamDefinitions(SDID_TEASERTEXTS, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("Known Teaser Texts"));
+
+ EnumSpamDefinitions(SDID_URLTLDS, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("URL Recognition"));
+ EnumSpamDefinitions(SDID_PHONENUMBERS, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("Phone Number Recognition"));
+
+ EnumSpamDefinitions(SDID_SPECIALCHARS_REPLACE_TEXT, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("Different Format Recognition: Text"));
+ EnumSpamDefinitions(SDID_SPECIALCHARS_REPLACE_NUMBERS, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("Different Format Recognition: Numbers"));
+ EnumSpamDefinitions(SDID_SPECIALCHARS_SEPARATORS, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("Different Format Recognition: Separators"));
+ break;
+ }
+
+ case SFT_DISLIKEDMESSAGES_FILTER:
+ {
+ SetWindowText(hwndDlg, TranslateT("Spam Definitions: Disliked Messages Filter"));
+
+ EnumSpamDefinitions(SDID_HOAXTEXTS, TRUE, EnumSpamDefinitions_InsertItem, (WPARAM)hwndList, (LPARAM)TranslateT("Known Hoax Texts"));
+ break;
+ }
+ }
+
+ if (iSelectedIndex >= 0)
+ {
+ if (iSelectedIndex >= ListView_GetItemCount(hwndList))
+ iSelectedIndex = ListView_GetItemCount(hwndList)-1;
+
+ ListView_SetSelectionMark(hwndList, iSelectedIndex);
+ ListView_EnsureVisible(hwndList, iSelectedIndex, FALSE);
+ }
+
+ FixListHeader(hwndList);
+ return TRUE;
+ }
+
+ case SFM_REMOVE_SPAMDEFINITIONFILE:
+ {
+ int iItem = (int)wParam;
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_LISTVIEW_SPAMDEFINITIONS);
+
+ LVITEM lvi;
+
+ ZeroMemory(&lvi, sizeof(lvi));
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = iItem;
+
+ // Get item
+ if (ListView_GetItem(hwndList, &lvi))
+ {
+ if (lvi.lParam)
+ {
+ SHFILEOPSTRUCT sfo;
+
+ // Doubly-null terminated pFrom (as required)
+ WCHAR szFrom[MAX_PATH+1];
+ mir_sntprintf(szFrom, ARRAYSIZE(szFrom)-1, _T("%s"), lvi.lParam);
+ szFrom[lstrlen(szFrom)+1] = _T('\0');
+
+ ZeroMemory(&sfo, sizeof(sfo));
+ sfo.hwnd = hwndDlg;
+ sfo.wFunc = FO_DELETE;
+ sfo.pFrom = szFrom;
+ sfo.fFlags = FOF_SIMPLEPROGRESS|FOF_NO_CONNECTED_ELEMENTS;
+ sfo.lpszProgressTitle = lvi.pszText;
+
+ // Ask to delete file
+ if (SHFileOperation(&sfo) == 0)
+ if (!sfo.fAnyOperationsAborted)
+ if (ListView_DeleteItem(hwndList, iItem))
+ {
+ // Select next item
+ if (iItem >= ListView_GetItemCount(hwndList))
+ iItem--;
+
+ ListView_SetSelectionMark(hwndList, iItem);
+ }
+
+ }
+ }
+
+ return TRUE;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR pnmhdr = (LPNMHDR)lParam;
+
+ switch (pnmhdr->idFrom)
+ {
+ case IDC_TAB_SPAMDEFINITIONS:
+ {
+ switch (pnmhdr->code)
+ {
+ case TCN_SELCHANGE:
+ {
+ TCITEM tci;
+ tci.mask = TCIF_PARAM;
+
+ if (TabCtrl_GetItem(pnmhdr->hwndFrom, TabCtrl_GetCurSel(pnmhdr->hwndFrom), &tci))
+ SendMessage(hwndDlg, SFM_REFRESH_SPAMDEFINITIONS, (WPARAM)FALSE, (LPARAM)tci.lParam);
+
+ return TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case IDC_LISTVIEW_SPAMDEFINITIONS:
+ {
+ switch (pnmhdr->code)
+ {
+ case LVN_DELETEITEM:
+ {
+ LVITEM lvi;
+
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ((LPNMLISTVIEW)lParam)->iItem;
+
+ // Release lParam memory of removed item
+ if (ListView_GetItem(pnmhdr->hwndFrom, &lvi))
+ if (lvi.lParam)
+ mir_free((WCHAR*)lvi.lParam);
+
+ return FALSE;
+ }
+
+ case LVN_KEYDOWN:
+ {
+ switch (((NMLVKEYDOWN*)lParam)->wVKey)
+ {
+ case VK_DELETE:
+ {
+ PostMessage(hwndDlg, SFM_REMOVE_SPAMDEFINITIONFILE, (WPARAM)ListView_GetSelectionMark(pnmhdr->hwndFrom), 0);
+
+ // Prevent default processing
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)FALSE);
+ return TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case NM_RCLICK:
+ {
+ LPNMLISTVIEW pnmlw = (LPNMLISTVIEW)lParam;
+
+ HMENU hMenu, hContextMenu;
+ LVITEM lvi;
+
+ // Click was not on an item
+ if (pnmlw->iItem < 0) break;
+
+ ZeroMemory(&lvi, sizeof(lvi));
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = pnmlw->iItem;
+
+ // Get item
+ if (!ListView_GetItem(pnmhdr->hwndFrom, &lvi)) break;
+ if (!lvi.lParam) break;
+ if (!ClientToScreen(pnmhdr->hwndFrom, &(pnmlw->ptAction))) break;
+
+ // Context menu
+ hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_CONTEXT));
+ hContextMenu = GetSubMenu(hMenu, 1);
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM)hContextMenu, 0);
+ switch (TrackPopupMenuEx(hContextMenu, TPM_RETURNCMD|TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON, pnmlw->ptAction.x, pnmlw->ptAction.y, pnmhdr->hwndFrom, NULL))
+ {
+ case ID_MENU_DELETE:
+ {
+ PostMessage(hwndDlg, SFM_REMOVE_SPAMDEFINITIONFILE, (WPARAM)pnmlw->iItem, 0);
+ break;
+ }
+ }
+
+ // Destroy context menu
+ if (hMenu) DestroyMenu(hMenu);
+
+ // Prevent default processing
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)FALSE);
+ return TRUE;
+ }
+
+ } // switch
+
+ break;
+ }
+
+ } // switch
+
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ hwndSpamDefinitionsInfo = NULL;
+
+ // Free font
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_GETFONT, 0, 0));
+
+ // Save dlg position
+ Utils_SaveWindowPosition(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_SPAMDEF_DIALOGPOSITION);
+
+ return TRUE;
+ }
+
+ case WM_CLOSE:
+ {
+ // Different method of closing the window depending on if the window
+ // was created modal or not
+ if (!GetParent(hwndDlg))
+ DestroyWindow(hwndDlg);
+ else
+ EndDialog(hwndDlg, IDCANCEL);
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_URL_GETMORE:
+ {
+ CallService(MS_UTILS_OPENURL, (WPARAM)TRUE, (LPARAM)Translate("http://addons.miranda-im.org/index.php?action=display&id=92"));
+ return TRUE;
+ }
+
+ case IDOK:
+ case IDCANCEL:
+ // WM_CLOSE is transformed into an IDCANCEL by dialog manager
+ {
+ // Different method of closing the window depending on if the window
+ // was created modal or not
+ if (!GetParent(hwndDlg))
+ DestroyWindow(hwndDlg);
+ else
+ EndDialog(hwndDlg, LOWORD(wParam));
+
+ return TRUE;
+ }
+
+ } // switch wParam
+
+ break;
+ }
+
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd;
+
+ ZeroMemory(&urd, sizeof(urd));
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = hInstance;
+ urd.hwndDlg = hwndDlg;
+ urd.lpTemplate = MAKEINTRESOURCEA(IDD_INFO_SPAMDEFINITIONS);
+ urd.pfnResizer = ResizeProcSpamDefinitionsInfo;
+
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+
+ // Resize listview to exactly fit tab control
+ {
+ RECT rcDisplay;
+ POINT pt = {0, 0};
+
+ if (GetWindowRect(GetDlgItem(hwndDlg, IDC_TAB_SPAMDEFINITIONS), &rcDisplay))
+ {
+ TabCtrl_AdjustRect(GetDlgItem(hwndDlg, IDC_TAB_SPAMDEFINITIONS), FALSE, &rcDisplay);
+ if (ClientToScreen(hwndDlg, &pt))
+ OffsetRect(&rcDisplay, -pt.x, -pt.y);
+
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_LISTVIEW_SPAMDEFINITIONS), HWND_TOP, rcDisplay.left, rcDisplay.top, rcDisplay.right-rcDisplay.left, rcDisplay.bottom-rcDisplay.top, SWP_NOACTIVATE);
+ }
+ }
+
+ FixListHeader(GetDlgItem(hwndDlg, IDC_LISTVIEW_SPAMDEFINITIONS));
+ return TRUE;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = 340;
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = 150;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+void Spammers_ResetListOptions(HWND hwndList)
+{
+ int i;
+
+ // Same layout as Ignore options
+ SendMessage(hwndList, CLM_SETBKBITMAP, 0, (LPARAM)(HBITMAP)NULL);
+ SendMessage(hwndList, CLM_SETBKCOLOR, GetSysColor(COLOR_WINDOW),0);
+ SendMessage(hwndList, CLM_SETGREYOUTFLAGS, 0, 0);
+ SendMessage(hwndList, CLM_SETLEFTMARGIN, 4, 0);
+ SendMessage(hwndList, CLM_SETINDENT, 10, 0);
+ SendMessage(hwndList, CLM_SETHIDEEMPTYGROUPS, TRUE, 0);
+ SendMessage(hwndList, CLM_SETUSEGROUPS, FALSE, 0);
+
+ for(i=0;i<=FONTID_MAX;i++)
+ SendMessage(hwndList, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT));
+}
+
+int Spammers_GetListIcon(HWND hwndList, HANDLE hItem)
+{
+ int i;
+
+ for (i=0; i<SendMessage(hwndList, CLM_GETEXTRACOLUMNS, 0, 0); i++)
+ if (SendMessage(hwndList, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(i, 0)) == i)
+ return i;
+
+ return -1;
+}
+
+void Spammers_SetListIcon(HWND hwndList, HANDLE hItem, int iColumn)
+{
+ int i, iColCount;
+
+ iColCount = SendMessage(hwndList, CLM_GETEXTRACOLUMNS, 0, 0);
+ for (i=0; i<iColCount; i++)
+ SendMessage(hwndList, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(i, (i==iColumn)?i:iColCount));
+}
+
+void Spammers_SetAllListIcons(HWND hwndList, int iColumn, BOOL bForce, BOOL bWatchForDeleted)
+{
+ HANDLE hContact, hItem;
+
+ for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); hContact; hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0))
+ {
+ hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0);
+ if (!hItem) continue;
+
+ if (CallService(MS_SPAMFILTER_CONTACT_ISSPAMMER, (WPARAM)hContact, 0))
+ {
+ if (bWatchForDeleted)
+ if (DBGetContactSettingByte(hContact, "CList", "Delete", 0)) {
+ Spammers_SetListIcon(hwndList, hItem, 1);
+ BOX("wathce ditem found");
+ continue;
+ }
+ if (bForce || (Spammers_GetListIcon(hwndList, hItem) == -1))
+ Spammers_SetListIcon(hwndList, hItem, iColumn);
+ } else {
+ SendMessage(hwndList, CLM_DELETEITEM, (WPARAM)hItem, 0); // Remove no-spammers
+ }
+ }
+}
+
+
+static int ResizeProcSpammersInfo(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL *urc)
+{
+ switch(urc->wId)
+ {
+ case IDC_TEXT_DESCRIPTION:
+ return RD_ANCHORY_TOP|RD_ANCHORX_WIDTH;
+
+ case IDC_CLIST:
+ return RD_ANCHORY_HEIGHT|RD_ANCHORX_WIDTH;
+
+ case IDC_GROUP:
+ case IDC_CHECKBOX_GROUP:
+ case IDC_CHECKBOX_HIDE:
+ case IDC_EDITBOX_GROUP:
+ return RD_ANCHORY_BOTTOM|RD_ANCHORX_WIDTH;
+
+ case IDC_ICON_SPAM:
+ case IDC_TEXT_SPAM:
+ case IDC_ICON_NORMAL:
+ case IDC_TEXT_NORMAL:
+ case IDC_ICON_DELETE:
+ case IDC_TEXT_DELETE:
+ return RD_ANCHORY_TOP|RD_ANCHORX_RIGHT;
+
+ //case IDOK:
+ //case IDC_BUTTON_RESET:
+ //case IDC_SCROLLBAR_SIZEGRIP:
+ default:
+ return RD_ANCHORY_BOTTOM|RD_ANCHORX_RIGHT;
+ }
+}
+
+BOOL CALLBACK DlgProcSpammersInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ HIMAGELIST hIml;
+ HICON hSpam, hNormal, hDelete, hDots;
+
+ hwndSpammersInfo = hwndDlg;
+
+ // Translate dialog first
+ TranslateDialogDefault(hwndDlg);
+
+ // Init set/unset icons (Loading icons out of miranda32.exe)
+ hIml = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), (IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK, 4, 0);
+ hSpam = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(212), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+ ImageList_AddIcon(hIml, hSpam);
+ hDelete = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(175), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+ ImageList_AddIcon(hIml, hDelete);
+ hNormal = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(213), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+ ImageList_AddIcon(hIml, hNormal);
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hIml);
+ hDots = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(211), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+ ImageList_AddIcon(hIml, hDots);
+
+ Spammers_ResetListOptions(GetDlgItem(hwndDlg, IDC_CLIST));
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETEXTRACOLUMNS, (WPARAM)3, 0);
+ Spammers_SetAllListIcons(GetDlgItem(hwndDlg, IDC_CLIST), 0, TRUE, TRUE);
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ICON_SPAM), GWLP_USERDATA, (LONG_PTR)0);
+ SendDlgItemMessage(hwndDlg, IDC_ICON_SPAM, STM_SETICON, (WPARAM)hSpam, 0);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ICON_DELETE), GWLP_USERDATA, (LONG_PTR)1);
+ SendDlgItemMessage(hwndDlg, IDC_ICON_DELETE, STM_SETICON, (WPARAM)hDelete, 0);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ICON_NORMAL), GWLP_USERDATA, (LONG_PTR)2);
+ SendDlgItemMessage(hwndDlg, IDC_ICON_NORMAL, STM_SETICON, (WPARAM)hNormal, 0);
+
+ // New window icon
+ SendMessage(hwndDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hSpam);
+
+ // Make top text
+ {
+ HFONT hBoldFont;
+ LOGFONT lf;
+
+ GetObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_GETFONT, 0, 0), sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hBoldFont = CreateFontIndirect(&lf);
+
+ SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ }
+
+ // Spammers group
+ {
+ DBVARIANT dbv;
+
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_MOVETOGROUPNAME, &dbv) == 0) {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_GROUP, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else {
+ SetDlgItemText(hwndDlg, IDC_EDITBOX_GROUP, DEFAULT_SETTING_MOVETOGROUPNAME);
+ }
+
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_GROUP, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_MOVETOGROUP, (BYTE)DEFAULT_SETTING_MOVETOGROUP) ? BST_CHECKED : BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_CHECKBOX_HIDE, (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_HIDEFROMCLIST, (BYTE)DEFAULT_SETTING_HIDEFROMCLIST) ? BST_CHECKED : BST_UNCHECKED));
+ }
+
+ SendMessage(hwndDlg, SFM_ENABLE_SUBCTRLS, 0, 0);
+
+ // Restore dlg position (at the end, so the dialog does not get shown early)
+ Utils_RestoreWindowPosition(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_SPAMMERS_DIALOGPOSITION);
+ // Make sure the dialog is not moved out of sight
+ SendMessage(hwndDlg, DM_REPOSITION, 0, 0);
+ SendMessage(hwndDlg, WM_SIZE, 0, 0); //simulate message
+
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), (LPARAM)TRUE);
+ return FALSE;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR pnmh = (LPNMHDR)lParam;
+
+ switch (pnmh->idFrom)
+ {
+ case IDC_CLIST:
+ {
+ switch (pnmh->code)
+ {
+ case CLN_NEWCONTACT:
+ case CLN_CONTACTMOVED:
+ case CLN_LISTREBUILT:
+ Spammers_SetAllListIcons(pnmh->hwndFrom, 0, FALSE, TRUE);
+ return TRUE;
+
+ case CLN_OPTIONSCHANGED:
+ Spammers_ResetListOptions(pnmh->hwndFrom);
+ return TRUE;
+
+ case NM_CLICK:
+ {
+ HANDLE hItem;
+ NMCLISTCONTROL* nm = (NMCLISTCONTROL*)lParam;
+ DWORD hitFlags;
+
+ if (nm->iColumn < 0) break;
+ hItem = (HANDLE)SendMessage(pnmh->hwndFrom, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y));
+ if (hItem == NULL) break;
+ if (!(hitFlags&CLCHT_ONITEMEXTRA)) break;
+
+ Spammers_SetListIcon(pnmh->hwndFrom, hItem, nm->iColumn);
+ return TRUE;
+ }
+ }
+
+ break;
+ }
+
+ }
+ break;
+ }
+
+ case SFM_REFRESH_SPAMMERS:
+ {
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_AUTOREBUILD, 0, 0);
+ return TRUE;
+ }
+
+ case SFM_ENABLE_SUBCTRLS:
+ {
+ BOOL bStatus = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_GROUP) == BST_CHECKED);
+ EnableDlgItem(hwndDlg, IDC_EDITBOX_GROUP, bStatus);
+ return TRUE;
+ }
+
+ case WM_CLOSE:
+ {
+ // Different method of closing the window depending on if the window
+ // was created modal or not
+ if (!GetParent(hwndDlg))
+ DestroyWindow(hwndDlg);
+ else
+ EndDialog(hwndDlg, IDCANCEL);
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_ICON_SPAM:
+ case IDC_ICON_NORMAL:
+ case IDC_ICON_DELETE:
+ {
+ if (HIWORD(wParam) == STN_CLICKED)
+ {
+ Spammers_SetAllListIcons(GetDlgItem(hwndDlg, IDC_CLIST), (int)GetWindowLongPtr(GetDlgItem(hwndDlg, LOWORD(wParam)), GWLP_USERDATA), TRUE, FALSE);
+ return TRUE;
+ }
+ break;
+ }
+
+ case IDC_CHECKBOX_GROUP:
+ {
+ SendMessage(hwndDlg, SFM_ENABLE_SUBCTRLS, 0, 0);
+ return TRUE;
+ }
+
+ case IDOK:
+ {
+ HANDLE hContact, hItem;
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_CLIST);
+
+ EnableDlgItem(hwndDlg, IDOK, FALSE);
+
+ // Spammers group
+ {
+ HWND hwndEdit = GetDlgItem(hwndDlg, IDC_EDITBOX_GROUP);
+ int iTextLen = GetWindowTextLength(hwndEdit);
+ WCHAR* pszText = (WCHAR*)mir_alloc((iTextLen+1)*sizeof(WCHAR));
+ BOOL bUseGroup, bGroupCreated;
+ DBVARIANT dbv;
+
+ // Get entered text
+ if (pszText)
+ {
+ if (GetWindowText(hwndEdit, pszText, iTextLen+1) == 0)
+ pszText[0] = _T('\0');
+
+ // Save text
+ if (lstrlen(pszText) > 0) {
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_MOVETOGROUPNAME, pszText);
+ bUseGroup = (IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_GROUP) == BST_CHECKED);
+ } else {
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_MOVETOGROUPNAME, DEFAULT_SETTING_MOVETOGROUPNAME);
+ bUseGroup = FALSE;
+ }
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_MOVETOGROUP, (BYTE)bUseGroup);
+ } else {
+ bUseGroup = FALSE;
+ }
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_HIDEFROMCLIST, (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CHECKBOX_HIDE) == BST_CHECKED));
+
+ // Remove previous group (if changed)
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_MOVETOGROUPNAME, &dbv) == 0)
+ {
+ // Show remove group UI
+ if (StrCmp(dbv.ptszVal, pszText) != 0)
+ RemoveContactListGroup(dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ // Move all spammers to this group
+ bGroupCreated = FALSE;
+ for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); hContact; hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0))
+ {
+ hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0);
+ if (!hItem) continue;
+
+ switch (Spammers_GetListIcon(hwndList, hItem))
+ {
+ case 1: // delete
+ {
+ int status;
+ char* pszProto;
+
+ // Check if protocol uses server side lists
+ pszProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (pszProto)
+ if ((DWORD)CallProtoService(pszProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_SERVERCLIST)
+ {
+ status = CallProtoService(pszProto, PS_GETSTATUS, 0, 0);
+ if (status == ID_STATUS_OFFLINE || (status >= ID_STATUS_CONNECTING && status < ID_STATUS_CONNECTING + MAX_CONNECT_RETRIES))
+ {
+ DBWriteContactSettingByte(hContact, "CList", "Delete", 1);
+ break;
+ }
+ }
+
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0);
+ break;
+ }
+ case 2: // normal
+ DBDeleteContactSetting(hContact, "CList", "Delete");
+ CallService(MS_SPAMFILTER_CONTACT_UNSETSPAMMER, (WPARAM)hContact, 0);
+ break;
+
+ case 0: // spammer
+ if (bUseGroup && !bGroupCreated) {
+ CreateContactListGroup(pszText);
+ bGroupCreated = TRUE;
+ }
+ if (bUseGroup) {
+ if (DBGetContactSettingTString(hContact, "CList", "Group", &dbv) == 0)
+ {
+ if (StrCmp(dbv.ptszVal, pszText) != 0)
+ DBWriteContactSettingTString(hContact, "CList", "Group", pszText);
+ DBFreeVariant(&dbv);
+ }
+ } else {
+ DBDeleteContactSetting(hContact, "CList", "Group");
+ }
+ DBDeleteContactSetting(hContact, "CList", "Delete");
+ break;
+ }
+ }
+
+ // Display remove group window
+ //if (!bUseGroup || !bGroupCreated)
+ // RemoveContactListGroup(pszText);
+
+ if (pszText) mir_free(pszText);
+ }
+
+ // fall through
+ }
+
+ case IDCANCEL:
+ // WM_CLOSE is transformed into an IDCANCEL by dialog manager
+ {
+ // Different method of closing the window depending on if the window
+ // was created modal or not
+ if (!GetParent(hwndDlg))
+ DestroyWindow(hwndDlg);
+ else
+ EndDialog(hwndDlg, LOWORD(wParam));
+
+ return TRUE;
+ }
+
+ }
+
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ hwndSpammersInfo = NULL;
+
+ ImageList_Destroy((HIMAGELIST)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETEXTRAIMAGELIST, 0, 0));
+
+ // Free font
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_GETFONT, 0, 0));
+
+ // Save dlg position
+ Utils_SaveWindowPosition(hwndDlg, NULL, DB_MODULE_NAME, DB_SETTING_SPAMMERS_DIALOGPOSITION);
+ return TRUE;
+ }
+
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd;
+
+ ZeroMemory(&urd, sizeof(urd));
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = hInstance;
+ urd.hwndDlg = hwndDlg;
+ urd.lpTemplate = MAKEINTRESOURCEA(IDD_INFO_SPAMMERS);
+ urd.pfnResizer = ResizeProcSpammersInfo;
+
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+ return TRUE;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = 370;
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = 220;
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+
+// -- ListBox helper functions
+
+LRESULT CALLBACK WordsListBoxSubclassProc(HWND hwndListBox, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ int iButtonAdd;
+ int iButtonDelete;
+
+ // Get associated list box control id
+ switch (GetDlgCtrlID(hwndListBox))
+ {
+ case IDC_LISTBOX_CUSTOMIZEDBADWORDS:
+ {
+ iButtonAdd = IDC_BUTTON_ADDBADWORD;
+ iButtonDelete = IDC_BUTTON_DELETEBADWORD;
+ break;
+ }
+
+ case IDC_LISTBOX_CUSTOMIZEDGOODWORDS:
+ {
+ iButtonAdd = IDC_BUTTON_ADDGOODWORD;
+ iButtonDelete = IDC_BUTTON_DELETEGOODWORD;
+ break;
+ }
+
+ case IDC_LISTBOX_CUSTOMIZEDWORDS:
+ {
+ iButtonAdd = IDC_BUTTON_ADDWORD;
+ iButtonDelete = IDC_BUTTON_DELETEWORD;
+ break;
+ }
+
+ default:
+ {
+ iButtonAdd = -1;
+ iButtonDelete = -1;
+ break;
+ }
+ }
+
+ switch (uMsg)
+ {
+ case LB_INSERTSTRING:
+ case LB_ADDSTRING:
+ {
+ // Set horizontal extent
+ int iHorExt;
+
+ if (!lParam) break;
+
+ iHorExt = GetCtrlTextExtent(hwndListBox, (WCHAR*)lParam, TRUE);
+
+ if (iHorExt > SendMessage(hwndListBox, LB_GETHORIZONTALEXTENT, 0, 0))
+ PostMessage(hwndListBox, LB_SETHORIZONTALEXTENT, (LPARAM)iHorExt, 0);
+
+ break;
+ }
+
+ case LB_DELETESTRING:
+ {
+ // Set horizontal extent
+ int i;
+ int iLen, iHorExt = 0;
+ int iItemCount = (int)SendMessage(hwndListBox, LB_GETCOUNT, 0, 0);
+ WCHAR* pszItemText;
+
+ for (i=0; i<iItemCount; i++)
+ {
+ // Ignore the item that is to be deleted
+ if (i == (int)wParam) continue;
+
+ iLen = (int)SendMessage(hwndListBox, LB_GETTEXTLEN, (WPARAM)i, 0);
+ if (iLen < 0) continue;
+
+ pszItemText = (WCHAR*)mir_alloc((iLen+1)*sizeof(WCHAR));
+ if (!pszItemText) continue;
+
+ if (SendMessage(hwndListBox, LB_GETTEXT, (WPARAM)i, (LPARAM)pszItemText) == LB_ERR)
+ *pszItemText = _T('\0');
+
+ iHorExt = max(iHorExt, GetCtrlTextExtent(hwndListBox, pszItemText, TRUE));
+ mir_free(pszItemText);
+ }
+
+ if (iHorExt != SendMessage(hwndListBox, LB_GETHORIZONTALEXTENT, 0, 0))
+ PostMessage(hwndListBox, LB_SETHORIZONTALEXTENT, (WPARAM)iHorExt, 0);
+
+ break;
+ }
+
+ case LB_RESETCONTENT:
+ {
+ // Reset horizontal extent
+ PostMessage(hwndListBox, LB_SETHORIZONTALEXTENT, 0, 0);
+
+ break;
+ }
+
+ case LB_SETHORIZONTALEXTENT:
+ {
+ RECT rc;
+ BOOL bShowHScroll;
+
+ // Do not show horizontal scrollbars if not needed
+ if (GetClientRect(hwndListBox, &rc))
+ bShowHScroll = ((int)wParam > (rc.right-rc.left));
+ else
+ bShowHScroll = TRUE;
+
+ // Show/Hide horizontal scrollbars; DISABLENOSCROLL does not work correctly
+ ShowScrollBar(hwndListBox, SB_HORZ, bShowHScroll);
+
+ // Scroll to left if scrollbar hidden
+ if (!bShowHScroll)
+ PostMessage(hwndListBox, WM_HSCROLL, SB_LEFT, 0);
+
+ break;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ HMENU hMenu, hContextMenu;
+ int iHoveredItem;
+ POINT pt;
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+
+ // Menu started by keyboard shortcut
+ if ((pt.x == -1) && (pt.y == -1))
+ {
+ RECT rc;
+
+ iHoveredItem = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
+
+ // Get item pos
+ if (SendMessage(hwndListBox, LB_GETITEMRECT, (WPARAM)iHoveredItem, (LPARAM)&rc) != LB_ERR)
+ if (!GetWindowRect(hwndListBox, &rc))
+ rc.left = rc.top = rc.right = rc.bottom = 0;
+
+ // Position it in the middle of the rect
+ pt.x = rc.left + (int)((rc.right-rc.left)/2);
+ pt.y = rc.top + (int)((rc.bottom-rc.top)/2);
+
+ } else {
+ // LBItemFromPt returns an item if no item is in list
+ if (SendMessage(hwndListBox, LB_GETCOUNT, 0, 0) == 0)
+ iHoveredItem = LB_ERR;
+ else
+ iHoveredItem = LBItemFromPt(hwndListBox, pt, FALSE);
+
+ // Select list box item
+ if (iHoveredItem >= 0)
+ SendMessage(hwndListBox, LB_SETCURSEL, iHoveredItem, 0);
+ }
+
+ // Context menu
+ hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_CONTEXT));
+ hContextMenu = GetSubMenu(hMenu, 0);
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM)hContextMenu, 0);
+
+ // If user did not click on a list item
+ if (iHoveredItem < 0)
+ RemoveMenu(hContextMenu, ID_MENU_DELETE, MF_BYCOMMAND);
+
+ switch (TrackPopupMenuEx(hContextMenu, TPM_RETURNCMD|TPM_TOPALIGN|TPM_LEFTALIGN|TPM_RIGHTBUTTON, pt.x, pt.y, hwndListBox, NULL))
+ {
+ case ID_MENU_DELETE:
+ {
+ // Simulate 'Delete' button
+ if (iButtonDelete >= 0)
+ SendMessage(GetParent(hwndListBox), WM_COMMAND, MAKEWPARAM(iButtonDelete, BN_CLICKED), (LPARAM)GetDlgItem(GetParent(hwndListBox), iButtonDelete));
+ break;
+ }
+
+ case ID_MENU_ADD:
+ {
+ // Simulate 'Add' button
+ if (iButtonAdd >= 0)
+ SendMessage(GetParent(hwndListBox), WM_COMMAND, MAKEWPARAM(iButtonAdd, BN_CLICKED), (LPARAM)GetDlgItem(GetParent(hwndListBox), iButtonAdd));
+ break;
+ }
+ }
+
+ // Destroy context menu
+ if (hMenu) DestroyMenu(hMenu);
+
+ return 0;
+ }
+
+ case WM_KEYDOWN:
+ {
+ int iSelectedItem = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
+
+ if (iSelectedItem >= 0)
+ {
+ switch (wParam)
+ {
+ case VK_BACK:
+ case VK_SUBTRACT:
+ case VK_DELETE:
+ {
+ // Simulate 'Delete' button
+ if (iButtonDelete >= 0)
+ SendMessage(GetParent(hwndListBox), WM_COMMAND, MAKEWPARAM(iButtonDelete, BN_CLICKED), (LPARAM)GetDlgItem(GetParent(hwndListBox), iButtonDelete));
+
+ return TRUE;
+ }
+
+ case VK_SPACE:
+ case VK_ADD:
+ case VK_INSERT:
+ {
+ // Simulate 'Add' button
+ if (iButtonAdd >= 0)
+ SendMessage(GetParent(hwndListBox), WM_COMMAND, MAKEWPARAM(iButtonAdd, BN_CLICKED), (LPARAM)GetDlgItem(GetParent(hwndListBox), iButtonAdd));
+
+ return TRUE;
+ }
+
+ //case VK_PRIOR:
+ case VK_UP:
+ {
+ // Select one entry up if not first entry
+ if (iSelectedItem > 0)
+ SendMessage(hwndListBox, LB_SETCURSEL, (WPARAM)iSelectedItem-1, 0);
+
+ return TRUE;
+ }
+
+ //case VK_NEXT:
+ case VK_DOWN:
+ {
+ // Select one entry down
+ SendMessage(hwndListBox, LB_SETCURSEL, (WPARAM)iSelectedItem+1, 0);
+
+ return TRUE;
+ }
+
+ } // switch wParam
+
+ }
+
+ return FALSE;
+ }
+
+ case WM_GETDLGCODE:
+ {
+ return DLGC_WANTALLKEYS;
+ }
+
+ } //switch uMsg
+
+ return CallWindowProc((WNDPROC)GetWindowLongPtr(hwndListBox, GWLP_USERDATA), hwndListBox, uMsg, wParam, lParam);
+}
+
+
+BOOL CALLBACK DlgProcWordsListBoxAddItem(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ // Get passed data
+ struct WORDSLISTBOXADDITEMPARAMS* pwlbaip = (struct WORDSLISTBOXADDITEMPARAMS*)lParam;
+
+ SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pwlbaip);
+
+ // Translate dialog
+ TranslateDialogDefault(hwndDlg);
+
+ // Set text
+ SetWindowText(hwndDlg, pwlbaip->pszTitle);
+
+ // Make button and caption use message box font
+ {
+ NONCLIENTMETRICS ncm;
+ HFONT hBoldFont;
+
+ ZeroMemory(&ncm, sizeof(ncm));
+ ncm.cbSize = sizeof(ncm);
+ if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0))
+ {
+ hBoldFont = CreateFontIndirect(&ncm.lfMessageFont);
+
+ SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ //SendDlgItemMessage(hwndDlg, IDOK, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ //SendDlgItemMessage(hwndDlg, IDCANCEL, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT_QUICKHELP, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT_HELPNESTED, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ }
+ }
+
+ // Show/Hide RegExp hint and help
+ // More info about Regular Expressions: http://perldoc.perl.org/perlrequick.html
+ {
+ // Set icon and resize button
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_BUTTON_REGEXP), NULL, 0, 0, 20, 20, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+ SendDlgItemMessage(hwndDlg, IDC_BUTTON_REGEXP, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)LoadSystemIcon(IDB_STD_SMALL_COLOR, STD_HELP));
+ PostMessage(GetDlgItem(hwndDlg, IDC_BUTTON_REGEXP), BUTTONADDTOOLTIP, (WPARAM)Translate("&How to use Regular Expressions"), 0);
+ SendMessage(GetDlgItem(hwndDlg, IDC_BUTTON_REGEXP), BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(GetDlgItem(hwndDlg, IDC_BUTTON_REGEXP), BUTTONSETASPUSHBTN, 0, 0);
+
+ // Do not show hint/help if RegExp DLL not available
+ if (!RegExpEnabled())
+ {
+ RECT rcEdit, rcBtn;
+
+ SetDlgItemText(hwndDlg, IDC_TEXT_REGEXP, TranslateT("(Regular Expressions disabled)"));
+ ShowDlgItem(hwndDlg, IDC_BUTTON_REGEXP, FALSE);
+
+ if (GetWindowRect(GetDlgItem(hwndDlg, IDC_EDITBOX_NEWITEM), &rcEdit))
+ if (GetWindowRect(GetDlgItem(hwndDlg, IDC_BUTTON_REGEXP), &rcBtn))
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_EDITBOX_NEWITEM), NULL, 0, 0, (rcBtn.right-rcEdit.left), (rcEdit.bottom-rcEdit.top), SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+ }
+ }
+
+ // Position/Resize window
+ {
+ RECT rcWin, rcSep;
+ int iHeight, iWidth;
+
+ // Get sizes
+ if ( GetWindowRect(hwndDlg, &rcWin) &&
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_RECT_SEPARATOR), &rcSep) )
+ {
+ iHeight = rcSep.top-rcWin.top + (int)((rcSep.bottom-rcSep.top)/2);
+ iWidth = rcWin.right-rcWin.left;
+
+ // Remember normal height
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_RECT_SEPARATOR), GWLP_USERDATA, (LONG_PTR)(rcWin.bottom-rcWin.top));
+
+ // Hide separator
+ ShowDlgItem(hwndDlg, IDC_RECT_SEPARATOR, FALSE);
+
+ } else {
+ iHeight = 0;
+ iWidth = 0;
+ }
+
+ // Position the window (and hide RegExp help initially)
+ SetWindowPos(hwndDlg, NULL, pwlbaip->ptPos.x, pwlbaip->ptPos.y, iWidth, iHeight, (((iHeight==0)||(iWidth==0))?SWP_NOSIZE:0)|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+
+ // Make sure the dialog is not moved out of sight
+ SendMessage(hwndDlg, DM_REPOSITION, 0, 0);
+ }
+
+ // SetFocus (SetFocus() is not allowed in Dialogs)
+ SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_EDITBOX_NEWITEM), (LPARAM)TRUE);
+ return FALSE;
+ }
+
+ case WM_SETTINGCHANGE:
+ {
+ NONCLIENTMETRICS ncm;
+ HFONT hBoldFont;
+
+ // Free old message box font
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_GETFONT, 0, 0));
+
+ // Update message box font
+ ZeroMemory(&ncm, sizeof(ncm));
+ ncm.cbSize = sizeof(ncm);
+ if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0))
+ {
+ hBoldFont = CreateFontIndirect(&ncm.lfMessageFont);
+
+ SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ //SendDlgItemMessage(hwndDlg, IDOK, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ //SendDlgItemMessage(hwndDlg, IDCANCEL, WM_SETFONT, (WPARAM)hBoldFont, 0);
+ }
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ {
+ // Destroy help icon
+ DestroyIcon((HICON)SendDlgItemMessage(hwndDlg, IDC_BUTTON_REGEXP, BM_GETIMAGE, (WPARAM)IMAGE_ICON, 0));
+
+ // Free message box font
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_TEXT_DESCRIPTION, WM_GETFONT, 0, 0));
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_BUTTON_REGEXP:
+ {
+ // Show/Hide RegExp help part
+ RECT rcWin, rcSep;
+ int iHeight, iWidth;
+
+ if (GetWindowRect(hwndDlg, &rcWin))
+ {
+ iWidth = rcWin.right-rcWin.left;
+
+ // Hide/Show help
+ if (!IsDlgButtonChecked(hwndDlg, IDC_BUTTON_REGEXP))
+ {
+ if (GetWindowRect(GetDlgItem(hwndDlg, IDC_RECT_SEPARATOR), &rcSep))
+ {
+ iHeight = rcSep.top-rcWin.top + (int)((rcSep.bottom-rcSep.top)/2);
+
+ // Position the window
+ ShowDlgItem(hwndDlg, IDC_RECT_SEPARATOR, FALSE);
+ SetWindowPos(hwndDlg, NULL, 0, 0, iWidth, iHeight, SWP_NOMOVE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOACTIVATE);
+ }
+ } else {
+ iHeight = (int)GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_RECT_SEPARATOR), GWLP_USERDATA);
+
+ // Position the window
+ SetWindowPos(hwndDlg, NULL, 0, 0, iWidth, iHeight, SWP_NOMOVE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOACTIVATE);
+ ShowDlgItem(hwndDlg, IDC_RECT_SEPARATOR, TRUE);
+ }
+ }
+
+ // Make sure the dialog is not enlarged out of sight
+ SendMessage(hwndDlg, DM_REPOSITION, 0, 0);
+
+ return TRUE;
+ }
+
+ case IDOK:
+ {
+ struct WORDSLISTBOXADDITEMPARAMS* pwlbaip;
+ HWND hwndEdit;
+ int iNewItemSize;
+
+ pwlbaip = (struct WORDSLISTBOXADDITEMPARAMS*)GetWindowLongPtr(hwndDlg, DWLP_USER);
+
+ // Get entered text
+ if (pwlbaip)
+ {
+ hwndEdit = GetDlgItem(hwndDlg, IDC_EDITBOX_NEWITEM);
+ iNewItemSize = GetWindowTextLength(hwndEdit)+1;
+ pwlbaip->pszNewItem = (WCHAR*)mir_alloc((iNewItemSize)*sizeof(WCHAR));
+
+ if (pwlbaip->pszNewItem)
+ if (GetWindowText(hwndEdit, pwlbaip->pszNewItem, iNewItemSize) == 0) // GetWindowText takes the size not the len of the string
+ *pwlbaip->pszNewItem = _T('\0');
+ }
+
+ // fall through
+ }
+
+ case IDCANCEL:
+ // WM_CLOSE is transformed into an IDCANCEL by dialog manager
+ {
+ EndDialog(hwndDlg, LOWORD(wParam));
+ return TRUE;
+ }
+
+ case IDC_EDITBOX_NEWITEM:
+ {
+ switch (HIWORD(wParam))
+ {
+ case EN_CHANGE:
+ {
+ HWND hwndEdit = GetDlgItem(hwndDlg, IDC_EDITBOX_NEWITEM);
+ int iNewItemLen = GetWindowTextLength(hwndEdit);
+ WCHAR* pszNewItem = (WCHAR*)mir_alloc((iNewItemLen+1)*sizeof(WCHAR));
+
+ if (pszNewItem)
+ {
+ // Get entered text
+ if (GetWindowText(hwndEdit, pszNewItem, iNewItemLen+1) == 0)
+ pszNewItem[0] = _T('\0');
+
+ // Check if OK button can be released
+ EnableDlgItem(hwndDlg, IDOK, (lstrlen(pszNewItem) > 0));
+
+ mir_free(pszNewItem);
+ }
+
+ return TRUE;
+ }
+ } // switch HIWORD(wParam)
+ break;
+ }
+
+ } // switch wParam
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+
+void WordsListBoxInit(HWND hwndDlg, int iIDListBox, int iIDAddButton, int iIDDeleteButton, STRINGLIST* pslContents, const char* pszAddTooltip, const char* pszDeleteTooltip)
+{
+ HWND hwndListBox = GetDlgItem(hwndDlg, iIDListBox);
+ HWND hwndAddButton = GetDlgItem(hwndDlg, iIDAddButton);
+ HWND hwndDeleteButton = GetDlgItem(hwndDlg, iIDDeleteButton);
+ HICON hAddIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_ADD_BUTTON), IMAGE_ICON, 0, 0, LR_SHARED);
+ HICON hDeleteIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DELETE_BUTTON), IMAGE_ICON, 0, 0, LR_SHARED);
+
+ // Subclass to get key strokes/contextmenu/horizontal extent
+ SetWindowLongPtr(hwndListBox, GWLP_USERDATA, SetWindowLongPtr(hwndListBox, GWLP_WNDPROC, (LONG_PTR)WordsListBoxSubclassProc));
+
+ // Init drag-drop support
+ MakeDragList(hwndListBox);
+
+ // Insert contents
+ SLAssignToListBoxCtrl(pslContents, hwndListBox);
+
+ // Select first item
+ SendMessage(hwndListBox, LB_SETCURSEL, (WPARAM)0, 0);
+
+ // Init add button
+ SetWindowPos(hwndAddButton, NULL, 0, 0, 20, 20, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+ SendMessage(hwndAddButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hAddIcon);
+ PostMessage(hwndAddButton, BUTTONADDTOOLTIP, (WPARAM)pszAddTooltip, 0);
+ SendMessage(hwndAddButton, BUTTONSETASFLATBTN, 0, 0);
+
+ // Init delete button
+ SetWindowPos(hwndDeleteButton, NULL, 0, 0, 20, 20, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+ SendMessage(hwndDeleteButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hDeleteIcon);
+ PostMessage(hwndDeleteButton, BUTTONADDTOOLTIP, (WPARAM)pszDeleteTooltip, 0);
+ SendMessage(hwndDeleteButton, BUTTONSETASFLATBTN, 0, 0);
+}
+
+
+BOOL WordsListBoxDragDrop(HWND hwndDlg, int iIDListBox, UINT uMsg, LPARAM lParam, BOOL bAllowCopy)
+{
+ // This function needs to be called in the default handler of the dialog
+
+ if (uMsg == RegisterWindowMessage(DRAGLISTMSGSTRING))
+ {
+ DRAGLISTINFO* pdli = (DRAGLISTINFO*)lParam;
+
+ if (GetDlgItem(hwndDlg, iIDListBox) == pdli->hWnd)
+ {
+ int iHoveredItem = LBItemFromPt(pdli->hWnd, pdli->ptCursor, (pdli->uNotification==DL_DRAGGING));
+
+ switch (pdli->uNotification)
+ {
+ case DL_BEGINDRAG:
+ {
+ // Remember start position
+ SetProp(pdli->hWnd, DRAGLISTMSGSTRING, (HANDLE)iHoveredItem);
+
+ // Allow/Disallow drag
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)(BOOL)(iHoveredItem >= 0));
+ return TRUE;
+ }
+
+ case DL_DRAGGING:
+ {
+ // LBItemFromPt returns an item if no item is in list
+ if (SendMessage(pdli->hWnd, LB_GETCOUNT, 0, 0) <= 1)
+ iHoveredItem = LB_ERR;
+
+ if (iHoveredItem >= 0)
+ {
+ BOOL bMakeCopy = ((bAllowCopy) && (GetAsyncKeyState(VK_CONTROL) < 0));
+
+ // Show insertion mark
+ DrawInsert(hwndDlg, pdli->hWnd, iHoveredItem);
+
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)(bMakeCopy?DL_COPYCURSOR:DL_MOVECURSOR));
+
+ } else {
+ // Cursor moved to impossible position
+ DrawInsert(hwndDlg, pdli->hWnd, LB_ERR);
+
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)DL_STOPCURSOR);
+ }
+
+ return TRUE;
+ }
+
+ case DL_DROPPED:
+ {
+ int iDragItem = (int)GetProp(pdli->hWnd, DRAGLISTMSGSTRING);
+
+ if ((iHoveredItem >= 0 ) && (iHoveredItem != iDragItem) && (iHoveredItem != iDragItem+1))
+ {
+ LPARAM lParamItemData;
+ int iLenItem;
+ WCHAR* pszItemText;
+
+ iLenItem = (int)SendMessage(pdli->hWnd, LB_GETTEXTLEN, (WPARAM)iDragItem, 0);
+ if (iLenItem == LB_ERR) iLenItem = 0;
+ pszItemText = (WCHAR*)mir_alloc((iLenItem+1)*sizeof(WCHAR));
+
+ if (pszItemText)
+ {
+ if (SendMessage(pdli->hWnd, LB_GETTEXT, (WPARAM)iDragItem, (LPARAM)pszItemText) != LB_ERR)
+ {
+ lParamItemData = (LPARAM)SendMessage(pdli->hWnd, LB_GETITEMDATA, (WPARAM)iDragItem, 0);
+
+ // Make copy of item
+ iHoveredItem = (int)SendMessage(pdli->hWnd, LB_INSERTSTRING, (WPARAM)iHoveredItem, (LPARAM)pszItemText);
+
+ if (iHoveredItem >= 0)
+ {
+ BOOL bMakeCopy = ((bAllowCopy) && (GetAsyncKeyState(VK_CONTROL) < 0));
+
+ SendMessage(pdli->hWnd, LB_SETITEMDATA, (WPARAM)iHoveredItem, (LPARAM)lParamItemData);
+
+ // Delete old item if moving
+ if (!bMakeCopy)
+ {
+ if (iHoveredItem < iDragItem)
+ iDragItem++;
+
+ SendMessage(pdli->hWnd, LB_DELETESTRING, (WPARAM)iDragItem, 0);
+ }
+
+ // Set current selection
+ SendMessage(pdli->hWnd, LB_SETCURSEL, (WPARAM)iHoveredItem, 0);
+ }
+ }
+
+ mir_free(pszItemText);
+ }
+ }
+
+ // fall trough
+ }
+
+ case DL_CANCELDRAG:
+ {
+ // Remove insertion mark
+ DrawInsert(hwndDlg, pdli->hWnd, LB_ERR);
+
+ // Remove buffer data
+ RemoveProp(pdli->hWnd, DRAGLISTMSGSTRING);
+ return TRUE;
+ }
+
+ } // switch
+ }
+ }
+
+ return FALSE;
+}
+
+
+BOOL WordsListBoxAddItem(HWND hwndDlg, int iIDListBox, STRINGLIST* pslOtherContents, const WCHAR* pszMsgTitle, const WCHAR* pszMsgTextInList, const WCHAR* pszMsgTextInOther, int iIDButtonDelete)
+{
+ HWND hwndListBox = GetDlgItem(hwndDlg, iIDListBox);
+ BOOL bDoAdd;
+ struct WORDSLISTBOXADDITEMPARAMS wlbaip;
+ RECT rc;
+
+ if (!hwndListBox) return FALSE;
+
+ if (GetWindowRect(hwndListBox, &rc)) {
+ wlbaip.ptPos.x = rc.left-30;
+ wlbaip.ptPos.y = rc.top-5;
+ } else {
+ wlbaip.ptPos.x = 0;
+ wlbaip.ptPos.y = 0;
+ }
+
+ wlbaip.pszTitle = (WCHAR*)pszMsgTitle;
+
+ // Show dialog
+ bDoAdd = (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_LISTBOX_ADD_ITEM), hwndDlg, DlgProcWordsListBoxAddItem, (LPARAM)&wlbaip) == IDOK);
+ if (bDoAdd)
+ {
+ // Check if regular expression is valid (if any)
+ if (!wlbaip.pszNewItem || !ValidateRegExp(wlbaip.pszNewItem))
+ {
+ WCHAR* pszBoxTextFmt = TranslateT("The entered phrase \"%s\" consists of a Regular Expression containing a syntax error!\r\n\r\nAdding an malformed Regular Expression will cause the filter\r\nto treat the expression as a normal phrase.\r\n\r\nShall the malformed Regular Expression be added anyway?");
+ WCHAR* pszBoxText = (WCHAR*)mir_alloc((lstrlen(pszBoxTextFmt)+lstrlen(wlbaip.pszNewItem)+1)*sizeof(WCHAR));
+ bDoAdd = FALSE;
+
+ if (pszBoxTextFmt && pszBoxText)
+ {
+ mir_sntprintf(pszBoxText, (lstrlen(pszBoxTextFmt)+lstrlen(wlbaip.pszNewItem)+1), pszBoxTextFmt, wlbaip.pszNewItem);
+
+ // Show message box that the item is malformed
+ if (!Miranda_Terminated())
+ bDoAdd = (MessageBoxEx(hwndDlg, pszBoxText, TranslateT("Syntax Error in Regular Expression"), MB_YESNO|MB_ICONEXCLAMATION|MB_DEFBUTTON2, LANGIDFROMLCID(GetThreadLocale())) == IDYES);
+ }
+
+ if (pszBoxTextFmt) mir_free(pszBoxTextFmt);
+ if (pszBoxText) mir_free(pszBoxText);
+
+ } else {
+ bDoAdd = TRUE;
+ }
+
+ if (bDoAdd)
+ {
+ int iIndexIfFound;
+
+ // Look for same item in list (not case sensitive)
+ iIndexIfFound = SendMessage(hwndListBox, LB_FINDSTRINGEXACT, 0, (LPARAM)wlbaip.pszNewItem);
+ if (iIndexIfFound >= 0)
+ {
+ WCHAR* pszBoxText = (WCHAR*)mir_alloc((lstrlen(pszMsgTextInList)+lstrlen(wlbaip.pszNewItem)+1)*sizeof(WCHAR));
+ bDoAdd = FALSE;
+
+ if (pszBoxText)
+ {
+ mir_sntprintf(pszBoxText, (lstrlen(pszMsgTextInList) + lstrlen(wlbaip.pszNewItem) + 1), pszMsgTextInList, wlbaip.pszNewItem);
+
+ SendMessage(hwndListBox, LB_SETCURSEL, (WPARAM)iIndexIfFound, 0);
+
+ // Show message box that the item can not be added
+ if (!Miranda_Terminated())
+ bDoAdd = (MessageBoxEx(hwndDlg, pszBoxText, pszMsgTitle, MB_YESNO|MB_ICONEXCLAMATION|MB_DEFBUTTON2, LANGIDFROMLCID(GetThreadLocale())) == IDYES);
+
+ mir_free(pszBoxText);
+ }
+
+ } else if (pslOtherContents && SLIsItem(pslOtherContents, wlbaip.pszNewItem, FALSE) )
+ {
+ WCHAR* pszBoxText = (WCHAR*)mir_alloc((lstrlen(pszMsgTextInOther)+lstrlen(wlbaip.pszNewItem)+1)*sizeof(WCHAR));
+ bDoAdd = FALSE;
+
+ if (pszBoxText)
+ {
+ // Show message box that it is not good to add the item
+ mir_sntprintf(pszBoxText, (lstrlen(pszMsgTextInOther) + lstrlen(wlbaip.pszNewItem) + 1), pszMsgTextInOther, wlbaip.pszNewItem);
+
+ if (!Miranda_Terminated())
+ bDoAdd = (MessageBoxEx(hwndDlg, pszBoxText, pszMsgTitle, MB_YESNO|MB_ICONEXCLAMATION|MB_DEFBUTTON2, LANGIDFROMLCID(GetThreadLocale())) == IDYES);
+
+ mir_free(pszBoxText);
+ }
+
+ } else {
+ bDoAdd = TRUE;
+ }
+
+ }
+
+ // Add the item if everything is OK
+ if (bDoAdd)
+ {
+ int iPos;
+
+ // Add new item to list
+ iPos = SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)wlbaip.pszNewItem);
+ if (iPos >= 0)
+ {
+ // Select new entry
+ SendMessage(hwndListBox, LB_SETCURSEL, iPos, 0);
+
+ // Activate delete button
+ EnableDlgItem(hwndDlg, iIDButtonDelete, TRUE);
+
+ } else {
+ bDoAdd = FALSE;
+ }
+ }
+
+ // Free allocated memory
+ mir_free(wlbaip.pszNewItem);
+ }
+
+ // Set focus on list box
+ SetFocus(hwndListBox);
+
+ return bDoAdd;
+}
+
+
+int WordsListBoxDeleteItem(HWND hwndDlg, int iIDListBox, int iIDButtonDelete)
+{
+ HWND hwndListBox = GetDlgItem(hwndDlg, iIDListBox);
+ int iSelectedItem;
+ int iItemCount;
+
+ if (!hwndListBox) return LB_ERR;
+
+ iSelectedItem = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
+ if (iSelectedItem >= 0)
+ {
+ // Delete entry and get new list item count
+ iItemCount = SendMessage(hwndListBox, LB_DELETESTRING, (WPARAM)iSelectedItem, 0);
+
+ // Deactivate delete button?
+ if (iItemCount == 0)
+ EnableDlgItem(hwndDlg, iIDButtonDelete, FALSE);
+
+ // Select next item for 'fast delete'
+ if (SendMessage(hwndListBox, LB_SETCURSEL, (WPARAM)iSelectedItem, 0) < 0)
+ SendMessage(hwndListBox, LB_SETCURSEL, (WPARAM)((iSelectedItem>0)?(iSelectedItem-1):0), 0);
+
+ } else {
+ iItemCount = LB_ERR;
+ }
+
+ // Set focus on list box
+ SetFocus(hwndListBox);
+
+ return iItemCount;
+}
+
+
+BOOL ToggleTreeViewItemState(HWND hwndTreeView, HTREEITEM hTreeItem)
+{
+ UINT uCurrentState;
+ BOOL bChangeAllowed;
+
+ uCurrentState = TreeView_GetItemState(hwndTreeView, hTreeItem, TVIS_STATEIMAGEMASK);
+
+ // No toggle if item is section item
+ if (uCurrentState&INDEXTOSTATEIMAGEMASK(0)) // no checkbox
+ {
+ bChangeAllowed = FALSE;
+
+ // Check if another checkbox is still checked before unchecking this one
+ } else if (uCurrentState&INDEXTOSTATEIMAGEMASK(2)) // checkbox checked
+ {
+ HTREEITEM hItem;
+ HTREEITEM hSectionItem;
+
+ bChangeAllowed = FALSE;
+ for (hSectionItem=TreeView_GetRoot(hwndTreeView); hSectionItem; hSectionItem=TreeView_GetNextSibling(hwndTreeView, hSectionItem))
+ {
+ for (hItem=TreeView_GetChild(hwndTreeView, hSectionItem); hItem; hItem=TreeView_GetNextSibling(hwndTreeView, hItem))
+ {
+ if (hItem == hTreeItem) continue;
+
+ // Check setting (checkbox checked?)
+ if (TreeView_GetItemState(hwndTreeView, hItem, TVIS_STATEIMAGEMASK)&INDEXTOSTATEIMAGEMASK(2))
+ {
+ bChangeAllowed = TRUE;
+ break;
+ }
+ }
+ }
+
+ // System default sound (mistake notify)
+ if (!bChangeAllowed)
+ MessageBeep(MB_OK);
+
+ } else {
+ bChangeAllowed = TRUE;
+ }
+
+ if (bChangeAllowed)
+ TreeView_SetItemState(hwndTreeView, hTreeItem, INDEXTOSTATEIMAGEMASK((uCurrentState&INDEXTOSTATEIMAGEMASK(2))?1:2), TVIS_STATEIMAGEMASK);
+
+ return bChangeAllowed;
+}
+
+
+MESSAGETYPEDESC* GetDataFromTreeViewItem(HWND hwndTreeView, HTREEITEM hTreeItem)
+{
+ TVITEM tvi;
+
+ tvi.hItem = hTreeItem;
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+
+ if (TreeView_GetItem(hwndTreeView, &tvi))
+ if (((unsigned int)tvi.lParam >= 0) && ((unsigned int)tvi.lParam < uMsgTypesCount))
+ return &pamtdMsgTypes[(unsigned int)tvi.lParam];
+
+ return NULL;
+}
+
+
+BOOL MakeVariablesHelpButton(HWND hwndDlg, int iIDButton)
+{
+ BOOL bReturn;
+
+ // Set icon and resize button
+ SetWindowPos(GetDlgItem(hwndDlg, iIDButton), NULL, 0, 0, 20, 20, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
+ SendDlgItemMessage(hwndDlg, iIDButton, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)LoadSystemIcon(IDB_STD_SMALL_COLOR, STD_HELP));
+ PostMessage(GetDlgItem(hwndDlg, iIDButton), BUTTONADDTOOLTIP, (WPARAM)Translate("Use variables in the message text"), 0);
+ SendMessage(GetDlgItem(hwndDlg, iIDButton), BUTTONSETASFLATBTN, 0, 0);
+
+ // Do not show help button if variables are not available
+ bReturn = (ServiceExists(MS_VARS_SHOWHELP) && ServiceExists(MS_VARS_FORMATSTRING));
+ ShowDlgItem(hwndDlg, iIDButton, bReturn);
+
+ return bReturn;
+}
+
+
+// ------------------------------
+
+
+static int OptionsInitialise(WPARAM wParam, LPARAM lParam)
+{
+ // (Options page max. size: 314, 240)
+
+ // These controls will be hidden in simple (non-expert) options mode
+ static UINT SpamFilterExpertOnlyControls[] = {IDC_BUTTON_ADVERTISMENTFILTER, IDC_BUTTON_DISLIKEDMESSAGEFILTER, IDC_BUTTON_ROBOTFILTER, IDC_BUTTON_RESETAUTOIGNORE, IDC_GROUP_MESSAGETYPES, IDC_TEXT_MESSAGETYPES, IDC_TREEVIEW_MESSAGETYPES};
+
+ OPTIONSDIALOGPAGE odp;
+ INITCOMMONCONTROLSEX icc;
+
+ // Add Spam Filter option page
+ ZeroMemory(&odp, sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInstance;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_MAIN);
+ odp.position = 900000001; // 900000000 = Ignore options
+ odp.ptszTitle = TranslateT("Spam Filter");
+ odp.ptszGroup = TranslateT("Events");
+ odp.flags = ODPF_BOLDGROUPS|ODPF_UNICODE;
+ odp.pfnDlgProc = DlgProcSpamFilterOpt;
+ //odp.nIDBottomSimpleControl = IDC_GROUP_POPUP; // In simple mode the dlg will be cut off after this control
+ odp.expertOnlyControls = SpamFilterExpertOnlyControls;
+ odp.nExpertOnlyControls = ARRAYSIZE(SpamFilterExpertOnlyControls);
+
+ // Ensure the needed common controls are loaded
+ ZeroMemory(&icc, sizeof(icc));
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_TREEVIEW_CLASSES|ICC_UPDOWN_CLASS;
+ InitCommonControlsEx(&icc);
+
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM)&odp);
+ return 0;
+}
+
+
+static int DbSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ DBCONTACTWRITESETTING* pdcws = (DBCONTACTWRITESETTING*)lParam;
+ HANDLE hContact = (HANDLE)wParam;
+
+ // Ignore Options Extension
+ if ( (hContact == NULL) && (StrCmpA(pdcws->szModule, "Ignore") == 0))
+ {
+ if ( (( (IsWindow(hwndSpamFilterOpt) ? IsDlgButtonChecked(hwndSpamFilterOpt, IDC_CHECKBOX_ADVERTISMENTFILTER) : FALSE) || DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)DEFAULT_SETTING_ADVERTISMENTFILTER) )
+ || ( (IsWindow(hwndSpamFilterOpt) ? IsDlgButtonChecked(hwndSpamFilterOpt, IDC_CHECKBOX_ROBOTFILTER) : FALSE) || DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOTFILTER, (BYTE)DEFAULT_SETTING_ROBOTFILTER) ))
+ && (BOOL)CallService(MS_IGNORE_ISIGNORED, (WPARAM)NULL, (LPARAM)IGNOREEVENT_MESSAGE)
+ && (BOOL)CallService(MS_IGNORE_ISIGNORED, (WPARAM)NULL, (LPARAM)IGNOREEVENT_URL)
+ && (BOOL)CallService(MS_IGNORE_ISIGNORED, (WPARAM)NULL, (LPARAM)IGNOREEVENT_FILE)
+ && (BOOL)CallService(MS_IGNORE_ISIGNORED, (WPARAM)NULL, (LPARAM)IGNOREEVENT_AUTHORIZATION) )
+ {
+ if (!Miranda_Terminated())
+ {
+ HWND hwndParent = IsWindow(hwndSpamFilterOpt) ? GetParent(hwndSpamFilterOpt) : NULL;
+
+ if (MessageBoxEx(hwndParent, TranslateT("You chose to ignore all kind of messages from contacts that are not on your contact list although the spam filter is activated.\r\n\r\nShall the spam filter be deactivated?"), TranslateT("Spam Filter"), MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON1|(!hwndParent?(MB_SETFOREGROUND|MB_TASKMODAL):0), LANGIDFROMLCID(GetThreadLocale())) == IDYES)
+ {
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)FALSE);
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOTFILTER, (BYTE)FALSE);
+
+ if (IsWindow(hwndSpamFilterOpt))
+ {
+ CheckDlgButton(hwndSpamFilterOpt, IDC_CHECKBOX_ADVERTISMENTFILTER, BST_UNCHECKED);
+ CheckDlgButton(hwndSpamFilterOpt, IDC_CHECKBOX_ROBOTFILTER, BST_UNCHECKED);
+ }
+ }
+ }
+
+ }
+ }
+
+ // Watch "Hidden" setting to sync with IsSammer-state (Ignore opts change)
+/* if (StrCmpA(pdcws->szModule, "CList") == 0)
+ if (StrCmpA(pdcws->szSetting, "Hidden") == 0)
+ if ((pdcws->value.type==DBVT_DELETED) || !pdcws->value.bVal)
+ if ((BOOL)CallService(MS_SPAMFILTER_CONTACT_ISSPAMMER, (WPARAM)hContact, 0))
+ DBWriteContactSettingByte((HANDLE)wParam, DB_MODULE_NAME, DB_SETTING_ISSPAMMER, (BYTE)FALSE); // no service call here!
+*/
+
+ // Cache starting/stopping according to Filter settings (to save a little memory)
+ if ( (hContact == NULL) && (StrCmpA(pdcws->szModule, DB_MODULE_NAME) == 0) &&
+ (StrCmpA(pdcws->szSetting, DB_SETTING_ADVERTISMENTFILTER) == 0) )
+ {
+ BOOL bAdvertismentFilter = (pdcws->value.type==DBVT_BYTE) ? pdcws->value.bVal : DEFAULT_SETTING_ADVERTISMENTFILTER;
+ BOOL bDislikedMessagesFilter = DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, (BYTE)DEFAULT_SETTING_DISLIKEDMESSAGEFILTER);
+
+ ToggleSpamDefinitionsCache(bAdvertismentFilter || bDislikedMessagesFilter);
+ }
+ if ( (hContact == NULL) && (StrCmpA(pdcws->szModule, DB_MODULE_NAME) == 0) &&
+ (StrCmpA(pdcws->szSetting, DB_SETTING_DISLIKEDMESSAGEFILTER) == 0) )
+ {
+ BOOL bDislikedMessagesFilter = (pdcws->value.type==DBVT_BYTE) ? pdcws->value.bVal : DEFAULT_SETTING_DISLIKEDMESSAGEFILTER;
+ BOOL bAdvertismentFilter = DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)DEFAULT_SETTING_ADVERTISMENTFILTER);
+
+ ToggleSpamDefinitionsCache(bAdvertismentFilter || bDislikedMessagesFilter);
+ }
+
+ return 0;
+}
+
+
+// ------------------------------------
+
+
+void InitOptions(void)
+{
+ hHookOptInit = HookEvent(ME_OPT_INITIALISE, OptionsInitialise);
+ hHookDbSettingChangedOpt = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DbSettingChanged);
+}
+
+void UninitOptions(void)
+{
+ if (hHookOptInit) UnhookEvent(hHookOptInit);
+ if (hHookDbSettingChangedOpt) UnhookEvent(hHookDbSettingChangedOpt);
+
+ // Dialogs that are created without parent (non-modal) need to be destroyed
+ if (IsWindow(hwndAdvertismentFilter))
+ SendMessage(hwndAdvertismentFilter, WM_CLOSE, 0, 0);
+ if (IsWindow(hwndRobotFilter))
+ SendMessage(hwndRobotFilter, WM_CLOSE, 0, 0);
+ if (IsWindow(hwndDislikedMessagesFilter))
+ SendMessage(hwndDislikedMessagesFilter, WM_CLOSE, 0, 0);
+ if (IsWindow(hwndSpamDefinitionsInfo))
+ SendMessage(hwndSpamDefinitionsInfo, WM_CLOSE, 0, 0);
+ if (IsWindow(hwndSpammersInfo))
+ SendMessage(hwndSpammersInfo, WM_CLOSE, 0, 0);
+} \ No newline at end of file
diff --git a/spamfilter/options.h b/spamfilter/options.h
new file mode 100644
index 0000000..b5b0380
--- /dev/null
+++ b/spamfilter/options.h
@@ -0,0 +1,60 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_OPTIONS_H__
+#define SF_OPTIONS_H__ 1
+
+// -- Defines: Self-defined dialog messages
+#define SFM_REBUILD_MSGTYPES (WM_APP+111) // wParam=lParam=0
+#define SFM_VALIDATE_LOGFILENAME (WM_APP+112) // wParam=lParam=0
+#define SFM_CHECK_AUTOIGNORE (WM_APP+113) // wParam=lParam=0
+#define SFM_RELOAD_DEFAULTBADWORDS (WM_APP+114) // wParam=lParam=0
+#define SFM_REFRESH_SPAMDEFINITIONS (WM_APP+115) // wParam=bForceRefresh; lParam=0 or lParam=dwFilterType
+#define SFM_REFRESH_SPAMMERS (WM_APP+116) // wParam=lParam=0
+
+// -- Protoypes
+BOOL CALLBACK DlgProcSpamFilterOpt(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+BOOL CALLBACK DlgProcConfigureAdvertismentFilter(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+BOOL CALLBACK DlgProcConfigureDislikedMessagesFilter(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+BOOL CALLBACK DlgProcConfigureRobotFilter(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL CALLBACK DlgProcSpamDefinitionsInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+BOOL CALLBACK DlgProcSpammersInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+static int OptionsInitialise(WPARAM wParam, LPARAM lParam);
+static int DbSettingChanged(WPARAM wParam, LPARAM lParam);
+
+LRESULT CALLBACK WordsListBoxSubclassProc(HWND hwndList, UINT uMsg, WPARAM wParam, LPARAM lParam);
+BOOL CALLBACK DlgProcWordsListBoxAddItem(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+void WordsListBoxInit(HWND hwndDlg, int iIDListBox, int iIDAddButton, int iIDDeleteButton, STRINGLIST* pslContents, const char* pszAddTooltip, const char* pszDeleteTooltip);
+BOOL WordsListBoxDragDrop(HWND hwndDlg, int iIDListBox, UINT uMsg, LPARAM lParam, BOOL bAllowCopy);
+BOOL WordsListBoxAddItem(HWND hwndDlg, int iIDListbox, STRINGLIST* pslOtherContents, const WCHAR* pszMsgTitle, const WCHAR* pszMsgTextInList, const WCHAR* pszMsgTextInOther, int iIDButtonDelete);
+int WordsListBoxDeleteItem(HWND hwndDlg, int iIDListBox, int iIDButtonDelete);
+
+BOOL ToggleTreeViewItemState(HWND hwndTreeView, HTREEITEM hItem) ;
+MESSAGETYPEDESC* GetDataFromTreeViewItem(HWND hwndTreeView, HTREEITEM hTreeItem);
+
+BOOL MakeVariablesHelpButton(HWND hwndDlg, int iIDButton);
+
+void InitOptions(void);
+void UninitOptions(void);
+
+#endif // SF_OPTIONS_H__
diff --git a/spamfilter/path.c b/spamfilter/path.c
new file mode 100644
index 0000000..b829c68
--- /dev/null
+++ b/spamfilter/path.c
@@ -0,0 +1,541 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+
+// -----------------------------------------
+
+
+BOOL PPreparePathForWrite(HWND hwndDlg, const WCHAR* pszPath, BOOL bAskDirCreate, BOOL bContainsFileName)
+{
+ // Using SHPathPrepareForWrite on Win2000+ (best, only if avail)
+ {
+ HRESULT (STDAPICALLTYPE *MySHPathPrepareForWrite)(HWND, IUnknown*, LPCTSTR, DWORD);
+
+ #if defined(UNICODE)
+ *(FARPROC*)&MySHPathPrepareForWrite = GetProcAddress(GetModuleHandle(_T("USER32")), "SHPathPrepareForWriteW");
+ #else
+ *(FARPROC*)&MySHPathPrepareForWrite = GetProcAddress(GetModuleHandle(_T("USER32")), "SHPathPrepareForWriteA");
+ #endif
+
+ if (MySHPathPrepareForWrite)
+ {
+ DWORD dwFlags = SHPPFW_NOWRITECHECK; // Not impl anyway
+
+ if (bContainsFileName)
+ dwFlags |= SHPPFW_IGNOREFILENAME;
+
+ dwFlags |= bAskDirCreate ? SHPPFW_ASKDIRCREATE : SHPPFW_DIRCREATE;
+
+ return SUCCEEDED(MySHPathPrepareForWrite(hwndDlg, NULL, (LPCTSTR)pszPath, dwFlags));
+ }
+ }
+
+ // Emulating SHPathPrepareForWrite:
+ // Using SHCreateDirectoryEx on WinME
+ // And: Using emulated version of SHCreateDirectoryEx on Win95/98
+ {
+ WCHAR* pszDir;
+ BOOL bReturn = FALSE;
+
+ pszDir = mir_wstrdup(pszPath);
+ if (!pszDir) return FALSE;
+
+ if (bContainsFileName)
+ PathRemoveFileSpec((LPWSTR)pszDir);
+
+ // No dialog is emulated, instead always behave as
+ // if "No" was clicked
+ if (bAskDirCreate)
+ return FALSE;
+
+ // Create all sub directories
+ {
+ int (STDAPICALLTYPE *MySHCreateDirectoryEx)(HWND, LPCTSTR, SECURITY_ATTRIBUTES*);
+
+ #if defined(UNICODE)
+ *(FARPROC*)&MySHCreateDirectoryEx = (FARPROC)GetProcAddress(GetModuleHandle(_T("SHELL32")), "SHCreateDirectoryExW");
+ #else
+ *(FARPROC*)&MySHCreateDirectoryEx = (FARPROC)GetProcAddress(GetModuleHandle(_T("SHELL32")), "SHCreateDirectoryExA");
+ #endif
+
+ if (MySHCreateDirectoryEx)
+ {
+ int iRet = (SHCreateDirectoryEx(hwndDlg, pszDir, NULL) == ERROR_SUCCESS);
+ bReturn = ((iRet == ERROR_SUCCESS) || (iRet == ERROR_ALREADY_EXISTS));
+
+ } else {
+
+ int iSlashOcc;
+ int iAbsSlashOcc;
+ WCHAR* pszSlash;
+ WCHAR szDirTemp[MAX_PATH];
+ WCHAR szDirOutput[MAX_PATH];
+
+ ZeroMemory(&szDirTemp, sizeof(szDirTemp));
+ ZeroMemory(&szDirOutput, sizeof(szDirOutput));
+
+ // Skip drive letter
+ pszSlash = StrChr(pszDir, _T('\\')); // Search for the first :'\\'
+
+ if (pszSlash != NULL) // does not contain any slashs
+ {
+ iSlashOcc = (int)(pszSlash-pszDir+1); // Pointer arithmetic
+ StrCpyN(szDirTemp, pszDir+iSlashOcc, lstrlen(pszDir)-iSlashOcc); // Pointer arithmetic
+ iAbsSlashOcc = iSlashOcc;
+
+ bReturn = TRUE;
+ pszSlash = szDirTemp;
+ while (pszSlash)
+ {
+ pszSlash = StrChr(szDirTemp, _T('\\'));
+
+ iSlashOcc = (int)(pszSlash-szDirTemp+1); // Pointer arithmetic
+ iAbsSlashOcc = iAbsSlashOcc + iSlashOcc;
+
+ StrCpyN(szDirOutput, pszPath, iAbsSlashOcc); // Pointer arithmetic
+
+ StrCpyN(szDirTemp, szDirTemp+iSlashOcc, lstrlen(szDirTemp)-iSlashOcc); // Pointer arithmetic
+ ZeroMemory((PBYTE)szDirTemp+((lstrlen(szDirTemp)-iSlashOcc)*sizeof(WCHAR)), iSlashOcc*sizeof(WCHAR));
+
+ bReturn = (CreateDirectory(szDirOutput, NULL) && bReturn);
+ }
+ }
+ }
+ }
+
+ mir_free(pszDir);
+ return bReturn;
+ }
+}
+
+
+BOOL PIsValidFile(const WCHAR* pszFileName, const WCHAR* aszFileExtensions[], int nFileExtensions, const WCHAR* aszContentTypes[], int nContentTypes)
+{
+ BOOL bIsOk = FALSE;
+ int i;
+
+ if (!pszFileName) return FALSE;
+
+ // Check file extensions
+ for (i=0; i<nFileExtensions; i++)
+ {
+ bIsOk = (StrCmpI(PathFindExtension(pszFileName), aszFileExtensions[i]) == 0);
+ if (bIsOk) break;
+ }
+
+ if (!bIsOk)
+ {
+ // Check content types
+ for (i=0; i<nContentTypes; i++)
+ {
+ bIsOk = PathIsContentType(pszFileName, aszContentTypes[i]);
+ if (bIsOk) break;
+ }
+
+ if (!bIsOk)
+ return FALSE;
+ }
+
+ // Check if the file name is actually a directory
+ if (PathIsDirectory(pszFileName))
+ return FALSE;
+
+ // Finally check if file is present
+ return PathFileExists(pszFileName);
+}
+
+
+void PMakePathPretty(WCHAR* pszPath, DWORD dwFlags)
+{
+ WCHAR szBuf[MAX_PATH];
+ WCHAR szRelativeTo[MAX_PATH];
+
+ if (!pszPath)
+ return;
+
+ if (!PConstructLocalPath(szRelativeTo, CSIDL_EXT_EXECUTABLE, NULL, NULL, NULL))
+ szRelativeTo[0] = _T('\0');
+
+ // Create absolute path if path is relative
+ if (PathIsRelative(pszPath))
+ {
+ if (PathCombine(szBuf, szRelativeTo, pszPath))
+ {
+ mir_sntprintf(pszPath, MAX_PATH, _T("%s"), szBuf);
+ dwFlags |= PC_ABSOLUTERELATIVE; // Convert back after making pretty
+ }
+ }
+
+ // Make pretty
+ {
+ if (PathCanonicalize(szBuf, pszPath))
+ mir_sntprintf(pszPath, MAX_PATH, _T("%s"), szBuf);
+
+ // Convert slightly wrong chars
+ ReplaceSubStringWithStringBuf(pszPath, 0, _T("/"), _T("\\"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszPath, 0, _T("\\\\"), _T("\\"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszPath, 0, _T("\""), NULL, TRUE, FALSE);
+
+ /*
+ {
+ int i;
+
+ // Remove wildcard characters
+ if (dwFlags&PC_ALLOW_WILDCARDS)
+ for (i=lstrlen(pszPath); i>=0; i--)
+ if (PathGetCharType(pszPath[i])&GCT_WILD)
+ RemoveSubStr(pszPath, i, 1);
+
+ // Remove disallowed TCHARs
+ for (i=lstrlen(pszPath); i>=0; i--)
+ if (PathGetCharType(pszPath[i])&GCT_INVALID)
+ RemoveSubStr(pszPath, i, 1);
+ }
+ */
+
+ PathRemoveBlanks(pszPath);
+ PathUnquoteSpaces(pszPath);
+
+ // Upper case drive TCHAR
+ if (PathGetDriveNumber(pszPath) != -1)
+ CharUpperBuff(pszPath, 1);
+
+ // Make it use the long path name (Win98+/Win2000+)
+ {
+ DWORD (WINAPI *MyGetLongPathName)(LPCTSTR, LPCTSTR, DWORD);
+
+ #if defined(UNICODE)
+ *(FARPROC*)&MyGetLongPathName = GetProcAddress(GetModuleHandle(_T("KERNEL32")), "GetLongPathNameW");
+ #else
+ *(FARPROC*)&MyGetLongPathName = GetProcAddress(GetModuleHandle(_T("KERNEL32")), "GetLongPathNameA");
+ #endif
+
+ if (MyGetLongPathName)
+ {
+ if (MyGetLongPathName(pszPath, pszPath, MAX_PATH) == 0)
+ {
+ // Try to remove file name and see if it works then
+ mir_sntprintf(szBuf, ARRAYSIZE(szBuf), _T("%s"), pszPath);
+
+ if (PathRemoveFileSpec(szBuf) && (MyGetLongPathName(szBuf, szBuf, ARRAYSIZE(szBuf)) != 0))
+ {
+ PathAppend(szBuf, PathFindFileName(pszPath));
+ mir_sntprintf(pszPath, MAX_PATH, _T("%s"), szBuf);
+ }
+ }
+ }
+ }
+
+ // Lower case path if all upper case (only if user activated this feature for Windows-Explorer)
+ {
+ SHELLFLAGSTATE sfs;
+ SHGetSettings(&sfs, SSF_DONTPRETTYPATH);
+
+ if (!sfs.fDontPrettyPath)
+ PathMakePretty(pszPath);
+ }
+ }
+
+ // Make relative
+ if (dwFlags&PC_ABSOLUTERELATIVE)
+ {
+ if (PathRelativePathTo(szBuf, szRelativeTo, FILE_ATTRIBUTE_DIRECTORY, pszPath, FILE_ATTRIBUTE_NORMAL))
+ {
+ mir_sntprintf(pszPath, MAX_PATH, _T("%s"), szBuf);
+
+ // Remove backslash in front -> does not belong there (causes by PathRelativePathTo)
+ if ((pszPath[0] == _T('\\')) || (pszPath[0] == _T('/')))
+ MoveMemory(pszPath, CharNext(pszPath), (lstrlen(CharNext(pszPath))+1)*sizeof(WCHAR));
+ }
+
+ // Miranda Utils contains a quite less featured implementation of relative paths
+ // (not all cases are captured, does not support Unicode, too)
+ //if (CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)pszPath, (LPARAM)szBuf) == 0)
+ // mir_sntprintf(pszPath, MAX_PATH, _T("%s"), szBuf);
+ }
+
+ // Environment strings
+ if (dwFlags&PC_ENVIRONMENTSTRINGS)
+ {
+ BOOL (STDAPICALLTYPE *MyPathUnExpandEnvStrings)(LPCTSTR, LPTSTR, UINT);
+
+ // PathUnExpandEnvStrings is Win98/2000+
+ #if defined(UNICODE)
+ *(FARPROC*)&MyPathUnExpandEnvStrings = (FARPROC)GetProcAddress(GetModuleHandle(_T("SHLWAPI")), "PathUnExpandEnvStringsW");
+ #else
+ *(FARPROC*)&MyPathUnExpandEnvStrings = (FARPROC)GetProcAddress(GetModuleHandle(_T("SHLWAPI")), "PathUnExpandEnvStringsA");
+ #endif
+
+ if (MyPathUnExpandEnvStrings)
+ if (MyPathUnExpandEnvStrings(pszPath, szBuf, MAX_PATH))
+ mir_sntprintf(pszPath, MAX_PATH, _T("%s"), szBuf);
+ }
+}
+
+
+DWORD PMakePathUsable(WCHAR* pszPath)
+{
+ WCHAR szBuf[MAX_PATH];
+ DWORD dwFlags = 0;
+
+ if (!pszPath)
+ return dwFlags;
+
+ PathUnquoteSpaces(pszPath);
+
+ // Environment strings
+ {
+ if (ExpandEnvironmentStrings(pszPath, szBuf, ARRAYSIZE(szBuf)) != 0)
+ {
+ // Success?
+ if (StrCmp(pszPath, szBuf) != 0)
+ dwFlags |= PC_ENVIRONMENTSTRINGS;
+
+ mir_sntprintf(pszPath, MAX_PATH, _T("%s"), szBuf);
+ }
+ }
+
+ // Absolute/relative path
+ {
+ WCHAR szRelativeTo[MAX_PATH];
+ PConstructLocalPath(szRelativeTo, CSIDL_EXT_EXECUTABLE, NULL, NULL, NULL);
+
+ // Miranda Utils contains not a very good implementation of relative paths
+ // (not all cases are captured)
+ //(CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)szBuf, (LPARAM)pszPath) != lstrlenA(szBuf))
+
+ // Create absolute path if path is relative
+ if (PathIsRelative(pszPath))
+ {
+ if (PathCombine(szBuf, szRelativeTo, pszPath))
+ {
+ dwFlags |= PC_ABSOLUTERELATIVE;
+ mir_sntprintf(pszPath, MAX_PATH, _T("%s"), szBuf);
+ }
+ }
+ }
+
+ return dwFlags;
+}
+
+
+
+BOOL PConstructLocalPath(WCHAR* pszReturn, int nMainFolder, const WCHAR* pszSubFolders, const WCHAR* pszFileName, const WCHAR* pszOptFileExtension)
+{
+ // pszReturn muss MAX_PATH groß sein
+ if (!pszReturn) return FALSE;
+
+ pszReturn[0] = _T('\0');
+
+ switch (nMainFolder)
+ {
+ case CSIDL_EXT_MIRANDAPROFILE:
+ {
+ // Profile directory of Miranda's database
+ WCHAR szBuf[MAX_PATH];
+
+ if (CallService(MS_DB_GETPROFILEPATH, (WPARAM)ARRAYSIZE(szBuf), (LPARAM)szBuf) != 0)
+ return FALSE;
+
+ #if defined(UNICODE)
+ {
+ WCHAR* pszBuf = (WCHAR*)mir_utf8encodeW(szBuf);
+ if (!pszBuf) return FALSE;
+
+ mir_sntprintf(pszReturn, MAX_PATH, _T("%s"), pszBuf);
+ mir_free(pszBuf);
+ }
+ #else
+ mir_sntprintf(pszReturn, MAX_PATH, _T("%s"), szBuf);
+ #endif
+
+ break;
+ }
+
+ case CSIDL_EXT_MODULE:
+ {
+ // Current DLL's directory
+ if (GetModuleFileName(hInstance, pszReturn, MAX_PATH) == 0)
+ return FALSE;
+
+ PathRemoveFileSpec(pszReturn);
+ break;
+ }
+
+ case CSIDL_EXT_EXECUTABLE:
+ {
+ // Current executable path
+ if (GetModuleFileName(NULL, pszReturn, MAX_PATH) == 0)
+ return FALSE;
+
+ PathRemoveFileSpec(pszReturn);
+ break;
+ }
+
+ case CSIDL_EXT_TEMP:
+ {
+ // Temp directory
+ if (GetTempPath(MAX_PATH, pszReturn) == 0)
+ return FALSE;
+ break;
+ }
+
+ case CSIDL_EXT_CURRENT:
+ {
+ // Current directory
+ if (GetCurrentDirectory(MAX_PATH, pszReturn) == 0)
+ return FALSE;
+ break;
+ }
+
+ default:
+ {
+ // CSIDL directory
+ if (!SHGetSpecialFolderPath(NULL, pszReturn, nMainFolder, FALSE))
+ return FALSE;
+
+ break;
+ }
+ }
+
+ if (!PathAddBackslash(pszReturn))
+ return FALSE;
+
+ if (pszSubFolders)
+ {
+ if (!PathAppend(pszReturn, pszSubFolders))
+ return FALSE;
+
+ if (!PathAddBackslash(pszReturn))
+ return FALSE;
+ }
+
+ if (pszFileName)
+ if (!PathAppend(pszReturn, pszFileName))
+ return FALSE;
+
+ if (pszFileName && pszOptFileExtension)
+ if (!PathAddExtension(pszReturn, pszOptFileExtension))
+ return FALSE;
+
+ return TRUE;
+}
+
+// Move files or whole folder from module to destination directory
+BOOL PInstallFile(const WCHAR* pszFileName, const WCHAR* pszDestDir)
+{
+ BOOL bReturn;
+ WCHAR szFileFrom[MAX_PATH+1];
+ WCHAR szFileTo[MAX_PATH+1];
+
+ if (!pszFileName) return FALSE;
+ PConstructLocalPath(szFileFrom, CSIDL_EXT_MODULE, NULL, pszFileName, NULL);
+ if (!PathFileExists(szFileFrom)) return FALSE;
+ mir_sntprintf(szFileTo, ARRAYSIZE(szFileTo), _T("%s"), pszDestDir);
+
+ if (PathIsDirectory(szFileFrom))
+ {
+ SHFILEOPSTRUCT sfo;
+
+ ZeroMemory(&sfo, sizeof(sfo));
+ sfo.fFlags = FOF_MULTIDESTFILES|FOF_NOERRORUI|FOF_NOCONFIRMATION|FOF_NOCONFIRMMKDIR|FOF_SILENT;
+ sfo.wFunc = FO_MOVE;
+ szFileFrom[lstrlen(szFileFrom)+1] = _T('\0');
+ sfo.pFrom = szFileFrom;
+ szFileTo[lstrlen(szFileTo)+1] = _T('\0');
+ sfo.pTo = szFileTo;
+ bReturn = (SHFileOperation(&sfo) == 0);
+
+ } else {
+ PathAppend(szFileTo, pszFileName);
+
+ DeleteFile(szFileTo);
+ bReturn = MoveFile(szFileFrom, szFileTo); // MoveFile does only support directories limitedly
+ }
+
+ return bReturn;
+}
+
+BOOL PInstallDLLFile(const WCHAR* pszFileName, BOOL bIgnoreLanguage, BOOL bIgnoreOpSystem)
+{
+ DWORD dwErr;
+ WCHAR szSrcDir[MAX_PATH];
+ WCHAR szDestDir[MAX_PATH];
+ WCHAR szTmpFile[MAX_PATH];
+ UINT uTmpFileSize = ARRAYSIZE(szTmpFile);
+
+ if (!pszFileName) return FALSE;
+
+ // Test if file-to-be-installed exists
+ PConstructLocalPath(szSrcDir, CSIDL_EXT_MODULE, NULL, pszFileName, NULL);
+ if (!PathFileExists(szSrcDir)) return FALSE;
+
+ // Source: module dir (Plugins directory)
+ PConstructLocalPath(szSrcDir, CSIDL_EXT_MODULE, NULL, NULL, NULL);
+
+ // Destination: app dir
+ PConstructLocalPath(szDestDir, CSIDL_EXT_EXECUTABLE, NULL, NULL, NULL);
+
+ // Install DLL using version verfication
+ szTmpFile[0] = _T('\0'); // needs to be empty (docs)
+ dwErr = VerInstallFile(0, (WCHAR*)pszFileName, (WCHAR*)pszFileName, szSrcDir, szDestDir, szDestDir, szTmpFile, &uTmpFileSize);
+ if (!(dwErr&VIF_WRITEPROT) && !(dwErr&VIF_SRCOLD))
+ {
+ // Ignore language/codepage of the DLL (install anyway) -> useful for DLLs without user interface
+ if (bIgnoreLanguage)
+ if ((dwErr&VIF_DIFFLANG) && (dwErr&VIF_DIFFCODEPG))
+ dwErr = VerInstallFile(VIFF_FORCEINSTALL, (WCHAR*)pszFileName, (WCHAR*)pszFileName, szSrcDir, szDestDir, szDestDir, szTmpFile, &uTmpFileSize);
+
+ // Ignore operating system of the DLL (install anyway) -> useful for DLLs that work anyway
+ if (bIgnoreOpSystem)
+ if (dwErr&VIF_DIFFTYPE)
+ dwErr = VerInstallFile(VIFF_FORCEINSTALL, (WCHAR*)pszFileName, (WCHAR*)pszFileName, szSrcDir, szDestDir, szDestDir, szTmpFile, &uTmpFileSize);
+ }
+
+ // Delete temp file
+ if ((dwErr&VIF_TEMPFILE) && !(dwErr&VIF_BUFFTOOSMALL)) // buffer too small for temp file
+ DeleteFile(szTmpFile);
+
+ // Test for success
+ PConstructLocalPath(szDestDir, CSIDL_EXT_EXECUTABLE, NULL, pszFileName, NULL);
+ if (PathFileExists(szDestDir))
+ {
+ PConstructLocalPath(szSrcDir, CSIDL_EXT_MODULE, NULL, pszFileName, NULL);
+ DeleteFile(szSrcDir);
+ return TRUE;
+ }
+
+ // Debug output
+ OutputDebugString(_T("Spam Filter: VersionInfo DLL (VERSION.DLL) could not be used to install DLL file.\r\n"));
+
+ // Source: module dir (Plugins directory)
+ PConstructLocalPath(szSrcDir, CSIDL_EXT_MODULE, NULL, pszFileName, NULL);
+ // Destination: app dir
+ PConstructLocalPath(szDestDir, CSIDL_EXT_EXECUTABLE, NULL, pszFileName, NULL);
+
+ // Copy without version checking [danger!]
+ DeleteFile(szDestDir);
+ return MoveFile(szSrcDir, szDestDir);
+} \ No newline at end of file
diff --git a/spamfilter/path.h b/spamfilter/path.h
new file mode 100644
index 0000000..0c9929d
--- /dev/null
+++ b/spamfilter/path.h
@@ -0,0 +1,52 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_PATH_H__
+#define SF_PATH_H__ 1
+
+
+// -- Defines
+// PMakePathPretty() and PMakePathUsable()
+#define PC_ENVIRONMENTSTRINGS 0x00000001
+#define PC_ABSOLUTERELATIVE 0x00000002
+#define PC_ALLOW_WILDCARDS 0x00000004
+
+// ConstructLocalPath():
+// (Reusing CSIDLs that are virtual folders)
+#define CSIDL_EXT_EXECUTABLE CSIDL_DESKTOP
+#define CSIDL_EXT_MODULE CSIDL_DRIVES
+#define CSIDL_EXT_TEMP CSIDL_PRINTERS
+#define CSIDL_EXT_CURRENT CSIDL_CONTROLS
+#define CSIDL_EXT_MIRANDAPROFILE CSIDL_INTERNET
+
+// -- Prototypes
+BOOL PConstructLocalPath(WCHAR* pszReturn, int nMainFolder, const WCHAR* pszSubFolders, const WCHAR* pszFileName, const WCHAR* pszOptFileExtension);
+BOOL PPreparePathForWrite(HWND hwndDlg, const WCHAR* pszPath, BOOL bAskDirCreate, BOOL bContainsFileName);
+
+BOOL PIsValidFile(const WCHAR* pszFileName, const WCHAR* aszFileExtensions[], int nFileExtensions, const WCHAR* aszContentTypes[], int nContentTypes);
+
+void PMakePathPretty(WCHAR* pszPath, DWORD dwFlags);
+DWORD PMakePathUsable(WCHAR* pszPath);
+
+BOOL PInstallFile(const WCHAR* pszFileName, const WCHAR* pszDestDir);
+BOOL PInstallDLLFile(const WCHAR* pszFileName, BOOL bIgnoreLanguage, BOOL bIgnoreOpSystem);
+
+#endif // SF_PATH_H__ \ No newline at end of file
diff --git a/spamfilter/popup.c b/spamfilter/popup.c
new file mode 100644
index 0000000..818d3a9
--- /dev/null
+++ b/spamfilter/popup.c
@@ -0,0 +1,285 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -----------------------------------------
+
+
+// -- Popup Menu Item --
+
+static int InternalServicePopupMenuItemCommand(WPARAM wParam, LPARAM lParam)
+{
+ BOOL bPopupState = !DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_POPUP, DEFAULT_SETTING_POPUP);
+
+ DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_POPUP, (BYTE)bPopupState);
+ SetPopupMenuItem(bPopupState, FALSE);
+
+ return 0;
+}
+
+void SetPopupMenuItem(BOOL bPopupsActivated, BOOL bGrayItem)
+{
+ static HANDLE hPopupMenuItem = NULL;
+
+ CLISTMENUITEM cmi;
+
+ ZeroMemory(&cmi, sizeof(cmi));
+ cmi.cbSize = sizeof(cmi);
+
+ if (bPopupsActivated)
+ {
+ cmi.pszName = Translate("Disable &spam popups");
+ // Getting icons out of from popup.dll (Plus/Non-Plus) (so they are always up-to-date)
+ cmi.hIcon = SkinGetIcon("popup_enabled", 0, FALSE);
+ if (!cmi.hIcon)
+ cmi.hIcon = GetModuleHandle(_T("popup")) ? (HICON)LoadImage(GetModuleHandleA("popup"), MAKEINTRESOURCE(118), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED) : NULL;
+
+ } else {
+ cmi.pszName = Translate("Enable &spam popups");
+ // Getting icons out of from popup.dll (Plus/Non-Plus) (so they are always up-to-date)
+ cmi.hIcon = SkinGetIcon("popup_disabled", 0, FALSE);
+ if (!cmi.hIcon)
+ cmi.hIcon = GetModuleHandle(_T("popup")) ? (HICON)LoadImage(GetModuleHandleA("popup"), MAKEINTRESOURCE(120), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED) : NULL;
+ }
+
+ if (hPopupMenuItem)
+ {
+ cmi.flags = CMIM_NAME|CMIM_ICON|CMIM_FLAGS;
+
+ if (bGrayItem)
+ cmi.flags |= CMIF_GRAYED;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hPopupMenuItem, (LPARAM)&cmi);
+
+ } else {
+
+ // Item can't be grayed out the first time
+ //cmi.flags = bGrayItem ? CMIF_GRAYED : 0;
+
+ cmi.pszPopupName = Translate("PopUps");
+ cmi.pszService = "SpamFilter/PopupMenuItemCommand";
+
+ CreateServiceFunction(cmi.pszService, InternalServicePopupMenuItemCommand);
+ hPopupMenuItem = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, (WPARAM)0, (LPARAM)&cmi);
+
+ // Call twice to initilialize the grayed out setting
+ if (bGrayItem)
+ SetPopupMenuItem(bPopupsActivated, bGrayItem);
+ }
+}
+
+
+
+// -- Spam Popup --
+BOOL ShowSpamPopup(const char* pszMsgTypeSection, const char* pszMsgTypeName, const WCHAR* pszCustomUserName, HANDLE hContact, DWORD dwFilterType)
+{
+ BOOL bReturn = FALSE;
+ int iMsgType;
+
+ // Support for Popup / PopupPlus
+ #if defined(UNICODE)
+ if (ServiceExists(MS_POPUP_ADDPOPUPW))
+ {
+ POPUPDATAW ppd;
+ HICON hIconBuf;
+ WCHAR* pszText;
+
+ switch (dwFilterType)
+ {
+ case SFT_DISLIKEDMESSAGES_FILTER:
+ pszText = TranslateW("Disliked Message Alert!");
+ break;
+ case SFT_ADVERTISMENT_FILTER:
+ pszText = TranslateW("Advertisment Alert!");
+ break;
+ case SFT_ROBOT_FILTER:
+ pszText = TranslateW("Robot Alert!");
+ break;
+ default:
+ pszText = NULL;
+ }
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.colorText = SkinGetColor(DB_COLOR_POPUPTEXT_SETTING, Translate("Popup Text"), Translate("Spam Filter"), RGB(255,255,255), FALSE);
+ ppd.colorBack = SkinGetColor(DB_COLOR_POPUPBACKGROUND_SETTING, Translate("Popup Background"), Translate("Spam Filter"), RGB(0,0,0), FALSE);
+ if (pszText) mir_sntprintf(ppd.lpwzText, ARRAYSIZE(ppd.lpwzText), _T("%s"), pszText);
+ if (pszCustomUserName) mir_sntprintf(ppd.lpwzContactName, ARRAYSIZE(ppd.lpwzContactName), _T("%s"), pszCustomUserName);
+
+ // Determine main icon
+ EnterCriticalSection(&csMsgTypes); // thread safety
+ iMsgType = GetMsgTypeID(pszMsgTypeSection, pszMsgTypeName);
+
+ if ((iMsgType >= 0) && ((int)pamtdMsgTypes[iMsgType].hIcon < 0))
+ hIconBuf = LoadSkinnedIcon((-1)*(int)pamtdMsgTypes[iMsgType].hIcon); // skinned icon
+ else if ((iMsgType >= 0) && pamtdMsgTypes[iMsgType].hIcon)
+ hIconBuf = pamtdMsgTypes[iMsgType].hIcon;
+ else
+ hIconBuf = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+
+ // Overlay small spam icon
+ if (hIconBuf)
+ ppd.lchIcon = OverlayIcon(hIconBuf, SkinGetIcon(DB_ICON_SPAMLAYER_SETTING, IDI_SPAM_LAYER, FALSE), GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
+
+ // I don't know if popup module author made it thread safe...
+ if (CallServiceSync(MS_POPUP_ADDPOPUPW, (WPARAM)&ppd, 0) > 0)
+ bReturn = TRUE;
+
+ if (ppd.lchIcon) DestroyIcon(ppd.lchIcon);
+ }
+ #else
+ if (ServiceExists(MS_POPUP_ADDPOPUPEX))
+ {
+ POPUPDATAEX ppd;
+ HICON hIconBuf;
+ char* pszText;
+
+ switch (dwFilterType)
+ {
+ case SFT_DISLIKEDMESSAGES_FILTER:
+ pszText = Translate("Disliked Message Alert!");
+ break;
+ case SFT_ADVERTISMENT_FILTER:
+ pszText = Translate("Advertisment Alert!");
+ break;
+ case SFT_ROBOT_FILTER:
+ pszText = Translate("Robot Alert!");
+ break;
+ default:
+ pszText = NULL;
+ }
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.colorText = SkinGetColor(DB_COLOR_POPUPTEXT_SETTING, Translate("Popup Text"), Translate("Spam Filter"), RGB(255,255,255), FALSE);
+ ppd.colorBack = SkinGetColor(DB_COLOR_POPUPBACKGROUND_SETTING, Translate("Popup Background"), Translate("Spam Filter"), RGB(0,0,0), FALSE);
+ if (pszText)
+ mir_snprintf(ppd.lpzText, ARRAYSIZE(ppd.lpzText), "%s", pszText);
+ if (pszCustomUserName)
+ mir_snprintf(ppd.lpzContactName, ARRAYSIZE(ppd.lpzContactName), "%s", pszCustomUserName);
+
+ // Determine main icon
+ EnterCriticalSection(&csMsgTypes); // thread safety
+ iMsgType = GetMsgTypeID(pszMsgTypeSection, pszMsgTypeName);
+
+ if ((iMsgType >= 0) && ((int)pamtdMsgTypes[iMsgType].hIcon < 0))
+ hIconBuf = LoadSkinnedIcon((-1)*(int)pamtdMsgTypes[iMsgType].hIcon); // skinned icon
+ else if ((iMsgType >= 0) && pamtdMsgTypes[iMsgType].hIcon)
+ hIconBuf = pamtdMsgTypes[iMsgType].hIcon;
+ else
+ hIconBuf = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+
+ // Overlay small spam icon
+ if (hIconBuf)
+ ppd.lchIcon = OverlayIcon(hIconBuf, SkinGetIcon(DB_ICON_SPAMLAYER_SETTING, IDI_SPAM_LAYER, FALSE), GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
+
+ // Support for Popup Plus
+ if (ServiceExists(MS_POPUP_ADDCLASS))
+ {
+ ppd.skinBack = ppd.colorBack;
+ ppd.colorBack = POPUP_USE_SKINNED_BG;
+ ppd.lpzClass = (LPCTSTR)POPUP_CLASS_SPAM; // type "LPCTSTR" of lpzClass is wrong (or: POPUP_CLASS_DEFAULT)
+ }
+
+ // I don't know if popup module author made it thread safe...
+ if (CallServiceSync(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd, 0) > 0)
+ bReturn = TRUE;
+
+ if (ppd.lchIcon) DestroyIcon(ppd.lchIcon);
+ }
+ #endif
+
+ // Support for OSD / WanneBeOSD
+ if (ServiceExists("OSD/Announce")) // no m_osd.h file yet available.. .don't like it :-/
+ {
+ WCHAR* pszText;
+
+ switch (dwFilterType)
+ {
+ case SFT_DISLIKEDMESSAGES_FILTER:
+ pszText = TranslateT("Disliked Message Alert!");
+ break;
+
+ case SFT_ADVERTISMENT_FILTER:
+ pszText = TranslateT("Advertisment Alert!");
+ break;
+
+ case SFT_ROBOT_FILTER:
+ pszText = TranslateT("Robot Alert!");
+ break;
+
+ default:
+ pszText = NULL;
+ }
+
+ if (pszText)
+ {
+ // I don't know if OSD module author made it thread safe...
+ // quite bad: return value of OSDAnnounce does not indicate anything (SendMessage result)
+ // bad unicode implementation (simply expects unicode text when wbosdW.dll is used)
+ // -> might cuase crashes (buffer overruns) when SF-Unicode is run with OSD-Ansi
+ if (CallService("OSD/Announce", (WPARAM)pszText, (LPARAM)0) == 0);
+ bReturn = TRUE;
+ }
+ }
+
+ return bReturn;
+}
+
+
+// -- Popup Available --
+
+BOOL IsPopupAvailable(BOOL bAlsoOSD)
+{
+ #if defined(UNICODE)
+ return (ServiceExists(MS_POPUP_ADDPOPUPW) || (bAlsoOSD ? ServiceExists("OSD/Announce") : FALSE));
+ #else
+ return (ServiceExists(MS_POPUP_ADDPOPUPEX) || (bAlsoOSD ? ServiceExists("OSD/Announce") : FALSE));
+ #endif
+}
+
+
+// ------------------------------------
+
+
+void InitPopup(void)
+{
+ // Add class (Support for PopupPlus)
+ #if !defined(UNICODE)
+ if (ServiceExists(MS_POPUP_ADDCLASS))
+ CallService(MS_POPUP_ADDCLASS, 0, (LPARAM)POPUP_CLASS_SPAM);
+ #endif
+
+ if (IsPopupAvailable(FALSE))
+ SkinAddNewIcon(DB_ICON_SPAMLAYER_SETTING, TranslateT("Spam Filter"), TranslateT("Spam Message Overlay"), IDI_SPAM_LAYER, FALSE);
+}
+
+
+void UninitPopup(void)
+{
+} \ No newline at end of file
diff --git a/spamfilter/popup.h b/spamfilter/popup.h
new file mode 100644
index 0000000..c37e8e8
--- /dev/null
+++ b/spamfilter/popup.h
@@ -0,0 +1,39 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_POPUP_H__
+#define SF_POPUP_H__ 1
+
+
+// -- Defines
+#define POPUP_CLASS_SPAM "Spam Alerts" // must not be translated
+
+// -- Prototypes
+void SetPopupMenuItem(BOOL bPopupsActivated, BOOL bGrayItem);
+
+BOOL ShowSpamPopup(const char* pszMsgTypeSection, const char* pszMsgTypeName, const WCHAR* pszCustomUserName, HANDLE hContact, DWORD dwFilterType);
+BOOL IsPopupAvailable(BOOL bAlsoOSD);
+
+void InitPopup(void);
+void UninitPopup(void);
+
+
+#endif // SF_POPUP_H__
diff --git a/spamfilter/regexp.c b/spamfilter/regexp.c
new file mode 100644
index 0000000..9f15df6
--- /dev/null
+++ b/spamfilter/regexp.c
@@ -0,0 +1,225 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Defines
+//#define DISABLE_REGEXP_SUPPORT
+
+// -- Includes
+#include "common.h"
+#include "Utilities/PCRE/include/pcre.h"
+
+// -- Variables
+HMODULE hPcreDLL = NULL;
+
+// -- Function Pointers
+int (*dyn_pcre_config)(int, void*);
+pcre* (*dyn_pcre_compile)(const char*, int, const char**, int*, const unsigned char*);
+pcre_extra* (*dyn_pcre_study)(const pcre*, int, const char **);
+int (*dyn_pcre_exec)(const pcre*, const pcre_extra*, const char*, int, int, int, int*, int);
+void (**dyn_pointer_pcre_free)(void*);
+#define dyn_pcre_free (*dyn_pointer_pcre_free);
+
+
+// -----------------------------------------
+
+
+BOOL RegExpExistsInString(const WCHAR* pszString, const WCHAR* pszRegEx, BOOL bCaseSensitive, BOOL* pbRegExMalformed)
+{
+ if (pbRegExMalformed) *pbRegExMalformed = FALSE;
+
+ if (!pszString || !pszRegEx) return FALSE;
+
+ if (hPcreDLL)
+ {
+ int iRc;
+ pcre* pPcre;
+ pcre_extra* pExtra;
+
+ // Following are dummy buffers that are needed for the functions to work
+ const char* pszErr;
+ int iErrOffset;
+
+ // 1.) Interpret regular expression
+ #if defined(UNICODE)
+ {
+ char* pszRegExUtf8 = mir_utf8encodeW(pszRegEx);
+ if (pszRegExUtf8)
+ {
+ pPcre = dyn_pcre_compile(pszRegExUtf8, (bCaseSensitive ? 0 : PCRE_CASELESS)|PCRE_UTF8|PCRE_NO_UTF8_CHECK, &pszErr, &iErrOffset, NULL);
+ mir_free(pszRegExUtf8);
+ } else {
+ pPcre = NULL;
+ }
+ }
+ #else
+ pPcre = dyn_pcre_compile(pszRegEx, (bCaseSensitive ? 0 : PCRE_CASELESS), &pszErr, &iErrOffset, NULL);
+ #endif
+
+ if (pPcre)
+ {
+ // 2.) Study regular expression (not necessary)
+ pExtra = dyn_pcre_study(pPcre, 0, &pszErr);
+
+ // 3.) Search for regular expression
+ #if defined(UNICODE)
+ {
+ char* pszStringUtf8 = mir_utf8encodeW(pszString);
+
+ if (pszStringUtf8)
+ {
+ iRc = dyn_pcre_exec(pPcre, pExtra, (char*)pszStringUtf8, (int)lstrlenA(pszStringUtf8), 0, PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK, NULL, 0);
+ mir_free(pszStringUtf8);
+ } else {
+ iRc = 0;
+ }
+ }
+ #else
+ iRc = dyn_pcre_exec(pPcre, pExtra, pszString, (int)lstrlen(pszString), 0, PCRE_NOTEMPTY, NULL, 0);
+ #endif
+
+ // Release memory
+ dyn_pcre_free(pPcre);
+ if (pExtra) dyn_pcre_free(pExtra);
+
+ return (iRc >= 0);
+ } else {
+ if (pbRegExMalformed)
+ *pbRegExMalformed = TRUE;
+
+ #if !defined(_DEBUG)
+ if (!pbRegExMalformed)
+ #endif
+ {
+ WCHAR szOut[128];
+ mir_sntprintf(szOut, ARRAYSIZE(szOut), _T("Spam Filter: Syntax error in RegExp (%s).\r\n"), pszRegEx);
+ OutputDebugString(szOut);
+ }
+ }
+ }
+
+ // Default string search (fallback)
+ return (bCaseSensitive ? StrStr(pszString, pszRegEx) : StrStrI(pszString, pszRegEx)) ? TRUE : FALSE;
+}
+
+
+
+BOOL ValidateRegExp(const WCHAR* pszRegEx)
+{
+ if (hPcreDLL)
+ {
+ pcre* pPcre;
+ char* pszErr;
+ int iErrOffset;
+
+ // Parameter check
+ if (!pszRegEx) return FALSE;
+
+ // Interpret regular expression
+ #if defined(UNICODE)
+ {
+ char* pszRegExUtf8 = mir_utf8encodeW(pszRegEx);
+
+ if (pszRegExUtf8) {
+ pPcre = dyn_pcre_compile(pszRegExUtf8, PCRE_UTF8|PCRE_NO_UTF8_CHECK, &pszErr, &iErrOffset, NULL);
+ mir_free(pszRegExUtf8);
+ } else {
+ pPcre = NULL;
+ }
+ }
+ #else
+ pPcre = dyn_pcre_compile(pszRegEx, 0, &pszErr, &iErrOffset, NULL);
+ #endif
+
+ if (pPcre) {
+ dyn_pcre_free(pPcre); // Release memory
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+
+ } else {
+ return TRUE;
+ }
+}
+
+
+BOOL RegExpEnabled(void)
+{
+ return (hPcreDLL ? TRUE : FALSE);
+}
+
+
+// ------------------------------------
+
+
+void InitRegExp(void)
+{
+ #if !defined(DISABLE_REGEXP_SUPPORT)
+
+ hPcreDLL = LoadLibrary(_T("PCRE"));
+ if (hPcreDLL)
+ {
+ // Exported functions
+ // GetProcAddress returns the pointer to the function
+ *(FARPROC*)&dyn_pcre_config = GetProcAddress(hPcreDLL, "pcre_config");
+ *(FARPROC*)&dyn_pcre_compile = GetProcAddress(hPcreDLL, "pcre_compile");
+ *(FARPROC*)&dyn_pcre_study = GetProcAddress(hPcreDLL, "pcre_study");
+ *(FARPROC*)&dyn_pcre_exec = GetProcAddress(hPcreDLL, "pcre_exec");
+
+ // Exported variables
+ // GetProcAddress returns the pointer to the variable containing the pointer to the function...jippieh! :-)
+ *(FARPROC*)&dyn_pointer_pcre_free = GetProcAddress(hPcreDLL, "pcre_free");
+
+ if (dyn_pcre_compile && dyn_pcre_exec && dyn_pointer_pcre_free && (*dyn_pointer_pcre_free))
+ {
+ #if defined(UNICODE)
+ int iUtf8Supported = 0;
+
+ if (dyn_pcre_config)
+ dyn_pcre_config(PCRE_CONFIG_UTF8, &iUtf8Supported);
+
+ if (iUtf8Supported != 0)
+ return;
+ #else
+ return;
+ #endif
+ }
+
+ // Error: Unloading
+ FreeLibrary(hPcreDLL);
+ hPcreDLL = NULL;
+ }
+
+ // Debug output
+ OutputDebugString(_T("Spam Filter: RegExp DLL (PCRE.DLL) not found or not compatible.\r\n"));
+
+ #else
+ // Debug output
+ OutputDebugString(_T("Spam Filter: RegExp support disabled.\r\n"));
+ #endif
+}
+
+
+void UninitRegExp(void)
+{
+ if (hPcreDLL) FreeLibrary(hPcreDLL);
+} \ No newline at end of file
diff --git a/spamfilter/regexp.h b/spamfilter/regexp.h
new file mode 100644
index 0000000..4972b6d
--- /dev/null
+++ b/spamfilter/regexp.h
@@ -0,0 +1,35 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_REGEXP_H__
+#define SF_REGEXP_H__ 1
+
+
+// -- Prototypes
+BOOL RegExpExistsInString(const WCHAR* pszString, const WCHAR* pszRegEx, BOOL bCaseSensitive, BOOL* pbRegExInvalid);
+BOOL ValidateRegExp(const WCHAR* pszRegEx);
+BOOL RegExpEnabled(void);
+
+void InitRegExp(void);
+void UninitRegExp(void);
+
+
+#endif // SF_REGEXP_H__ \ No newline at end of file
diff --git a/spamfilter/resource.h b/spamfilter/resource.h
new file mode 100644
index 0000000..37e7d5b
--- /dev/null
+++ b/spamfilter/resource.h
@@ -0,0 +1,139 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by SpamFilter.rc
+//
+#define IDI_DEFAULT 101
+#define IDI_DEFAULT_LARGE 102
+#define IDI_SPAM_LAYER 103
+#define IDI_ADD_BUTTON 104
+#define IDI_DELETE_BUTTON 105
+#define IDI_SPAM_DEFINITIONS 106
+#define IDD_OPTIONS_MAIN 107
+#define IDD_OPTIONS_CONFIGURE_ADVERTISMENTFILTER 108
+#define IDD_OPTIONS_CONFIGURE_ROBOTFILTER 109
+#define IDD_OPTIONS_CONFIGURE_DISLIKEDMESSAGEFILTER 110
+#define IDD_LISTBOX_ADD_ITEM 111
+#define IDD_INFO_SPAMDEFINITIONS 112
+#define IDD_INFO_SPAMMERS 113
+#define IDR_CONTEXT 114
+#define IDR_XMLFILETEMPLATE 115
+#define IDR_XMLITEMTEMPLATE 116
+#define IDR_XSLLAYOUTFILETEMPLATE 117
+#define IDC_TEXT_DESCRIPTION 1001
+#define IDC_EDITBOX_INSTRUCTION 1002
+#define IDC_GROUP_BEHAVIOUR 1003
+#define IDC_GROUP_SPAMMERS 1004
+#define IDC_GROUP_MESSAGETYPES 1005
+#define IDC_EDITBOX_CONFIRMATION 1006
+#define IDC_GROUP_LOGGING 1007
+#define IDC_CHECKBOX_LOGGING 1008
+#define IDC_CHECKBOX_POPUP 1009
+#define IDC_CHECKBOX_CAPITALIZED 1010
+#define IDC_EDITBOX_NEWITEM 1011
+#define IDC_EDITBOX_LOGFILE 1012
+#define IDC_CHECKBOX_EMPTY 1013
+#define IDC_GROUP_IGNORE 1014
+#define IDC_EDITBOX_ALLOWEDBADWORDS 1015
+#define IDC_EDITBOX_ALLOWEDGOODWORDS 1016
+#define IDC_EDITBOX_ALLOWEDFORMATEDWORDS 1017
+#define IDC_EDITBOX_ALLOWEDSPAMMESSAGES 1018
+#define IDC_BUTTON_LOGBROWSE 1019
+#define IDC_RADIO_MARKREADMSG 1020
+#define IDC_RADIO_DELETEMSG 1021
+#define IDC_TEXT_ALLOWEDBADWORDS 1022
+#define IDC_SPIN_ALLOWEDBADWORDS 1023
+#define IDC_TEXT_ALLOWEDGOODWORDS 1024
+#define IDC_TEXT_ALLOWEDFORMATEDWORDS 1025
+#define IDC_SPIN_ALLOWEDGOODWORDS 1026
+#define IDC_SPIN_ALLOWEDFORMATEDWORDS 1027
+#define IDC_CHECKBOX_FORMATING 1028
+#define IDC_TEXT_ALLOWEDSPAMMESSAGES 1029
+#define IDC_SPIN_ALLOWEDSPAMMESSAGES 1030
+#define IDC_CHECKBOX_ADVERTISMENTFILTER 1031
+#define IDC_BUTTON_ADVERTISMENTFILTER 1032
+#define IDC_TEXT_ADVERTISMENTFILTER 1033
+#define IDC_CHECKBOX_DISLIKEDMESSAGEFILTER 1034
+#define IDC_BUTTON_DISLIKEDMESSAGEFILTER 1035
+#define IDC_TEXT_DISLIKEDMESSAGEFILTER 1036
+#define IDC_CHECKBOX_AUTOADDSPAMMERS 1037
+#define IDC_LISTBOX_CUSTOMIZEDWORDS 1038
+#define IDC_CHECKBOX_ROBOTFILTER 1039
+#define IDC_CHECKBOX_NOTIFYSENDER 1040
+#define IDC_TEXT_ROBOTFILTER 1041
+#define IDC_LISTBOX_CUSTOMIZEDGOODWORDS 1042
+#define IDC_BUTTON_ROBOTFILTER 1043
+#define IDC_GROUP_WORDS 1044
+#define IDC_GROUP_NOTIFYSENDER 1045
+#define IDC_GROUP_OTHERCONTENTS 1046
+#define IDC_GROUP_BADWORDS 1047
+#define IDC_CHECKBOX_DEFAULTSPAMMERS 1048
+#define IDC_CHECKBOX_HOAXTEXTS 1049
+#define IDC_GROUP_GOODWORDS 1050
+#define IDC_CHECKBOX_CUSTOMIZEDWORDS 1051
+#define IDC_CHECKBOX_URL 1052
+#define IDC_BUTTON_ADDWORD 1053
+#define IDC_BUTTON_DELETEWORD 1054
+#define IDC_CHECKBOX_CUSTOMIZEDGOODWORDS 1055
+#define IDC_EDITBOX_NOTIFYSENDER 1056
+#define IDC_BUTTON_ADDGOODWORD 1057
+#define IDC_TEXT_NOTIFYSENDER 1058
+#define IDC_BUTTON_DELETEGOODWORD 1059
+#define IDC_CHECKBOX_MULTIPLEMESSAGES 1060
+#define IDC_CHECKBOX_PHONENUMBERS 1061
+#define IDC_BUTTON_OPENLOG 1062
+#define IDC_CHECKBOX_TEASERMESSAGES 1063
+#define IDC_BUTTON_RESETAUTOIGNORE 1064
+#define IDC_LISTBOX_CUSTOMIZEDBADWORDS 1065
+#define IDC_BUTTON_SHOWSPAMMERS 1066
+#define IDC_BUTTON_ADDBADWORD 1067
+#define IDC_BUTTON_DELETEBADWORD 1068
+#define IDC_CHECKBOX_CUSTOMIZEDBADWORDS 1069
+#define IDC_CHECKBOX_DEFAULTBADWORDS 1070
+#define IDC_TREEVIEW_MESSAGETYPES 1071
+#define IDC_TEXT_MESSAGETYPES 1072
+#define IDC_CHECKBOX_ADDTOHISTORY 1073
+#define IDC_GROUP_MESSAGETEXTS 1074
+#define IDC_GROUP_ANSWER 1075
+#define IDC_EDITBOX_ANSWER 1076
+#define IDC_BUTTON_ANSWER 1077
+#define IDC_TEXT_REGEXP 1078
+#define IDC_CHECKBOX_ADDTOHISTORY2 1078
+#define IDC_BUTTON_REGEXP 1079
+#define IDC_CLIST 1080
+#define IDC_BUTTON_NOTIFYSENDER_VARIABLES 1081
+#define IDC_TEXT_VARS 1082
+#define IDC_BUTTON_CONFIRMATION_VARIABLES 1083
+#define IDC_BUTTON_INSTRUCTION_VARIABLES 1084
+#define IDC_TEXT_WARNING 1085
+#define IDC_TEXT_QUICKHELP 1086
+#define IDC_TEXT_HELPNESTED 1087
+#define IDC_RECT_SEPARATOR 1088
+#define IDC_LISTVIEW_SPAMDEFINITIONS 1089
+#define IDC_BUTTON_SPAMDEFINITIONS 1090
+#define IDC_URL_GETMORE 1091
+#define IDC_SCROLLBAR_SIZEGRIP 1092
+#define IDC_TAB_SPAMDEFINITIONS 1093
+#define IDC_ICON_SPAM 1094
+#define IDC_TEXT_SPAM 1095
+#define IDC_ICON_DELETE 1096
+#define IDC_TEXT_DELETE 1097
+#define IDC_ICON_NORMAL 1098
+#define IDC_TEXT_NORMAL 1099
+#define IDC_EDITBOX_GROUP 1100
+#define IDC_CHECKBOX_GROUP 1101
+#define IDC_CHECKBOX_HIDE 1102
+#define IDC_GROUP 1103
+#define ID_MENU_ADD 40001
+#define ID_MENU_DELETE 40002
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 118
+#define _APS_NEXT_COMMAND_VALUE 40003
+#define _APS_NEXT_CONTROL_VALUE 1104
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/spamfilter/services.c b/spamfilter/services.c
new file mode 100644
index 0000000..188cf41
--- /dev/null
+++ b/spamfilter/services.c
@@ -0,0 +1,2038 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// -- Includes
+#include "common.h"
+
+// -- Variables: Events/Hooks
+HANDLE hEventPreRegisterMessageType = NULL;
+HANDLE hEventOkToSpamCheck = NULL;
+HANDLE hEventOkToSpamDetection = NULL;
+HANDLE hEventSpamReceived = NULL;
+HANDLE hEventModuleLoaded = NULL;
+HANDLE hEventContactSpammerStateChanged = NULL;
+
+// -- Variables: Services
+HANDLE* hServices[19] = {NULL};
+
+
+// -----------------------------------------
+
+// Service: MS_SPAMFILTER_GETHANDLE
+static int ServiceGetHandle(WPARAM wParam, LPARAM lParam)
+{
+ switch ((UINT)wParam)
+ {
+ //case 1: return NULL; // hInstance was removed in v2.5.1.0
+
+ case SFHT_HWND_PLUGIN_OPTIONS: return IsWindow(hwndSpamFilterOpt) ? (int)hwndSpamFilterOpt : (int)NULL;
+ case SFHT_HWND_ADVERTISMENT_FILTER: return IsWindow(hwndAdvertismentFilter) ? (int)hwndAdvertismentFilter : (int)NULL;
+ case SFHT_HWND_ROBOT_FILTER: return IsWindow(hwndRobotFilter) ? (int)hwndRobotFilter : (int)NULL;
+ case SFHT_HWND_DISLIKEDMESSAGES_FILTER: return IsWindow(hwndDislikedMessagesFilter) ? (int)hwndDislikedMessagesFilter : (int)NULL;
+ case SFHT_HWND_SPAMDEFINITIONS_INFO: return IsWindow(hwndSpamDefinitionsInfo) ? (int)hwndSpamDefinitionsInfo : (int)NULL;
+ case SFHT_HWND_SPAMMERS_INFO: return IsWindow(hwndSpammersInfo) ? (int)hwndSpammersInfo : (int)NULL;
+
+ case SFHT_HICON_SPAM: return (int)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
+ case SFHT_HICON_SPAM_LARGE: return (int)LoadImage(hInstance, MAKEINTRESOURCE(IDI_DEFAULT_LARGE), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
+ case SFHT_HICON_SPAM_LAYER: return (int)LoadImage(hInstance, MAKEINTRESOURCE(IDI_SPAM_LAYER), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
+
+ case 100: // backward compatibility
+ #if defined(UNICODE)
+ case SFHT_BOOL_IS_UNICODE: return TRUE;
+ #else
+ case SFHT_BOOL_IS_UNICODE: return FALSE;
+ #endif
+
+ default: return (int)NULL;
+ }
+}
+
+
+// Service: MS_SPAMFILTER_SHOWERROR
+static int ServiceShowError(WPARAM wParam, LPARAM lParam)
+{
+ switch ((UINT)wParam)
+ {
+ case SFSE_CRITICAL_ERROR:
+ {
+ ShowInfoMessage(NIIF_ERROR, TranslateW("Spam Filter Critical Error"), TranslateW("A very critical internal error was detected!\r\nProbably the filter will not work at all.\r\n\r\nPlease update to the most current version if this problem persists."), 0);
+ return 0;
+ }
+
+ case SFSE_SEND_FAILED:
+ {
+ WCHAR* pszUserName;
+ if (lParam && (((SPAMCHECKDATA*)lParam)->cbSize < sizeof(SPAMCHECKDATA))) return 1;
+ pszUserName = lParam?SCD_GetContactCustomDisplayName((SPAMCHECKDATA*)lParam):NULL;
+ ShowInfoMessage(NIIF_WARNING, TranslateW("Spam Filter Error"), TranslateW("Spam Filter was not able to send an automated message to the user \"%s\"."), pszUserName?lstrlen(pszUserName):0, pszUserName?pszUserName:_T(""));
+ if (pszUserName) mir_free(pszUserName);
+ return 0;
+ }
+
+ default:
+ return 1;
+ }
+}
+
+
+// Service: MS_SPAMFILTER_COPYSPAMCHECKDATA
+static int ServiceCopySpamCheckData(WPARAM wParam, LPARAM lParam)
+{
+ SPAMCHECKDATA* to = (SPAMCHECKDATA*)wParam;
+ SPAMCHECKDATA* from = (SPAMCHECKDATA*)lParam;
+
+
+ if (!from || !to || (from && (from->cbSize < sizeof(SPAMCHECKDATA))) ) {
+ if (to) to->cbSize = sizeof(SPAMCHECKDATA);
+ return 1;
+ }
+ CopyMemory(to, from, from->cbSize);
+ if (to->pszMsgTypeName) to->pszMsgTypeName = mir_strdup(to->pszMsgTypeName);
+ if (to->pszMsgTypeSection) to->pszMsgTypeSection = mir_strdup(to->pszMsgTypeSection);
+ if (to->pszMsgText) {
+ if (to->dwFlags&SCDF_UNICODE)
+ to->pszMsgText = mir_strdup(to->pszMsgText);
+ else
+ to->pwszMsgText = mir_wstrdup(to->pwszMsgText);
+ }
+ if ((to->dwFlags&SCDF_NO_CONTACT) && to->pszUserName) {
+ if (to->dwFlags&SCDF_UNICODE)
+ to->pszUserName = mir_strdup(to->pszUserName);
+ else
+ to->pwszUserName = mir_wstrdup(to->pwszUserName);
+ }
+ to->cbSize = sizeof(SPAMCHECKDATA);
+ return 0;
+}
+
+
+// Service: MS_SPAMFILTER_FREESPAMCHECKDATA
+static int ServiceFreeSpamCheckData(WPARAM wParam, LPARAM lParam)
+{
+ SPAMCHECKDATA* pscd = (SPAMCHECKDATA*)wParam;
+
+ if (!pscd || (pscd->cbSize < sizeof(SPAMCHECKDATA))) return 1;
+ if (pscd->pszMsgTypeName) mir_free(pscd->pszMsgTypeName);
+ if (pscd->pszMsgTypeSection) mir_free(pscd->pszMsgTypeSection);
+ if (pscd->ptszMsgText) mir_free(pscd->ptszMsgText);
+ if ((pscd->dwFlags&SCDF_NO_CONTACT) && pscd->ptszUserName) mir_free(pscd->ptszUserName);
+ return 0;
+}
+
+
+// Service: MS_SPAMFILTER_SHOWFILTERDIALOG
+static int ServiceShowFilterDialog(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndParent = (HWND)wParam;
+ DWORD dwFilterType = (DWORD)lParam;
+
+ if (dwFilterType&SFTEXF_SPAMDEFINITIONS_INFO)
+ {
+ INITCOMMONCONTROLSEX icc;
+
+ dwFilterType ^= SFTEXF_SPAMDEFINITIONS_INFO;
+ if ((dwFilterType != SFT_ADVERTISMENT_FILTER) && (dwFilterType != SFT_DISLIKEDMESSAGES_FILTER))
+ return 1;
+
+ if (IsWindow(hwndSpamDefinitionsInfo))
+ {
+ PostMessage(hwndSpamDefinitionsInfo, SFM_REFRESH_SPAMDEFINITIONS, (WPARAM)FALSE, (LPARAM)dwFilterType);
+ SetForegroundWindow(hwndSpamDefinitionsInfo);
+ return 0;
+ }
+
+ // Ensure the needed common controls are loaded
+ ZeroMemory(&icc, sizeof(icc));
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_TAB_CLASSES|ICC_LISTVIEW_CLASSES;
+ InitCommonControlsEx(&icc);
+
+ if (hwndParent) {
+ // hwndSpamDefinitionsInfo will be set on WM_INITDIALOG and set to NULL on WM_DESTROY
+ // *** using ANSI version here because Hyperlink control of Miranda doesn't support Unicode ***
+ //return (DialogBoxParamA(hInstance, MAKEINTRESOURCEA(IDD_INFO_SPAMDEFINITIONS), hwndParent, DlgProcSpamDefinitionsInfo, dwFilterType)>0) ? 0 : 1;
+ return (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_INFO_SPAMDEFINITIONS), hwndParent, DlgProcSpamDefinitionsInfo, dwFilterType)>0) ? 0 : 1;
+
+ } else {
+ // *** using ANSI version here because Hyperlink control of Miranda doesn't (yet) support Unicode ***
+ //hwndSpamDefinitionsInfo = CreateDialogParamA(hInstance, MAKEINTRESOURCEA(IDD_INFO_SPAMDEFINITIONS), (HWND)NULL, DlgProcSpamDefinitionsInfo, dwFilterType);
+ hwndSpamDefinitionsInfo = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_INFO_SPAMDEFINITIONS), (HWND)NULL, DlgProcSpamDefinitionsInfo, dwFilterType);
+ return (hwndSpamDefinitionsInfo ? 0 : 1);
+ }
+
+ } else if (dwFilterType&SFTEXF_SPAMMERS_INFO)
+ {
+ dwFilterType ^= SFTEXF_SPAMMERS_INFO;
+ if (dwFilterType != SFTEX_OPTIONS_PAGE)
+ return 1;
+
+ if (IsWindow(hwndSpammersInfo)) {
+ SetForegroundWindow(hwndSpammersInfo);
+ return 0;
+ }
+
+ if (hwndParent) {
+ // hwndSpammersInfo will be set on WM_INITDIALOG and set to NULL on WM_DESTROY
+ return (DialogBox(hInstance, MAKEINTRESOURCE(IDD_INFO_SPAMMERS), hwndParent, DlgProcSpammersInfo)>0) ? 0 : 1;
+
+ } else {
+ hwndSpammersInfo = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_INFO_SPAMMERS), (HWND)NULL, DlgProcSpammersInfo);
+ return (hwndSpammersInfo ? 0 : 1);
+ }
+
+ } else {
+
+ switch (dwFilterType)
+ {
+ case SFTEX_OPTIONS_PAGE:
+ {
+ OPENOPTIONSDIALOG ood;
+
+ ZeroMemory(&ood, sizeof(ood));
+ ood.cbSize = sizeof(ood);
+ ood.pszGroup = Translate("Events");
+ ood.pszPage = Translate("Spam Filter");
+
+ return CallService(MS_OPT_OPENOPTIONS, 0, (LPARAM)&ood);
+ }
+
+ case SFT_DISLIKEDMESSAGES_FILTER:
+ {
+ if (IsWindow(hwndDislikedMessagesFilter)) {
+ SetForegroundWindow(hwndDislikedMessagesFilter);
+ return 0;
+ }
+
+ if (hwndParent) {
+ // hwndDislikedMessagesFilter will be set on WM_INITDIALOG and set to NULL on WM_DESTROY
+ return (DialogBox(hInstance, MAKEINTRESOURCE(IDD_OPTIONS_CONFIGURE_DISLIKEDMESSAGEFILTER), hwndParent, DlgProcConfigureDislikedMessagesFilter)>0) ? 0 : 1;
+
+ } else {
+ hwndDislikedMessagesFilter = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_OPTIONS_CONFIGURE_DISLIKEDMESSAGEFILTER), (HWND)NULL, DlgProcConfigureDislikedMessagesFilter);
+ return (hwndDislikedMessagesFilter ? 0 : 1);
+ }
+ }
+
+ case SFT_ADVERTISMENT_FILTER:
+ {
+ INITCOMMONCONTROLSEX icc;
+
+ if (IsWindow(hwndAdvertismentFilter)) {
+ SetForegroundWindow(hwndAdvertismentFilter);
+ return 0;
+ }
+
+ // Ensure the needed common controls are loaded
+ ZeroMemory(&icc, sizeof(icc));
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_UPDOWN_CLASS;
+ InitCommonControlsEx(&icc);
+
+ if (hwndParent) {
+ // hwndAdvertismentFilter will be set on WM_INITDIALOG and set to NULL on WM_DESTROY
+ return (DialogBox(hInstance, MAKEINTRESOURCE(IDD_OPTIONS_CONFIGURE_ADVERTISMENTFILTER), hwndParent, DlgProcConfigureAdvertismentFilter)>0) ? 0 : 1;
+
+ } else {
+ hwndAdvertismentFilter = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_OPTIONS_CONFIGURE_ADVERTISMENTFILTER), (HWND)NULL, DlgProcConfigureAdvertismentFilter);
+ return (hwndAdvertismentFilter ? 0 : 1);
+ }
+ }
+
+ case SFT_ROBOT_FILTER:
+ {
+ if (IsWindow(hwndRobotFilter)) {
+ SetForegroundWindow(hwndRobotFilter);
+ return 0;
+ }
+
+ if (hwndParent) {
+ // hwndRobotFilter will be set on WM_INITDIALOG and set to NULL on WM_DESTROY
+ return (DialogBox(hInstance, MAKEINTRESOURCE(IDD_OPTIONS_CONFIGURE_ROBOTFILTER), hwndParent, DlgProcConfigureRobotFilter)>0) ? 0 : 1;
+
+ } else {
+ hwndRobotFilter = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_OPTIONS_CONFIGURE_ROBOTFILTER), (HWND)NULL, DlgProcConfigureRobotFilter);
+ return (hwndRobotFilter ? 0 : 1);
+ }
+ }
+
+ default:
+ return 1;
+ }
+
+ }
+}
+
+
+// Service: MS_SPAMFILTER_RESETAUTOIGNORE
+static int EnumAutoIgnoreSettingsProc(const char* pszSetting, LPARAM lParam)
+{
+ WCHAR* pszBuf;
+ mir_utf8decode((char*)pszSetting,&pszBuf);
+ if (pszBuf) {
+ SLAddItem((STRINGLIST*)lParam, pszBuf);
+ mir_free(pszBuf);
+ }
+ return 0;
+}
+
+static int ServiceResetAutoIgnore(WPARAM wParam, LPARAM lParam)
+{
+ BOOL bOnlyCount = (BOOL)wParam;
+
+ int iReturn;
+ DBCONTACTENUMSETTINGS dbces;
+ STRINGLIST* pslSettings = SLNewList();
+ int i;
+ #if defined(UNICODE)
+ char* pszBuf;
+ #endif
+
+ // Enum settings
+ dbces.szModule = DB_MODULE_NAME_PRESPAMMERS;
+ dbces.pfnEnumProc = EnumAutoIgnoreSettingsProc;
+ dbces.lParam = (LPARAM)pslSettings;
+
+ CallService(MS_DB_CONTACT_ENUMSETTINGS, (WPARAM)NULL, (LPARAM)&dbces);
+
+ // Delete settings
+ if (!bOnlyCount)
+ {
+ iReturn = 0;
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(pslSettings); i++)
+ {
+ #if defined(UNICODE)
+ pszBuf = mir_utf8encodeW(SLGetItem(pslSettings, i));
+ if (pszBuf)
+ {
+ if (DBDeleteContactSetting(NULL, dbces.szModule, pszBuf) != 0)
+ iReturn++;
+ }
+ #else
+ if (DBDeleteContactSetting(NULL, dbces.szModule, SLGetItem(pslSettings, i)) != 0)
+ iReturn++;
+ #endif
+ }
+ } else {
+ iReturn = SLGetItemCount(pslSettings);
+ }
+
+ // Free memory
+ SLFreeList(pslSettings);
+
+ return iReturn;
+}
+
+
+// Service: MS_SPAMFILTER_CONTACT_SETASSPAMMER
+static int ServiceContactSetAsSpammer(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ DWORD dwFlags = (DWORD)lParam;
+
+ if (!hContact || (hContact == INVALID_HANDLE_VALUE)) return 1;
+
+ if (dwFlags & SCASF_NO_NOTIFY)
+ {
+ if (dwFlags & SCASF_USE_ROBOT_SOUND)
+ SkinPlaySound(DB_SOUND_ROBOT_SETTING);
+ else
+ SkinPlaySound(DB_SOUND_ADVERTISMENT_SETTING);
+ }
+
+ // Add contact to spammer list
+ AddContactToIgnoreList(hContact, !(dwFlags&SCASF_NO_REMOVE_HISTORY), !(dwFlags&SCASF_NO_DENY_AUTHREQUESTS), (dwFlags & SCASF_NO_NOTIFY));
+
+ // Remove pre-spammer flag if exists (now is real spammer)
+ {
+ SPAMCHECKDATA scd;
+
+ ZeroMemory(&scd, sizeof(scd));
+ scd.cbSize = sizeof(scd);
+ scd.hContact = hContact;
+
+ RemovePreSpammerCount(&scd);
+ }
+
+ return 0;
+}
+
+
+// Service: MS_SPAMFILTER_CONTACT_SHOWSETASSPAMMERDIALOG
+static int ServiceContactShowSetAsSpammerDialog(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ HWND hwndParent = (HWND)lParam;
+ BOOL bDlgOK;
+
+ WCHAR* pszFmt;
+ WCHAR* pszUserName;
+ MSGBOXPARAMS mbp;
+
+ if (!hContact || (hContact == INVALID_HANDLE_VALUE)) return 1;
+
+ // Correct wrong call (backward compatibility)
+ if (hwndParent == INVALID_HANDLE_VALUE) hwndParent = NULL;
+
+ ZeroMemory(&mbp, sizeof(mbp));
+ mbp.cbSize = sizeof(mbp);
+ mbp.dwLanguageId = LANGIDFROMLCID(GetThreadLocale()); // Current system language: MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
+ mbp.hwndOwner = hwndParent;
+ mbp.hInstance = hInstance;
+ mbp.lpszIcon = MAKEINTRESOURCE(IDI_DEFAULT_LARGE);
+ mbp.dwStyle = MB_USERICON|MB_YESNO|MB_DEFBUTTON1|MB_SETFOREGROUND|MB_TASKMODAL; // MB_RTLREADING works also on non-arabic systems (Win 2000/XP)
+ mbp.lpszCaption = TranslateW("Mark Contact as Spammer");
+ pszUserName = (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE);
+ pszFmt = TranslateW("Are you sure that the contact \"%s\" shall be marked as spammer?\r\n\r\nMarking a contact as spammer means that all associated messages will be recognized as spam and the contact will be completely ignored in the future.\r\n\r\nA contact that was marked as spammer can be restored using the options dialog.");
+ mbp.lpszText = (WCHAR*)mir_alloc((lstrlen(pszFmt)+(pszUserName?lstrlen(pszUserName):0)+1)*sizeof(WCHAR));
+ mir_sntprintf((WCHAR*)mbp.lpszText, (lstrlen(pszFmt) + (pszUserName?lstrlen(pszUserName):0) + 1), pszFmt, pszUserName?pszUserName:_T(""));
+
+ bDlgOK = (MessageBoxIndirect(&mbp) == IDYES);
+ if ((char*)mbp.lpszText) mir_free((char*)mbp.lpszText);
+
+ if (bDlgOK)
+ {
+ BOOL bNoLog;
+ BOOL bKeepHistory;
+
+ // Get mark-read / delete setting
+ bKeepHistory = ((DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_BEHAVIOUR, DEFAULT_SETTING_BEHAVIOUR) == 1) ? TRUE : FALSE);
+
+ // Check id ctrl key is pressed to manually cancel logging
+ if (GetAsyncKeyState(VK_CONTROL)&0x8000)
+ bNoLog = FALSE;
+ else
+ bNoLog = TRUE;
+
+ SkinPlaySound(DB_SOUND_ADVERTISMENT_SETTING);
+
+ // Add contact to spammer list
+ AddContactToIgnoreList(hContact, bKeepHistory, TRUE, bNoLog);
+
+ // Remove pre-spammer flag (now is real spammer)
+ {
+ SPAMCHECKDATA scd;
+
+ ZeroMemory(&scd, sizeof(scd));
+ scd.cbSize = sizeof(scd);
+ scd.hContact = hContact;
+
+ RemovePreSpammerCount(&scd);
+ }
+ }
+
+ return (int)bDlgOK;
+}
+
+
+// Service: MS_SPAMFILTER_CONTACT_ISSPAMMER
+static int ServiceContactIsSpammer(WPARAM wParam, LPARAM lParam)
+{
+ return (BYTE)(DBGetContactSettingByte((HANDLE)wParam, DB_MODULE_NAME, DB_SETTING_ISSPAMMER, (BYTE)FALSE)?TRUE:FALSE);
+}
+
+
+// Service: MS_SPAMFILTER_CONTACT_UNSETSPAMMER
+static int ServiceContactUnSetSpammer(WPARAM wParam, LPARAM lParam)
+{
+ // Internal note: lParam is hwndParent when used as menu item service
+
+ if ((HANDLE)wParam && ((HANDLE)wParam != INVALID_HANDLE_VALUE) )
+ return RemoveContactFromIgnoreList((HANDLE)wParam) ? 0 : 1;
+ else
+ return 1;
+}
+
+
+// Service: MS_SPAMFILTER_CHANGEFILTERACTIVATION
+static int ServiceChangeFilterActivation(WPARAM wParam, LPARAM lParam)
+{
+ BOOL bNewStatus = (BOOL)lParam;
+ DWORD dwFilterType = (DWORD)wParam;
+
+ switch (dwFilterType)
+ {
+ case SFT_DISLIKEDMESSAGES_FILTER:
+ {
+ if (IsWindow(hwndSpamFilterOpt))
+ CheckDlgButton(hwndSpamFilterOpt, IDC_CHECKBOX_DISLIKEDMESSAGEFILTER, (bNewStatus ? BST_CHECKED : BST_UNCHECKED));
+
+ return DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, (BYTE)bNewStatus);
+ }
+
+ case SFT_ADVERTISMENT_FILTER:
+ {
+ if (IsWindow(hwndSpamFilterOpt))
+ CheckDlgButton(hwndSpamFilterOpt, IDC_CHECKBOX_ADVERTISMENTFILTER, (bNewStatus ? BST_CHECKED : BST_UNCHECKED));
+
+ return DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)bNewStatus);
+ }
+
+ case SFT_ROBOT_FILTER:
+ {
+ if (IsWindow(hwndSpamFilterOpt))
+ CheckDlgButton(hwndSpamFilterOpt, IDC_CHECKBOX_ROBOTFILTER, (bNewStatus ? BST_CHECKED : BST_UNCHECKED));
+
+ return DBWriteContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOTFILTER, (BYTE)bNewStatus);
+ }
+
+ default:
+ return 1;
+ }
+}
+
+
+// Service: MS_SPAMFILTER_GETSPAMCHECKINFO
+static int ServiceGetSpamCheckInfo(WPARAM wParam, LPARAM lParam)
+{
+ DWORD dwInfoType = (DWORD)wParam;
+
+ switch (dwInfoType)
+ {
+ case SFSCI_ACTIVATION_FILTER:
+ {
+ switch ((DWORD)lParam)
+ {
+ case SFT_DISLIKEDMESSAGES_FILTER:
+ return (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, DEFAULT_SETTING_DISLIKEDMESSAGEFILTER)?TRUE:FALSE);
+
+ case SFT_ADVERTISMENT_FILTER:
+ return (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, DEFAULT_SETTING_ADVERTISMENTFILTER)?TRUE:FALSE);
+
+ case SFT_ROBOT_FILTER:
+ return (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOTFILTER, DEFAULT_SETTING_ROBOTFILTER)?TRUE:FALSE);
+ }
+
+ break;
+ }
+
+ case SFSCI_ADD_TO_HISTORY:
+ {
+ switch ((DWORD)lParam)
+ {
+ // Disliked Messages Filter
+ case SFSCI_MSGTEXT_NOTIFYA:
+ case SFSCI_MSGTEXT_NOTIFYW:
+ return (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_ADDTOHISTORY, DEFAULT_SETTING_DISLIKEDMESSAGE_ADDTOHISTORY)?TRUE:FALSE);
+
+ // Robot Filter
+ case SFSCI_MSGTEXT_INSTRUCTIONA:
+ case SFSCI_MSGTEXT_INSTRUCTIONW:
+ return (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ADDTOHISTORY, DEFAULT_SETTING_ROBOT_ADDTOHISTORY)?TRUE:FALSE);
+
+ case SFSCI_MSGTEXT_CONFIRMATIONA:
+ case SFSCI_MSGTEXT_CONFIRMATIONW:
+ return (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ADDTOHISTORY, DEFAULT_SETTING_ROBOT_ADDTOHISTORY)?TRUE:FALSE);
+ }
+ }
+
+ case SFSCI_MSGTEXT_NOTIFYA:
+ case SFSCI_MSGTEXT_INSTRUCTIONA:
+ case SFSCI_MSGTEXT_CONFIRMATIONA:
+ case SFSCI_MSGTEXT_NOTIFYW:
+ case SFSCI_MSGTEXT_INSTRUCTIONW:
+ case SFSCI_MSGTEXT_CONFIRMATIONW:
+ {
+ SPAMCHECKDATA* pscd = (SPAMCHECKDATA*)lParam;
+ char* pszSetting;
+ WCHAR* pszBuf;
+ WCHAR* pReturn;
+ DBVARIANT dbv;
+ STRINGLIST* pslFromTo;
+
+ if (!pscd || (pscd->cbSize < sizeof(SPAMCHECKDATA)) || !SCD_IsUserValid(pscd))
+ break;
+
+ switch (dwInfoType)
+ {
+ // Disliked Messages Filter
+ case SFSCI_MSGTEXT_NOTIFYA:
+ case SFSCI_MSGTEXT_NOTIFYW:
+ pszSetting = DB_SETTING_DISLIKEDMESSAGE_RETURNMESSAGE; // readonly allocated
+ break;
+
+ // Robot Filter
+ case SFSCI_MSGTEXT_INSTRUCTIONA:
+ case SFSCI_MSGTEXT_INSTRUCTIONW:
+ pszSetting = DB_SETTING_ROBOT_INSTRUCTION; // readonly allocated
+ break;
+
+ case SFSCI_MSGTEXT_CONFIRMATIONA:
+ case SFSCI_MSGTEXT_CONFIRMATIONW:
+ pszSetting = DB_SETTING_ROBOT_CONFIRMATION; // readonly allocated
+ break;
+
+ default:
+ pszSetting = NULL;
+ }
+
+ if (pszSetting)
+ {
+ WCHAR* pszVariablesOut = NULL;
+ pslFromTo = SLNewList();
+
+ if ((dwInfoType != SFSCI_MSGTEXT_NOTIFYA) && (dwInfoType != SFSCI_MSGTEXT_NOTIFYW))
+ {
+ // %securitycode%
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ANSWER, &dbv) == 0)
+ {
+ SLAddItemPair(pslFromTo, _T("%securitycode%"), dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else {
+ WCHAR szRnd[RANDOM_ANSWER_LENGTH+1];
+ GetRandomString(szRnd, ARRAYSIZE(szRnd));
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ANSWER, szRnd);
+ SLAddItemPair(pslFromTo, _T("%securitycode%"), szRnd);
+ }
+
+ // %triesleft%
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_AUTOADDSPAMMERS, DEFAULT_SETTING_AUTOADDSPAMMERS))
+ {
+ WCHAR szTriesLeft[MAX_INT_LENGTH+1];
+ mir_sntprintf(szTriesLeft, ARRAYSIZE(szTriesLeft), _T("%u"), DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ALLOWEDSPAMMESSAGES, DEFAULT_SETTING_ALLOWEDSPAMMESSAGES)-GetPreSpammerCount(pscd)+1);
+ SLAddItemPair(pslFromTo, _T("%triesleft%"), szTriesLeft);
+ } else {
+ SLAddItemPair(pslFromTo, _T("%triesleft%"), TranslateW("unlimited"));
+ }
+ }
+
+ // %message%
+ #if defined(UNICODE)
+ if (pscd->dwFlags&SCDF_UNICODE)
+ pszBuf = pscd->ptszMsgText;
+ else
+ pszBuf = (WCHAR*)mir_utf8encodeW((WCHAR*)pscd->pszMsgText);
+
+ SLAddItemPair(pslFromTo, _T("%message%"), pszBuf);
+
+ if (pszBuf && !(pscd->dwFlags&SCDF_UNICODE))
+ mir_free(pszBuf);
+ #else
+ if (pscd->dwFlags&SCDF_UNICODE)
+ pszBuf = UnicodeToAnsi(pscd->pwszMsgText);
+ else
+ pszBuf = pscd->ptszMsgText;
+
+ SLAddItemPair(pslFromTo, _T("%message%"), pszBuf);
+
+ if ((pscd->dwFlags&SCDF_UNICODE) && pszBuf)
+ mir_free(pszBuf);
+ #endif
+
+ // %sender%
+ pszBuf = SCD_GetContactName(pscd, FALSE);
+ SLAddItemPair(pslFromTo, _T("%sender%"), pszBuf);
+ if (pszBuf) mir_free(pszBuf);
+
+ // Retrieve message text
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, pszSetting, &dbv) == 0) {
+ pszBuf = ReplaceSubStringWithStringMultiple(dbv.ptszVal, pslFromTo, TRUE, FALSE, NULL);
+ DBFreeVariant(&dbv);
+ } else {
+ pszBuf = NULL;
+ }
+
+ // Retrieve message text
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, pszSetting, &dbv) == 0) {
+ pszBuf = ReplaceSubStringWithStringMultiple(dbv.ptszVal, pslFromTo, TRUE, FALSE, NULL);
+ DBFreeVariant(&dbv);
+ } else {
+ pszBuf = NULL;
+ }
+ SLFreeList(pslFromTo);
+
+ // Support for "Variables" plugin
+ if (ServiceExists(MS_VARS_FORMATSTRING))
+ {
+ FORMATINFO fi;
+
+ ZeroMemory(&fi, sizeof(fi));
+ fi.cbSize = sizeof(fi);
+ fi.flags = FIF_UNICODE;
+ fi.tszFormat = pszBuf;
+ fi.tszExtraText = pscd->ptszMsgText; // %message% variable is handled earlier by Spam Filter itself
+ fi.hContact = (pscd->dwFlags&SCDF_NO_CONTACT)?NULL:pscd->hContact;
+
+ pszVariablesOut = (WCHAR*)CallService(MS_VARS_FORMATSTRING, (WPARAM)&fi, 0);
+ }
+
+
+ // Convert return value
+ pReturn = NULL;
+ if (pszVariablesOut?pszVariablesOut:pszBuf)
+ {
+ switch (dwInfoType)
+ {
+ case SFSCI_MSGTEXT_NOTIFYA:
+ case SFSCI_MSGTEXT_INSTRUCTIONA:
+ case SFSCI_MSGTEXT_CONFIRMATIONA:
+ {
+ #if defined(UNICODE)
+ // UnicodeToAnsi uses mir_alloc
+ mir_utf8decode((char*)pszVariablesOut?(char*)pszVariablesOut:(char*)pszBuf,&pReturn);
+ #else
+ // AnsiDup uses mir_alloc
+ pReturn = (PBYTE)mir_strdup(pszVariablesOut?pszVariablesOut:pszBuf);
+ #endif
+ break;
+ }
+
+ case SFSCI_MSGTEXT_NOTIFYW:
+ case SFSCI_MSGTEXT_INSTRUCTIONW:
+ case SFSCI_MSGTEXT_CONFIRMATIONW:
+ {
+ #if defined(UNICODE)
+ // UnicodeDup uses mir_alloc
+ pReturn = mir_wstrdup(pszVariablesOut?pszVariablesOut:pszBuf);
+ #else
+ // mir_utf8encodeW uses mir_alloc
+ pReturn = (PBYTE)mir_utf8encodeW(pszVariablesOut?pszVariablesOut:pszBuf);
+ #endif
+ break;
+ }
+ }
+ }
+
+
+ if (pszBuf)
+ SLFreeReturn(pszBuf);
+
+ // Support for "Variables" plugin
+ if (pszVariablesOut)
+ CallService(MS_VARS_FREEMEMORY, (WPARAM)pszVariablesOut, 0);
+
+ return (int)pReturn;
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+// Service: MS_SPAMFILTER_REGISTERMESSAGETYPE
+static int ServiceRegisterMessageType(WPARAM wParam, LPARAM lParam)
+{
+ MESSAGETYPEDESC* pmtdNew = (MESSAGETYPEDESC*)lParam;
+
+ if (!pmtdNew->pszName || !pmtdNew->pszSection || (pmtdNew->cbSize < MESSAGETYPEDESC_V2000_SIZE))
+ return 1;
+
+ // Do not add if already added
+ EnterCriticalSection(&csMsgTypes); // thread safety
+ if (GetMsgTypeID(pmtdNew->pszSection, pmtdNew->pszName) >= 0) {
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return 1;
+ }
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+
+ // outside critical section:
+ // Hand out event (asking other plugins if default data is ok)
+ if (NotifyEventHooks(hEventPreRegisterMessageType, (WPARAM)wParam, (LPARAM)lParam) == 1) {
+ OutputDebugString(_T("Spam Filter: Adding message type denied (by event).\r\n"));
+ return 1;
+ }
+
+ // Do not add if already added (check again)
+ EnterCriticalSection(&csMsgTypes); // thread safety
+ if (GetMsgTypeID(pmtdNew->pszSection, pmtdNew->pszName) >= 0) {
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return 1;
+ }
+
+ // Resize list array
+ {
+ MESSAGETYPEDESC* pamtdMsgTypesBuf;
+ pamtdMsgTypesBuf = (MESSAGETYPEDESC*)mir_realloc(pamtdMsgTypes, (uMsgTypesCount+1)*sizeof(MESSAGETYPEDESC));
+
+ if (pamtdMsgTypesBuf) {
+ pamtdMsgTypes = pamtdMsgTypesBuf;
+ } else {
+ // Fatal error: list was not reallocated, insufficient memory (old data still valid)
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return 1;
+ }
+ }
+
+ pamtdMsgTypes[uMsgTypesCount].cbSize = sizeof(MESSAGETYPEDESC);
+ pamtdMsgTypes[uMsgTypesCount].dwFlags = pmtdNew->dwFlags;
+
+ // Backward compatibility (struct extended in v2.1.1.0)
+ if (pmtdNew->cbSize > MESSAGETYPEDESC_V2000_SIZE) {
+ pamtdMsgTypes[uMsgTypesCount].iPosition = pmtdNew->iPosition;
+ pamtdMsgTypes[uMsgTypesCount].iSectionPosition = pmtdNew->iSectionPosition;
+ } else {
+ pamtdMsgTypes[uMsgTypesCount].iPosition = 0;
+ pamtdMsgTypes[uMsgTypesCount].iSectionPosition = pmtdNew->iSectionPosition;
+ }
+
+ pamtdMsgTypes[uMsgTypesCount].pszSection = mir_strdup(pmtdNew->pszSection);
+ pamtdMsgTypes[uMsgTypesCount].pszName = mir_strdup(pmtdNew->pszName);
+
+ #if defined(UNICODE)
+ if (pmtdNew->dwFlags&MTDF_UNICODE) {
+ pamtdMsgTypes[uMsgTypesCount].ptszSectionDescription = pmtdNew->pwszSectionDescription ? mir_wstrdup(pmtdNew->pwszSectionDescription) : (WCHAR*)mir_utf8encodeW((WCHAR*)Translate((WCHAR*)pmtdNew->pszSection));
+ pamtdMsgTypes[uMsgTypesCount].ptszDescription = pmtdNew->pwszDescription ? mir_wstrdup(pmtdNew->pwszDescription) : (WCHAR*)mir_utf8encodeW((WCHAR*)Translate((WCHAR*)pmtdNew->pszName));
+ } else {
+ pamtdMsgTypes[uMsgTypesCount].dwFlags |= MTDF_UNICODE;
+ pamtdMsgTypes[uMsgTypesCount].ptszSectionDescription = pmtdNew->pszSectionDescription ? (WCHAR*)mir_utf8encodeW((WCHAR*)pmtdNew->pszSectionDescription) : (WCHAR*)mir_utf8encodeW((WCHAR*)Translate((WCHAR*)pmtdNew->pszSection));
+ pamtdMsgTypes[uMsgTypesCount].ptszDescription = pmtdNew->pszDescription ? (WCHAR*)mir_utf8encodeW((WCHAR*)pmtdNew->pszDescription) : (WCHAR*)mir_utf8encodeW((WCHAR*)Translate((WCHAR*)pmtdNew->pszName));
+ }
+ #else
+ if (pmtdNew->dwFlags&MTDF_UNICODE) {
+ pamtdMsgTypes[uMsgTypesCount].dwFlags ^= MTDF_UNICODE;
+ pamtdMsgTypes[uMsgTypesCount].ptszSectionDescription = pmtdNew->pwszSectionDescription ? UnicodeToAnsi(pmtdNew->pwszSectionDescription) : mir_strdup(Translate(pmtdNew->pszSection));
+ pamtdMsgTypes[uMsgTypesCount].ptszDescription = pmtdNew->pwszDescription ? UnicodeToAnsi(pmtdNew->pwszDescription) : mir_strdup(Translate(pmtdNew->pszName));
+ } else {
+ pamtdMsgTypes[uMsgTypesCount].ptszSectionDescription = pmtdNew->pszSectionDescription ? AnsiDup(pmtdNew->pszSectionDescription) : AnsiDup(Translate(pmtdNew->pszSection));
+ pamtdMsgTypes[uMsgTypesCount].ptszDescription = pmtdNew->pszDescription ? AnsiDup(pmtdNew->ptszDescription) : AnsiDup(Translate(pmtdNew->pszName));
+ }
+ #endif
+
+ // Fatal error (strings were not allocated, insufficient memory)
+ if (!pamtdMsgTypes[uMsgTypesCount].pszSection || !pamtdMsgTypes[uMsgTypesCount].pszSectionDescription || !pamtdMsgTypes[uMsgTypesCount].pszName || !pamtdMsgTypes[uMsgTypesCount].pszDescription)
+ {
+ if (pamtdMsgTypes[uMsgTypesCount].pszSection) mir_free(pamtdMsgTypes[uMsgTypesCount].pszSection);
+ if (pamtdMsgTypes[uMsgTypesCount].pszSectionDescription) mir_free(pamtdMsgTypes[uMsgTypesCount].pszSectionDescription);
+ if (pamtdMsgTypes[uMsgTypesCount].pszName) mir_free(pamtdMsgTypes[uMsgTypesCount].pszName);
+ if (pamtdMsgTypes[uMsgTypesCount].pszDescription) mir_free(pamtdMsgTypes[uMsgTypesCount].pszDescription);
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return 1;
+ }
+
+ pamtdMsgTypes[uMsgTypesCount].hSectionIcon = pmtdNew->hSectionIcon ? (HICON)CopyImage(pmtdNew->hSectionIcon, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_COPYFROMRESOURCE) : NULL;
+
+ if ((int)pmtdNew->hIcon < 0) // if is miranda skinned icon constant
+ pamtdMsgTypes[uMsgTypesCount].hIcon = pmtdNew->hIcon;
+ else
+ pamtdMsgTypes[uMsgTypesCount].hIcon = pmtdNew->hIcon ? (HICON)CopyImage(pmtdNew->hIcon, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_COPYFROMRESOURCE) : NULL;
+
+
+ uMsgTypesCount++;
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+
+ // Refresh shown list
+ if (IsWindow(hwndSpamFilterOpt))
+ SendMessage(hwndSpamFilterOpt, SFM_REBUILD_MSGTYPES, 0, 0);
+
+ return 0;
+}
+
+
+// Service: MS_SPAMFILTER_REMOVEMESSAGETYPE
+static int ServiceRemoveMessageType(WPARAM wParam, LPARAM lParam)
+{
+ char* pszMsgTypeSection = (char*)wParam;
+ char* pszMsgTypeName = (char*)lParam;
+
+ MESSAGETYPEDESC* pamtdMsgTypesBuf;
+ int iReturn;
+ char* pszSetting;
+ unsigned int u;
+
+ if (!pszMsgTypeName || !pszMsgTypeSection) return 1;
+ iReturn = 1;
+
+ EnterCriticalSection(&csMsgTypes); // thread safety
+
+ // Remove array item
+ if (pamtdMsgTypes)
+ {
+ for (u=0; u<uMsgTypesCount; u++)
+ {
+ // Same pszSection?
+ if (StrCmpA(pamtdMsgTypes[u].pszSection, pszMsgTypeSection) == 0)
+ {
+ // Same pszName? (when no name is specified all items of the same section are removed)
+ if (StrCmpA(pamtdMsgTypes[u].pszName, pszMsgTypeName) == 0)
+ {
+ // -> Item to remove found
+
+ // Delete database setting
+ pszSetting = GetMsgTypeSettingName(pamtdMsgTypes[u].pszSection, pamtdMsgTypes[u].pszName);
+ if (pszSetting)
+ {
+ DBDeleteContactSetting(NULL, DB_MODULE_NAME_MSGTYPES, pszSetting);
+ mir_free(pszSetting);
+ }
+
+ // Free array item memory
+ if (pamtdMsgTypes[u].pszSection) mir_free(pamtdMsgTypes[u].pszSection);
+ if (pamtdMsgTypes[u].pszName) mir_free(pamtdMsgTypes[u].pszName);
+ if (pamtdMsgTypes[u].ptszSectionDescription) mir_free(pamtdMsgTypes[u].ptszSectionDescription);
+ if (pamtdMsgTypes[u].ptszDescription) mir_free(pamtdMsgTypes[u].ptszDescription);
+ if (pamtdMsgTypes[u].hIcon && ((int)pamtdMsgTypes[u].hIcon > 0)) DestroyIcon(pamtdMsgTypes[u].hIcon);
+ if (pamtdMsgTypes[u].hSectionIcon) DestroyIcon(pamtdMsgTypes[u].hSectionIcon);
+
+ // Move other array items (if not last item)
+ if ((u+1) < uMsgTypesCount)
+ MoveMemory(&pamtdMsgTypes[u], &pamtdMsgTypes[u+1], ((uMsgTypesCount-u-1)*sizeof(MESSAGETYPEDESC)));
+
+ // Resize array
+ pamtdMsgTypesBuf = (MESSAGETYPEDESC*)mir_realloc(pamtdMsgTypes, (uMsgTypesCount-1)*sizeof(MESSAGETYPEDESC));
+ if (pamtdMsgTypesBuf)
+ pamtdMsgTypes = pamtdMsgTypesBuf;
+
+ // Decrase list count
+ uMsgTypesCount--;
+
+ iReturn = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+
+ // Refresh shown list
+ if (IsWindow(hwndSpamFilterOpt))
+ SendMessage(hwndSpamFilterOpt, SFM_REBUILD_MSGTYPES, 0, 0);
+
+ return iReturn;
+}
+
+
+// Service: MS_SPAMFILTER_ISMESSAGETYPEACTIVATED
+static int ServiceIsMessageTypeActivated(WPARAM wParam, LPARAM lParam)
+{
+ int iMsgType;
+ BOOL bReturn = FALSE;
+
+ EnterCriticalSection(&csMsgTypes); // thread safety
+
+ iMsgType = GetMsgTypeID((const char*)wParam, (const char*)lParam);
+ if (iMsgType != -1)
+ if (pamtdMsgTypes[iMsgType].dwFlags&MTDF_HIDDEN)
+ {
+ bReturn = !(pamtdMsgTypes[iMsgType].dwFlags&MTDF_DEFAULTDISABLED);
+ } else {
+ char* pszSetting = GetMsgTypeSettingName(pamtdMsgTypes[iMsgType].pszSection, pamtdMsgTypes[iMsgType].pszName);
+ if (pszSetting)
+ {
+ bReturn = DBGetContactSettingByte(NULL, DB_MODULE_NAME_MSGTYPES, pszSetting, (BYTE)!(pamtdMsgTypes[iMsgType].dwFlags&MTDF_DEFAULTDISABLED));
+ mir_free(pszSetting);
+ }
+ }
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return (int)bReturn;
+}
+
+
+// Service: MS_SPAMFILTER_ROBOTCHECK
+static int ServiceRobotCheck(WPARAM wParam, LPARAM lParam) // wParam = SPAMCHECKDATA, lParam = 0
+{
+ SPAMCHECKDATA* pscd = (SPAMCHECKDATA*)wParam;
+
+ if (!pscd || !pscd->pszMsgTypeSection || !pscd->pszMsgTypeName || (pscd->cbSize < sizeof(SPAMCHECKDATA)) || !SCD_IsUserValid(pscd))
+ return SFF_ISNORMAL;
+
+ // Check id ctrl key is pressed to manually cancel spam check
+ if (!(pscd->dwFlags&SCDF_NO_CANCEL) && (GetAsyncKeyState(VK_CONTROL)&0x8000))
+ return SFF_ISNORMAL;
+
+ if ( !CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)pscd->pszMsgTypeSection, (LPARAM)pscd->pszMsgTypeName)
+ || !CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ACTIVATION_FILTER, (LPARAM)SFT_ROBOT_FILTER) )
+ return SFF_ISNORMAL;
+
+ if (NotifyEventHooks(hEventOkToSpamCheck, (WPARAM)pscd, (LPARAM)SFT_ROBOT_FILTER) == 1) {
+ OutputDebugString(_T("Spam Filter: Spam check denied (by event).\r\n"));
+ return SFF_ISNORMAL;
+ }
+
+ {
+ WCHAR* pszNormalMsgText;
+ WCHAR* pszClearedMsgText = NULL;
+
+ DWORD dwReturn = SFF_ISNORMAL;
+
+ // Buffer the msg content
+ if (pscd->ptszMsgText)
+ {
+ #if defined(UNICODE)
+ if (pscd->dwFlags&SCDF_UNICODE)
+ pszNormalMsgText = pscd->ptszMsgText;
+ else
+ pszNormalMsgText = (WCHAR*)mir_utf8encodeW((WCHAR*)pscd->pszMsgText);
+ #else
+ if (pscd->dwFlags&SCDF_UNICODE)
+ pszNormalMsgText = UnicodeToAnsi(pscd->pwszMsgText);
+ else
+ pszNormalMsgText = pscd->ptszMsgText;
+ #endif
+
+ } else {
+ pszNormalMsgText = NULL;
+ }
+
+ pszClearedMsgText = mir_wstrdup(pszNormalMsgText);
+ if (pszClearedMsgText)
+ RemoveWhiteSpaces(pszClearedMsgText, FALSE, TRUE);
+ else
+ pszClearedMsgText = NULL;
+
+ if (pszClearedMsgText)
+ {
+ DBVARIANT dbv;
+
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ANSWER, &dbv) == 0)
+ {
+ // If message contains correct answer
+ RemoveWhiteSpaces(dbv.ptszVal, FALSE, TRUE);
+ if ((StrCmp(pszClearedMsgText, dbv.ptszVal) == 0) || // or allow for quotes (below)
+ (StrCmpN(&pszClearedMsgText[1], dbv.ptszVal, lstrlen(pszClearedMsgText)-2) == 0))
+ dwReturn |= SFF_SENDMSG_CONFIRMATION|SFF_TESTPASSED|(DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_ADDTOHISTORY, DEFAULT_SETTING_ROBOT_ADDTOHISTORY) ? SFF_MARKREAD : SFF_DELETE);
+
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ if (pszClearedMsgText) mir_free(pszClearedMsgText);
+
+ #if defined(UNICODE)
+ if (!(pscd->dwFlags&SCDF_UNICODE))
+ if (pszNormalMsgText) mir_free(pszNormalMsgText);
+ #else
+ if ((pscd->dwFlags&SCDF_UNICODE))
+ if (pszNormalMsgText) mir_free(pszNormalMsgText);
+ #endif
+
+ // Kick out spam
+ if ( (!(dwReturn&SFF_TESTPASSED)) && (NotifyEventHooks(hEventOkToSpamDetection, (WPARAM)pscd, (LPARAM)SFT_ROBOT_FILTER) == 0))
+ {
+ dwReturn = SFF_ISSPAM|((DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_BEHAVIOUR, DEFAULT_SETTING_BEHAVIOUR) == 1) ? SFF_MARKREAD : SFF_DELETE);
+
+ // Add user to ignore list
+ if (!(pscd->dwFlags&SCDF_NO_AUTOIGNORE) && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_AUTOADDSPAMMERS, DEFAULT_SETTING_AUTOADDSPAMMERS) )
+ {
+ if (IncreasePreSpammerCount(pscd) > DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ALLOWEDSPAMMESSAGES, DEFAULT_SETTING_ALLOWEDSPAMMESSAGES))
+ {
+ RemovePreSpammerCount(pscd);
+ dwReturn |= SFF_IGNORE;
+ }
+ }
+
+ // Do not send question message when next messages will be ignored
+ if (!(dwReturn&SFF_IGNORE)) {
+ dwReturn |= SFF_SENDMSG_INSTRUCTION;
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ROBOT_HIDEUSERS, (BYTE)DEFAULT_SETTING_ROBOT_HIDEUSERS))
+ dwReturn |= SFF_HIDE;
+ }
+
+ if (!(pscd->dwFlags&SCDF_NO_NOTIFY))
+ {
+ // Play sound file
+ SkinPlaySound(DB_SOUND_ROBOT_SETTING);
+
+ // Show Popup
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_POPUP, DEFAULT_SETTING_POPUP))
+ {
+ WCHAR* pszDisplayName = SCD_GetContactCustomDisplayName(pscd);
+
+ ShowSpamPopup(pscd->pszMsgTypeSection, pscd->pszMsgTypeName, pszDisplayName, (pscd->dwFlags&SCDF_NO_CONTACT)?NULL:pscd->hContact, SFT_ROBOT_FILTER);
+ if (pszDisplayName) mir_free(pszDisplayName);
+ }
+
+ // Append to log file
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_LOGGING, DEFAULT_SETTING_LOGGING))
+ {
+ STRINGLIST* pslRecognition = SLNewList();
+ WCHAR* pszUserName = SCD_GetContactName(pscd, TRUE);
+
+ SLAddItem(pslRecognition, TranslateW("Robot message"));
+ WriteToLogFile(pscd->pszMsgTypeSection, pscd->pszMsgTypeName, pszUserName, pszNormalMsgText, pslRecognition, dwReturn);
+
+ if (pszUserName) mir_free(pszUserName);
+ SLFreeList(pslRecognition);
+ }
+
+ // Hand out event
+ NotifyEventHooks(hEventSpamReceived, (WPARAM)pscd, (LPARAM)SFT_ROBOT_FILTER);
+
+ // Trigger Plugin Support
+ TriggerIncomingSpam();
+ }
+
+
+ } else {
+
+ if (!(pscd->dwFlags&SCDF_NO_AUTOIGNORE))
+ RemovePreSpammerCount(pscd);
+
+ // Message is normal
+ }
+
+ return dwReturn;
+ }
+}
+
+
+// Service: MS_SPAMFILTER_DISLIKEDMESSAGECHECK
+static int ServiceDislikedMessageCheck(WPARAM wParam, LPARAM lParam) // wParam = SPAMCHECKDATA, lParam = 0
+{
+ SPAMCHECKDATA* pscd = (SPAMCHECKDATA*)wParam;
+
+ if (!pscd || !pscd->pszMsgTypeSection || !pscd->pszMsgTypeName || (pscd->cbSize < sizeof(SPAMCHECKDATA)) || !SCD_IsUserValid(pscd))
+ return SFF_ISNORMAL;
+
+ // Check id ctrl key is pressed to manually cancel spam check
+ if (!(pscd->dwFlags&SCDF_NO_CANCEL) && (GetAsyncKeyState(VK_CONTROL)&0x8000))
+ return SFF_ISNORMAL;
+
+ if ( !CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)pscd->pszMsgTypeSection, (LPARAM)pscd->pszMsgTypeName)
+ || !CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ACTIVATION_FILTER, (LPARAM)SFT_DISLIKEDMESSAGES_FILTER) )
+ return SFF_ISNORMAL;
+
+ if (NotifyEventHooks(hEventOkToSpamCheck, (WPARAM)pscd, (LPARAM)SFT_DISLIKEDMESSAGES_FILTER) == 1) {
+ OutputDebugString(_T("Spam Filter: Spam check denied (by event).\r\n"));
+ return SFF_ISNORMAL;
+ }
+
+ {
+ BOOL bDoLog = ( (!(pscd->dwFlags&SCDF_NO_NOTIFY)) && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_LOGGING, DEFAULT_SETTING_LOGGING) );
+ BOOL bKickOut = FALSE;
+
+ int i;
+ WCHAR* pszNormalMsgText;
+ WCHAR* pszClearedMsgText;
+ STRINGLIST* pslRecognition = NULL;
+
+ // Create recognition texts list
+ if (bDoLog) pslRecognition = SLNewList();
+
+ // Buffer the msg content
+ if (pscd->pszMsgText)
+ {
+ #if defined(UNICODE)
+ if (pscd->dwFlags&SCDF_UNICODE)
+ pszNormalMsgText = pscd->ptszMsgText;
+ else
+ pszNormalMsgText = (WCHAR*)mir_utf8encodeW((WCHAR*)pscd->pszMsgText);
+ #else
+ if (pscd->dwFlags&SCDF_UNICODE)
+ pszNormalMsgText = UnicodeToAnsi(pscd->pwszMsgText);
+ else
+ pszNormalMsgText = pscd->ptszMsgText;
+ #endif
+
+ pszClearedMsgText = mir_wstrdup(pszNormalMsgText);
+ if (pszClearedMsgText)
+ RemoveWhiteSpaces(pszClearedMsgText, TRUE, TRUE);
+
+ } else {
+ pszNormalMsgText = NULL;
+ pszClearedMsgText = NULL;
+ }
+
+ // -SPAMCHECKS-
+
+ // Customized indicating words
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_CUSTOMIZEDWORDS, (BYTE)TRUE))
+ {
+ STRINGLIST* pslChainWords = SLNewList();
+ unsigned int uFoundChainWords = 0;
+
+ GetSpamDefinitionData(pslChainWords, SDID_CUSTOM_DISLIKEDWORDS);
+
+ // Check how many words exist in the message
+ for (i=0; i<=SLGetMaxPos(pslChainWords); i++)
+ if (RegExpExistsInString(pszClearedMsgText, SLGetItem(pslChainWords, i), FALSE, NULL))
+ uFoundChainWords++;
+
+ SLFreeList(pslChainWords);
+
+ if (uFoundChainWords > 0)
+ {
+ if (bDoLog) SLItemPrintf(pslRecognition, SLAddItem(pslRecognition, TranslateW("Contains %u disliked indicating phrase(s)/pattern(s)")), MAX_INT_LENGTH, uFoundChainWords);
+ bKickOut = TRUE;
+ if (!bDoLog) goto DislikedMessageResult;
+ }
+ }
+
+
+ // Multiple message check
+ if (pszNormalMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_MULTIPLEMESSAGES, DEFAULT_SETTING_DISLIKEDMESSAGE_MULTIPLEMESSAGES))
+ {
+ DBVARIANT dbv;
+ BOOL bFound;
+ WCHAR* pszSender;
+
+ pszSender = SCD_GetContactUniqueStr(pscd, FALSE);
+
+ bFound = FALSE;
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_LASTMESSAGE, &dbv) == 0)
+ {
+ if (StrCmp(pszNormalMsgText, dbv.ptszVal) == 0)
+ {
+ DBFreeVariant(&dbv);
+
+ if (pszSender)
+ {
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_LASTSENDER, &dbv) == 0)
+ if (StrCmp(pszSender, dbv.ptszVal) == 0)
+ bFound = TRUE;
+
+ } else {
+ bFound = TRUE;
+ }
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ // Always save current msg content
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_LASTMESSAGE, pszNormalMsgText);
+ if (pszSender)
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_LASTSENDER, pszSender);
+ else
+ DBDeleteContactSetting(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_LASTSENDER);
+
+ if (pszSender) mir_free(pszSender);
+
+ if (bFound)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("Is multiple"));
+ bKickOut = TRUE;
+ if (!bDoLog) goto DislikedMessageResult;
+ }
+
+ } else if (!pszNormalMsgText) {
+ DBDeleteContactSetting(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_LASTMESSAGE);
+ }
+
+
+ // Default hoax texts
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_HOAXTEXTS, DEFAULT_SETTING_DISLIKEDMESSAGE_HOAXTEXTS))
+ {
+ STRINGLIST* pslHoaxTexts = SLNewList();
+ BOOL bFound;
+
+ // Load hoax texts
+ GetSpamDefinitionData(pslHoaxTexts, SDID_HOAXTEXTS);
+
+ bFound = FALSE;
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(pslHoaxTexts); i++)
+ if (StrCmpI(pszClearedMsgText, SLGetItem(pslHoaxTexts, i)) == 0)
+ {
+ bFound = TRUE;
+ break;
+ }
+
+ SLFreeList(pslHoaxTexts);
+
+ if (bFound)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("Is hoax text"));
+ bKickOut = TRUE;
+ if (!bDoLog) goto DislikedMessageResult;
+ }
+ }
+
+
+DislikedMessageResult:
+
+ if (pszClearedMsgText) mir_free(pszClearedMsgText);
+
+ #if defined(UNICODE)
+ if (!(pscd->dwFlags&SCDF_UNICODE) && pszNormalMsgText)
+ if (pszNormalMsgText) mir_free(pszNormalMsgText);
+ #else
+ if ((pscd->dwFlags&SCDF_UNICODE) && pszNormalMsgText)
+ mir_free(pszNormalMsgText);
+ #endif
+
+ // Kick out spam
+ if ( bKickOut && (NotifyEventHooks(hEventOkToSpamDetection, (WPARAM)pscd, (LPARAM)SFT_DISLIKEDMESSAGES_FILTER) == 0))
+ {
+ DWORD dwReturn = SFF_ISSPAM|((DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_BEHAVIOUR, DEFAULT_SETTING_BEHAVIOUR) == 1) ? SFF_MARKREAD : SFF_DELETE);
+
+ // Add user to ignore list
+ if (!(pscd->dwFlags&SCDF_NO_AUTOIGNORE) && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_AUTOADDSPAMMERS, DEFAULT_SETTING_AUTOADDSPAMMERS) )
+ {
+ if (IncreasePreSpammerCount(pscd) > DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ALLOWEDSPAMMESSAGES, DEFAULT_SETTING_ALLOWEDSPAMMESSAGES))
+ {
+ RemovePreSpammerCount(pscd);
+ dwReturn |= SFF_IGNORE;
+ }
+ }
+
+ // Reply to sender
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGE_NOTIFYSENDER, DEFAULT_SETTING_DISLIKEDMESSAGE_NOTIFYSENDER))
+ dwReturn |= SFF_SENDMSG_NOTIFY;
+
+ if (!(pscd->dwFlags&SCDF_NO_NOTIFY))
+ {
+ // Play sound file
+ SkinPlaySound(DB_SOUND_DISLIKEDMESSAGE_SETTING);
+
+ // Show Popup
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_POPUP, DEFAULT_SETTING_POPUP))
+ {
+ WCHAR* pszDisplayName = SCD_GetContactCustomDisplayName(pscd);
+ ShowSpamPopup(pscd->pszMsgTypeSection, pscd->pszMsgTypeName, pszDisplayName, (pscd->dwFlags&SCDF_NO_CONTACT)?NULL:pscd->hContact, SFT_DISLIKEDMESSAGES_FILTER);
+ if (pszDisplayName) mir_free(pszDisplayName);
+ }
+
+ // Hand out event
+ NotifyEventHooks(hEventSpamReceived, (WPARAM)pscd, (LPARAM)SFT_DISLIKEDMESSAGES_FILTER);
+
+ // Trigger Plugin Support
+ TriggerIncomingSpam();
+ }
+
+ // Append to log file
+ if (bDoLog)
+ {
+ WCHAR* pszUserName = SCD_GetContactName(pscd, TRUE);
+
+ WriteToLogFile(pscd->pszMsgTypeSection, pscd->pszMsgTypeName, pszUserName, pszNormalMsgText, pslRecognition, dwReturn);
+ if (pszUserName) mir_free(pszUserName);
+
+ SLFreeList(pslRecognition);
+ }
+
+ return dwReturn;
+
+ } else {
+
+ if (bDoLog)
+ SLFreeList(pslRecognition);
+
+ if (!(pscd->dwFlags&SCDF_NO_AUTOIGNORE))
+ RemovePreSpammerCount(pscd); // count can be removed here
+
+ // Message is normal
+ return SFF_ISNORMAL;
+ }
+
+ }
+}
+
+
+// Service : MS_SPAMFILTER_ADVERTISMENTCHECK
+static int ServiceAdvertismentCheck(WPARAM wParam, LPARAM lParam) // wParam = SPAMCHECKDATA, lParam = 0
+{
+ SPAMCHECKDATA* pscd = (SPAMCHECKDATA*)wParam;
+
+ if (!pscd || !pscd->pszMsgTypeSection || !pscd->pszMsgTypeName || (pscd->cbSize < sizeof(SPAMCHECKDATA)) || !SCD_IsUserValid(pscd))
+ return SFF_ISNORMAL;
+
+ // Check id ctrl key is pressed to manually cancel spam check
+ if (!(pscd->dwFlags&SCDF_NO_CANCEL) && (GetAsyncKeyState(VK_CONTROL)&0x8000))
+ return SFF_ISNORMAL;
+
+ if ( !CallService(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, (WPARAM)pscd->pszMsgTypeSection, (LPARAM)pscd->pszMsgTypeName)
+ || !CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ACTIVATION_FILTER, (LPARAM)SFT_ADVERTISMENT_FILTER) )
+ return SFF_ISNORMAL;
+
+ if (NotifyEventHooks(hEventOkToSpamCheck, (WPARAM)pscd, (LPARAM)SFT_ADVERTISMENT_FILTER) == 1) {
+ OutputDebugString(_T("Spam Filter: Spam check denied (by event).\r\n"));
+ return SFF_ISNORMAL;
+ }
+
+ {
+ BOOL bDoLog = ( (!(pscd->dwFlags&SCDF_NO_NOTIFY)) && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_LOGGING, DEFAULT_SETTING_LOGGING) );
+ BOOL bIsKnownSpammer = FALSE;
+ BOOL bKickOut = FALSE;
+ BOOL bFound;
+
+ int i;
+ WCHAR* pszNormalMsgText;
+ WCHAR* pszClearedMsgText;
+ WCHAR* pszFormatedMsgText;
+ STRINGLIST* pslRecognition = NULL;
+
+ // Create recognition texts list
+ if (bDoLog) pslRecognition = SLNewList();
+
+ // Buffer the msg content
+ if (pscd->pszMsgText)
+ {
+ #if defined(UNICODE)
+ if (pscd->dwFlags&SCDF_UNICODE)
+ pszNormalMsgText = pscd->ptszMsgText;
+ else
+ pszNormalMsgText = (WCHAR*)mir_utf8encodeW((WCHAR*)pscd->pszMsgText);
+ #else
+ if (pscd->dwFlags&SCDF_UNICODE)
+ pszNormalMsgText = UnicodeToAnsi(pscd->pwszMsgText);
+ else
+ pszNormalMsgText = pscd->ptszMsgText;
+ #endif
+
+ pszClearedMsgText = mir_wstrdup(pszNormalMsgText);
+ if (pszClearedMsgText)
+ RemoveWhiteSpaces(pszClearedMsgText, TRUE, TRUE);
+
+ } else {
+ pszNormalMsgText = NULL;
+ pszClearedMsgText = NULL;
+ }
+
+ // Create different format check text
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_FORMATING, DEFAULT_SETTING_ADVERTISMENT_FORMATING))
+ {
+ pszFormatedMsgText = mir_wstrdup(pszClearedMsgText);
+ if (pszFormatedMsgText)
+ {
+ STRINGLIST* pslFormat = SLNewList();
+
+ // Some words may have special chars in between the chars
+ GetSpamDefinitionData(pslFormat, SDID_SPECIALCHARS_SEPARATORS);
+
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(pslFormat); i++)
+ ReplaceSubStringWithStringBuf(pszFormatedMsgText, 0, SLGetItem(pslFormat, i), _T(" "), TRUE, FALSE);
+
+ SLClearList(pslFormat);
+
+ // Translate some chars
+ GetSpamDefinitionData(pslFormat, SDID_SPECIALCHARS_REPLACE_TEXT);
+ ReplaceSubStringWithStringMultipleBuf(pszFormatedMsgText, 0, pslFormat, TRUE, FALSE);
+
+ SLFreeList(pslFormat);
+ }
+ } else {
+ pszFormatedMsgText = NULL;
+ }
+
+ // -SPAMCHECKS-
+ // Multiple message check
+ if (pszNormalMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_MULTIPLEMESSAGES, DEFAULT_SETTING_ADVERTISMENT_MULTIPLEMESSAGES))
+ {
+ DBVARIANT dbv;
+ bFound = FALSE;
+
+ // Do not consider sender of NotOnList messages
+ if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_LASTMESSAGE, &dbv) == 0)
+ {
+ if (StrCmp(pszNormalMsgText, dbv.ptszVal) == 0)
+ bFound = TRUE;
+
+ DBFreeVariant(&dbv);
+ }
+
+ // Always save current msg content
+ DBWriteContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_LASTMESSAGE, pszNormalMsgText);
+
+ if (bFound)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("Is multiple"));
+ bKickOut = TRUE;
+ if (!bDoLog) goto AdvertismentResult;
+ }
+
+ } else if (!pszNormalMsgText) {
+ DBDeleteContactSetting(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_LASTMESSAGE);
+ }
+
+ // Empty message?
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_EMPTY, DEFAULT_SETTING_ADVERTISMENT_EMPTY))
+ {
+ if (lstrlen(pszClearedMsgText) <= 0)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("Is empty"));
+ bKickOut = TRUE;
+ if (!bDoLog) goto AdvertismentResult;
+ }
+ }
+
+ // Message capitalized?
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CAPITALIZED, DEFAULT_SETTING_ADVERTISMENT_CAPITALIZED))
+ {
+ bFound = TRUE;
+
+ for (i=0; i<(int)lstrlen(pszClearedMsgText); i++)
+ if (IsCharLower(pszClearedMsgText[i]))
+ {
+ bFound = FALSE;
+ break;
+ }
+
+ if (bFound)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("Is capitalized"));
+ bKickOut = TRUE;
+ if (!bDoLog) goto AdvertismentResult;
+ }
+ }
+
+ // Sender is default spammer?
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DEFAULTSPAMMERS, DEFAULT_SETTING_ADVERTISMENT_DEFAULTSPAMMERS))
+ {
+ STRINGLIST* psl = SLNewList();
+ WCHAR* pszUnique;
+
+ // Load spammers definitions
+ GetSpamDefinitionData(psl, SDID_SPAMMERS);
+
+ // Check if unique user id is listed in def file
+ pszUnique = SCD_GetContactUniqueStr(pscd, FALSE);
+ if (pszUnique)
+ {
+ bFound = SLIsItem(psl, pszUnique, TRUE);
+ mir_free(pszUnique);
+ } else {
+ bFound = FALSE;
+ }
+
+ // Check if nickname is listed in def file
+ if (!bFound)
+ {
+ pszUnique = SCD_GetContactUniqueStr(pscd, TRUE);
+ if (pszUnique)
+ {
+ bFound = SLIsItem(psl, pszUnique, TRUE);
+ mir_free(pszUnique);
+ }
+ }
+ SLFreeList(psl);
+
+ if (bFound)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("User is known as spammer"));
+ bKickOut = bIsKnownSpammer = TRUE;
+ if (!bDoLog) goto AdvertismentResult;
+ }
+ }
+
+ // Teaser Message?
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_TEASERMESSAGES, DEFAULT_SETTING_ADVERTISMENT_TEASERMESSAGES))
+ {
+ STRINGLIST* pslTeasers = SLNewList();
+ bFound = FALSE;
+
+ // Load teasers
+ GetSpamDefinitionData(pslTeasers, SDID_TEASERTEXTS);
+
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(pslTeasers); i++)
+ if (StrCmp(pszClearedMsgText, SLGetItem(pslTeasers, i)) == 0)
+ {
+ bFound = TRUE;
+ break;
+ }
+
+ SLFreeList(pslTeasers);
+
+ if (bFound)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("Is teaser"));
+ bKickOut = TRUE;
+ if (!bDoLog) goto AdvertismentResult;
+ }
+ }
+
+ // Message containing URLs?
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_URL, DEFAULT_SETTING_ADVERTISMENT_URL))
+ {
+ STRINGLIST* psl = SLNewList();
+ WCHAR* pszModUrlInd = NULL;
+ bFound = FALSE;
+
+ // Load url definition files
+ GetSpamDefinitionData(psl, SDID_URLTLDS);
+
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(psl); i++)
+ {
+ // Different formated check?
+ if (pszFormatedMsgText)
+ pszModUrlInd = InsertSubStrEveryNChars(SLGetItem(psl, i), 1, _T(" "), FALSE, FALSE); // check for words with spaces/linefeeds/slashes between each char
+
+ if (StrStrI(pszClearedMsgText, SLGetItem(psl, i)) || ((pszModUrlInd && pszFormatedMsgText) ? StrStrI(pszFormatedMsgText, pszModUrlInd) : FALSE) )
+ {
+ bFound = TRUE;
+ break;
+ }
+
+ if (pszModUrlInd)
+ {
+ mir_free(pszModUrlInd);
+ pszModUrlInd = NULL;
+ }
+ }
+
+ SLFreeList(psl);
+
+ if (bFound)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("Contains URL address"));
+ bKickOut = TRUE;
+ if (!bDoLog) goto AdvertismentResult;
+ }
+ }
+
+ // Message containing phone numbers?
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_PHONENUMBERS, DEFAULT_SETTING_ADVERTISMENT_PHONENUMBERS))
+ {
+ STRINGLIST* psl = SLNewList();
+ WCHAR* pszModContent;
+ bFound = FALSE;
+
+ // Load localized phonenumbers
+ GetSpamDefinitionData(psl, SDID_PHONENUMBERS);
+
+ pszModContent = mir_wstrdup(pszClearedMsgText);
+ if (pszModContent)
+ {
+ // Check for normal formated phonennumbers
+ {
+ // Remove spaces
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T(" "), NULL, TRUE, FALSE);
+ // Replace wrong slashs
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("\\"), _T("/"), TRUE, FALSE);
+
+ // Replace numbers
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("0"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("1"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("2"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("3"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("4"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("5"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("6"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("7"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("8"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("9"), _T("#"), TRUE, FALSE);
+
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(psl); i++)
+ if (StrStrI(pszModContent, SLGetItem(psl, i)))
+ {
+ bFound = TRUE;
+ break;
+ }
+ }
+
+ // Check different formated phone numbers
+ if (!bFound && pszFormatedMsgText)
+ {
+ WCHAR* pszNumberFormatedMsgText = mir_wstrdup(pszClearedMsgText);
+ if (pszNumberFormatedMsgText)
+ {
+ // -- Create number formated text
+ STRINGLIST* pslFormat = SLNewList();
+
+ // Some words may have special chars in between the chars
+ GetSpamDefinitionData(pslFormat, SDID_SPECIALCHARS_SEPARATORS);
+
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(pslFormat); i++)
+ ReplaceSubStringWithStringBuf(pszNumberFormatedMsgText, 0, SLGetItem(pslFormat, i), _T(" "), TRUE, FALSE);
+
+ SLClearList(pslFormat);
+
+ // Translate some chars
+ GetSpamDefinitionData(pslFormat, SDID_SPECIALCHARS_REPLACE_NUMBERS);
+ ReplaceSubStringWithStringMultipleBuf(pszNumberFormatedMsgText, 0, pslFormat, TRUE, FALSE);
+
+ SLFreeList(pslFormat);
+
+
+ //-- Check the text
+ // Remove spaces
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T(" "), NULL, TRUE, FALSE);
+ // Replace wrong slashs
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("\\"), _T("/"), TRUE, FALSE);
+
+ // Replace numbers
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("0"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("1"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("2"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("3"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("4"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("5"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("6"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("7"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("8"), _T("#"), TRUE, FALSE);
+ ReplaceSubStringWithStringBuf(pszModContent, 0, _T("9"), _T("#"), TRUE, FALSE);
+
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(psl); i++)
+ if (StrStrI(pszModContent, SLGetItem(psl, i)))
+ {
+ bFound = TRUE;
+ break;
+ }
+ }
+ }
+
+ mir_free(pszModContent);
+ }
+
+ SLFreeList(psl);
+
+ if (bFound)
+ {
+ if (bDoLog) SLAddItem(pslRecognition, TranslateW("Contains phone number"));
+ bKickOut = TRUE;
+ if (!bDoLog) goto AdvertismentResult;
+ }
+ }
+
+ // Message contains bad words and/or formated bad words?
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CUSTOMIZEDBADWORDS, (BYTE)TRUE) || DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DEFAULTBADWORDS, DEFAULT_SETTING_ADVERTISMENT_DEFAULTBADWORDS) )
+ {
+ STRINGLIST* psl = SLNewList();
+ unsigned int uFoundBadWords = 0;
+ unsigned int uFoundFormatedWords = 0;
+ BOOL bRegExpMalformed;
+ unsigned int uRegExpErrors;
+
+ // Load cutomized bad words if activated
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CUSTOMIZEDBADWORDS, (BYTE)TRUE) )
+ GetSpamDefinitionData(psl, SDID_CUSTOM_BADWORDS);
+
+ // Check customized bad words...
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(psl); i++)
+ if (RegExpExistsInString(pszClearedMsgText, SLGetItem(psl, i), FALSE, NULL))
+ uFoundBadWords++;
+
+ // Load all default bad words files if activated
+ SLClearList(psl);
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_DEFAULTBADWORDS, DEFAULT_SETTING_ADVERTISMENT_DEFAULTBADWORDS) )
+ GetSpamDefinitionData(psl, SDID_BADWORDS);
+
+ // Check default bad words...
+ uRegExpErrors = 0;
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(psl); i++)
+ {
+ if (RegExpExistsInString(pszClearedMsgText, SLGetItem(psl, i), FALSE, &bRegExpMalformed))
+ uFoundBadWords++;
+
+ if (bRegExpMalformed)
+ uRegExpErrors++;
+ }
+ if (uRegExpErrors > 0)
+ ShowInfoMessage(NIIF_WARNING, TranslateW("Spam Filter Warning"), TranslateW("The filter has detected %u malformed Regular Expression(s) in the default bad words!\r\n\r\nPlease update your installed Spam Definitions to the latest versions to resolve this problem.\r\nThis problem might also be caused by Spam Definitions that are not compatible with your system codepage."), MAX_INT_LENGTH, uRegExpErrors);
+
+ // Check different formated bad words...
+ if (pszFormatedMsgText)
+ {
+ WCHAR* pszDiffWord;
+
+ // AGAIN!: Load cutomized bad words again if activated
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CUSTOMIZEDBADWORDS, (BYTE)TRUE) )
+ GetSpamDefinitionData(psl, SDID_CUSTOM_BADWORDS);
+
+ // Check again for bad words
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(psl); i++)
+ {
+ pszDiffWord = InsertSubStrEveryNChars(SLGetItem(psl, i), 1, _T(" "), FALSE, FALSE); // check for words with spaces/linefeeds between each char
+
+ if (RegExpExistsInString(pszFormatedMsgText, SLGetItem(psl, i), FALSE, NULL))
+ {
+ uFoundFormatedWords++;
+
+ } else {
+ pszDiffWord = InsertSubStrEveryNChars(SLGetItem(psl, i), 1, _T(" "), FALSE, FALSE); // check for words with spaces/linefeeds between each char
+ if (pszDiffWord)
+ {
+ if (StrStrI(pszFormatedMsgText, pszDiffWord))
+ uFoundFormatedWords++;
+
+ mir_free(pszDiffWord);
+ }
+ }
+ }
+
+ // Calculate diff
+ uFoundFormatedWords = (uFoundFormatedWords > uFoundBadWords) ? uFoundFormatedWords = uFoundFormatedWords-uFoundBadWords : 0;
+ }
+
+ SLFreeList(psl);
+
+ // Enough bad words for spam found?
+ if (uFoundBadWords > DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDBADWORDS, DEFAULT_SETTING_ADVERTISMENT_ALLOWEDBADWORDS))
+ {
+ if (bDoLog) SLItemPrintf(pslRecognition, SLAddItem(pslRecognition, TranslateW("Contains %u bad phrase(s)/pattern(s)")) , MAX_INT_LENGTH, uFoundBadWords);
+ bKickOut = TRUE;
+ }
+
+ // Enough formated bad words found?
+ if (uFoundFormatedWords > DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDFORMATEDWORDS, DEFAULT_SETTING_ADVERTISMENT_ALLOWEDFORMATEDWORDS))
+ {
+ if (bDoLog) SLItemPrintf(pslRecognition, SLAddItem(pslRecognition, TranslateW("Contains %u differently formated bad phrase(s)/pattern(s)")), MAX_INT_LENGTH, uFoundFormatedWords);
+ bKickOut = TRUE;
+ }
+
+ if (bKickOut && !bDoLog) goto AdvertismentResult;
+ }
+
+ // Good words check
+ if (pszClearedMsgText && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_CUSTOMIZEDGOODWORDS, (BYTE)TRUE) )
+ {
+ STRINGLIST* psl = SLNewList();
+ unsigned int uFoundGoodWords = 0;
+
+ // Load customized good words
+ GetSpamDefinitionData(psl, SDID_CUSTOM_GOODWORDS);
+
+ for (i=SL_MIN_POS; i<=SLGetMaxPos(psl); i++)
+ if (RegExpExistsInString(pszClearedMsgText, SLGetItem(psl, i), FALSE, NULL))
+ uFoundGoodWords++;
+
+ SLFreeList(psl);
+
+ // Enough good words for non-spam found?
+ if (uFoundGoodWords > DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS, DEFAULT_SETTING_ADVERTISMENT_ALLOWEDGOODWORDS))
+ {
+ if (bDoLog) SLItemPrintf(pslRecognition, SLAddItem(pslRecognition, TranslateW("Contains %u good phrase(s)/pattern(s)")), MAX_INT_LENGTH, uFoundGoodWords);
+ bKickOut = FALSE;
+ if (!bDoLog) goto AdvertismentResult;
+ }
+ }
+
+
+AdvertismentResult:
+
+ if (pszClearedMsgText) mir_free(pszClearedMsgText);
+ if (pszFormatedMsgText) mir_free(pszFormatedMsgText);
+
+ #if defined(UNICODE)
+ if (!(pscd->dwFlags&SCDF_UNICODE) && pszNormalMsgText)
+ mir_free(pszNormalMsgText);
+ #else
+ if ((pscd->dwFlags&SCDF_UNICODE) && pszNormalMsgText)
+ mir_free(pszNormalMsgText);
+ #endif
+
+
+ // Kick out spam
+ if ( bKickOut && (NotifyEventHooks(hEventOkToSpamDetection, (WPARAM)pscd, (LPARAM)SFT_ADVERTISMENT_FILTER) == 0))
+ {
+ DWORD dwReturn = SFF_ISSPAM | ((DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_BEHAVIOUR, DEFAULT_SETTING_BEHAVIOUR) == 1) ? SFF_MARKREAD : SFF_DELETE);
+
+ if (!(pscd->dwFlags&SCDF_NO_NOTIFY))
+ {
+ // Play sound file
+ SkinPlaySound(DB_SOUND_ADVERTISMENT_SETTING);
+
+ // Show Popup
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_POPUP, DEFAULT_SETTING_POPUP))
+ {
+ WCHAR* pszDisplayName = SCD_GetContactCustomDisplayName(pscd);
+
+ ShowSpamPopup(pscd->pszMsgTypeSection, pscd->pszMsgTypeName, pszDisplayName, (pscd->dwFlags&SCDF_NO_CONTACT)?NULL:pscd->hContact, SFT_ADVERTISMENT_FILTER);
+ if (pszDisplayName) mir_free(pszDisplayName);
+ }
+
+ // Hand out event
+ NotifyEventHooks(hEventSpamReceived, (WPARAM)pscd, (LPARAM)SFT_ADVERTISMENT_FILTER);
+
+ // Trigger Plugin Support
+ TriggerIncomingSpam();
+ }
+
+ // Append to log file
+ if (bDoLog)
+ {
+ WCHAR* pszUserName = SCD_GetContactName(pscd, TRUE);
+
+ WriteToLogFile(pscd->pszMsgTypeSection, pscd->pszMsgTypeName, pszUserName, pszNormalMsgText, pslRecognition, dwReturn);
+ if (pszUserName) mir_free(pszUserName);
+
+ SLFreeList(pslRecognition);
+ }
+
+ // Add user to ignore list
+ if (!(pscd->dwFlags&SCDF_NO_AUTOIGNORE) && DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_AUTOADDSPAMMERS, DEFAULT_SETTING_AUTOADDSPAMMERS) )
+ {
+ if (IncreasePreSpammerCount(pscd) > DBGetContactSettingWord(NULL, DB_MODULE_NAME, DB_SETTING_ALLOWEDSPAMMESSAGES, DEFAULT_SETTING_ALLOWEDSPAMMESSAGES))
+ {
+ RemovePreSpammerCount(pscd);
+ dwReturn |= SFF_IGNORE;
+ }
+ }
+
+ return dwReturn;
+
+ } else {
+
+ if (bDoLog)
+ SLFreeList(pslRecognition);
+
+ //if (!(pscd->dwFlags&SCDF_NO_AUTOIGNORE))
+ // RemovePreSpammerCount(scd); // do not remove count here! (will cause problems)
+
+ // Message is normal
+ return SFF_ISNORMAL;
+ }
+
+ }
+}
+
+
+// --------------------------------
+
+int GetMsgTypeID(const char* pszSection, const char* pszName)
+{
+ unsigned int u;
+
+ EnterCriticalSection(&csMsgTypes); // thread safety
+
+ if (pszSection && pszName && pamtdMsgTypes)
+ for (u=0; u<uMsgTypesCount; u++)
+ if (StrCmpA(pamtdMsgTypes[u].pszSection, pszSection) == 0)
+ if (StrCmpA(pamtdMsgTypes[u].pszName, pszName) == 0)
+ {
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return u;
+ }
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return -1;
+}
+
+
+char* GetMsgTypeSettingName(const char* pszSection, const char* pszName)
+{
+ // returned char* needs to be freed
+ char* pszReturn;
+ int iSize;
+
+ if (!pszSection || !pszName) return NULL;
+
+ iSize = lstrlenA(pszSection)+1+lstrlenA(pszName)+1;
+ pszReturn = (char*)mir_alloc(iSize*sizeof(char));
+
+ if (pszReturn)
+ mir_snprintf(pszReturn, iSize, "%s_%s", pszSection, pszName);
+
+ return pszReturn;
+}
+
+
+BOOL IsOnlyOneMsgTypeSectionRegistered(void)
+{
+ unsigned int u;
+
+ EnterCriticalSection(&csMsgTypes); // thread safety
+
+ if (pamtdMsgTypes)
+ for (u=0; u<(uMsgTypesCount-1); u++)
+ if (StrCmpA(pamtdMsgTypes[u].pszSection, pamtdMsgTypes[u+1].pszSection) != 0)
+ {
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return FALSE;
+ }
+
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ return TRUE;
+}
+
+
+void NotifyModuleLoaded(void)
+{
+ // Hand out event (spam filter functionality now fully available)
+ NotifyEventHooks(hEventModuleLoaded, 0, 0);
+}
+
+void NotifySpammerStateChanged(HANDLE hContact, BOOL bNewIsSpammer)
+{
+ // Hand out event (spammer state setting was changed)
+ NotifyEventHooks(hEventContactSpammerStateChanged, (WPARAM)hContact, (LPARAM)bNewIsSpammer);
+}
+
+// --------------------------------
+
+
+void InitServices(void)
+{
+ // Init message type list
+ uMsgTypesCount = 0;
+ pamtdMsgTypes = NULL;
+ InitializeCriticalSection(&csMsgTypes); // thread safety
+
+ hEventPreRegisterMessageType = CreateHookableEvent(ME_SPAMFILTER_PREREGISTERMESSAGETYPE);
+ hEventOkToSpamCheck = CreateHookableEvent(ME_SPAMFILTER_OKTOSPAMCHECK);
+ hEventOkToSpamDetection = CreateHookableEvent(ME_SPAMFILTER_OKTOSPAMDETECTION);
+ hEventSpamReceived = CreateHookableEvent(ME_SPAMFILTER_SPAMRECEIVED);
+ hEventModuleLoaded = CreateHookableEvent(ME_SPAMFILTER_MODULELOADED);
+ hEventContactSpammerStateChanged = CreateHookableEvent(ME_SPAMFILTER_CONTACT_SPAMMERSTATECHANGED);
+
+ hServices[0] = CreateServiceFunction(MS_SPAMFILTER_ADVERTISMENTCHECK, ServiceAdvertismentCheck);
+ hServices[1] = CreateServiceFunction(MS_SPAMFILTER_DISLIKEDMESSAGESCHECK, ServiceDislikedMessageCheck);
+ hServices[2] = CreateServiceFunction(MS_SPAMFILTER_ROBOTCHECK, ServiceRobotCheck);
+
+ hServices[3] = CreateServiceFunction(MS_SPAMFILTER_REGISTERMESSAGETYPE, ServiceRegisterMessageType);
+ hServices[4] = CreateServiceFunction(MS_SPAMFILTER_REMOVEMESSAGETYPE, ServiceRemoveMessageType);
+ hServices[5] = CreateServiceFunction(MS_SPAMFILTER_ISMESSAGETYPEACTIVATED, ServiceIsMessageTypeActivated);
+
+ hServices[6] = CreateServiceFunction(MS_SPAMFILTER_GETHANDLE, ServiceGetHandle);
+ hServices[7] = CreateServiceFunction(MS_SPAMFILTER_SHOWERROR, ServiceShowError);
+ hServices[8] = CreateServiceFunction(MS_SPAMFILTER_SHOWFILTERDIALOG, ServiceShowFilterDialog);
+
+ hServices[9] = CreateServiceFunction(MS_SPAMFILTER_CHANGEFILTERACTIVATION, ServiceChangeFilterActivation);
+ hServices[10] = CreateServiceFunction(MS_SPAMFILTER_GETSPAMCHECKINFO, ServiceGetSpamCheckInfo);
+
+ hServices[11] = CreateServiceFunction(MS_SPAMFILTER_RESETAUTOIGNORE, ServiceResetAutoIgnore);
+
+ hServices[12] = CreateServiceFunction(MS_SPAMFILTER_CONTACT_SETASSPAMMER, ServiceContactSetAsSpammer);
+ hServices[13] = CreateServiceFunction(MS_SPAMFILTER_CONTACT_SHOWSETASSPAMMERDIALOG, ServiceContactShowSetAsSpammerDialog);
+ hServices[14] = CreateServiceFunction(MS_SPAMFILTER_CONTACT_ISSPAMMER, ServiceContactIsSpammer);
+ hServices[15] = CreateServiceFunction(MS_SPAMFILTER_CONTACT_UNSETSPAMMER, ServiceContactUnSetSpammer);
+
+ hServices[16] = CreateServiceFunction(MS_SPAMFILTER_COPYSPAMCHECKDATA, ServiceCopySpamCheckData);
+ hServices[17] = CreateServiceFunction(MS_SPAMFILTER_FREESPAMCHECKDATA, ServiceFreeSpamCheckData);
+
+ // Backward compatibility (for v1.0.1.0)
+ hServices[18] = CreateServiceFunction(MS_SPAMFILTER_SETSPAMMANUALLY, ServiceContactShowSetAsSpammerDialog);
+}
+
+void UninitServices(void)
+{
+ // Destroy services
+ int i;
+ for (i=0; i<ARRAYSIZE(hServices); i++)
+ if (hServices[i]) DestroyServiceFunction(hServices[i]);
+
+ if (hEventPreRegisterMessageType)
+ DestroyHookableEvent(hEventPreRegisterMessageType);
+ if (hEventOkToSpamCheck)
+ DestroyHookableEvent(hEventOkToSpamCheck);
+ if (hEventOkToSpamDetection)
+ DestroyHookableEvent(hEventOkToSpamDetection);
+ if (hEventSpamReceived)
+ DestroyHookableEvent(hEventSpamReceived);
+ if (hEventModuleLoaded)
+ DestroyHookableEvent(hEventModuleLoaded);
+ if (hEventContactSpammerStateChanged)
+ DestroyHookableEvent(hEventContactSpammerStateChanged);
+
+ // Unload message type list
+ EnterCriticalSection(&csMsgTypes); // thread safety (waiting for operations to fisnish)
+ if (pamtdMsgTypes)
+ {
+ unsigned int u;
+ for (u=0; u<uMsgTypesCount; u++)
+ {
+ if (pamtdMsgTypes[u].pszSection) mir_free(pamtdMsgTypes[u].pszSection);
+ if (pamtdMsgTypes[u].pszSectionDescription) mir_free(pamtdMsgTypes[u].pszSectionDescription);
+ if (pamtdMsgTypes[u].pszName) mir_free(pamtdMsgTypes[u].pszName);
+ if (pamtdMsgTypes[u].pszDescription) mir_free(pamtdMsgTypes[u].pszDescription);
+ if (pamtdMsgTypes[u].hIcon && ((int)pamtdMsgTypes[u].hIcon > 0)) DestroyIcon(pamtdMsgTypes[u].hIcon);
+ if (pamtdMsgTypes[u].hSectionIcon) DestroyIcon(pamtdMsgTypes[u].hSectionIcon);
+ }
+ mir_free(pamtdMsgTypes);
+ }
+ uMsgTypesCount = 0;
+ pamtdMsgTypes = NULL;
+ LeaveCriticalSection(&csMsgTypes); // thread safety
+ DeleteCriticalSection(&csMsgTypes); // thread safety
+} \ No newline at end of file
diff --git a/spamfilter/services.h b/spamfilter/services.h
new file mode 100644
index 0000000..d5ad3f7
--- /dev/null
+++ b/spamfilter/services.h
@@ -0,0 +1,39 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_SERVICES_H__
+#define SF_SERVICES_H__ 1
+
+
+// -- Protoypes
+int GetMsgTypeID(const char* pszSection, const char* pszName);
+BOOL IsOnlyOneMsgTypeSectionRegistered(void);
+char* GetMsgTypeSettingName(const char* pszSection, const char* pszName);
+
+int QueryAutoIgnore(BOOL bReset);
+void NotifyModuleLoaded(void);
+void NotifySpammerStateChanged(HANDLE hContact, BOOL bNewIsSpammer);
+
+void InitServices(void);
+void UninitServices(void);
+
+
+#endif // SF_SERVICES_H__
diff --git a/spamfilter/spamcheck.c b/spamfilter/spamcheck.c
new file mode 100644
index 0000000..7078941
--- /dev/null
+++ b/spamfilter/spamcheck.c
@@ -0,0 +1,683 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -- Variables: Events/Hooks/Services
+HANDLE hHookDbContactAdded = NULL;
+HANDLE hHookDbEventFilterAdd = NULL;
+
+
+// -----------------------------------------
+
+static int SpamCheckProtoRecv(WPARAM wParam, LPARAM lParam)
+{
+ CCSDATA* pccsd = (CCSDATA*)lParam;
+ BOOL bNotOnList;
+ SPAMCHECKDATA scd;
+ DWORD dwSpamResult;
+ int iReturn;
+
+ ZeroMemory(&scd, sizeof(scd));
+ Netlib_Logf(NULL, "Spam Filter: Catching MS_PROTO_CHAINRECV");
+
+ // Message already marked as read (skipping spam check)
+ if (((PROTORECVEVENT*)pccsd->lParam)->flags&PREF_CREATEREAD)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Already marked as read (skipping spam check)");
+ return CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+ }
+
+ if (StrCmpA(pccsd->szProtoService, PSR_URL) == 0)
+ {
+ PROTORECVEVENT* ppre = ((PROTORECVEVENT*)pccsd->lParam);
+
+ scd.hContact = pccsd->hContact;
+
+ // Interpret blob (ignore url itself, only description)
+ scd.pszMsgText = ppre->szMessage;
+
+ scd.pszMsgTypeName = SFMT_URL;
+
+ } else if (StrCmpA(pccsd->szProtoService, PSR_AUTH) == 0)
+ {
+ PROTORECVEVENT* ppre = ((PROTORECVEVENT*)pccsd->lParam);
+
+ scd.hContact = (*((PHANDLE)(ppre->szMessage+sizeof(DWORD))));
+
+ // Check if stored hContact is still valid
+ if (!CallService(MS_DB_CONTACT_IS, (WPARAM)scd.hContact, 0))
+ {
+ Netlib_Logf(NULL, "Spam Filter: Contact invalid (skipping spam check)");
+
+ // Continue the chain
+ return CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+ }
+
+ // Interpret blob (only use reason text)
+ scd.pszMsgText = (char*)(ppre->szMessage+sizeof(DWORD)+sizeof(HANDLE)); // uin, hcontact, nick
+ scd.pszMsgText = scd.pszMsgText+(lstrlenA(scd.pszMsgText)+1)*sizeof(char); // first
+ scd.pszMsgText = scd.pszMsgText+(lstrlenA(scd.pszMsgText)+1)*sizeof(char); // last
+ scd.pszMsgText = scd.pszMsgText+(lstrlenA(scd.pszMsgText)+1)*sizeof(char); // email
+ scd.pszMsgText = scd.pszMsgText+(lstrlenA(scd.pszMsgText)+1)*sizeof(char); // reason
+
+ // If no reason text is given deactivate the text checking
+ if (strlen(scd.pszMsgText) <= 0)
+ scd.pszMsgText = NULL;
+
+ scd.pszMsgTypeName = SFMT_AUTHREQUEST;
+
+ } else if (StrCmpA(pccsd->szProtoService, PSR_MESSAGE) == 0)
+ {
+ PROTORECVEVENT* ppre = ((PROTORECVEVENT*)pccsd->lParam);
+
+ scd.hContact = pccsd->hContact;
+
+ // Interpret blob (use all message)
+ if (ppre->flags&PREF_UNICODE) {
+ scd.dwFlags |= SCDF_UNICODE;
+ scd.pszMsgText = ppre->szMessage+lstrlenA(ppre->szMessage)+1;
+ } else {
+ scd.pszMsgText = ppre->szMessage;
+ }
+
+ scd.pszMsgTypeName = SFMT_MESSAGE;
+
+ } else if (StrCmpA(pccsd->szProtoService, PSR_FILE) == 0)
+ {
+ PROTORECVFILE* pprf = ((PROTORECVFILE*)pccsd->lParam);
+
+ scd.hContact = pccsd->hContact;
+
+ // Interpret blob (only use description text)
+ scd.pszMsgText = pprf->szDescription;
+
+ scd.pszMsgTypeName = SFMT_FILE;
+
+ } else if (StrCmpA(pccsd->szProtoService, PSR_CONTACTS) == 0)
+ {
+ scd.hContact = pccsd->hContact;
+
+ // Does not have any text to filter only contact names: nick, first, last, email
+ scd.pszMsgText = NULL;
+
+ scd.pszMsgTypeName = SFMT_CONTACTS;
+
+ } else {
+ Netlib_Logf(NULL, "Spam Filter: Format unknown (skipping spam check)");
+
+ // Continue the chain
+ return CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+ }
+
+ scd.cbSize = sizeof(scd);
+ scd.pszMsgTypeSection = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)scd.hContact, 0);
+
+ // If message is from "Unknown Contact" (very unlikely) -> MS_PROTO_GETCONTACTBASEPROTO returns NULL
+ if (!scd.pszMsgTypeSection)
+ {
+ Netlib_Logf(NULL, "Spam Filter Result: No base protocol (skipping spam check)");
+ return CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+ }
+
+ Netlib_Logf(NULL, "Spam Filter: Type = %s_%s", scd.pszMsgTypeSection, scd.pszMsgTypeName);
+
+ // Result check
+ bNotOnList = (DBGetContactSettingByte(scd.hContact, "CList", "NotOnList", 0) ? TRUE : FALSE);
+ if (bNotOnList)
+ {
+ // If message is from test passed contact
+ if(DBGetContactSettingByte(scd.hContact, DB_MODULE_NAME, DB_SETTING_ROBOT_TESTPASSED, (BYTE)FALSE))
+ {
+ Netlib_Logf(NULL, "Spam Filter: Result = Already passed robot test (no spam)");
+ return CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+ }
+
+ Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_ADVERTISMENTCHECK");
+ dwSpamResult = CallService(MS_SPAMFILTER_ADVERTISMENTCHECK, (WPARAM)&scd, 0);
+ if (dwSpamResult&SFF_ISNORMAL)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_ROBOTCHECK");
+ dwSpamResult = CallService(MS_SPAMFILTER_ROBOTCHECK, (WPARAM)&scd, 0);
+ }
+
+ } else {
+ Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_DISLIKEDMESSAGESCHECK");
+ dwSpamResult = CallService(MS_SPAMFILTER_DISLIKEDMESSAGESCHECK, (WPARAM)&scd, 0);
+ }
+
+ if (dwSpamResult&SFF_ISNORMAL) {
+ Netlib_Logf(NULL, "Spam Filter: Recognition = No-Spam");
+ } else {
+ Netlib_Logf(NULL, "Spam Filter: Recognition = Spam!");
+ }
+
+
+ // Message handling
+ if (dwSpamResult&SFF_DELETE)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Result = Event suppressed (stopping recv chain)");
+
+ // Cache chain (see reply messages)
+ iReturn = 1; // Stop processing the chain
+
+ } else if (dwSpamResult&SFF_MARKREAD)
+ {
+ (((PROTORECVEVENT*)pccsd->lParam)->flags) |= PREF_CREATEREAD;
+ Netlib_Logf(NULL, "Spam Filter: Result = PREF_CREATEREAD flag set (continuing recv chain)");
+
+ // Cache chain (see reply messages)
+ iReturn = CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+
+ } else {
+ Netlib_Logf(NULL, "Spam Filter: Result = Event accepted (continuing recv chain)");
+
+ // Cache chain (see reply messages)
+ iReturn = CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+ }
+
+
+ // IMPORTANT!: The following is done *after* the call to MS_PROTO_CHAINRECV so that
+ // the protocol has had the chance to add the recved event to the db.
+ // The events to be sent are then ordered correctly in db.
+
+ // The contact might have been deleted etc. by another filter protcol in recv chain
+ // -> just a safety check
+ if (!CallService(MS_DB_CONTACT_IS, (WPARAM)scd.hContact, 0))
+ {
+ Netlib_Logf(NULL, "Spam Filter: Contact deleted by other filter (stopping processing)");
+ return iReturn;
+ }
+
+ // Remember test passed (robot filter)
+ if (dwSpamResult&SFF_TESTPASSED)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_TESTPASSED (restoring contact)");
+
+ // Mark contact as answered
+ DBWriteContactSettingByte(scd.hContact, DB_MODULE_NAME, DB_SETTING_ROBOT_TESTPASSED, (BYTE)TRUE);
+ DBDeleteContactSetting(scd.hContact, "CList", "Hidden");
+
+ // Show the old events again
+ MarkAllContactEventsUnRead(scd.hContact);
+ }
+
+ // Contact handling
+ if (dwSpamResult&SFF_IGNORE)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_IGNORE (ignoring contact)");
+ AddContactToIgnoreList(scd.hContact, (dwSpamResult&SFF_MARKREAD), TRUE, FALSE);
+ }
+ if (dwSpamResult&SFF_HIDE)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_HIDE (hiding contact)");
+ DBWriteContactSettingByte(scd.hContact, "CList", "Hidden", 1);
+ }
+
+ // Reply messages
+ if (dwSpamResult&SFF_SENDMSG_NOTIFY)
+ {
+ WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_NOTIFY, (LPARAM)&scd);
+ BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_NOTIFY);
+
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_NOTIFY (sending reply)");
+ if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory))
+ CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd);
+
+ if (pszMsgText) mir_free(pszMsgText);
+ }
+
+ if (dwSpamResult&SFF_SENDMSG_INSTRUCTION)
+ {
+ WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_INSTRUCTION, (LPARAM)&scd);
+ BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_INSTRUCTION);
+
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_INSTRUCTION (sending reply)");
+ if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory))
+ CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd);
+
+ if (pszMsgText) mir_free(pszMsgText);
+ }
+
+ if (dwSpamResult&SFF_SENDMSG_CONFIRMATION)
+ {
+ WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_CONFIRMATION, (LPARAM)&scd);
+ BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_CONFIRMATION);
+
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_CONFIRMATION (sending reply)");
+ if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory))
+ CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd);
+
+ if (pszMsgText) mir_free(pszMsgText);
+ }
+
+
+ // Contact handling
+ if ((dwSpamResult&SFF_DELETE) && !(dwSpamResult&SFF_IGNORE))
+ {
+ if (bNotOnList && !(dwSpamResult&SFF_TESTPASSED) && (CallService(MS_DB_EVENT_GETCOUNT, (WPARAM)scd.hContact, 0) == 0) )
+ {
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_DELETE (deleting contact)");
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)scd.hContact, 0);
+ }
+ }
+
+ // return queue value
+ return iReturn;
+}
+
+
+static int DbEventFilterAdd(WPARAM wParam, LPARAM lParam)
+{
+ DBEVENTINFO* pdbei = (DBEVENTINFO*)lParam;
+ HANDLE hContact = (HANDLE)wParam;
+
+ // Filtring events that don't have any protorecv service
+ // -> hacky workaround: filtering db adds
+
+ // Only filter incoming events
+ if (!pdbei || !pdbei->pBlob ||
+ (pdbei->flags&DBEF_SENT) || (pdbei->flags&DBEF_READ) )
+ return 0; // continue adding the event
+
+
+ // Only filter following events
+ if ( (pdbei->eventType != EVENTTYPE_ADDED) &&
+ (pdbei->eventType != ICQEVENTTYPE_EMAILEXPRESS) &&
+ (pdbei->eventType != ICQEVENTTYPE_WEBPAGER) &&
+ (pdbei->eventType != ICQEVENTTYPE_SMS) )
+ return 0;
+
+ {
+ BOOL bNotOnList;
+ BOOL bAlsoDoRobotCheck;
+ SPAMCHECKDATA scd;
+ DWORD dwSpamResult;
+
+ Netlib_Logf(NULL, "Spam Filter: Catching ME_DB_EVENT_FILTER_ADD");
+
+ // Collecting data out of blob
+ ZeroMemory(&scd, sizeof(scd));
+ switch (pdbei->eventType)
+ {
+ case EVENTTYPE_ADDED:
+ {
+ // Blob: uin(DWORD), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ)
+ if (hContact)
+ scd.hContact = hContact;
+ else
+ scd.hContact = *((PHANDLE)(pdbei->pBlob+sizeof(DWORD)));
+
+ // Check if stored hContact is still valid
+ if (!CallService(MS_DB_CONTACT_IS, (WPARAM)scd.hContact, 0))
+ {
+ Netlib_Logf(NULL, "Spam Filter: Contact invalid (skipping spam check)");
+
+ return 0; // continue adding the event
+ }
+
+ // No text to be checked available
+ scd.pszMsgText = NULL;
+
+ scd.pszMsgTypeName = SFMT_ADDED;
+
+ bAlsoDoRobotCheck = TRUE;
+ break;
+ }
+
+ case ICQEVENTTYPE_EMAILEXPRESS: // (ICQ only)
+ {
+ // Blob: text, usually "Sender IP: xxx.xxx.xxx.xxx\r\n%s"(ASCIIZ), name(ASCIIZ), email(ASCIIZ)
+
+ // Interpret blob (get sender name)
+ if (hContact) {
+ scd.hContact = hContact;
+ } else {
+ scd.pszUserName = (char*)pdbei->pBlob; // text
+ scd.pszUserName = scd.pszUserName+lstrlenA(scd.pszUserName)+1; // name
+
+ scd.dwFlags = SCDF_NO_CONTACT|SCDF_NO_AUTOIGNORE;
+ }
+
+ // Interpret blob (only use message text)
+ scd.pszMsgText = (char*)pdbei->pBlob; // text
+
+ scd.pszMsgTypeName = "EmailExpress";
+
+ bAlsoDoRobotCheck = FALSE;
+ break;
+ }
+
+ case ICQEVENTTYPE_WEBPAGER: // (ICQ only)
+ {
+ // Blob: text, usually "Subject: %s\r\n%s"(ASCIIZ), name(ASCIIZ), email(ASCIIZ)
+
+ if (hContact) {
+ scd.hContact = hContact;
+ } else {
+ // Interpret blob (get sender name)
+ scd.pszUserName = (char*)pdbei->pBlob; // text
+ scd.pszUserName = scd.pszUserName+lstrlenA(scd.pszUserName)+1; // name
+
+ scd.dwFlags = SCDF_NO_CONTACT|SCDF_NO_AUTOIGNORE;
+ }
+
+ // Interpret blob (only use message text)
+ scd.pszMsgText = (char*)pdbei->pBlob; // text
+
+ scd.pszMsgTypeName = "WebPager";
+
+ bAlsoDoRobotCheck = FALSE;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ scd.pszMsgTypeSection = pdbei->szModule;
+ if (!scd.pszMsgTypeSection)
+ {
+ Netlib_Logf(NULL, "Spam Filter: No base protocol (skipping spam check)");
+ return 0; // continue adding the event
+ }
+
+ scd.cbSize = sizeof(scd);
+
+ Netlib_Logf(NULL, "Spam Filter: Type = %s_%s", scd.pszMsgTypeSection, scd.pszMsgTypeName);
+
+ // Result check
+ bNotOnList = !bAlsoDoRobotCheck || (DBGetContactSettingByte(scd.hContact, "CList", "NotOnList", 0) ? TRUE : FALSE);
+ if (bNotOnList)
+ {
+ if (bAlsoDoRobotCheck)
+ {
+ // If message is from test passed contact
+ if(DBGetContactSettingByte(scd.hContact, DB_MODULE_NAME, DB_SETTING_ROBOT_TESTPASSED, (BYTE)FALSE))
+ {
+ Netlib_Logf(NULL, "Spam Filter: Result = Already passed robot test (no spam)");
+ return 0; // continue adding the event
+ }
+ }
+
+ Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_ADVERTISMENTCHECK");
+ dwSpamResult = CallService(MS_SPAMFILTER_ADVERTISMENTCHECK, (WPARAM)&scd, 0);
+ if (dwSpamResult&SFF_ISNORMAL)
+ {
+ if (bAlsoDoRobotCheck)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_ROBOTCHECK");
+ dwSpamResult = CallService(MS_SPAMFILTER_ROBOTCHECK, (WPARAM)&scd, 0);
+ }
+ }
+
+ } else {
+ Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_DISLIKEDMESSAGESCHECK");
+ dwSpamResult = CallService(MS_SPAMFILTER_DISLIKEDMESSAGESCHECK, (WPARAM)&scd, 0);
+ }
+
+ if (dwSpamResult&SFF_ISNORMAL) {
+ Netlib_Logf(NULL, "Spam Filter: Recognition = No-Spam");
+ } else {
+ Netlib_Logf(NULL, "Spam Filter: Recognition = Spam!");
+ }
+
+ // Remember test passed (robot filter)
+ if (dwSpamResult&SFF_TESTPASSED)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_TESTPASSED (restoring contact)");
+
+ // Mark contact as answered
+ DBWriteContactSettingByte(scd.hContact, DB_MODULE_NAME, DB_SETTING_ROBOT_TESTPASSED, (BYTE)TRUE);
+ DBDeleteContactSetting(scd.hContact, "CList", "Hidden");
+
+ // Show the old events again
+ MarkAllContactEventsUnRead(scd.hContact);
+ }
+
+ // Contact handling
+ if (dwSpamResult&SFF_IGNORE)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_IGNORE (ignoring contact)");
+ AddContactToIgnoreList(scd.hContact, (dwSpamResult&SFF_MARKREAD), TRUE, FALSE);
+ }
+ if (dwSpamResult&SFF_HIDE)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_HIDE (hiding contact)");
+ DBWriteContactSettingByte(scd.hContact, "CList", "Hidden", 1);
+ }
+
+ // Reply messages
+ if (dwSpamResult&SFF_SENDMSG_NOTIFY)
+ {
+ WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_NOTIFY, (LPARAM)&scd);
+ BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_NOTIFY);
+
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_NOTIFY (sending reply)");
+ if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory))
+ CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd);
+
+ if (pszMsgText) mir_free(pszMsgText);
+ }
+
+ if (dwSpamResult&SFF_SENDMSG_INSTRUCTION)
+ {
+ WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_INSTRUCTION, (LPARAM)&scd);
+ BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_INSTRUCTION);
+
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_INSTRUCTION (sending reply)");
+ if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory))
+ CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd);
+
+ if (pszMsgText) mir_free(pszMsgText);
+ }
+
+ if (dwSpamResult&SFF_SENDMSG_CONFIRMATION)
+ {
+ WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_CONFIRMATION, (LPARAM)&scd);
+ BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_CONFIRMATION);
+
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_CONFIRMATION (sending reply)");
+ if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory))
+ CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd);
+
+ if (pszMsgText) mir_free(pszMsgText);
+ }
+
+ // Contact handling
+ if ((dwSpamResult&SFF_DELETE) && !(dwSpamResult&SFF_IGNORE))
+ {
+ if (bNotOnList && !(dwSpamResult&SFF_TESTPASSED) && (CallService(MS_DB_EVENT_GETCOUNT, (WPARAM)scd.hContact, 0) == 0) )
+ {
+ Netlib_Logf(NULL, "Spam Filter: Action = SFF_DELETE (deleting contact)");
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)scd.hContact, 0);
+ }
+ }
+
+ // Message handling
+ if (dwSpamResult&SFF_DELETE)
+ {
+ Netlib_Logf(NULL, "Spam Filter: Result = Event add prevented");
+ return 1; // Stop adding the event
+
+ } else if (dwSpamResult&SFF_MARKREAD)
+ {
+ pdbei->flags |= DBEF_READ;
+ Netlib_Logf(NULL, "Spam Filter: Result = DBEF_READ flag set (continuing add)");
+ return 0;
+
+ } else {
+ Netlib_Logf(NULL, "Spam Filter: Result = Event accepted (continuing add)");
+ return 0;
+ }
+ }
+}
+
+
+
+// ----------------------------------
+
+
+static int SpamCheckProtoGetCaps(WPARAM wParam, LPARAM lParam)
+{
+ // Non-network-access modules should return flags
+ // to represent the things they actually actively use
+
+ switch (wParam)
+ {
+ case PFLAGNUM_1:
+ return PF1_IMSEND|PF1_IMRECV|PF1_URLRECV|PF1_FILERECV|PF1_CONTACTRECV|PF1_AUTHREQ|PF1_ADDED;
+ default:
+ return 0;
+ }
+}
+
+
+#if defined(UNICODE)
+static int SpamCheckProtoGetNameW(WPARAM wParam, LPARAM lParam)
+{
+ int cchName = (int)wParam;
+ WCHAR* pszName = (WCHAR*)lParam;
+
+ if (pszName && (cchName > 0)) {
+ mir_sntprintf(pszName, cchName, _T("%s"), TranslateT("Spam Filter"));
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+
+static int SpamCheckProtoGetName(WPARAM wParam, LPARAM lParam)
+{
+ int cchName = (int)wParam;
+ char* pszName = (char*)lParam;
+
+ if (pszName && (cchName > 0))
+ {
+ mir_snprintf(pszName, cchName, "%s", Translate("Spam Filter"));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int DbContactAdded(WPARAM wParam, LPARAM lParam)
+{
+ // Error here is very very unlikely
+ if (CallService(MS_PROTO_ADDTOCONTACT, wParam, (LPARAM)DB_MODULE_NAME) == 0)
+ Netlib_Logf(NULL, "Spam Filter: Protocol filter attached to contact \"%s\" (%i)", (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)wParam, (LPARAM)GCDNF_UNICODE), wParam);
+ else
+ CallService(MS_SPAMFILTER_SHOWERROR, SFSE_CRITICAL_ERROR, 0);
+
+ return 0;
+}
+
+
+// ----------------------------------
+
+
+void AssignAllContacts(void)
+{
+ HANDLE hContact;
+
+ // MS_PROTO_ADDTOCONTACT needs to be call in AllModulesLoaded!
+ // Bug-Tracker #787:
+ // If MS_PROTO_ADDTOCONTACT is called when some plugin mentioned
+ // in modules chain isn't loaded, the chain will be sorted incorrectly,
+ // because the call to Proto_IsProtocolLoaded in service Proto_AddToContact
+ // returns NULL pointer to PROTOCOLDESCRIPTOR and the service can't determine the order.
+
+ for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); hContact; hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
+ if (!CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)hContact, (LPARAM)DB_MODULE_NAME))
+ {
+ if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)DB_MODULE_NAME) == 0)
+ Netlib_Logf(NULL, "Spam Filter: Protocol filter attached to contact \"%s\" (%i)", (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE), hContact);
+ else
+ CallService(MS_SPAMFILTER_SHOWERROR, SFSE_CRITICAL_ERROR, 0);
+ }
+}
+
+
+void InitSpamCheck(void)
+{
+ BOOL bSuccess;
+ PROTOCOLDESCRIPTOR pd;
+
+ ZeroMemory(&pd, sizeof(pd));
+ pd.cbSize = sizeof(pd);
+ pd.szName = DB_MODULE_NAME;
+ pd.type = PROTOTYPE_IGNORE+100; // Ignore module = PROTOTYPE_IGNORE (old: PROTOTYPE_FILTER-100)
+
+ if (CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM)&pd) == 0)
+ {
+ bSuccess = TRUE;
+
+ // General protocol functions
+ CreateProtoServiceFunction(pd.szName, PS_GETNAME, SpamCheckProtoGetName);
+ CreateProtoServiceFunction(pd.szName, PS_GETCAPS, SpamCheckProtoGetCaps);
+ #if defined(UNICODE)
+ CreateProtoServiceFunction(pd.szName, PS_GETNAME"W", SpamCheckProtoGetNameW);
+ #endif
+
+ // Recv chain functions
+ if (!CreateProtoServiceFunction(pd.szName, PSR_AUTH, SpamCheckProtoRecv))
+ bSuccess = FALSE;
+ if (!CreateProtoServiceFunction(pd.szName, PSR_MESSAGE, SpamCheckProtoRecv))
+ bSuccess = FALSE;
+ if (!CreateProtoServiceFunction(pd.szName, PSR_URL, SpamCheckProtoRecv))
+ bSuccess = FALSE;
+ if (!CreateProtoServiceFunction(pd.szName, PSR_FILE, SpamCheckProtoRecv))
+ bSuccess = FALSE;
+ if (!CreateProtoServiceFunction(pd.szName, PSR_CONTACTS, SpamCheckProtoRecv))
+ bSuccess = FALSE;
+
+ // Register for events not added via recv chain
+ hHookDbEventFilterAdd = HookEvent(ME_DB_EVENT_FILTER_ADD, DbEventFilterAdd);
+ if (!hHookDbEventFilterAdd) bSuccess = FALSE;
+
+ // Add filter protocol to all contacts
+ hHookDbContactAdded = HookEvent(ME_DB_CONTACT_ADDED, DbContactAdded);
+ if (!hHookDbContactAdded) bSuccess = FALSE;
+
+ } else {
+ bSuccess = FALSE;
+ }
+
+ // Error here is very very unlikely
+ if (bSuccess)
+ Netlib_Logf(NULL, "Spam Filter: Protocol filter initialized (%hs)", DB_MODULE_NAME);
+ else
+ CallService(MS_SPAMFILTER_SHOWERROR, SFSE_CRITICAL_ERROR, 0);
+}
+
+
+void UninitSpamCheck(void)
+{
+ if (hHookDbContactAdded) UnhookEvent(hHookDbContactAdded);
+ if (hHookDbEventFilterAdd) UnhookEvent(hHookDbEventFilterAdd);
+
+ Netlib_Logf(NULL, "Spam Filter: Protocol filter uninitialized (%hs)", DB_MODULE_NAME);
+} \ No newline at end of file
diff --git a/spamfilter/spamcheck.h b/spamfilter/spamcheck.h
new file mode 100644
index 0000000..5ec035b
--- /dev/null
+++ b/spamfilter/spamcheck.h
@@ -0,0 +1,33 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_SPAMCHECK_H__
+#define SF_SPAMCHECK_H__ 1
+
+
+// -- Prototypes
+void AssignAllContacts(void);
+
+void InitSpamCheck(void);
+void UninitSpamCheck(void);
+
+
+#endif // SF_SPAMCHECK_H__
diff --git a/spamfilter/spamcheckdata.c b/spamfilter/spamcheckdata.c
new file mode 100644
index 0000000..5e27150
--- /dev/null
+++ b/spamfilter/spamcheckdata.c
@@ -0,0 +1,273 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+
+// -----------------------------------------
+
+
+
+WCHAR* SCD_DecodeUserName(SPAMCHECKDATA* pscd)
+{
+ if (pscd->dwFlags & SCDF_NO_CONTACT)
+ {
+ #if defined(UNICODE)
+ if (pscd->dwFlags&SCDF_UNICODE)
+ return mir_wstrdup(pscd->pwszUserName);
+ else
+ return (WCHAR*)mir_utf8encodeW((WCHAR*)pscd->pszUserName);
+ #else
+ if (pscd->dwFlags&SCDF_UNICODE)
+ return UnicodeToAnsi(pscd->pwszUserName);
+ else
+ return mir_strdup(pscd->pszUserName);
+ #endif
+
+ } else {
+ return NULL;
+ }
+}
+
+
+BOOL SCD_IsUserValid(SPAMCHECKDATA* pscd)
+{
+ if (pscd->dwFlags&SCDF_NO_CONTACT)
+ return (pscd->ptszUserName ? TRUE : FALSE);
+ else
+ return (pscd->hContact && (pscd->hContact != INVALID_HANDLE_VALUE));
+}
+
+
+
+WCHAR* SCD_GetContactCustomDisplayName(SPAMCHECKDATA* pscd)
+{
+ // MS_CLIST_GETCONTACTDISPLAYNAME caches it's internal calls to
+ // MS_CONTACT_GETCONTACTINFO, we use it directly here (avoiding cached data)
+
+ // GCDNF_NOCACHE flag for MS_CLIST_GETCONTACTDISPLAYNAME
+ // could be used, too
+
+ WCHAR* pszReturn;
+
+ // No contact available
+ if (pscd->dwFlags&SCDF_NO_CONTACT)
+ {
+ return SCD_DecodeUserName(pscd);
+
+
+ // Normal hContact (CList name)
+ } else {
+
+ CONTACTINFO ci;
+
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.hContact = pscd->hContact;
+ #if defined(UNICODE)
+ ci.dwFlag = CNF_DISPLAY|CNF_UNICODE;
+ #else
+ ci.dwFlag = CNF_DISPLAY;
+ #endif
+
+ pszReturn = NULL;
+ if (CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci) == 0)
+ if (ci.type == CNFT_ASCIIZ)
+ if (lstrlen(ci.pszVal) > 0)
+ pszReturn = ci.pszVal; // MS_CONTACT_GETCONTACTINFO uses mir_alloc
+
+ if (!pszReturn)
+ {
+ #if defined(UNICODE)
+ ci.dwFlag = CNF_UNIQUEID|CNF_UNICODE;
+ #else
+ ci.dwFlag = CNF_UNIQUEID;
+ #endif
+
+ if (CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci) == 0)
+ {
+ if (ci.type == CNFT_ASCIIZ) {
+ pszReturn = ci.pszVal; // MS_CONTACT_GETCONTACTINFO uses mir_alloc
+ } else if (ci.type == CNFT_DWORD) {
+ pszReturn = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszReturn)
+ mir_sntprintf(pszReturn, (MAX_INT_LENGTH+1), _T("%u"), ci.dVal);
+ } else if (ci.type == CNFT_WORD) {
+ pszReturn = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszReturn)
+ mir_sntprintf(pszReturn, (MAX_INT_LENGTH+1), _T("%u"), ci.wVal);
+ } else if (ci.type == CNFT_BYTE) {
+ pszReturn = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszReturn)
+ mir_sntprintf(pszReturn, (MAX_INT_LENGTH+1), _T("%u"), ci.bVal);
+ }
+ }
+ }
+
+ }
+
+ return pszReturn;
+}
+
+
+WCHAR* SCD_GetContactName(SPAMCHECKDATA* pscd, BOOL bIncludeUniqueID)
+{
+ // No contact available
+ if (pscd->dwFlags & SCDF_NO_CONTACT)
+ {
+ return SCD_DecodeUserName(pscd);
+
+ // Normal hContact (CList name)
+ } else {
+
+ WCHAR* pszNick;
+ CONTACTINFO ci;
+
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.hContact = pscd->hContact;
+ #if defined(UNICODE)
+ ci.dwFlag = CNF_NICK|CNF_UNICODE;
+ #else
+ ci.dwFlag = CNF_NICK;
+ #endif
+
+ pszNick = NULL;
+ if (CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci) == 0)
+ if (ci.type == CNFT_ASCIIZ)
+ if (lstrlen(ci.pszVal) > 0)
+ pszNick = ci.pszVal; // MS_CONTACT_GETCONTACTINFO uses mir_alloc
+
+ // Append unique id (if whished) or if nick is not available
+ if (bIncludeUniqueID || !pszNick)
+ {
+ WCHAR* pszUniqueID = NULL;
+
+ #if defined(UNICODE)
+ ci.dwFlag = CNF_UNIQUEID|CNF_UNICODE;
+ #else
+ ci.dwFlag = CNF_UNIQUEID;
+ #endif
+
+ if (CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci) == 0)
+ {
+ if (ci.type == CNFT_ASCIIZ) {
+ pszUniqueID = ci.pszVal; // MS_CONTACT_GETCONTACTINFO uses mir_alloc
+ } else if (ci.type == CNFT_DWORD) {
+ pszUniqueID = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszUniqueID)
+ mir_sntprintf(pszUniqueID, (MAX_INT_LENGTH+1), _T("%u"), ci.dVal);
+ } else if (ci.type == CNFT_WORD) {
+ pszUniqueID = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszUniqueID)
+ mir_sntprintf(pszUniqueID, (MAX_INT_LENGTH+1), _T("%u"), ci.wVal);
+ } else if (ci.type == CNFT_BYTE) {
+ pszUniqueID = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszUniqueID)
+ mir_sntprintf(pszUniqueID, (MAX_INT_LENGTH+1), _T("%u"), ci.bVal);
+ }
+ }
+
+ if (!pszNick) {
+ return pszUniqueID;
+
+ } else if (!pszUniqueID) {
+ return pszNick;
+
+ } else {
+
+ int iSize;
+ WCHAR* pszReturn;
+ WCHAR* pszFmt = TranslateT("%s (%s)");
+
+ iSize = (lstrlen(pszFmt) + lstrlen(pszNick) + lstrlen(pszUniqueID) + 1);
+
+ pszReturn = (WCHAR*)mir_alloc(iSize*sizeof(WCHAR));
+ if (!pszReturn) return pszNick;
+
+ mir_sntprintf(pszReturn, iSize, pszFmt, pszNick, pszUniqueID);
+
+ mir_free(pszUniqueID);
+ mir_free(pszNick);
+ return pszReturn;
+ }
+
+ } else {
+ return pszNick;
+ }
+
+ }
+}
+
+
+WCHAR* SCD_GetContactUniqueStr(SPAMCHECKDATA* pscd, BOOL bUseNick)
+{
+ // No contact available
+ if (pscd->dwFlags & SCDF_NO_CONTACT)
+ {
+ return SCD_DecodeUserName(pscd);
+
+
+ // Normal hContact (CList name)
+ } else {
+
+ CONTACTINFO ci;
+ WCHAR* pszReturn;
+
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.hContact = pscd->hContact;
+ #if defined(UNICODE)
+ if (bUseNick)
+ ci.dwFlag = CNF_NICK|CNF_UNICODE;
+ else
+ ci.dwFlag = CNF_UNIQUEID|CNF_UNICODE;
+ #else
+ if (bUseNick)
+ ci.dwFlag = CNF_NICK;
+ else
+ ci.dwFlag = CNF_UNIQUEID;
+ #endif
+
+ pszReturn = NULL;
+ if (CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci) == 0)
+ {
+ if (ci.type == CNFT_ASCIIZ) {
+ pszReturn = (WCHAR*)mir_alloc((lstrlen(ci.pszVal) + lstrlenA(ci.szProto) + 2)*sizeof(WCHAR));
+ mir_sntprintf(pszReturn, (lstrlen(ci.pszVal) + lstrlenA(ci.szProto) + 2), _T("%hs:%s"), ci.szProto ? ci.szProto : "", ci.pszVal);
+ mir_free(ci.pszVal);
+ } else if (ci.type == CNFT_DWORD) {
+ pszReturn = (WCHAR*)mir_alloc((MAX_INT_LENGTH + lstrlenA(ci.szProto) + 2)*sizeof(WCHAR));
+ mir_sntprintf(pszReturn, (MAX_INT_LENGTH + lstrlenA(ci.szProto) + 2), _T("%hs:%u"), ci.szProto ? ci.szProto : "", ci.dVal);
+ } else if (ci.type == CNFT_WORD) {
+ pszReturn = (WCHAR*)mir_alloc((MAX_INT_LENGTH + lstrlenA(ci.szProto) + 2)*sizeof(WCHAR));
+ mir_sntprintf(pszReturn, (MAX_INT_LENGTH + lstrlenA(ci.szProto) + 2), _T("%hs:%u"), ci.szProto ? ci.szProto : "", ci.wVal);
+ } else if (ci.type == CNFT_BYTE) {
+ pszReturn = (WCHAR*)mir_alloc((MAX_INT_LENGTH + lstrlenA(ci.szProto) + 2)*sizeof(WCHAR));
+ mir_sntprintf(pszReturn, (MAX_INT_LENGTH + lstrlenA(ci.szProto) + 2), _T("%hs:%u"), ci.szProto ? ci.szProto : "", ci.bVal);
+ }
+ }
+
+ return pszReturn;
+ }
+} \ No newline at end of file
diff --git a/spamfilter/spamcheckdata.h b/spamfilter/spamcheckdata.h
new file mode 100644
index 0000000..9f020e6
--- /dev/null
+++ b/spamfilter/spamcheckdata.h
@@ -0,0 +1,36 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_SPAMCHECKDATA_H__
+#define SF_SPAMCHECKDATA_H__ 1
+
+
+// -- Prototypes
+WCHAR* SCD_DecodeUserName(SPAMCHECKDATA* pscd);
+
+BOOL SCD_IsUserValid(SPAMCHECKDATA* pscd);
+
+WCHAR* SCD_GetContactName(SPAMCHECKDATA* pscd, BOOL bIncludeUniqueID);
+WCHAR* SCD_GetContactCustomDisplayName(SPAMCHECKDATA* pscd);
+WCHAR* SCD_GetContactUniqueStr(SPAMCHECKDATA* pscd, BOOL bUseNick);
+
+
+#endif // SF_SPAMCHECKDATA_H__ \ No newline at end of file
diff --git a/spamfilter/spamdefinitions.c b/spamfilter/spamdefinitions.c
new file mode 100644
index 0000000..5cfe40b
--- /dev/null
+++ b/spamfilter/spamdefinitions.c
@@ -0,0 +1,850 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+
+// -- Defines
+#define CACHE_MODE_DISABLED (-1)
+
+// -- Variables
+STRINGLIST** apslCache = NULL;
+CRITICAL_SECTION csCacheAccess;
+int iCacheMode = CACHE_MODE_DISABLED;
+
+HANDLE hDefinitionPathChange = INVALID_HANDLE_VALUE;
+HANDLE hCustomPathChange = INVALID_HANDLE_VALUE;
+
+// -- Variables: Events/Hooks
+HANDLE hHookCustomFoldersChanged = NULL;
+
+// -- Prototypes
+void ToggleFileUpdateWatcher(BOOL bEnable); // HELPER
+
+
+// -----------------------------------------
+
+
+BOOL GetSpamDefinitionData(STRINGLIST* psl, UINT uSpamDefID)
+{
+ if (!psl || (uSpamDefID > SDID_MAX))
+ return FALSE;
+
+ // Return cached item if available
+ if (iCacheMode != CACHE_MODE_DISABLED)
+ {
+ EnterCriticalSection(&csCacheAccess);
+
+ if (apslCache[uSpamDefID])
+ if (SLMergeList(psl, apslCache[uSpamDefID]))
+ {
+ LeaveCriticalSection(&csCacheAccess);
+ return TRUE;
+ }
+
+ LeaveCriticalSection(&csCacheAccess);
+ }
+
+ {
+ WCHAR szFileName[MAX_PATH];
+ BOOL bReturn;
+
+ switch (uSpamDefID)
+ {
+ case SDID_CUSTOM_BADWORDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-badwords.sfw"));
+ break;
+ }
+
+ case SDID_CUSTOM_GOODWORDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-goodwords.sfw"));
+ break;
+ }
+
+ case SDID_CUSTOM_DISLIKEDWORDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-dislikedwords.sfw"));
+ break;
+ }
+
+ case SDID_BADWORDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\BadWords\\*.sfw"));
+ break;
+ }
+
+ case SDID_HOAXTEXTS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\HoaxTexts\\*.sft"));
+ break;
+ }
+
+ case SDID_TEASERTEXTS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\Teasers\\*.sft"));
+ break;
+ }
+
+ case SDID_URLTLDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\URLs\\*.sfc"));
+ break;
+ }
+
+ case SDID_PHONENUMBERS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\PhoneNumbers\\*.sfc"));
+ break;
+ }
+
+ case SDID_SPECIALCHARS_REPLACE_TEXT:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatText\\*.sfc"));
+ break;
+ }
+
+ case SDID_SPECIALCHARS_REPLACE_NUMBERS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatNumbers\\*.sfc"));
+ break;
+ }
+
+ case SDID_SPECIALCHARS_SEPARATORS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatSeparators\\*.sfc"));
+ break;
+ }
+
+ case SDID_SPAMMERS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\Spammers\\*.sfc"));
+ break;
+ }
+
+ default:
+ bReturn = FALSE;
+ }
+
+ if (!bReturn) return FALSE;
+ bReturn = SLLoadFromFile(psl, szFileName);
+
+ // Add item to cache
+ if (iCacheMode != CACHE_MODE_DISABLED)
+ {
+ EnterCriticalSection(&csCacheAccess);
+
+ // Insert new item
+ if (!apslCache[uSpamDefID])
+ apslCache[uSpamDefID] = SLNewList();
+
+ // Update cache item
+ SLClearList(apslCache[uSpamDefID]);
+ if (!SLMergeList(apslCache[uSpamDefID], psl)) {
+ SLFreeList(apslCache[uSpamDefID]);
+ apslCache[uSpamDefID] = NULL;
+ }
+
+ LeaveCriticalSection(&csCacheAccess);
+ }
+
+ return bReturn;
+ }
+}
+
+
+BOOL SetSpamDefinitionData(STRINGLIST* psl, UINT uSpamDefID, HWND hwndDlg)
+{
+ WCHAR szFileName[MAX_PATH];
+ BOOL bReturn;
+
+ switch (uSpamDefID)
+ {
+ case SDID_CUSTOM_BADWORDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-badwords.sfw"));
+ break;
+ }
+
+ case SDID_CUSTOM_GOODWORDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-goodwords.sfw"));
+ break;
+ }
+
+ case SDID_CUSTOM_DISLIKEDWORDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-dislikedwords.sfw"));
+ break;
+ }
+
+ // Default data can't be saved
+ default:
+ bReturn = FALSE;
+ }
+
+ if (!bReturn) return FALSE;
+
+ // Make sure the directory exists
+ PPreparePathForWrite(hwndDlg, szFileName, FALSE, TRUE);
+
+ // Temporaryly disable file change watcher
+ if (iCacheMode != CACHE_MODE_DISABLED)
+ iCacheMode++;
+
+ bReturn = SLSaveToFile(psl, szFileName);
+ OUT("re-enabled ignored event");
+
+ // Re-enable file change watcher
+ if (iCacheMode != CACHE_MODE_DISABLED)
+ iCacheMode--;
+
+ // Update item in cache
+ if (iCacheMode != CACHE_MODE_DISABLED)
+ {
+ EnterCriticalSection(&csCacheAccess);
+
+ // Insert new item
+ if (!apslCache[uSpamDefID])
+ apslCache[uSpamDefID] = SLNewList();
+
+ // Update cache
+ SLClearList(apslCache[uSpamDefID]);
+ if (!SLMergeList(apslCache[uSpamDefID], psl)) {
+ SLFreeList(apslCache[uSpamDefID]);
+ apslCache[uSpamDefID] = NULL;
+ }
+
+ LeaveCriticalSection(&csCacheAccess);
+ }
+
+ return bReturn;
+}
+
+
+BOOL IsSpamDefinitionDataPresent(UINT uSpamDefID)
+{
+ switch (uSpamDefID)
+ {
+ // Custom data can also be empty
+ case SDID_CUSTOM_BADWORDS:
+ {
+ WCHAR szFileName[MAX_PATH];
+ GetDefinitionsPath(szFileName, TRUE, _T("own-badwords.sfw"));
+
+ return PathFileExists(szFileName);
+ }
+
+ case SDID_CUSTOM_GOODWORDS:
+ {
+ WCHAR szFileName[MAX_PATH];
+ GetDefinitionsPath(szFileName, TRUE, _T("own-goodwords.sfw"));
+
+ return PathFileExists(szFileName);
+ }
+
+ case SDID_CUSTOM_DISLIKEDWORDS:
+ {
+ WCHAR szFileName[MAX_PATH];
+ GetDefinitionsPath(szFileName, TRUE, _T("own-dislikedwords.sfw"));
+
+ return PathFileExists(szFileName);
+ }
+
+ // Default data is not allowed to be empty
+ default:
+ {
+ STRINGLIST* psl = SLNewList();
+ BOOL bReturn;
+
+ GetSpamDefinitionData(psl, uSpamDefID);
+ bReturn = (SLGetItemCount(psl) > 0);
+
+ SLFreeList(psl);
+ return bReturn;
+ }
+ }
+}
+
+
+
+// -----------------------------------------
+
+
+BOOL EnumSpamDefinitions(UINT uSpamDefID, BOOL bAtLeastOneTime, SPAMDEFINITIONS_ENUMPROC psdenump, WPARAM wParam, LPARAM lParam)
+{
+ BOOL bReturn;
+
+ WCHAR szFileName[MAX_PATH];
+ WCHAR szFullPath[MAX_PATH];
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind;
+ SYSTEMTIME stDate;
+
+ if (!psdenump) return FALSE;
+
+ switch (uSpamDefID)
+ {
+ case SDID_BADWORDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\BadWords\\*.sfw"));
+ break;
+ }
+
+ case SDID_HOAXTEXTS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\HoaxTexts\\*.sft"));
+ break;
+ }
+
+ case SDID_TEASERTEXTS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\Teasers\\*.sft"));
+ break;
+ }
+
+ case SDID_URLTLDS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\URLs\\*.sfc"));
+ break;
+ }
+
+ case SDID_PHONENUMBERS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\PhoneNumbers\\*.sfc"));
+ break;
+ }
+
+ case SDID_SPECIALCHARS_REPLACE_TEXT:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatText\\*.sfc"));
+ break;
+ }
+
+ case SDID_SPECIALCHARS_REPLACE_NUMBERS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatNumbers\\*.sfc"));
+ break;
+ }
+
+ case SDID_SPECIALCHARS_SEPARATORS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatSeparators\\*.sfc"));
+ break;
+ }
+
+ case SDID_SPAMMERS:
+ {
+ bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\Spammers\\*.sfc"));
+ break;
+ }
+
+ /*
+ case SDID_CUSTOM_BADWORDS:
+ case SDID_CUSTOM_GOODWORDS:
+ case SDID_CUSTOM_DISLIKEDWORDS:
+ */
+ default:
+ bReturn = FALSE;
+ }
+
+ if (bReturn)
+ {
+ bReturn = FALSE;
+ hFind = FindFirstFile(szFileName, &wfd);
+ PathRemoveFileSpec(szFileName);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+
+ if (FileTimeToSystemTime(&wfd.ftLastWriteTime, &stDate))
+ {
+ // Construct full path
+ StrCpy(szFullPath, szFileName);
+ PathAppend(szFullPath, wfd.cFileName);
+
+ // Extract name only
+ PathRemoveExtension(wfd.cFileName);
+
+ // Ignore "Empty" placeholders
+ if (StrCmpI(wfd.cFileName, _T("Empty")) == 0)
+ continue;
+
+ if (psdenump(uSpamDefID, szFullPath, wfd.cFileName, &stDate, wParam, lParam))
+ bReturn = TRUE;
+ else
+ break;
+ }
+
+ } while (FindNextFile(hFind, &wfd));
+
+ FindClose(hFind);
+ }
+ }
+
+ if (bAtLeastOneTime && !bReturn)
+ psdenump(uSpamDefID, NULL, NULL, NULL, wParam, lParam);
+
+ return bReturn;
+}
+
+// -----------------------------------------
+
+static void ThreadSpamDefinitionCheck(void* arg)
+{
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+
+ // Give miranda some time to load
+ SleepEx(30000, TRUE);
+
+ OutputDebugString(_T("Spam Filter: Validating Spam Definitions...\r\n"));
+
+ if (!Miranda_Terminated())
+ {
+ BOOL bChecked = FALSE;
+ STRINGLIST* pslCheck = SLNewList();
+
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)DEFAULT_SETTING_ADVERTISMENTFILTER))
+ {
+ bChecked = TRUE;
+
+ GetSpamDefinitionData(pslCheck, SDID_BADWORDS);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+
+ GetSpamDefinitionData(pslCheck, SDID_TEASERTEXTS);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+
+ GetSpamDefinitionData(pslCheck, SDID_URLTLDS);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+
+ GetSpamDefinitionData(pslCheck, SDID_PHONENUMBERS);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+
+ GetSpamDefinitionData(pslCheck, SDID_SPAMMERS);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+
+ GetSpamDefinitionData(pslCheck, SDID_SPECIALCHARS_REPLACE_TEXT);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+
+ GetSpamDefinitionData(pslCheck, SDID_SPECIALCHARS_REPLACE_NUMBERS);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+
+ GetSpamDefinitionData(pslCheck, SDID_SPECIALCHARS_SEPARATORS);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+ }
+
+ if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, (BYTE)DEFAULT_SETTING_DISLIKEDMESSAGEFILTER))
+ {
+ bChecked = TRUE;
+ GetSpamDefinitionData(pslCheck, SDID_HOAXTEXTS);
+ if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk;
+ }
+
+ // Show warning if no spam definitions are present
+ if (bChecked)
+ ShowInfoMessage(NIIF_WARNING, TranslateT("Spam Filter Warning"), TranslateT("You did not install any Spam Definition files! Spam Filter can not work as powerful as it could.\r\n\r\nPlease install at least one language of the Spam Definitions."), 0);
+
+
+CheckResultOk:
+
+ SLFreeList(pslCheck);
+ }
+}
+
+
+// -----------------------------------------
+
+
+static int CustomFoldersChanged(WPARAM wParam, LPARAM lParam)
+{
+ if ((hDefinitionPathChange != INVALID_HANDLE_VALUE) || (hCustomPathChange != INVALID_HANDLE_VALUE))
+ {
+ ToggleFileUpdateWatcher(FALSE);
+ ToggleFileUpdateWatcher(TRUE);
+ }
+
+ return 0;
+}
+
+
+static int InternalServiceWaitOnDataPathChange(WPARAM wParam, LPARAM lParam) // HELPER
+{
+ int i;
+
+ OutputDebugString(_T("Spam Filter: Clearing Spam Definitions cache (file was modified).\r\n"));
+
+ EnterCriticalSection(&csCacheAccess);
+ for (i=0; i<=SDID_MAX; i++)
+ {
+ // Skip custom file cache items when waited separately
+ if (i <= SDID_CUSTOMMAX)
+ if (ServiceExists("SpamFilter/Wait/CustomPathChange"))
+ continue;
+
+ // Reset cache item
+ SLFreeList(apslCache[i]);
+ apslCache[i] = NULL;
+ }
+ LeaveCriticalSection(&csCacheAccess);
+
+ // Refresh windows
+ if (IsWindow(hwndSpamDefinitionsInfo))
+ PostMessage(hwndSpamDefinitionsInfo, SFM_REFRESH_SPAMDEFINITIONS, (WPARAM)TRUE, (LPARAM)0);
+ if (IsWindow(hwndAdvertismentFilter))
+ PostMessage(hwndAdvertismentFilter, SFM_RELOAD_DEFAULTBADWORDS, 0, 0);
+
+
+ // Register for next notification
+ if (!FindNextChangeNotification((HANDLE)wParam))
+ ToggleFileUpdateWatcher(FALSE);
+
+ return 0;
+}
+
+static int InternalServiceWaitOnCustomPathChange(WPARAM wParam, LPARAM lParam) // HELPER
+{
+ int i;
+
+ OutputDebugString(_T("Spam Filter: Clearing Custom Definitions cache (file was modified).\r\n"));
+
+ // Reset custom cache
+ EnterCriticalSection(&csCacheAccess);
+
+ for (i=0; i<=SDID_CUSTOMMAX; i++)
+ {
+ // Reset CUSTOM cache item
+ SLFreeList(apslCache[i]);
+ apslCache[i] = NULL;
+ }
+
+ LeaveCriticalSection(&csCacheAccess);
+
+ // Register for next notification
+ return (FindNextChangeNotification((HANDLE)wParam) ? 0 : 1);
+}
+
+void ToggleFileUpdateWatcher(BOOL bEnable) // HELPER
+{
+ static HANDLE hDefinitionPathService;
+ static HANDLE hCustomPathService;
+
+ // * START WATCHER
+ // Get watch handles and tell Miranda to wait for those handles
+ if (bEnable)
+ {
+ WCHAR szDataPath[MAX_PATH];
+ WCHAR szCustomPath[MAX_PATH];
+ char* pszService;
+ BOOL bStarted = FALSE;
+
+ const DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE;
+
+ // Already started?
+ if (hDefinitionPathChange != INVALID_HANDLE_VALUE)
+ return;
+
+ // Watch the data directory "Spam Filter" for any changes
+ GetDefinitionsPath(szDataPath, FALSE, NULL);
+ hDefinitionPathChange = FindFirstChangeNotification(szDataPath, TRUE, dwNotifyFilter);
+ if (hDefinitionPathChange != INVALID_HANDLE_VALUE)
+ {
+ pszService = "SpamFilter/Wait/DataPathChange";
+ hDefinitionPathService = CreateServiceFunction(pszService, InternalServiceWaitOnDataPathChange);
+
+ if (hDefinitionPathService)
+ if (CallService(MS_SYSTEM_WAITONHANDLE, (WPARAM)hDefinitionPathChange, (LPARAM)pszService) == 0)
+ bStarted = TRUE;
+ }
+
+ // Watch the profile sub-directory "Spam Filter" (custom files) for any changes
+ if (bStarted)
+ {
+ GetDefinitionsPath(szCustomPath, TRUE, NULL);
+ if (StrCmp(szDataPath, szCustomPath) != 0)
+ {
+ hCustomPathChange = FindFirstChangeNotification(szCustomPath, FALSE, dwNotifyFilter);
+ if (hCustomPathChange != INVALID_HANDLE_VALUE)
+ {
+ pszService = "SpamFilter/Wait/CustomPathChange";
+ hCustomPathService = CreateServiceFunction(pszService, InternalServiceWaitOnCustomPathChange);
+
+ if (hCustomPathService)
+ if (CallService(MS_SYSTEM_WAITONHANDLE, (WPARAM)hCustomPathChange, (LPARAM)pszService) != 0)
+ {
+ DestroyServiceFunction(hDefinitionPathService);
+ hDefinitionPathService = NULL;
+ }
+ }
+ }
+
+ if (IsCustomFoldersAvailable())
+ hHookCustomFoldersChanged = HookEvent(ME_FOLDERS_PATH_CHANGED, CustomFoldersChanged);
+
+ OutputDebugString(_T("Spam Filter: Spam Definitions file watcher started.\r\n"));
+ return; // SUCCESS!
+ } // else fall through to cleanup
+ }
+
+ // * STOP WATCHER
+ // Stop data path wait
+ if (hDefinitionPathChange != INVALID_HANDLE_VALUE)
+ {
+ CallService(MS_SYSTEM_REMOVEWAIT, (WPARAM)hDefinitionPathChange, 0);
+ Sleep(0); // give rest of time slice to miranda (maybe currently waiting) ~10-15ms
+ if (hDefinitionPathService)
+ DestroyServiceFunction(hDefinitionPathService);
+
+ FindCloseChangeNotification(hDefinitionPathChange);
+ hDefinitionPathChange = INVALID_HANDLE_VALUE;
+
+ // Stop custom path wait
+ if (hCustomPathChange != INVALID_HANDLE_VALUE)
+ {
+ CallService(MS_SYSTEM_REMOVEWAIT, (WPARAM)hCustomPathChange, 0);
+ Sleep(0); // give rest of time slice to miranda (maybe currently waiting) ~10-15ms
+ if (hCustomPathService)
+ DestroyServiceFunction(hCustomPathService);
+
+ FindCloseChangeNotification(hCustomPathChange);
+ hCustomPathChange = INVALID_HANDLE_VALUE;
+ }
+
+ // Custom Folders Plugin support
+ if (hHookCustomFoldersChanged) UnhookEvent(hHookCustomFoldersChanged);
+
+ OutputDebugString(_T("Spam Filter: Spam Definitions file watcher stopped.\r\n"));
+ }
+}
+
+/*
+static void ThreadFileUpdateWatcher(void* arg)
+{
+ static bThreadRunning = FALSE;
+
+ DWORD dwWaitStatus;
+ BOOL bResetCache;
+ HANDLE ahChangeHandles[2];
+
+ // Allow thread only run once at a time
+ if (bThreadRunning) return;
+ bThreadRunning = TRUE;
+
+ OutputDebugString(_T("Spam Filter: Data file watcher started\r\n"));
+
+ // Get watch handles
+ {
+ TCHAR szDataPath[MAX_PATH];
+ TCHAR szCustomPath[MAX_PATH];
+
+ // FIXME!:
+ // Custom Folder Plugin: not reloaded when user changed folder
+
+ // Watch the data directory "Spam Filter" for any changes
+ GetDefinitionsPath(szDataPath, FALSE, NULL);
+ ahChangeHandles[0] = FindFirstChangeNotification(szDataPath, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE);
+ if (ahChangeHandles[0] == INVALID_HANDLE_VALUE) {
+ ahChangeHandles[1] = INVALID_HANDLE_VALUE;
+ goto CleanupChangeHandles;
+ }
+
+ // Watch the profile sub-directory "Spam Filter" (custom files) for any changes
+ GetDefinitionsPath(szCustomPath, TRUE, NULL);
+ if (StrCmp(szDataPath, szCustomPath) != 0)
+ ahChangeHandles[1] = FindFirstChangeNotification(szCustomPath, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE);
+ else
+ ahChangeHandles[1] = INVALID_HANDLE_VALUE;
+ }
+
+ // Change notification is set:
+ // Now wait on both notification handles and refresh accordingly.
+ while (!Miranda_Terminated() && (iCacheMode != CACHE_MODE_DISABLED))
+ {
+ if (ahChangeHandles[1] == INVALID_HANDLE_VALUE)
+ dwWaitStatus = WaitForSingleObjectEx(ahChangeHandles[0], INFINITE, TRUE);
+ else
+ dwWaitStatus = WaitForMultipleObjectsEx(ARRAYSIZE(ahChangeHandles), ahChangeHandles, FALSE, INFINITE, TRUE);
+
+ bResetCache = (iCacheMode == 0);
+
+ if ((iCacheMode == CACHE_MODE_DISABLED) || Miranda_Terminated()) break;
+
+ switch (dwWaitStatus)
+ {
+ // Something in data directory was changed (-> reset data cache)
+ case WAIT_OBJECT_0:
+ {
+ // Check if file watcher is temporaryly disabled
+ if (bResetCache)
+ {
+ int i;
+ OutputDebugString(_T("Spam Filter: File change event, Spam Definition file cache reset\r\n"));
+
+ EnterCriticalSection(&csCacheAccess);
+
+ for (i=0; i<=SDID_MAX; i++)
+ {
+ if (i <= SDID_CUSTOMMAX)
+ if (ahChangeHandles[1] != INVALID_HANDLE_VALUE)
+ continue;
+
+ // Reset cache item
+ SLFreeList(apslCache[i]);
+ apslCache[i] = NULL;
+ }
+
+ LeaveCriticalSection(&csCacheAccess);
+
+ if (IsWindow(hwndSpamDefinitionsInfo))
+ PostMessage(hwndSpamDefinitionsInfo, SFM_REFRESH_SPAMDEFINITIONS, (WPARAM)TRUE, (LPARAM)0);
+
+ if (IsWindow(hwndAdvertismentFilter))
+ PostMessage(hwndAdvertismentFilter, SFM_RELOAD_DEFAULTBADWORDS, 0, 0);
+ }
+
+ if (!FindNextChangeNotification(ahChangeHandles[0]))
+ goto CleanupChangeHandles;
+ break;
+ }
+
+ // Custom files changed (-> reset custom cache)
+ case WAIT_OBJECT_0+1:
+ {
+ // Check if file watcher is temporaryly disabled
+ if (bResetCache)
+ {
+ OutputDebugString(_T("Spam Filter: File change event, custom data file cache reset\r\n"));
+
+ // Reset custom cache
+ EnterCriticalSection(&csCacheAccess);
+
+ SLFreeList(apslCache[SDID_CUSTOM_BADWORDS]);
+ apslCache[SDID_CUSTOM_BADWORDS] = NULL;
+
+ SLFreeList(apslCache[SDID_CUSTOM_GOODWORDS]);
+ apslCache[SDID_CUSTOM_GOODWORDS] = NULL;
+
+ SLFreeList(apslCache[SDID_CUSTOM_DISLIKEDWORDS]);
+ apslCache[SDID_CUSTOM_DISLIKEDWORDS] = NULL;
+
+ LeaveCriticalSection(&csCacheAccess);
+ }
+
+ // Register for next notification
+ FindNextChangeNotification(ahChangeHandles[1]);
+ break;
+ }
+ }
+ }
+
+
+CleanupChangeHandles:
+
+ bThreadRunning = FALSE;
+
+ // Close change notification handles
+ if (ahChangeHandles[0] != INVALID_HANDLE_VALUE)
+ FindCloseChangeNotification(ahChangeHandles[0]);
+
+ if (ahChangeHandles[1] != INVALID_HANDLE_VALUE)
+ FindCloseChangeNotification(ahChangeHandles[1]);
+
+ OutputDebugString(_T("Spam Filter: Data file watcher closed\r\n"));
+}
+*/
+
+
+// -----------------------------------------
+
+
+void ToggleSpamDefinitionsCache(BOOL bEnable)
+{
+ if ((iCacheMode != CACHE_MODE_DISABLED) == bEnable)
+ return;
+
+ if (bEnable)
+ {
+ // Show warning if no spam definitions are present
+ if (bEnable)
+ forkthread(ThreadSpamDefinitionCheck, 0, NULL);
+
+ OutputDebugString(_T("Spam Filter: Starting Spam Definitions cache...\r\n"));
+
+ apslCache = (STRINGLIST**)mir_alloc(sizeof(STRINGLIST*) * (SDID_MAX+1));
+ if (apslCache)
+ {
+ ZeroMemory(apslCache, sizeof(STRINGLIST*) * (SDID_MAX+1));
+ InitializeCriticalSection(&csCacheAccess);
+ iCacheMode = 0;
+
+ // Start file change watcher
+ // Replaced with Miranda's wait service
+ //forkthread(ThreadFileUpdateWatcher, 0, NULL);
+ ToggleFileUpdateWatcher(TRUE);
+ }
+
+ } else {
+
+ int i;
+
+ ToggleFileUpdateWatcher(FALSE);
+
+ // Clear cache
+ iCacheMode = CACHE_MODE_DISABLED;
+
+ // Release memory
+ EnterCriticalSection(&csCacheAccess);
+ for (i=0; i<=SDID_MAX; i++) {
+ SLFreeList(apslCache[i]);
+ apslCache[i] = NULL;
+ }
+ LeaveCriticalSection(&csCacheAccess);
+
+ mir_free(apslCache);
+ apslCache = NULL;
+ DeleteCriticalSection(&csCacheAccess);
+
+ OutputDebugString(_T("Spam Filter: Spam Definitions cache stopped.\r\n"));
+ }
+}
+
+
+// -----------------------------------------
+
+
+void InitSpamDefinitions(void)
+{
+ // Only start cache if Disliked Messages or Advertisment Filter is enabled (saving memory)
+ if ( DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)DEFAULT_SETTING_ADVERTISMENTFILTER) ||
+ DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, (BYTE)DEFAULT_SETTING_DISLIKEDMESSAGEFILTER) )
+ {
+ ToggleSpamDefinitionsCache(TRUE);
+ }
+}
+
+void UninitSpamDefinitions(void)
+{
+ ToggleSpamDefinitionsCache(FALSE);
+} \ No newline at end of file
diff --git a/spamfilter/spamdefinitions.h b/spamfilter/spamdefinitions.h
new file mode 100644
index 0000000..00b2bb0
--- /dev/null
+++ b/spamfilter/spamdefinitions.h
@@ -0,0 +1,59 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_SPAMDEFINITIONS_H__
+#define SF_SPAMDEFINITIONS_H__ 1
+
+
+// -- Defines
+#define SDID_CUSTOM_BADWORDS 0
+#define SDID_CUSTOM_GOODWORDS 1
+#define SDID_CUSTOM_DISLIKEDWORDS 2
+#define SDID_BADWORDS 3
+#define SDID_HOAXTEXTS 4
+#define SDID_TEASERTEXTS 5
+#define SDID_URLTLDS 6
+#define SDID_PHONENUMBERS 7
+#define SDID_SPECIALCHARS_REPLACE_TEXT 8
+#define SDID_SPECIALCHARS_REPLACE_NUMBERS 9
+#define SDID_SPECIALCHARS_SEPARATORS 10
+#define SDID_SPAMMERS 11
+
+#define SDID_CUSTOMMAX 2
+#define SDID_MAX 11
+
+
+// -- Callbacks
+typedef BOOL (CALLBACK* SPAMDEFINITIONS_ENUMPROC)(UINT uSpamDefID, WCHAR* pszFilePath, WCHAR* pszName, SYSTEMTIME* pstDate, WPARAM wParam, LPARAM lParam);
+
+// -- Prototypes
+BOOL GetSpamDefinitionData(STRINGLIST* psl, UINT uSpamDefID);
+BOOL SetSpamDefinitionData(STRINGLIST* psl, UINT uSpamDefID, HWND hwndDlg);
+
+BOOL IsSpamDefinitionDataPresent(UINT uSpamDefID);
+BOOL EnumSpamDefinitions(UINT uSpamDefID, BOOL AtLeastOneTime, SPAMDEFINITIONS_ENUMPROC psdenump, WPARAM wParam, LPARAM lParam);
+
+void ToggleSpamDefinitionsCache(BOOL bEnable);
+
+void InitSpamDefinitions(void);
+void UninitSpamDefinitions(void);
+
+#endif // SF_SPAMDEFINITIONS_H__
diff --git a/spamfilter/string.c b/spamfilter/string.c
new file mode 100644
index 0000000..2660b77
--- /dev/null
+++ b/spamfilter/string.c
@@ -0,0 +1,310 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+
+// -----------------------------------------
+
+
+/*
+int SubStringExistsInStringCount(const TCHAR* pszStr, const TCHAR* pszSubString, BOOL bCaseSensitive)
+{
+ int iSubStrLen;
+ char* pszOcc;
+ int iReturn;
+
+ if (!pszSubString) return 0;
+
+ pszOcc = pszStr;
+ iCount = 0;
+ iSubStrLen = lstrlen(pszSubString);
+ while ((pszOcc = bCaseSensitive?StrStr(pszOcc, pszOldSubStr):StrStrI(pszOcc, pszOldSubStr)) != NULL)
+ {
+ iCount++;
+ pszOcc = pszOcc+iSubStrLen;
+ }
+
+ return iCount;
+}
+*/
+
+
+WCHAR* RemoveSubStr(WCHAR* pszStr, int iPos, int iSubStrLen)
+{
+ if (!pszStr) return NULL;
+ if ((iPos > 0) && (iSubStrLen > 0) && (iPos < lstrlen(pszStr)))
+ MoveMemory(&pszStr[iPos], &pszStr[iPos+iSubStrLen], (lstrlen(&pszStr[iPos])+1)*sizeof(WCHAR));
+ return pszStr;
+}
+
+/*
+BOOL HasSubStr(const TCHAR* pszStr, int iPos, const TCHAR* pszSubStr);
+{
+ TCHAR* pszPos;
+
+ if (!pszSubStr || !pszStr || (iPos >= lstrlen(pszStr))) return FALSE;
+
+ // Test for suffix
+ if (iPos < 0)
+ return ((lstrlen(pszStr) <= lstrlen(pszSubStr)) || (StrCmp(pszStr+lstrlen(pszStr)-lstrlen(pszSubStr), pszSubStr) != 0));
+ else
+ return (StrCmpN(&pszStr[iPos], pszSubStr, lstrlen(pszSubStr)) != 0))
+}
+*/
+
+WCHAR* InsertSubStr(WCHAR* pszStr, int cchMaxSize, int iPos, const WCHAR* pszSubStr)
+{
+ int iSubStrLen;
+
+ if (!pszStr) return NULL;
+ if (!pszSubStr || (iPos < 0) || (iPos >= lstrlen(pszStr))) return pszStr;
+ iSubStrLen = lstrlen(pszSubStr);
+ if ((cchMaxSize > 0) && ((iSubStrLen+lstrlen(pszStr)+1) > cchMaxSize))
+ return pszStr;
+ MoveMemory(&pszStr[iPos+iSubStrLen], &pszStr[iPos], (lstrlen(&pszStr[iPos])+1)*sizeof(WCHAR));
+ CopyMemory(&pszStr[iPos], pszSubStr, iSubStrLen*sizeof(WCHAR)); // no terminating zero
+ return pszStr;
+}
+
+
+WCHAR* ReplaceSubStringWithString(const WCHAR* pszStr, const WCHAR* pszOldSubStr, const WCHAR* pszNewSubStr, BOOL bCaseSensitive, BOOL bAlsoSearchNewString, BOOL* pbSthReplaced)
+{
+ // returned TCHAR* needs to be mir_free()'d
+ // returns NULL on error
+
+ int iOldSubStrLen, iNewSubStrLen;
+ int iPos;
+ WCHAR* pszOcc;
+ WCHAR* pszReturn;
+ WCHAR* pszReturnBuf;
+
+ if (pbSthReplaced) *pbSthReplaced = FALSE;
+ if (!pszStr) return NULL;
+ if (!pszOldSubStr) {
+ *pbSthReplaced = TRUE;
+ return mir_wstrdup(pszStr);
+ }
+
+ iOldSubStrLen = (int)lstrlen(pszOldSubStr);
+ iNewSubStrLen = pszNewSubStr?(int)lstrlen(pszNewSubStr):0;
+
+ // Make sure that no infinite loop occurs (on str in new one)
+ if (bAlsoSearchNewString && pszNewSubStr)
+ if (bCaseSensitive?StrStr(pszNewSubStr, pszOldSubStr):StrStrI(pszNewSubStr, pszOldSubStr))
+ bAlsoSearchNewString = FALSE;
+
+ pszReturn = mir_wstrdup(pszStr);
+ if (!pszReturn) return NULL;
+
+ pszOcc = pszReturn;
+ while ((pszOcc = bCaseSensitive?StrStr(pszOcc, pszOldSubStr):StrStrI(pszOcc, pszOldSubStr)) != NULL)
+ {
+ // Reallocate more memory (memory block might get moved)
+ if (iNewSubStrLen > iOldSubStrLen)
+ {
+ pszReturnBuf = pszReturn;
+ pszReturn = (WCHAR*)mir_realloc(pszReturnBuf, (lstrlen(pszReturn)+iNewSubStrLen-iOldSubStrLen+1)*sizeof(WCHAR));
+ if (!pszReturn) {
+ mir_free(pszReturnBuf);
+ return NULL;
+ }
+ }
+
+ iPos = pszOcc-pszReturn;
+ RemoveSubStr(pszReturn, iPos, iOldSubStrLen);
+ if (pszNewSubStr) InsertSubStr(pszReturn, 0, iPos, pszNewSubStr);
+
+ // Get new start position for search
+ if (!bAlsoSearchNewString)
+ pszOcc = &pszReturn[iPos+iNewSubStrLen];
+
+ if (pbSthReplaced) *pbSthReplaced = TRUE;
+ }
+
+ // Correct memory allocation (if less needed)
+ if (iNewSubStrLen < iOldSubStrLen)
+ {
+ pszReturnBuf = pszReturn;
+ pszReturn = (WCHAR*)mir_realloc(pszReturnBuf, (lstrlen(pszReturn)+1)*sizeof(WCHAR));
+ if (!pszReturn) pszReturn = pszReturnBuf;
+ }
+
+ return pszReturn;
+}
+
+
+BOOL ReplaceSubStringWithStringBuf(WCHAR* pszStr, int cchMaxSize, const WCHAR* pszOldSubStr, const WCHAR* pszNewSubStr, BOOL bCaseSensitive, BOOL bAlsoSearchNewString)
+{
+ int iOldSubStrLen, iNewSubStrLen;
+ int iPos;
+ WCHAR* pszOcc;
+ BOOL bSthReplaced;
+
+ if (!pszStr) return FALSE;
+ if (!pszOldSubStr || (cchMaxSize <= 0)) return TRUE;
+
+ iOldSubStrLen = (int)lstrlen(pszOldSubStr);
+ iNewSubStrLen = pszNewSubStr?(int)lstrlen(pszNewSubStr):0;
+
+ // Make sure that no infinite loop occurs (on str in new one)
+ if (bAlsoSearchNewString && pszNewSubStr)
+ if (bCaseSensitive?StrStr(pszNewSubStr, pszOldSubStr):StrStrI(pszNewSubStr, pszOldSubStr))
+ bAlsoSearchNewString = FALSE;
+
+ pszOcc = pszStr;
+ bSthReplaced = FALSE;
+ while ((pszOcc = bCaseSensitive?StrStr(pszOcc, pszOldSubStr):StrStrI(pszOcc, pszOldSubStr)) != NULL)
+ {
+ if (cchMaxSize < lstrlen(pszStr)+iNewSubStrLen-iOldSubStrLen+1);
+ break;
+
+ iPos = pszOcc-pszStr;
+ RemoveSubStr(pszStr, iPos, iOldSubStrLen);
+ if (pszNewSubStr) InsertSubStr(pszStr, 0, iPos, pszNewSubStr);
+
+ // Get new start position for search
+ if (!bAlsoSearchNewString)
+ pszOcc = &pszStr[iPos+iNewSubStrLen];
+
+ bSthReplaced = TRUE;
+ }
+
+ return bSthReplaced;
+}
+
+
+WCHAR* ReplaceSubStringWithStringMultiple(const WCHAR* pszStr, STRINGLIST* pslFromTo, BOOL bCaseSensitive, BOOL bAlsoSearchNewString, BOOL* pbSthReplaced)
+{
+ WCHAR* pszReplaced;
+ WCHAR* pszStrBuf;
+ int i;
+ BOOL bSthReplacedBuf;
+
+ if (pbSthReplaced) *pbSthReplaced = FALSE;
+ if (!pszStr) return NULL;
+ pszStrBuf = mir_wstrdup(pszStr);
+ if (pszStrBuf && pslFromTo)
+ for (i=SL_MIN_POS; i<SLGetMaxPos(pslFromTo); i+=2)
+ {
+ pszReplaced = ReplaceSubStringWithString(pszStrBuf, SLGetItem(pslFromTo, i), SLGetItem(pslFromTo, i+1), bCaseSensitive, bAlsoSearchNewString, &bSthReplacedBuf);
+ if (!pszReplaced) continue;
+ if (pszStrBuf) mir_free(pszStrBuf);
+ pszStrBuf = pszReplaced;
+ if (pbSthReplaced && bSthReplacedBuf) *pbSthReplaced = TRUE;
+ }
+
+ return pszStrBuf;
+}
+
+
+BOOL ReplaceSubStringWithStringMultipleBuf(WCHAR* pszStr, int cchMaxSize, STRINGLIST* pslFromTo, BOOL bCaseSensitive, BOOL bAlsoSearchNewString)
+{
+ int i;
+ BOOL bSthReplaced;
+
+ if (!pszStr) return FALSE;
+ if (!pslFromTo) return TRUE;
+
+ bSthReplaced = FALSE;
+ for (i=SL_MIN_POS; i<SLGetMaxPos(pslFromTo); i+=2)
+ if (ReplaceSubStringWithStringBuf(pszStr, cchMaxSize, SLGetItem(pslFromTo, i), SLGetItem(pslFromTo, i+1), bCaseSensitive, bAlsoSearchNewString))
+ bSthReplaced = TRUE;
+
+ return bSthReplaced;
+}
+
+
+WCHAR* InsertSubStrEveryNChars(const WCHAR* pszStr, int iDist, const WCHAR* pszSubStr, BOOL bAlsoDoFirst, BOOL bAlsoDoLast)
+{
+ // returned char needs to be mir_free()'d
+ // returns NULL on error
+
+ int iSubStrLen;
+ int iLoops;
+ WCHAR* pszReturn;
+ int i;
+
+ if (!pszStr) return NULL;
+ if (iDist <= 0) return mir_wstrdup(pszStr);
+
+ iSubStrLen = pszSubStr?lstrlen(pszSubStr):0;
+ iLoops = (int)(lstrlen(pszStr)/iDist);
+ if ((iLoops == (int)(lstrlen(pszStr)/iDist)) && !bAlsoDoLast) iLoops--;
+ if (bAlsoDoFirst) iLoops++;
+
+ pszReturn = (WCHAR*)mir_alloc((lstrlen(pszStr)+(iLoops*iSubStrLen)+1)*sizeof(WCHAR));
+ if (!pszReturn) return NULL;
+ mir_sntprintf(pszReturn, lstrlen(pszStr)+(iLoops*iSubStrLen)+1, _T("%s"), pszStr);
+
+ if (iSubStrLen > 0)
+ for (i=0; i<iLoops; i++) // FIX ME: use NextChar() instead
+ InsertSubStr(pszReturn, 0, (bAlsoDoFirst?0:iDist)+(i*(iSubStrLen+iDist)), pszSubStr);
+
+ return pszReturn;
+}
+
+
+BOOL RemoveWhiteSpaces(WCHAR* pszStr, BOOL bReplaceWithSpaces, BOOL bAlsoUnneededSpaces)
+{
+ BOOL bReturn = FALSE;
+
+ if (!pszStr) return bReturn;
+
+ if (StrTrim(pszStr, _T("\r\n\t"))) bReturn = TRUE;
+ if (ReplaceSubStringWithStringBuf(pszStr, 0, _T("\r\n"), bReplaceWithSpaces?_T(" "):_T(""), TRUE, FALSE)) bReturn = TRUE;
+ if (ReplaceSubStringWithStringBuf(pszStr, 0, _T("\r"), bReplaceWithSpaces?_T(" "):_T(""), TRUE, FALSE)) bReturn = TRUE;
+ if (ReplaceSubStringWithStringBuf(pszStr, 0, _T("\n"), bReplaceWithSpaces?_T(" "):_T(""), TRUE, FALSE)) bReturn = TRUE;
+ if (ReplaceSubStringWithStringBuf(pszStr, 0, _T("\t"), bReplaceWithSpaces?_T(" "):_T(""), TRUE, FALSE)) bReturn = TRUE;
+
+ if (bAlsoUnneededSpaces)
+ {
+ if (StrTrim(pszStr, _T(" "))) bReturn = TRUE;
+ if (ReplaceSubStringWithStringBuf(pszStr, 0, _T(" "), _T(" "), TRUE, TRUE)) bReturn = TRUE;
+ }
+
+ return bReturn;
+}
+
+
+BOOL GetRandomString(WCHAR* pszStr, int cchSize)
+{
+ static BOOL bRandInited = FALSE;
+ const WCHAR* pszChars = TranslateT("abcdefghkmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWXYZ23456789"); // misunderstandable chars were removed (l=I=1, O=0=o, i=j)
+ int i;
+
+ if (!pszChars || !pszStr || (cchSize < 1)) return FALSE;
+
+ // Initialize random seed
+ if (!bRandInited)
+ {
+ srand((unsigned int)(GetTickCount()+time(NULL))); // very random
+ bRandInited = TRUE;
+ }
+
+ for (i=0; i<(cchSize-1); i++)
+ pszStr[i] = pszChars[(rand()*lstrlen(pszChars))/RAND_MAX]; // % operator is not a good choice
+
+ pszStr[cchSize-1] = _T('\0');
+ return TRUE;
+} \ No newline at end of file
diff --git a/spamfilter/string.h b/spamfilter/string.h
new file mode 100644
index 0000000..aec6b8e
--- /dev/null
+++ b/spamfilter/string.h
@@ -0,0 +1,41 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_STRING_H__
+#define SF_STRING_H__ 1
+
+
+// -- Prototypes
+WCHAR* InsertSubStr(WCHAR* pszStr, int cchSize, int iPos, const WCHAR* pszSubStr);
+WCHAR* RemoveSubStr(WCHAR* pszStr, int iPos, int iSubStrLen);
+BOOL HasSubStr(const WCHAR* pszStr, int iPos, const WCHAR* pszSubStr);
+
+WCHAR* InsertSubStrEveryNChars(const WCHAR* pszStr, int iDist, const WCHAR* pszSubStr, BOOL bAlsoDoFirst, BOOL bAlsoDoLast);
+WCHAR* ReplaceSubStringWithString(const WCHAR* pszStr, const WCHAR* pszOldSubStr, const WCHAR* pszNewSubStr, BOOL bCaseSensitive, BOOL bAlsoSearchNewString, BOOL* pbSthReplaced);
+BOOL ReplaceSubStringWithStringBuf(WCHAR* pszStr, int cchMaxSize, const WCHAR* pszOldSubStr, const WCHAR* pszNewSubStr, BOOL bCaseSensitive, BOOL bAlsoSearchNewString);
+WCHAR* ReplaceSubStringWithStringMultiple(const WCHAR* pszStr, STRINGLIST* pslFromTo, BOOL bCaseSensitive, BOOL bAlsoSearchNewString, BOOL* pbSthReplaced);
+BOOL ReplaceSubStringWithStringMultipleBuf(WCHAR* pszStr, int cchMaxSize, STRINGLIST* pslFromTo, BOOL bCaseSensitive, BOOL bAlsoSearchNewString);
+int SubStringExistsInStringCount(const WCHAR* pszStr, const WCHAR* pszSubString, BOOL bCaseSensitive);
+
+BOOL RemoveWhiteSpaces(WCHAR* pszStr, BOOL bReplaceWithSpaces, BOOL bAlsoUnneededSpaces);
+BOOL GetRandomString(WCHAR* pszStr, int cchSize);
+
+#endif // SF_STRING_H__
diff --git a/spamfilter/stringlist.c b/spamfilter/stringlist.c
new file mode 100644
index 0000000..e1d6de5
--- /dev/null
+++ b/spamfilter/stringlist.c
@@ -0,0 +1,1007 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -- Macros
+#define SLAlloc(s) mir_alloc(s)
+#define SLReAlloc(p, s) mir_realloc((p), (s))
+#define SLFree(p) mir_free(p)
+//#define SLAlloc(s) HeapAlloc(GetProcessHeap(), 0, (s))
+//#define SLReAlloc(p, s) HeapReAlloc(GetProcessHeap(), 0, (p), (s))
+//#define SLFree(p) HeapFree(GetProcessHeap(), 0, (p))
+
+
+// -----------------------------------------
+
+
+BOOL SLAssignToListBoxCtrl(LPSTRINGLIST psl, HWND hwndListBox)
+{
+ BOOL bReturn = FALSE;
+ int i;
+
+ if (!psl || !hwndListBox) return FALSE;
+ SendMessage(hwndListBox, LB_RESETCONTENT, 0, 0);
+ SendMessage(hwndListBox, LB_INITSTORAGE, (WPARAM)psl->uCount, (LPARAM)(SLGetSize(psl, TRUE)*sizeof(WCHAR)));
+ EnterCriticalSection(&psl->csListAccess);
+ for (i=SL_MIN_POS; i<(int)psl->uCount; i++)
+ if (SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)psl->papszList[i]) >= 0)
+ bReturn = TRUE;
+ LeaveCriticalSection(&psl->csListAccess);
+ return bReturn;
+}
+
+BOOL SLRetrieveFromListBoxCtrl(LPSTRINGLIST psl, HWND hwndListBox)
+{
+ int iCount, i, iPos;
+ BOOL bReturn;
+
+ if (!psl || !hwndListBox) return FALSE;
+ bReturn = FALSE;
+ EnterCriticalSection(&psl->csListAccess);
+ iCount = SendMessage(hwndListBox, LB_GETCOUNT, 0, 0);
+ for (i=0; i<iCount; i++)
+ {
+ iPos = SLPrepareItem(psl, SendMessage(hwndListBox, LB_GETTEXTLEN, (WPARAM)i, 0));
+ if (iPos != SL_INVALID_POS)
+ {
+ if (SendMessage(hwndListBox, LB_GETTEXT, (WPARAM)i, (LPARAM)psl->papszList[iPos]) != LB_ERR)
+ bReturn = TRUE;
+ else
+ SLDeleteItem(psl, iPos);
+ }
+ }
+ LeaveCriticalSection(&psl->csListAccess);
+ return bReturn;
+}
+
+/*
+BOOL SLLoadFromIniFile(LPSTRINGLIST psl, const TCHAR* pszSearchPath, const TCHAR* pszSection, const TCHAR* pszSettingPrefix)
+{
+ TCHAR szFile[MAX_PATH];
+ BOOL bFileFound;
+ BOOL bReturn = FALSE;
+ int i, iLen, iSize;
+ TCHAR* pszItem;
+ TCHAR* pszBuf;
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind;
+
+ if (!psl || !pszSection || !pszSearchPath)
+ return FALSE;
+
+ ZeroMemory(&wfd, sizeof(wfd));
+
+ EnterCriticalSection(&psl->csListAccess);
+
+ // Enum all files in the directory
+ hFind = FindFirstFile(pszSearchPath, &wfd);
+ for (bFileFound=(hFind!=INVALID_HANDLE_VALUE); bFileFound; bFileFound=FindNextFile(hFind, &wfd))
+ {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+
+ // Construct the file name
+ mir_sntprintf(szFile, ARRAYSIZE(szFile), _T("%s"), pszSearchPath);
+ PathRemoveFileSpec(szFile);
+ PathAppend(szFile, wfd.cFileName);
+
+ // Load the data from ini file
+ iSize = (pszSettingPrefix?(lstrlen(pszSettingPrefix)+1):0)+MAX_INT_LENGTH+1);
+ pszItem = (TCHAR*)mir_alloc(iSize*sizeof(TCHAR));
+ if (!pszItem) continue;
+
+ for (i=SL_MIN_POS;;i++) // no break condition
+ {
+ mir_sntprintf(pszItem, _T("%s%s%i"), iSize, pszSettingPrefix?pszSettingPrefix:_T(""), pszSettingPrefix?_T("|"):_T(""), i);
+
+ iLen = GetPrivateProfileString(pszSection, pszItem, NULL, NULL, 0, szFile);
+ if (iLen < 0) break;
+
+ pszBuf = (TCHAR*)mir_alloc((iLen+1)*sizeof(TCHAR));
+ if (!pszBuf) continue;
+
+ GetPrivateProfileString(pszSection, pszItem, NULL, pszBuf, iLen, szFile);
+ if (lstrlen(pszBuf) <= 0) break;
+
+ if (SLAddItem(psl, pszBuf) != SL_INVALID_POS)
+ if (!bReturn) bReturn = TRUE;
+
+ mir_free(pszBuf);
+ }
+
+ mir_free(pszItem);
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+
+ if (hFind) FindClose(hFind);
+
+ return bReturn;
+}
+
+
+
+BOOL SLSaveToIniFile(LPSTRINGLIST psl, const TCHAR* pszFileName, const TCHAR* pszSection, const TCHAR* pszSettingPrefix)
+{
+ int i;
+ char* pszItem;
+ int iSize;
+ BOOL bReturn = FALSE;
+
+ if (!psl || !pszSection || !pszFileName)
+ return FALSE;
+
+ iSize = (pszSettingPrefix?(lstrlen(pszSettingPrefix)+1):0)+MAX_INT_LENGTH+1;
+ pszItem = (TCHAR*)mir_alloc(iSize*sizeof(TCHAR));
+
+ if (pszItem)
+ {
+ EnterCriticalSection(&psl->csListAccess);
+
+ // Delete old items (empty section)
+ WritePrivateProfileString(pszSection, NULL, NULL, pszFileName);
+
+ for (i=SL_MIN_POS; i<(int)psl->uCount; i++)
+ {
+ mir_sntprintf(pszItem, iSize, _T("%s%s%i"), pszSettingPrefix?pszSettingPrefix:_T(""), pszSettingPrefix?_T("|"):_T(""), i);
+
+ if (WritePrivateProfileString(pszSection, pszItem, psl->papszList[i], pszFileName))
+ bReturn = TRUE;
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+
+ mir_free(pszItem);
+ }
+
+ return bReturn;
+}
+*/
+
+
+BOOL SLLoadFromFile(LPSTRINGLIST psl, const WCHAR* pszSearchPath)
+{
+ BOOL bReturn;
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind;
+ BOOL bFileFound;
+ WCHAR szFile[MAX_PATH];
+ HANDLE hFile;
+ DWORD dwRead;
+ int iAllocBufSize;
+ int iAllocBufStep;
+ char* pszAllocBuf;
+ char* pszAllocBufTmp;
+ const char szBOM[3] = {0xEF, 0xBB, 0xBF};
+ BOOL bWatchBOM;
+ BOOL bEndOfFile;
+ WCHAR* pszContentConv;
+ WCHAR* pszBuf;
+
+ if (!psl || !pszSearchPath) return FALSE;
+ EnterCriticalSection(&psl->csListAccess);
+
+ // Initial buffer size (gets auto-enlarged if needed)
+ iAllocBufSize = 128; // read-in chars buffer
+ pszAllocBuf = NULL;
+
+ // Enum all files in the directory
+ bReturn = FALSE;
+ hFind = FindFirstFile(pszSearchPath, &wfd);
+ for (bFileFound=(hFind!=INVALID_HANDLE_VALUE); bFileFound; bFileFound=FindNextFile(hFind, &wfd))
+ {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ mir_sntprintf(szFile, ARRAYSIZE(szFile), _T("%s"), pszSearchPath);
+ PathRemoveFileSpec(szFile);
+ PathAppend(szFile, wfd.cFileName);
+
+ hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) continue;
+ bWatchBOM = TRUE;
+ bEndOfFile = FALSE;
+ iAllocBufStep = 0;
+ while (!bEndOfFile)
+ {
+ // Enlarge buffer if needed
+ if (!pszAllocBuf || (iAllocBufStep >= iAllocBufSize))
+ {
+ pszAllocBufTmp = (char*)mir_realloc(pszAllocBuf, (iAllocBufSize*2)*sizeof(char));
+ if (pszAllocBufTmp) {
+ if (pszAllocBuf) iAllocBufSize *= 2;
+ pszAllocBuf = pszAllocBufTmp;
+ } else {
+ bEndOfFile = TRUE; // out of memory
+ }
+ }
+ if (!bEndOfFile) {
+ if (!ReadFile(hFile, &pszAllocBuf[iAllocBufStep], sizeof(char), &dwRead, NULL))
+ bEndOfFile = TRUE;
+ if (dwRead < sizeof(char))
+ bEndOfFile = TRUE;
+ }
+ if ((pszAllocBuf[iAllocBufStep] == '\n') || bEndOfFile)
+ {
+ if (!bEndOfFile && (iAllocBufStep>0) && (pszAllocBuf[iAllocBufStep-1] == '\r'))
+ pszAllocBuf[iAllocBufStep-1] = '\0';
+ else
+ pszAllocBuf[iAllocBufStep] = '\0';
+
+ if (lstrlenA(pszAllocBuf) > 0)
+ {
+ // Test UTF-8 BOM
+ // EF BB BF = UTF-8
+ // -> indicating UTF-8 Unicode text file (check only once)
+ if (bWatchBOM)
+ {
+ if (StrCmpNA(pszAllocBuf, szBOM, ARRAYSIZE(szBOM)) == 0)
+ MoveMemory(pszAllocBuf, (PBYTE)pszAllocBuf+sizeof(szBOM), ((lstrlenA(pszAllocBuf)+1)*sizeof(char))-sizeof(szBOM));
+ bWatchBOM = FALSE;
+ }
+
+ // Decode Utf8
+ mir_utf8decode(pszAllocBuf,&pszContentConv);
+ if (pszContentConv)
+ {
+ pszBuf = pszContentConv;
+ while (pszBuf)
+ {
+ // Find comment indicator
+ pszBuf = StrChr(pszBuf, _T(';')); // find ";" comment indicator
+ if (pszBuf && (*CharPrev(pszContentConv, pszBuf) != _T('\\'))) // ignore "\;" chars
+ {
+ *pszBuf = _T('\0');
+ break;
+ }
+ }
+
+ // Replace no-comment indicators
+ pszBuf = ReplaceSubStringWithString(pszContentConv, _T("\\;"), _T(";"), TRUE, FALSE, NULL);
+
+ // Add item to list (disallow empty items)
+ if (lstrlen(pszContentConv) > 0)
+ if (SLAddItem(psl, pszBuf?pszBuf:pszContentConv) >= 0)
+ bReturn = TRUE;
+
+ mir_free(pszContentConv);
+ if (pszBuf) mir_free(pszBuf);
+ }
+ }
+
+ // Reset step count
+ iAllocBufStep = 0;
+
+ } else {
+ // Step up in string
+ iAllocBufStep++;
+ }
+
+ } // while (!bEndOfFile)
+
+ CloseHandle(hFile);
+ }
+
+ if (hFind) FindClose(hFind);
+ if (pszAllocBuf) mir_free(pszAllocBuf);
+ LeaveCriticalSection(&psl->csListAccess);
+ return bReturn;
+}
+
+
+BOOL SLSaveToFile(LPSTRINGLIST psl, const WCHAR* pszFileName)
+{
+ BOOL bReturn;
+ int i;
+ const char szBOM[3] = {0xEF, 0xBB, 0xBF};
+ HANDLE hFile;
+ DWORD dwWritten; // Win98 compatibility
+ WCHAR* pszBuf;
+ char* pszContentUtf8;
+
+ if (!psl || !pszFileName) return FALSE;
+ hFile = CreateFile(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) return FALSE;
+ bReturn = FALSE;
+ EnterCriticalSection(&psl->csListAccess);
+
+ // Write UTF-8 BOM
+ // EF BB BF = UTF-8
+ // -> indicating UTF-8 Unicode text file
+ if (WriteFile(hFile, &szBOM, sizeof(szBOM), &dwWritten, NULL))
+ for (i=SL_MIN_POS; i<(int)psl->uCount; i++)
+ {
+ // Replace comment indicators
+ pszBuf = ReplaceSubStringWithString(psl->papszList[i], _T(";"), _T("\\;"), TRUE, FALSE, NULL);
+ if (!pszBuf) continue;
+ pszContentUtf8 = mir_utf8encodeW(pszBuf);
+ if (pszContentUtf8) {
+ if (WriteFile(hFile, pszContentUtf8, lstrlenA(pszContentUtf8)*sizeof(char), &dwWritten, NULL))
+ if (WriteFile(hFile, "\r\n", 2*sizeof(char), &dwWritten, NULL))
+ bReturn = TRUE;
+ mir_free(pszContentUtf8);
+ }
+ mir_free(pszBuf);
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+ SetEndOfFile(hFile); // if it was longer before
+ FlushFileBuffers(hFile); // just to be sure
+ CloseHandle(hFile);
+ return bReturn;
+}
+
+
+/*
+BOOL SLConvertFromString(LPSTRINGLIST psl, const TCHAR* pszString, int cchSize, const TCHAR* pszSeparator, BOOL bIgnoreEmptyItems)
+{
+ // If pszSeparator is NULL a _T('\0') is taken as separator
+ BOOL bReturn;
+ int cchSepLen;
+
+ BOOL bReachedEnd;
+ TCHAR* pszAfterPrev;
+ TCHAR* pszNext;
+ int iPos;
+
+ if (!pszString || !psl) return 0;
+
+ if (cchSize < 0) cchSize = lstrlen(pszString)+1;
+ cchSepLen = pszSeparator?lstrlen(pszSeparator):1;
+
+ EnterCriticalSection(&psl->csListAccess);
+
+ // Loop through string
+ bReturn = 0;
+ pszAfterPrev = (TCHAR*)pszString;
+ bReachedEnd = FALSE;
+ while (!bReachedEnd)
+ {
+ // Find first/next occurence
+ if (pszSeparator)
+ {
+ pszNext = StrStr(pszAfterPrev, pszSeparator);
+ if (!pszNext)
+ {
+ bReachedEnd = TRUE;
+ pszNext = (TCHAR*)pszString+(cchSize*sizeof(TCHAR));
+ }
+
+ } else {
+
+ // Find NULL separator
+ pszNext = pszAfterPrev;
+ while (*pszNext != _T('\0'));
+ {
+ pszNext = CharNext(pszNext);
+ if ( pszNext >= (szString+((cchSize-1)*sizeof(TCHAR)) )
+ {
+ bReachedEnd = TRUE;
+ break;
+ }
+ }
+ }
+
+ // Add item
+ if (!bIgnoreEmptyItems || (pszNext != pszAfterPrev))
+ {
+ i2
+ if (iPos < 0) continue;
+ CopyMemory((PBYTE)psl->papszList[iPos], pszAfterPrev, ((PBYTE)pszNext-(PBYTE)pszAfterPrev));
+
+ bReturn = TRUE;
+ }
+
+ // Step up one separated item
+ pszAfterPrev = pszNext+(cchSepLen*sizeof(TCHAR));
+ if ( pszAfterPrev >= pszString+((cchSize-1)*sizeof(TCHAR)) )
+ bReachedEnd = TRUE;
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+
+ return bReturn;
+}
+*/
+
+
+WCHAR* SLConvertToString(LPSTRINGLIST psl, const WCHAR* pszSeparator, BOOL bAlsoDoFirst, BOOL bAlsoDoLast, int* piSize)
+{
+ // Returned string needs to be freed with SLFreeReturn()
+ // If pszSeparator is NULL a _T('\0') is taken as separator
+
+ int i;
+ int iSepLen;
+ int iSize;
+ WCHAR* pszReturn;
+ WCHAR* pszStep;
+
+ if (piSize) *piSize = 0;
+ if (!psl) return NULL;
+
+ EnterCriticalSection(&psl->csListAccess);
+
+ // Get needed size
+ iSepLen = pszSeparator?lstrlen(pszSeparator):1;
+ iSize = SLGetSize(psl, FALSE)+1; // character count in string list
+ if (psl->uCount > 0) iSize += (psl->uCount-1)*iSepLen;
+ if ((psl->uCount > 0) && bAlsoDoFirst) iSize += iSepLen;
+ if (bAlsoDoLast) iSize += iSepLen;
+
+ pszReturn = (WCHAR*)SLAlloc(iSize*sizeof(WCHAR));
+ if (!pszReturn) {
+ LeaveCriticalSection(&psl->csListAccess);
+ return NULL;
+ }
+ if (piSize) *piSize = iSize;
+
+ // Add items to memory
+ pszStep = pszReturn;
+ for (i=SL_MIN_POS; i<(int)psl->uCount; i++)
+ {
+ if ((i>SL_MIN_POS) || bAlsoDoFirst)
+ {
+ if (pszSeparator)
+ CopyMemory(pszStep, pszSeparator, iSepLen*sizeof(WCHAR)); // no terminating zero
+ else
+ ZeroMemory(pszStep, sizeof(WCHAR));
+ pszStep = &pszStep[iSepLen];
+ }
+
+ // Add item
+ CopyMemory(pszStep, psl->papszList[i], lstrlen(psl->papszList[i])*sizeof(WCHAR)); // no terminating zero
+ pszStep = &pszStep[lstrlen(psl->papszList[i])];
+ }
+ LeaveCriticalSection(&psl->csListAccess);
+
+ if (bAlsoDoLast)
+ {
+ if (pszSeparator)
+ CopyMemory(pszStep, pszSeparator, iSepLen*sizeof(WCHAR)); // no terminating zero
+ else
+ ZeroMemory(pszStep, sizeof(WCHAR));
+ pszStep = &pszStep[iSepLen];
+ }
+
+ ZeroMemory(pszStep, sizeof(WCHAR));
+ return pszReturn;
+}
+
+void SLFreeReturn(void* pPntr)
+{
+ if (pPntr) SLFree(pPntr);
+}
+
+BOOL SLConvertToStringBuf(LPSTRINGLIST psl, WCHAR* pszOutput, int cchMaxSize, const WCHAR* pszSeparator, BOOL bAlsoDoFirst, BOOL bAlsoDoLast)
+{
+ BOOL bReturn;
+ int iSize;
+ WCHAR* pszBuf;
+
+ if (!psl || !pszOutput || (cchMaxSize <= 0)) return FALSE;
+ pszBuf = SLConvertToString(psl, pszSeparator, bAlsoDoFirst, bAlsoDoLast, &iSize);
+ if (pszBuf) {
+ if (cchMaxSize < iSize) iSize = cchMaxSize;
+ CopyMemory(pszOutput, pszBuf, iSize*sizeof(WCHAR));
+ SLFreeReturn(pszBuf);
+ bReturn = TRUE;
+ } else {
+ iSize = 1;
+ bReturn = FALSE;
+ }
+ if (cchMaxSize > 1)
+ ZeroMemory(&pszOutput[iSize-1], sizeof(WCHAR));
+ if (!pszSeparator && bAlsoDoLast && (cchMaxSize > 2))
+ ZeroMemory(&pszOutput[iSize], sizeof(WCHAR));
+ return bReturn;
+}
+
+
+BOOL SLMergeList(LPSTRINGLIST pslDest, LPSTRINGLIST pslFrom)
+{
+ BOOL bReturn;
+ int i;
+
+ if (!pslDest || !pslFrom || (pslDest == pslFrom)) return FALSE;
+ EnterCriticalSection(&pslFrom->csListAccess); // first list
+ if (pslFrom->uCount == 0) {
+ LeaveCriticalSection(&pslFrom->csListAccess);
+ return TRUE;
+ }
+ EnterCriticalSection(&pslDest->csListAccess); // second list
+ bReturn = FALSE;
+ for (i=SL_MIN_POS; i<(int)(pslFrom->uCount); i++)
+ if (SLAddItem(pslDest, pslFrom->papszList[i]) != SL_INVALID_POS)
+ bReturn = TRUE;
+ LeaveCriticalSection(&pslFrom->csListAccess); // first list
+ LeaveCriticalSection(&pslDest->csListAccess); // second list
+ return bReturn;
+}
+
+
+int SLFindItem(LPSTRINGLIST psl, const WCHAR* pszItem, BOOL bCaseSensitive, int iStartPos)
+{
+ int i, iReturn;
+
+ if (!psl || !pszItem) return SL_INVALID_POS;
+ EnterCriticalSection(&psl->csListAccess);
+ if (!SLIsValid(psl, iStartPos)) {
+ LeaveCriticalSection(&psl->csListAccess);
+ return SL_INVALID_POS;
+ }
+ iReturn = SL_INVALID_POS;
+ for (i=iStartPos; i<(int)psl->uCount; i++)
+ if ((bCaseSensitive?StrCmp(psl->papszList[i], pszItem):StrCmpI(psl->papszList[i], pszItem)) == 0) {
+ iReturn = i;
+ break;
+ }
+ LeaveCriticalSection(&psl->csListAccess);
+ return iReturn;
+}
+
+BOOL SLIsItem(LPSTRINGLIST psl, const WCHAR* pszItem, BOOL bCaseSensitive)
+{
+ return (SLFindItem(psl, pszItem, bCaseSensitive, SL_MIN_POS) != SL_INVALID_POS);
+}
+
+int SLAddItem(LPSTRINGLIST psl, const WCHAR* pszNewItem)
+{
+ WCHAR** apszBuf;
+ WCHAR* pszBuf;
+ int i;
+
+ if (!psl) return SL_INVALID_POS;
+ EnterCriticalSection(&psl->csListAccess);
+ if (((int)(psl->uCount+1) <= 0) || ((int)(psl->uCount+1) == psl->uCount)) {
+ #if defined(_DEBUG)
+ OutputDebugString(_T("Spam Filter: Stringlist overflow.\r\n"));
+ DebugBreak();
+ #endif
+ LeaveCriticalSection(&psl->csListAccess);
+ return SL_INVALID_POS;
+ }
+
+ // Create/Resize the array
+ if (!psl->papszList) {
+ apszBuf = (WCHAR**)SLAlloc(sizeof(WCHAR*));
+ psl->uCount = 0;
+ } else {
+ apszBuf = (WCHAR**)SLReAlloc(psl->papszList, (psl->uCount+1)*sizeof(WCHAR*));
+ }
+ if (apszBuf) {
+ psl->papszList = apszBuf;
+ } else {
+ LeaveCriticalSection(&psl->csListAccess);
+ return SL_INVALID_POS;
+ }
+
+ // Prepare new string memory
+ pszBuf = (WCHAR*)SLAlloc(((pszNewItem?lstrlen(pszNewItem):0)+1)*sizeof(WCHAR));
+ if (!pszBuf) {
+ LeaveCriticalSection(&psl->csListAccess);
+ return SL_INVALID_POS;
+ }
+ // Prepare new string
+ if (pszNewItem)
+ CopyMemory(pszBuf, pszNewItem, (lstrlen(pszNewItem)+1)*sizeof(WCHAR));
+ else
+ ZeroMemory(pszBuf, sizeof(WCHAR));
+
+ // Insert the new string
+ i = psl->uCount;
+ psl->papszList[i] = pszBuf;
+ psl->uCount++;
+
+ LeaveCriticalSection(&psl->csListAccess);
+ return i;
+}
+
+int SLAddItemPair(LPSTRINGLIST psl, const WCHAR* pszNewItem1, const WCHAR* pszNewItem2)
+{
+ int iPos;
+
+ if (!psl) return SL_INVALID_POS;
+ EnterCriticalSection(&psl->csListAccess);
+ iPos = SLAddItem(psl, pszNewItem1);
+ if (iPos != SL_INVALID_POS)
+ if (SLAddItem(psl, pszNewItem2) == SL_INVALID_POS)
+ SLDeleteItem(psl, iPos);
+ LeaveCriticalSection(&psl->csListAccess);
+ return iPos;
+}
+
+int SLPrepareItem(LPSTRINGLIST psl, int cchLength)
+{
+ int iPos;
+
+ if (!psl) return SL_INVALID_POS;
+ EnterCriticalSection(&psl->csListAccess);
+ iPos = SLAddItem(psl, NULL); // empty item
+ if (iPos != SL_INVALID_POS)
+ if (!SLSetItemLength(psl, iPos, cchLength)) {
+ SLDeleteItem(psl, iPos);
+ iPos = SL_INVALID_POS;
+ }
+ LeaveCriticalSection(&psl->csListAccess);
+ return iPos;
+}
+
+/*
+BOOL SLAddItems(LPSTRINGLIST psl, const TCHAR* apszNewItems[], UINT uNewItemsCount)
+{
+ int i;
+ TCHAR** apszBuf;
+
+ if (!psl || !apszNewItems || (uNewItemsCount <= 0))
+ return FALSE;
+
+ EnterCriticalSection(&psl->csListAccess);
+
+ // Create/Resize the array
+ if (!psl->papszList) {
+ apszBuf = (TCHAR**)SLAlloc(uNewItemCount*sizeof(TCHAR*));
+ psl->uCount = 0; // just to be sure
+ } else {
+ apszBuf = (TCHAR**)SLReAlloc(psl->papszList, (psl->uCount+uNewItemCount)*sizeof(TCHAR*));
+ }
+
+ if (apszBuf) {
+ psl->papszList = apszBuf;
+ } else {
+ LeaveCriticalSection(&psl->csListAccess);
+ return FALSE;
+ }
+
+ // Insert the new string
+ for (i=SL_MIN_POS; i<=iNewItemCount; i++)
+ {
+ psl->papszList[psl->uCount+i] = (TCHAR*)SLAlloc((lstrlen(apszNewItems[i])+1)*sizeof(TCHAR));
+
+ if (psl->papszList[psl->uCount+i])
+ {
+ CopyMemory((PBYTE)psl->papszList[psl->uCount+i], apszNewItems[i], (lstrlen(apszNewItems[i])+1)*sizeof(TCHAR));
+ // Increase list count
+ psl->uCount++;
+
+ } else {
+ LeaveCriticalSection(&psl->csListAccess);
+ return FALSE;
+ }
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+ return TRUE;
+}
+*/
+
+BOOL SLDeleteItems(LPSTRINGLIST psl, int iPosFirst, int iPosLast)
+{
+ int i;
+ WCHAR** apszBuf;
+
+ if (!psl) return FALSE;
+ if (iPosLast < iPosFirst) {
+ i = iPosLast;
+ iPosLast = iPosFirst;
+ iPosFirst = i;
+ }
+ EnterCriticalSection(&psl->csListAccess);
+ if (!SLIsValid(psl, iPosFirst) || !SLIsValid(psl, iPosLast)) {
+ LeaveCriticalSection(&psl->csListAccess);
+ return FALSE;
+ }
+
+ for (i=iPosFirst; i<=iPosLast; i++)
+ SLFree(psl->papszList[i]);
+ if ((iPosLast+1) < (int)uMsgTypesCount)
+ MoveMemory(&pamtdMsgTypes[iPosFirst], &pamtdMsgTypes[iPosLast+1], (iPosLast-iPosFirst+1)*sizeof(WCHAR));
+
+ psl->uCount -= (iPosLast-iPosFirst+1);
+
+ if (psl->papszList && (psl->uCount > 0)) {
+ apszBuf = (WCHAR**)SLReAlloc(psl->papszList, psl->uCount*sizeof(WCHAR*));
+ if (apszBuf) psl->papszList = apszBuf;
+ } else {
+ if (psl->papszList) SLFree(psl->papszList);
+ psl->papszList = NULL;
+ psl->uCount = 0;
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+ return TRUE;
+}
+
+BOOL SLDeleteItem(LPSTRINGLIST psl, int iPos)
+{
+ return SLDeleteItems(psl, iPos, iPos);
+}
+
+BOOL SLSetItemLength(LPSTRINGLIST psl, int iPos, int cchLength)
+{
+ WCHAR* pszBuf;
+ int iOldLen;
+ BOOL bReturn;
+
+ if (!psl) return FALSE;
+ if (cchLength < 0) cchLength = 0;
+ EnterCriticalSection(&psl->csListAccess);
+ if (!SLIsValid(psl, iPos)) {
+ LeaveCriticalSection(&psl->csListAccess);
+ return FALSE;
+ }
+
+ iOldLen = lstrlen(psl->papszList[iPos]);
+ pszBuf = (WCHAR*)SLReAlloc(psl->papszList[iPos], (cchLength+1)*sizeof(WCHAR));
+ if (pszBuf) {
+ psl->papszList[iPos] = pszBuf;
+ if (cchLength <= iOldLen)
+ ZeroMemory(&pszBuf[cchLength], sizeof(WCHAR));
+ else
+ ZeroMemory(&pszBuf[iOldLen], (cchLength-iOldLen+1)*sizeof(WCHAR));
+ bReturn = TRUE;
+ } else {
+ bReturn = FALSE;
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+ return bReturn;
+}
+
+
+BOOL SLChangeItem(LPSTRINGLIST psl, int iPos, WCHAR* pszNewStr, BOOL bDoAppend)
+{
+ BOOL bReturn;
+ WCHAR* pszBuf;
+
+ if (!psl) return FALSE;
+ EnterCriticalSection(&psl->csListAccess);
+ if (!SLIsValid(psl, iPos)) {
+ LeaveCriticalSection(&psl->csListAccess);
+ return FALSE;
+ }
+
+ pszBuf = (WCHAR*)SLReAlloc(psl->papszList[iPos], ((pszNewStr?lstrlen(pszNewStr):0) + (bDoAppend?lstrlen(psl->papszList[iPos]):0) + 1)*sizeof(WCHAR));
+ if (pszBuf)
+ {
+ psl->papszList[iPos] = pszBuf;
+
+ if (bDoAppend && pszNewStr)
+ CopyMemory(&pszBuf[lstrlen(pszBuf)], pszNewStr, (lstrlen(pszNewStr)+1)*sizeof(WCHAR));
+ else if (pszNewStr)
+ CopyMemory(pszBuf, pszNewStr, (lstrlen(pszNewStr)+1)*sizeof(WCHAR));
+ else if (!bDoAppend)
+ ZeroMemory(psl->papszList[iPos], sizeof(WCHAR));
+
+ bReturn = TRUE;
+ } else {
+ if (!bDoAppend) ZeroMemory(psl->papszList[iPos], sizeof(WCHAR));
+ bReturn = FALSE;
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+ return bReturn;
+}
+
+
+
+BOOL SLItemPrintf(LPSTRINGLIST psl, int iPos, int cchArgMaxLen, ...)
+{
+ BOOL bReturn = FALSE;
+ WCHAR* pszBuf;
+ WCHAR* pszFmt;
+ va_list arglist;
+
+ if (!psl) return FALSE;
+ if (cchArgMaxLen < 0) cchArgMaxLen = 0;
+ EnterCriticalSection(&psl->csListAccess);
+ if (SLIsValid(psl, iPos))
+ {
+ pszFmt = (WCHAR*)SLAlloc((lstrlen(psl->papszList[iPos])+1)*sizeof(WCHAR));
+ if (pszFmt)
+ {
+ CopyMemory(pszFmt, psl->papszList[iPos], (lstrlen(psl->papszList[iPos])+1)*sizeof(WCHAR));
+ pszBuf = (WCHAR*)SLReAlloc(psl->papszList[iPos], (lstrlen(psl->papszList[iPos])+cchArgMaxLen+1)*sizeof(WCHAR));
+ if (pszBuf)
+ {
+ psl->papszList[iPos] = pszBuf;
+
+ va_start(arglist, cchArgMaxLen);
+ mir_vsntprintf(pszBuf, lstrlen(pszBuf)+cchArgMaxLen+1, pszFmt, arglist);
+ va_end(arglist);
+
+ bReturn = TRUE;
+ mir_free(pszFmt);
+ }
+ }
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+ return bReturn;
+}
+
+
+/*
+BOOL SLMoveItem(LPSTRINGLIST psl, int iOldPos, int iNewPos, BOOL bOnlySwitch)
+{
+ TCHAR* pszBuf;
+
+ if (!psl) return FALSE;
+
+ EnterCriticalSection(&psl->csListAccess);
+
+ if (!SLIsValid(psl, iOldPos) || !SLIsValid(psl, iNewPos)) {
+ LeaveCriticalSection(&psl->csListAccess);
+ return FALSE;
+ }
+
+ if (iNewPos == iOldPos) {
+ LeaveCriticalSection(&psl->csListAccess);
+ return TRUE;
+ }
+
+ if (bOnlySwitch)
+ {
+ // Switch the items
+ pszBuf = psl->papszList[iOldPos];
+ psl->papszList[iOldPos] = psl->papszList[iNewPos];
+ psl->papszList[iOldPos] = pszBuf;
+
+ } else {
+
+ int i;
+
+ // Cache the old item
+ pszBuf = psl->papszList[iOldPos];
+
+ if (iNewPos > iOldPos)
+ {
+ // Move items in between one down
+ for (i=iNewPos; i<iOldPos; i++)
+ psl->papszList[i+1] = psl->papszList[i];
+ } else {
+ // Move items in between one up
+ for (i=(iOldPos+1); i<=iNewPos; i++)
+ psl->papszList[i] = psl->papszList[i+1];
+ }
+
+ psl->papszList[iNewPos] = pszBuf;
+ }
+
+ LeaveCriticalSection(&psl->csListAccess);
+ return TRUE;
+}
+*/
+
+BOOL SLIsValid(LPSTRINGLIST psl, int iPos)
+{
+ BOOL bReturn;
+
+ if (!psl) return FALSE;
+ EnterCriticalSection(&psl->csListAccess);
+ bReturn = (psl->papszList && (iPos>=SL_MIN_POS) && (iPos<(int)psl->uCount));
+ LeaveCriticalSection(&psl->csListAccess);
+ return bReturn;
+}
+
+WCHAR* SLGetItem(LPSTRINGLIST psl, int iPos)
+{
+ WCHAR* pszReturn;
+
+ EnterCriticalSection(&psl->csListAccess); // other operations finished
+ pszReturn = SLIsValid(psl, iPos) ? psl->papszList[iPos] : NULL;
+ LeaveCriticalSection(&psl->csListAccess);
+ return pszReturn;
+}
+
+int SLGetMaxPos(LPSTRINGLIST psl)
+{
+ int iReturn;
+
+ if (!psl) return SL_INVALID_POS;
+ EnterCriticalSection(&psl->csListAccess); // other operations finished
+ iReturn = (psl->uCount > 0) ? (int)(psl->uCount-1) : SL_INVALID_POS;
+ LeaveCriticalSection(&psl->csListAccess);
+ return iReturn;
+}
+
+
+int SLGetItemCount(LPSTRINGLIST psl)
+{
+ int iReturn;
+ if (!psl) return 0;
+
+ EnterCriticalSection(&psl->csListAccess); // other operations finished
+ iReturn = (int)psl->uCount;
+ LeaveCriticalSection(&psl->csListAccess);
+ return iReturn;
+}
+
+
+int SLGetSize(LPSTRINGLIST psl, BOOL bAlsoTerminatingZeros) // character count size
+{
+ int i, iReturn;
+
+ if (!psl) return 0;
+ EnterCriticalSection(&psl->csListAccess);
+ iReturn = 0;
+ for (i=SL_MIN_POS; i<(int)psl->uCount; i++)
+ iReturn += lstrlen(psl->papszList[i]);
+ if (bAlsoTerminatingZeros)
+ iReturn += (int)psl->uCount;
+ LeaveCriticalSection(&psl->csListAccess);
+ return iReturn;
+}
+
+BOOL SLClearList(LPSTRINGLIST psl)
+{
+ int i;
+
+ if (!psl) return FALSE;
+ EnterCriticalSection(&psl->csListAccess);
+
+ // Free string memory
+ for (i=SL_MIN_POS; i<(int)psl->uCount; i++)
+ SLFree(psl->papszList[i]);
+
+ // Free the array
+ psl->uCount = 0;
+ if (psl->papszList) SLFree(psl->papszList);
+ psl->papszList = NULL;
+
+ LeaveCriticalSection(&psl->csListAccess);
+ return TRUE;
+}
+
+/*
+BOOL SLBlockList(LPSTRINGLIST psl)
+{
+ if (!psl) return FALSE;
+ EnterCriticalSection(&psl->csListAccess);
+ return TRUE;
+}
+
+BOOL SLUnBlockList(LPSTRINGLIST psl)
+{
+ if (!psl) return FALSE;
+ LeaveCriticalSection(&psl->csListAccess);
+ return TRUE;
+}
+*/
+
+LPSTRINGLIST SLNewList(void)
+{
+ LPSTRINGLIST psl = (LPSTRINGLIST)SLAlloc(sizeof(STRINGLIST));
+ psl->papszList = NULL;
+ psl->uCount = 0;
+ InitializeCriticalSection(&psl->csListAccess);
+ return psl;
+
+}
+
+void SLFreeList(LPSTRINGLIST psl)
+{
+ if (!psl) return;
+ SLClearList(psl);
+ DeleteCriticalSection(&psl->csListAccess);
+ SLFree(psl);
+} \ No newline at end of file
diff --git a/spamfilter/stringlist.h b/spamfilter/stringlist.h
new file mode 100644
index 0000000..ec10883
--- /dev/null
+++ b/spamfilter/stringlist.h
@@ -0,0 +1,81 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_STRINGLIST_H__
+#define SF_STRINGLIST_H__ 1
+
+
+// -- Structs
+typedef struct {
+ UINT uCount; // protected by csListAccess
+ WCHAR** papszList; // protected by csListAccess
+ CRITICAL_SECTION csListAccess;
+} STRINGLIST, *LPSTRINGLIST;
+
+// -- Defines
+#define SL_MIN_POS 0
+#define SL_INVALID_POS (-1)
+
+// -- Prototypes
+LPSTRINGLIST SLNewList(void);
+void SLFreeList(LPSTRINGLIST psl);
+BOOL SLClearList(LPSTRINGLIST psl);
+BOOL SLMergeList(LPSTRINGLIST pslDest, LPSTRINGLIST pslFrom);
+
+int SLGetMaxPos(LPSTRINGLIST psl);
+int SLGetSize(LPSTRINGLIST psl, BOOL bAlsoTerminatingZeros); // character count size
+int SLGetItemCount(LPSTRINGLIST psl);
+WCHAR* SLGetItem(LPSTRINGLIST psl, int iPos);
+BOOL SLIsValid(LPSTRINGLIST psl, int iPos);
+
+BOOL SLMoveItem(LPSTRINGLIST psl, int iOldPos, int iNewPos, BOOL bOnlySwitch);
+BOOL SLItemPrintf(LPSTRINGLIST psl, int iPos, int cchArgMaxLen, ...);
+BOOL SLSetItemLength(LPSTRINGLIST psl, int iPos, int cchLength);
+BOOL SLChangeItem(LPSTRINGLIST psl, int iPos, WCHAR* pszNewStr, BOOL bDoAppend);
+
+BOOL SLDeleteItem(LPSTRINGLIST psl, int iPos);
+BOOL SLDeleteItems(LPSTRINGLIST psl, int iPosFirst, int iPosLast);
+
+BOOL SLAddItems(LPSTRINGLIST psl, const WCHAR* apszNewItems[], UINT uNewItemsCount);
+int SLAddItem(LPSTRINGLIST psl, const WCHAR* pszNewItem);
+int SLAddItemPair(LPSTRINGLIST psl, const WCHAR* pszNewItem1, const WCHAR* pszNewItem2);
+int SLPrepareItem(LPSTRINGLIST psl, int cchLength);
+
+BOOL SLIsItem(LPSTRINGLIST psl, const WCHAR* pszItem, BOOL bCaseSensitive);
+int SLFindItem(LPSTRINGLIST psl, const WCHAR* pszItem, BOOL bCaseSensitive, int iStartPos);
+
+WCHAR* SLConvertToString(LPSTRINGLIST psl, const WCHAR* pszSeparator, BOOL bAlsoDoFirst, BOOL bAlsoDoLast, int* piSize);
+void SLFreeReturn(void* pszPntr);
+BOOL SLConvertToStringBuf(LPSTRINGLIST psl, WCHAR* pszOutput, int cchMaxSize, const WCHAR* pszSeparator, BOOL bAlsoDoFirst, BOOL bAlsoDoLast);
+BOOL SLConvertFromString(LPSTRINGLIST psl, const WCHAR* pszString, int cchSize, const WCHAR* pszSeparator, BOOL bIgnoreEmptyItems);
+
+BOOL SLLoadFromFile(LPSTRINGLIST psl, const WCHAR* pszSearchPath);
+BOOL SLSaveToFile(LPSTRINGLIST psl, const WCHAR* pszFileName);
+BOOL SLLoadFromIniFile(LPSTRINGLIST psl, const WCHAR* pszSearchPath, const WCHAR* pszSection, const WCHAR* pszSettingPrefix);
+BOOL SLSaveToIniFile(LPSTRINGLIST psl, const WCHAR* pszFileName, const WCHAR* pszSection, const WCHAR* pszSettingPrefix);
+
+BOOL SLRetrieveFromListBoxCtrl(LPSTRINGLIST psl, HWND hwndListBox);
+BOOL SLAssignToListBoxCtrl(LPSTRINGLIST psl, HWND hwndListBox);
+
+BOOL SLBlockList(LPSTRINGLIST psl);
+BOOL SLUnBlockList(LPSTRINGLIST psl);
+
+#endif // SF_STRINGLIST_H__ \ No newline at end of file
diff --git a/spamfilter/trigger.c b/spamfilter/trigger.c
new file mode 100644
index 0000000..94ff0f3
--- /dev/null
+++ b/spamfilter/trigger.c
@@ -0,0 +1,63 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -----------------------------------------
+
+void TriggerIncomingSpam(void)
+{
+ if (ServiceExists(MS_TRIGGER_REGISTERTRIGGER) && ServiceExists(MS_TRIGGER_REPORTACTION))
+ {
+ REPORT_INFO ri;
+
+ ZeroMemory(&ri, sizeof(ri));
+ ri.cbSize = sizeof(ri);
+ ri.pszName = "Spam Filter: Incoming Spam"; // may be NULL if triggerID!=0
+ ri.flags = TRG_PERFORM;
+
+ CallService(MS_TRIGGER_REPORTACTION, 0, (LPARAM)&ri);
+ }
+}
+
+
+// ------------------------------------
+
+
+void InitTrigger(void)
+{
+ if (ServiceExists(MS_TRIGGER_REGISTERTRIGGER) && ServiceExists(MS_TRIGGER_REPORTACTION))
+ {
+ TRIGGERREGISTER tr;
+
+ ZeroMemory(&tr, sizeof(tr));
+ tr.cbSize = sizeof(tr);
+ tr.pszName = "Spam Filter: Incoming Spam"; // identifier; no translate!
+ CallService(MS_TRIGGER_REGISTERTRIGGER, 0, (LPARAM)&tr);
+ }
+}
+
+
+void UninitTrigger(void)
+{
+} \ No newline at end of file
diff --git a/spamfilter/trigger.h b/spamfilter/trigger.h
new file mode 100644
index 0000000..3438fd8
--- /dev/null
+++ b/spamfilter/trigger.h
@@ -0,0 +1,32 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_TRIGGER_H__
+#define SF_TRIGGER_H__ 1
+
+// -- Prototypes
+void TriggerIncomingSpam(void);
+
+void InitTrigger(void);
+void UninitTrigger(void);
+
+
+#endif // SF_TRIGGER_H__
diff --git a/spamfilter/utilities.c b/spamfilter/utilities.c
new file mode 100644
index 0000000..013c806
--- /dev/null
+++ b/spamfilter/utilities.c
@@ -0,0 +1,629 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+
+// -----------------------------------------
+
+// -- Error Output --
+
+static void ThreadMessageBox(void* arg)
+{
+ MSGBOXPARAMS* pmbp = (MSGBOXPARAMS*)arg;
+
+ MessageBoxIndirect(pmbp);
+
+ mir_free((WCHAR*)pmbp->lpszCaption);
+ mir_free((WCHAR*)pmbp->lpszText);
+ mir_free(pmbp);
+}
+
+
+void ShowInfoMessage(UINT uIconID, const WCHAR* pszCaption, const WCHAR* pszTextFmt, size_t cchExtraSize, ...)
+{
+ // Params:
+ // uIconID - NIIF_INFO, NIIF_WARNING or NIIF_ERROR
+ // pszCaption - Only 64chars of it will be used.
+ // pszTextFmt - Only 256chars of it will be used.
+
+ va_list va;
+ WCHAR* pszText;
+ BOOL bErrorShown = FALSE;
+
+ if (!pszCaption || !pszTextFmt || Miranda_Terminated()) return;
+ pszText = mir_alloc((lstrlen(pszTextFmt)+((cchExtraSize>0)?cchExtraSize:0)+1)*sizeof(WCHAR));
+ if (!pszText) return;
+
+ if (cchExtraSize <= 0) {
+ mir_sntprintf(pszText, lstrlen(pszTextFmt)+1, _T("%s"), pszTextFmt);
+ } else {
+ va_start(va, cchExtraSize);
+ mir_vsntprintf(pszText, lstrlen(pszTextFmt)+((cchExtraSize>0)?cchExtraSize:0)+1, pszTextFmt, va);
+ va_end(va);
+ }
+
+ if (ServiceExists(MS_CLIST_SYSTRAY_NOTIFY))
+ {
+ // Output balloon tip if supported
+ MIRANDASYSTRAYNOTIFY msn;
+
+ ZeroMemory(&msn, sizeof(msn)); // forward compatibility
+ msn.cbSize = sizeof(msn);
+ #if defined(UNICODE)
+ // SystrayNotify does not support Unicode
+ // We need to convert Unicode->Ansi
+ mir_utf8decode(msn.szInfoTitle,&pszCaption);
+ mir_utf8decode(msn.szInfo,&pszText);
+ #else
+ msn.szInfoTitle = (char*)pszCaption;
+ msn.szInfo = (char*)pszText;
+ #endif
+ //msn.szProto = NULL;
+ msn.uTimeout = 20000; // 30 sec is current max timeout that is possible
+ if (uIconID == NIIF_ERROR)
+ msn.dwInfoFlags = NIIF_ERROR;
+ else if (uIconID == NIIF_WARNING)
+ msn.dwInfoFlags = NIIF_WARNING;
+ else
+ msn.dwInfoFlags = NIIF_INFO;
+
+ // I don't know if service is thread safe therefor using sync
+ bErrorShown = (CallServiceSync(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&msn) == 0);
+
+ #if defined(UNICODE)
+ if (msn.szInfoTitle) mir_free(msn.szInfoTitle);
+ if (msn.szInfo) mir_free(msn.szInfo);
+ #endif
+
+
+ #if defined(UNICODE)
+ } else if (ServiceExists(MS_POPUP_ADDPOPUPW))
+ {
+ // Output a popup message as alternative
+ POPUPDATAW ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ mir_sntprintf(ppd.lpwzContactName, ARRAYSIZE(ppd.lpwzContactName), _T("%s"), pszCaption);
+ mir_sntprintf(ppd.lpwzText, ARRAYSIZE(ppd.lpwzText), _T("%s"), pszText);
+ //ppd.iSeconds = 0;
+ //ppd.lchContact = NULL;
+ //ppd.colorText = GetSysColor(COLOR_INFOTEXT); // or GetSysColor(COLOR_WINDOWTEXT)
+ //ppd.colorBack = GetSysColor(COLOR_INFOBK); // or GetSysColor(COLOR_3DFACE)
+ if (uIconID == NIIF_WARNING)
+ ppd.lchIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_WARNING), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+ else if (uIconID == NIIF_ERROR)
+ ppd.lchIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_ERROR), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+ else
+ ppd.lchIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_INFORMATION), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+
+ // I don't know if popup module author made it thread safe...
+ bErrorShown = (CallServiceSync(MS_POPUP_ADDPOPUPW, (WPARAM)&ppd, 0) >= 0);
+ }
+ #else
+ } else if (ServiceExists(MS_POPUP_ADDPOPUPEX))
+ {
+ // Output a popup message as alternative
+ POPUPDATAEX ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ mir_snprintf(ppd.lpzContactName, ARRAYSIZE(ppd.lpzContactName), "%s", pszCaption);
+ mir_snprintf(ppd.lpzText, ARRAYSIZE(ppd.lpzText), "%s", pszText);
+ //ppd.iSeconds = 0;
+ //ppd.lchContact = NULL;
+ //ppd.colorText = GetSysColor(COLOR_INFOTEXT); // or GetSysColor(COLOR_WINDOWTEXT)
+ //ppd.colorBack = GetSysColor(COLOR_INFOBK); // or GetSysColor(COLOR_3DFACE)
+
+ if (ServiceExists(MS_POPUP_ADDCLASS)) {
+ //ppd.skinBack = ppd.colorBack;
+ ppd.colorBack = POPUP_USE_SKINNED_BG;
+ }
+
+ if (uIconID == NIIF_WARNING) {
+ ppd.lchIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_WARNING), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+
+ if (ppd.colorBack == POPUP_USE_SKINNED_BG)
+ ppd.lpzClass = (LPCTSTR)POPUP_CLASS_NOTIFY; // type "LPCTSTR" of lpzClass is wrong!
+
+ } else if (uIconID == NIIF_ERROR) {
+ ppd.lchIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_ERROR), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+
+ if (ppd.colorBack == POPUP_USE_SKINNED_BG)
+ ppd.lpzClass = (LPCTSTR)POPUP_CLASS_WARNING; // type "LPCTSTR" of lpzClass is wrong!
+
+ } else {
+ ppd.lchIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_INFORMATION), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+
+ if (ppd.colorBack == POPUP_USE_SKINNED_BG)
+ ppd.lpzClass = (LPCTSTR)POPUP_CLASS_DEFAULT; // type "LPCTSTR" of lpzClass is wrong!
+ }
+
+ bErrorShown = (CallServiceSync(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd, 0) >= 0); // I don't know if popup module author made it thread safe...
+ }
+ #endif
+
+ if (!bErrorShown)
+ {
+ // Output normal message dialog when popup plugin is not installed
+ // or when Windows doesn't support balloon tips
+ // or if an popup error occured
+
+ MSGBOXPARAMS* pmbp = (MSGBOXPARAMS*)mir_alloc(sizeof(MSGBOXPARAMS));
+ if (pmbp)
+ {
+ ZeroMemory(pmbp, sizeof(MSGBOXPARAMS));
+ pmbp->cbSize = sizeof(MSGBOXPARAMS);
+ pmbp->lpszCaption = mir_wstrdup(pszCaption); // will be freed by thread
+ pmbp->lpszText = pszText; // will be freed by thread
+ pszText = NULL;
+ pmbp->hwndOwner = NULL;
+ pmbp->dwStyle = MB_OK|MB_SETFOREGROUND|MB_TASKMODAL;
+ pmbp->dwLanguageId = LANGIDFROMLCID(GetThreadLocale());
+ if (uIconID == NIIF_WARNING)
+ pmbp->dwStyle |= MB_ICONWARNING;
+ else if (uIconID == NIIF_ERROR)
+ pmbp->dwStyle |= MB_ICONERROR;
+ else
+ pmbp->dwStyle |= MB_ICONINFORMATION;
+
+ forkthread(ThreadMessageBox, 0, pmbp);
+ }
+ }
+
+ if (pszText) mir_free(pszText);
+}
+
+
+WCHAR* GetLastErrorDescription(DWORD dwLastError)
+{
+ WCHAR* pszReturn;
+
+ #if defined(UNICODE)
+
+ #if defined(MICROSOFT_LAYER_FOR_UNICODE)
+ char* pszLastError = NULL;
+
+ // FormatMessageW does not work with UnicoWS layer on Win9x/ME
+ if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPSTR)&pszLastError, 0, NULL) != 0) {
+ pszReturn = mir_utf8encodeW(pszLastError);
+ LocalFree(pszLastError);
+ } else {
+ pszReturn = NULL;
+ }
+
+ #else
+ WCHAR* pwszLastError;
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPWSTR)&pwszLastError, 0, NULL) != 0) {
+ pszReturn = mir_wstrdup(pwszLastError);
+ LocalFree(pwszLastError);
+ } else {
+ pszReturn = NULL;
+ }
+ #endif
+
+ #else
+ WCHAR* pszLastError;
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPTSTR)&pszLastError, 0, NULL) != 0) {
+ pszReturn = mir_strdup(pszLastError);
+ LocalFree(pszLastError);
+ } else {
+ pszReturn = NULL;
+ }
+ #endif
+
+ return pszReturn;
+}
+
+
+// -- Sounds --
+
+void SkinAddNewSoundBundled(const char* pszName, const char* pszSection, const char* pszDescription, const WCHAR* pszDefaultFile)
+{
+ SKINSOUNDDESCEX ssd;
+ WCHAR szFileName[MAX_PATH];
+ char szFileNameFin[MAX_PATH];
+
+ ZeroMemory(&ssd, sizeof(ssd));
+ ssd.cbSize = sizeof(ssd);
+ ssd.pszSection = (char*)pszSection;
+ ssd.pszName = (char*)pszName;
+ ssd.pszDescription = (char*)pszDescription;
+
+ PConstructLocalPath(szFileName, CSIDL_EXT_EXECUTABLE, SOUNDS_SUBDIRECTORY, (WCHAR*)pszDefaultFile, NULL);
+ if (PathFileExists(szFileName))
+ {
+ #if defined(UNICODE)
+ WCHAR* pszBuf;
+ mir_utf8decode((char*)szFileName,&pszBuf);
+ if (pszBuf) {
+ if (CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)pszBuf, (LPARAM)szFileNameFin) <= 0)
+ mir_snprintf(szFileNameFin, ARRAYSIZE(szFileNameFin), "%s", pszBuf);
+ ssd.pszDefaultFile = szFileNameFin;
+ mir_free(pszBuf);
+ }
+ #else
+ if (CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)szFileName, (LPARAM)szFileNameFin) <= 0)
+ ssd.pszDefaultFile = szFileName;
+ else
+ ssd.pszDefaultFile = szFileNameFin;
+ #endif
+ }
+
+ CallService(MS_SKIN_ADDNEWSOUND, 0, (LPARAM)&ssd);
+}
+
+
+// -- Icons --
+
+void SkinAddNewIcon(const char* pszName, const WCHAR* pszSection, const WCHAR* pszDescription, int iDefResID, BOOL bLarge)
+{
+ // Support for IcoLib
+ if (ServiceExists(MS_SKIN2_ADDICON) && ServiceExists(MS_SKIN2_GETICON))
+ {
+ SKINICONDESC sid;
+
+ ZeroMemory(&sid, sizeof(sid));
+ sid.cbSize = sizeof(sid);
+ sid.ptszSection = (WCHAR*)pszSection;
+ sid.ptszDescription = (WCHAR*)pszDescription;
+ sid.pszName = (char*)pszName;
+ sid.hDefaultIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(iDefResID), IMAGE_ICON, bLarge?0:GetSystemMetrics(SM_CXSMICON), bLarge?0:GetSystemMetrics(SM_CYSMICON), (bLarge?LR_DEFAULTSIZE:0)|LR_SHARED);
+ sid.cx = GetSystemMetrics(bLarge?SM_CXICON:SM_CXSMICON);
+ sid.cy = GetSystemMetrics(bLarge?SM_CYICON:SM_CYSMICON);
+ sid.flags = SIDF_UNICODE;
+
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+}
+
+
+HICON SkinGetIcon(const char* pszName, int iDefResID, BOOL bLarge)
+{
+ // Support for IcoLib
+ if (ServiceExists(MS_SKIN2_GETICON) && ServiceExists(MS_SKIN2_ADDICON)) {
+ HICON hIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)pszName);
+ if (hIcon) return hIcon;
+ }
+ if (iDefResID <= 0) return NULL;
+ return (HICON)LoadImage(hInstance, MAKEINTRESOURCE(iDefResID), IMAGE_ICON, bLarge?0:GetSystemMetrics(SM_CXSMICON), bLarge?0:GetSystemMetrics(SM_CYSMICON), (bLarge?LR_DEFAULTSIZE:0)|LR_SHARED);
+}
+
+
+// -- Hotkeys --
+
+/*
+void SkinAddNewHotkey(const char* pszName, const char* pszSection, const char* pszDescription, const int iDefHotkey, const char* pszServiceName)
+{
+ // Support for CList_MW
+ if (ServiceExists(MS_SKIN_ADDHOTKEY))
+ {
+ SKINHOTKEYDESCEX shd;
+
+ ZeroMemory(&shd, sizeof(shd));
+ shd.cbSize = sizeof(shd);
+ shd.pszName = (char*)pszName;
+ shd.pszDescription = (char*)pszDescription;
+ shd.pszSection = (char*)pszSection;
+ shd.pszService = (char*)pszServiceName;
+ shd.DefHotKey = iDefHotkey;
+
+ CallService(MS_SKIN_ADDHOTKEY, 0, (LPARAM)&shd);
+ }
+}
+*/
+
+
+// -- Fonts --
+
+/*
+void SkinAddNewFont(const char* pszName, const char* pszSection, const char* pszDescription, COLORREF clrColor, const char* pszFace, int iSize, DWORD dwStyleFlags)
+{
+ if (pszName && ServiceExists(MS_FONT_REGISTER))
+ {
+ FontID fid;
+
+ ZeroMemory(&fid, sizeof(fid));
+ fid.cbSize = sizeof(fid);
+ mir_snprintf(fid.name, ARRAYSIZE(fid.name), "%s", pszName);
+ mir_snprintf(fid.group, ARRAYSIZE(fid.group), "%s", pszSection ? pszSection : Translate("Other"));
+ mir_snprintf(fid.dbSettingsGroup, ARRAYSIZE(fid.dbSettingsGroup), "%s", DB_MODULE_NAME);
+ mir_snprintf(fid.prefix, ARRAYSIZE(fid.prefix), "%s", "Font_");
+ //fid.order = 0;
+ fid.flags = FIDF_DEFAULTVALID|FIDF_ALLOWEFFECTS;
+ fid.deffontsettings.charset = DEFAULT_CHARSET;
+ fid.deffontsettings.colour = clrColor;
+ fid.deffontsettings.size = (iSize > 0)?iSize:12;
+ fid.deffontsettings.style = dwStyleFlags;
+ mir_snprintf(fid.deffontsettings.szFace, ARRAYSIZE(fid.deffontsettings.szFace), "%s", pszFace ? pszFace : "MS Sans Serif");
+
+ CallService(MS_FONT_REGISTER, (WPARAM)&fid, 0);
+ }
+}
+
+
+BOOL SkinAddNewFontSameAs(const char* pszDescription, const char* pszSection, const char* pszSameAsDescription)
+{
+ if (ServiceExists(MS_FONT_REGISTER))
+ {
+ FontID fid;
+ LOGFONT lf;
+
+ ZeroMemory(&fid, sizeof(fid));
+ fid.cbSize = sizeof(fid);
+ mir_snprintf(fid.name, ARRAYSIZE(fid.name), "%s", pszDescription);
+ mir_snprintf(fid.group, ARRAYSIZE(fid.group), "%s", pszSection ? pszSection : Translate("Other"));
+ mir_snprintf(fid.dbSettingsGroup, ARRAYSIZE(fid.dbSettingsGroup), "%s", DB_MODULE_NAME);
+ mir_snprintf(fid.prefix, ARRAYSIZE(fid.name), "%s", "Font_");
+ //fid.order = 0;
+ fid.flags = FIDF_ALLOWEFFECTS;
+ if (pszSameAsDescription)
+ {
+ ZeroMemory(&lf, sizeof(lf)); // not good; SkinGetFont has no error indicating value (workaround)
+
+ fid.deffontsettings.colour = SkinGetFont(pszSameAsDescription, &lf);
+ fid.deffontsettings.charset = lf.lfCharSet;
+ fid.deffontsettings.size = (char)lf.lfHeight;
+ fid.deffontsettings.style = 0;
+
+ mir_snprintf(fid.deffontsettings.szFace, ARRAYSIZE(fid.deffontsettings.szFace), "%s", lf.lfFaceName);
+
+ if (lf.lfItalic) fid.deffontsettings.style = DBFONTF_ITALIC;
+ if (lf.lfWeight) fid.deffontsettings.style = DBFONTF_BOLD;
+ if (lf.lfUnderline) fid.deffontsettings.style = DBFONTF_UNDERLINE;
+ if (lf.lfStrikeOut) fid.deffontsettings.style = DBFONTF_STRIKEOUT;
+
+ fid.flags |= FIDF_DEFAULTVALID;
+
+ CallService(MS_FONT_REGISTER, (WPARAM)&fid, 0);
+ return TRUE;
+ } else {
+ CallService(MS_FONT_REGISTER, (WPARAM)&fid, 0);
+ return FALSE;
+ }
+ }
+}
+
+
+BOOL SkinGetFont(const char* pszDescription, const char* pszSection, COLORREF* pclrColor, LOGFONT* plfFont)
+{
+ if (pszName && pclrColor && plfFont && ServiceExists(MS_FONT_GET))
+ {
+ FontID fid;
+
+ mir_snprintf(fid.name, 64, "%s", pszDescription);
+ mir_snprintf(fid.group, 64, "%s", pszSection ? pszSection : Translate("Other"));
+
+ *pclrColor = (COLORREF)CallService(MS_FONT_GET, (WPARAM)&fid, (LPARAM)plfFont);
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+*/
+
+
+// -- Colors --
+
+void SkinAddNewColor(const char* pszDbName, const char* pszSection, const char* pszDescription, COLORREF clrDefColor, BOOL bFontRelated)
+{
+ if (!pszSection || (pszDbName && !pszDescription)) return;
+
+ // Support for FontService
+ if (bFontRelated && ServiceExists(MS_COLOUR_REGISTER) && ServiceExists(MS_COLOUR_GET))
+ {
+ ColourID cid;
+
+ ZeroMemory(&cid, sizeof(cid));
+ cid.cbSize = sizeof(cid);
+ cid.defcolour = clrDefColor;
+ mir_snprintf(cid.dbSettingsGroup, ARRAYSIZE(cid.dbSettingsGroup), "%s", DB_MODULE_NAME);
+ mir_snprintf(cid.group, ARRAYSIZE(cid.group), "%s", pszSection);
+ mir_snprintf(cid.setting, ARRAYSIZE(cid.setting), "%s", pszDbName?pszDbName:"BkgColour");
+ mir_snprintf(cid.name, ARRAYSIZE(cid.name), "%s", pszDbName?pszDescription:Translate("Background"));
+
+ CallService(MS_COLOUR_REGISTER, (WPARAM)&cid, 0);
+ return;
+ }
+
+}
+
+
+COLORREF SkinGetColor(const char* pszDbName, const char* pszDescription, const char* pszSection, COLORREF clrDefColor, BOOL bFontRelated)
+{
+ if (!pszSection || (pszDbName && !pszDescription)) return clrDefColor;
+
+ // Support for FontService
+ if (bFontRelated && ServiceExists(MS_COLOUR_REGISTER) && ServiceExists(MS_COLOUR_GET))
+ {
+ ColourID cid;
+
+ ZeroMemory(&cid, sizeof(cid));
+ cid.cbSize = sizeof(cid);
+ mir_snprintf(cid.setting, ARRAYSIZE(cid.setting), "%s", pszDbName?pszDbName:"BkgColour");
+ mir_snprintf(cid.group, ARRAYSIZE(cid.group), "%s", pszSection);
+ mir_snprintf(cid.name, ARRAYSIZE(cid.name), "%s", pszDescription?(char*)pszDescription:Translate("Background"));
+
+ return (COLORREF)CallService(MS_COLOUR_GET, (WPARAM)&cid, 0);
+ }
+
+ return clrDefColor;
+}
+
+
+// -- System Icons --
+
+HIMAGELIST LoadSystemCheckboxImageList(void)
+{
+ HWND hwndTreeView;
+ HIMAGELIST hImageList, hImgLstBuf;
+ INITCOMMONCONTROLSEX icc;
+
+ ZeroMemory(&icc, sizeof(icc));
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_TREEVIEW_CLASSES;
+ InitCommonControlsEx(&icc);
+
+ // Create hidden treeview
+ hwndTreeView = CreateWindowEx(0, WC_TREEVIEW, NULL, 0, 0, 0, 0, 0, NULL, 0, hInstance, NULL);
+ if (!hwndTreeView) return NULL;
+ SetWindowLongPtr(hwndTreeView, GWL_STYLE, (LONG_PTR)TVS_CHECKBOXES); // Style needs to be set after creating
+
+ // Get imagelist
+ hImgLstBuf = TreeView_GetImageList(hwndTreeView, TVSIL_STATE);
+ hImageList = ImageList_Duplicate(hImgLstBuf);
+
+ // Destroy treeview
+ TreeView_SetImageList(hwndTreeView, NULL, TVSIL_STATE); // Avoiding Access Violation in CommonControls DLL
+ ImageList_Destroy(hImgLstBuf);
+ DestroyWindow(hwndTreeView);
+
+ return hImageList;
+}
+
+
+HICON LoadSystemIcon(int iListID, int iIconIndex) // Returned icon needs to be destroyed using DestroyIcon()
+{
+/*
+Index values for IDB_HIST_LARGE_COLOR and IDB_HIST_SMALL_COLOR:
+(Microsoft Windows Explorer bitmaps)
+HIST_ADDTOFAVORITES
+Add to favorites.
+HIST_BACK
+Move back.
+HIST_FAVORITES
+Open favorites folder.
+HIST_FORWARD
+Move forward.
+HIST_VIEWTREE
+View tree.
+
+Index values for IDB_STD_LARGE_COLOR and IDB_STD_SMALL_COLOR:
+(Standard bitmaps)
+STD_COPY
+Copy operation.
+STD_CUT
+Cut operation.
+STD_DELETE
+Delete operation.
+STD_FILENEW
+New file operation.
+STD_FILEOPEN
+Open file operation.
+STD_FILESAVE
+Save file operation.
+STD_FIND
+Find operation.
+STD_HELP
+Help operation.
+STD_PASTE
+Paste operation.
+STD_PRINT
+Print operation.
+STD_PRINTPRE
+Print preview operation.
+STD_PROPERTIES
+Properties operation.
+STD_REDOW
+Redo operation.
+STD_REPLACE
+Replace operation.
+STD_UNDO
+Undo operation.
+
+Index values for IDB_VIEW_LARGE_COLOR and IDB_VIEW_SMALL_COLOR:
+(View bitmaps)
+VIEW_DETAILS
+Details view.
+VIEW_LARGEICONS
+Large icons view.
+VIEW_LIST
+List view.
+VIEW_NETCONNECT
+Connect to network drive.
+VIEW_NETDISCONNECT
+Disconnect from network drive.
+VIEW_NEWFOLDER
+New folder.
+VIEW_PARENTFOLDER
+Go to parent folder.
+VIEW_SMALLICONS
+Small icon view.
+VIEW_SORTDATE
+Sort by date.
+VIEW_SORTNAME
+Sort by name.
+VIEW_SORTSIZE
+Sort by size.
+VIEW_SORTTYPE
+Sort by type.
+*/
+
+ HWND hwndToolbar;
+ HICON hIcon;
+ INITCOMMONCONTROLSEX icc;
+
+ ZeroMemory(&icc, sizeof(icc));
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_BAR_CLASSES;
+ InitCommonControlsEx(&icc);
+
+ // Create hidden toolbar
+ hwndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, _T(""), 0, 0, 0, 0, 0, NULL, 0, hInstance, NULL);
+ if (!hwndToolbar) return NULL;
+ SendMessage(hwndToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
+
+ // Get icon
+ SendMessage(hwndToolbar, TB_LOADIMAGES, (WPARAM)iListID, (LPARAM)HINST_COMMCTRL);
+ hIcon = ImageList_GetIcon((HIMAGELIST)SendMessage(hwndToolbar, TB_GETIMAGELIST, 0, 0), iIconIndex, ILD_TRANSPARENT);
+
+ // Destroy toolbar
+ DestroyWindow(hwndToolbar);
+
+ return hIcon;
+}
+
+
+// -- Icon Overlay --
+
+HICON OverlayIcon(HICON hMainIcon, HICON hOverlayIcon, int cxWidth, int cyHeight)
+{
+ HICON hIconReturn;
+ HIMAGELIST himlIconList, himlIconListBuf; // New image lists
+ int iMainIcon, iOverlayIcon;
+
+ if ((cxWidth < 0) || (cyHeight < 0) || !hMainIcon || !hOverlayIcon)
+ return NULL;
+
+ himlIconList = ImageList_Create((cxWidth==0)?GetSystemMetrics(SM_CXICON):cxWidth, (cyHeight==0)?GetSystemMetrics(SM_CYICON):cyHeight, (IsWinVerXPPlus()?ILC_COLOR32:ILC_COLOR16)|ILC_MASK,2, 0);
+ iMainIcon = ImageList_AddIcon(himlIconList, hMainIcon);
+ iOverlayIcon = ImageList_AddIcon(himlIconList, hOverlayIcon);
+
+ himlIconListBuf = ImageList_Merge(himlIconList, iMainIcon, himlIconList, iOverlayIcon, 0, 0);
+ ImageList_Destroy(himlIconList);
+
+ hIconReturn = ImageList_GetIcon(himlIconListBuf, iMainIcon, ILD_TRANSPARENT);
+ ImageList_Destroy(himlIconListBuf);
+
+ return hIconReturn; // returned HICON needs to be destroyed (DestroyIcon())
+} \ No newline at end of file
diff --git a/spamfilter/utilities.h b/spamfilter/utilities.h
new file mode 100644
index 0000000..0e68a79
--- /dev/null
+++ b/spamfilter/utilities.h
@@ -0,0 +1,45 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_UTILITIES_H__
+#define SF_UTILITIES_H__ 1
+
+
+// -- Prototypes
+void ShowInfoMessage(UINT uIconID, const WCHAR* pszCaption, const WCHAR* pszTextFmt, size_t cchExtraSize,...);
+WCHAR* GetLastErrorDescription(DWORD dwLastError);
+
+void SkinAddNewSoundBundled(const char* pszName, const char* pszSection, const char* pszDescription, const WCHAR* pszDefaultFile);
+void SkinAddNewIcon(const char* pszName, const WCHAR* pszSection, const WCHAR* pszDescription, int iDefResID, BOOL bLarge);
+HICON SkinGetIcon(const char* pszDescription, int iDefResID, BOOL bLarge);
+void SkinAddNewHotkey(const char* pszName, const char* pszSection, const char* pszDescription, const int iDefHotkey, const char* pszServiceName);
+void SkinAddNewFont(const char* pszName, const char* pszSection, const char* pszDescription, const char* pszDefaultFile);
+BOOL SkinAddNewFontSameAs(const char* pszDescription, const char* pszSection, const char* pszSameAsDescription);
+BOOL SkinGetFont(const char* pszDescription, const char* pszSection, COLORREF* pclrColor, LOGFONT* plfFont);
+void SkinAddNewColor(const char* pszName, const char* pszSection, const char* pszDescription, COLORREF clrDefColor, BOOL bFontRelated);
+COLORREF SkinGetColor(const char* pszDbName, const char* pszDescription, const char* pszSection, COLORREF clrDefColor, BOOL bFontRelated);
+
+HIMAGELIST LoadSystemCheckboxImageList(void);
+HICON LoadSystemIcon(int iListID, int iIconIndex);
+
+HICON OverlayIcon(HICON hMainIcon, HICON hOverlayIcon, int cxWidth, int cyHeight); // returned HICON needs to be destroyed
+
+#endif // SF_UTILITIES_H__
diff --git a/spamfilter/variables.c b/spamfilter/variables.c
new file mode 100644
index 0000000..47de3a9
--- /dev/null
+++ b/spamfilter/variables.c
@@ -0,0 +1,65 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Includes
+#include "common.h"
+
+// -----------------------------------------
+
+// Resolve: %autoignore_count%
+static int ServiceVariables_autoignore_count(WPARAM wParam, LPARAM lParam)
+{
+ WCHAR* pszReturn = (WCHAR*)mir_alloc((MAX_INT_LENGTH+1)*sizeof(WCHAR));
+ if (pszReturn)
+ mir_sntprintf(pszReturn, MAX_INT_LENGTH, _T("%i"), (int)CallService(MS_SPAMFILTER_RESETAUTOIGNORE, (BOOL)TRUE, 0));
+
+ return (int)pszReturn;
+}
+
+
+// ------------------------------------
+
+
+void InitVariables(void)
+{
+ if (ServiceExists(MS_VARS_REGISTERTOKEN))
+ {
+ TOKENREGISTER tr;
+
+ // Register: %autoignore_count%
+ ZeroMemory(&tr, sizeof(tr));
+ tr.cbSize = sizeof(tr);
+ tr.memType = TR_MEM_MIRANDA;
+ tr.tszTokenString = _T("autoignore_count");
+ tr.flags = TRF_FIELD|TRF_UNICODE;
+ tr.szHelpText = "Spam Filter\tNumber of users currently being in danger of being ignored.";
+ tr.szService = "SpamFilter/Variables/autoignore_count";
+ CreateServiceFunction(tr.szService, ServiceVariables_autoignore_count);
+
+ CallService(MS_VARS_REGISTERTOKEN, 0, (LPARAM)&tr);
+ }
+}
+
+
+void UninitVariables(void)
+{
+} \ No newline at end of file
diff --git a/spamfilter/variables.h b/spamfilter/variables.h
new file mode 100644
index 0000000..90f7712
--- /dev/null
+++ b/spamfilter/variables.h
@@ -0,0 +1,30 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SF_VARIABLES_H__
+#define SF_VARIABLES_H__ 1
+
+// -- Prototypes
+void InitVariables(void);
+void UninitVariables(void);
+
+
+#endif // SF_VARIABLES_H__
diff --git a/spamfilter/version.h b/spamfilter/version.h
new file mode 100644
index 0000000..8689677
--- /dev/null
+++ b/spamfilter/version.h
@@ -0,0 +1,36 @@
+/*
+
+"Spam Filter"-Plugin for Miranda IM
+
+Copyright 2003-2006 Heiko Herkenrath
+
+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 ("SpamFilter-License.txt"); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+// -- Defines: Versions
+#define NEEDED_MIRANDA_VERSION PLUGIN_MAKE_VERSION(0, 7, 0, 0) // Needs CallServiceSync, etc. (after 0, 3, 3, 0)
+ // Unicode: Needs Unicode core and Unicode plugins, MS_LANGPACK_GETCODEPAGE
+#define NEEDED_MIRANDA_VERSION_STR "0.7.0"
+
+#define PLUGIN_VERSION PLUGIN_MAKE_VERSION(2, 5, 2, 3)
+
+
+// -- Defines: Internet
+#define PLUGIN_EMAIL "hrathh users.sourceforge.net"
+#define PLUGIN_EMAIL_ATT_POS 7 // position of the @-sign in the email adress above
+
+#define PLUGIN_FILELISTINGID 2188 // Spam Filter 1.0.4.0 = 791
+#define PLUGIN_FILELISTINGID_STR "2188" \ No newline at end of file
diff --git a/spamfilter/version.rc b/spamfilter/version.rc
new file mode 100644
index 0000000..62894ed
--- /dev/null
+++ b/spamfilter/version.rc
@@ -0,0 +1,57 @@
+#ifndef _MAC
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,5,2,3
+ PRODUCTVERSION 2,5,2,3
+ FILEFLAGSMASK 0x0L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "Comments", "Licensed under the terms of the GNU General Public License\0"
+ VALUE "FileDescription", "Spam Filter Plugin for Miranda IM\0"
+#ifdef _DEBUG
+ VALUE "FileVersion", "2.5.2.4 alpha\0"
+#else
+ VALUE "FileVersion", "2.5.2.3\0"
+#endif
+#ifdef _UNICODE
+ VALUE "InternalName", "SpamFilter v2 (Unicode)\0"
+#else
+ VALUE "InternalName", "SpamFilter v2\0"
+#endif
+ VALUE "LegalCopyright", "Copyright © 2003-2006 Heiko Herkenrath\0"
+#ifdef _UNICODE
+ VALUE "OriginalFilename", "spamfilterW.dll\0"
+#else
+ VALUE "OriginalFilename", "spamfilter.dll\0"
+#endif
+ VALUE "ProductName", "Spam Filter\0"
+ VALUE "ProductVersion", "2.5.2.3\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+
+