From 171e81205e357e0d54283a63997ed58ff97d54a9 Mon Sep 17 00:00:00 2001
From: Vadim Dashevskiy <watcherhd@gmail.com>
Date: Tue, 24 Jul 2012 11:48:31 +0000
Subject: UserInfoEx, Variables: changed folder structure

git-svn-id: http://svn.miranda-ng.org/main/trunk@1160 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
---
 .../UserInfoEx/src/Flags/svc_countrylistext.cpp    |  326 ++++
 plugins/UserInfoEx/src/Flags/svc_countrylistext.h  |   39 +
 plugins/UserInfoEx/src/Flags/svc_flags.cpp         |  765 ++++++++
 plugins/UserInfoEx/src/Flags/svc_flags.h           |  102 +
 plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp    |  456 +++++
 plugins/UserInfoEx/src/Flags/svc_flagsicons.h      |   43 +
 plugins/UserInfoEx/src/classMAnnivDate.cpp         |  836 +++++++++
 plugins/UserInfoEx/src/classMAnnivDate.h           |  132 ++
 plugins/UserInfoEx/src/classMTime.cpp              |  468 +++++
 plugins/UserInfoEx/src/classMTime.h                |  125 ++
 plugins/UserInfoEx/src/classPsTree.cpp             |  993 ++++++++++
 plugins/UserInfoEx/src/classPsTreeItem.cpp         |  697 +++++++
 plugins/UserInfoEx/src/commonheaders.cpp           |  180 ++
 plugins/UserInfoEx/src/commonheaders.h             |  234 +++
 plugins/UserInfoEx/src/ctrl_annivedit.cpp          |  636 +++++++
 plugins/UserInfoEx/src/ctrl_annivedit.h            |  104 +
 plugins/UserInfoEx/src/ctrl_base.cpp               |  301 +++
 plugins/UserInfoEx/src/ctrl_base.h                 |  238 +++
 plugins/UserInfoEx/src/ctrl_button.cpp             |  708 +++++++
 plugins/UserInfoEx/src/ctrl_button.h               |   36 +
 plugins/UserInfoEx/src/ctrl_combo.cpp              |  277 +++
 plugins/UserInfoEx/src/ctrl_combo.h                |   80 +
 plugins/UserInfoEx/src/ctrl_contact.cpp            | 1539 +++++++++++++++
 plugins/UserInfoEx/src/ctrl_contact.h              |   86 +
 plugins/UserInfoEx/src/ctrl_edit.cpp               |  381 ++++
 plugins/UserInfoEx/src/ctrl_edit.h                 |   80 +
 plugins/UserInfoEx/src/ctrl_tzcombo.cpp            |  306 +++
 plugins/UserInfoEx/src/ctrl_tzcombo.h              |   69 +
 plugins/UserInfoEx/src/dlg_anniversarylist.cpp     | 1120 +++++++++++
 plugins/UserInfoEx/src/dlg_anniversarylist.h       |   43 +
 plugins/UserInfoEx/src/dlg_msgbox.cpp              |  856 +++++++++
 plugins/UserInfoEx/src/dlg_msgbox.h                |  103 +
 plugins/UserInfoEx/src/dlg_propsheet.cpp           | 1867 ++++++++++++++++++
 plugins/UserInfoEx/src/dlg_propsheet.h             |  281 +++
 .../src/ex_import/classExImContactBase.cpp         |  581 ++++++
 .../src/ex_import/classExImContactBase.h           |  103 +
 .../src/ex_import/classExImContactXML.cpp          | 1141 +++++++++++
 .../UserInfoEx/src/ex_import/classExImContactXML.h |  103 +
 .../UserInfoEx/src/ex_import/dlg_ExImModules.cpp   |  464 +++++
 plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h |   39 +
 .../src/ex_import/dlg_ExImOpenSaveFile.cpp         |  371 ++++
 .../src/ex_import/dlg_ExImOpenSaveFile.h           |   39 +
 .../UserInfoEx/src/ex_import/dlg_ExImProgress.cpp  |  244 +++
 .../UserInfoEx/src/ex_import/dlg_ExImProgress.h    |   56 +
 plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h   |  371 ++++
 plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp   |  547 ++++++
 plugins/UserInfoEx/src/ex_import/svc_ExImINI.h     |   39 +
 plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp   | 1364 ++++++++++++++
 plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h     |  103 +
 plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp   |  453 +++++
 plugins/UserInfoEx/src/ex_import/svc_ExImXML.h     |   75 +
 plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp  |  382 ++++
 plugins/UserInfoEx/src/ex_import/svc_ExImport.h    |   62 +
 plugins/UserInfoEx/src/ex_import/tinystr.cpp       |  143 ++
 plugins/UserInfoEx/src/ex_import/tinystr.h         |  335 ++++
 plugins/UserInfoEx/src/ex_import/tinyxml.cpp       | 1978 ++++++++++++++++++++
 plugins/UserInfoEx/src/ex_import/tinyxml.h         | 1599 ++++++++++++++++
 plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp  |   76 +
 plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp | 1613 ++++++++++++++++
 plugins/UserInfoEx/src/init.cpp                    |  315 ++++
 plugins/UserInfoEx/src/mir_contactqueue.cpp        |  425 +++++
 plugins/UserInfoEx/src/mir_contactqueue.h          |  221 +++
 plugins/UserInfoEx/src/mir_db.cpp                  | 1334 +++++++++++++
 plugins/UserInfoEx/src/mir_db.h                    |  229 +++
 plugins/UserInfoEx/src/mir_icolib.cpp              |  401 ++++
 plugins/UserInfoEx/src/mir_icolib.h                |  148 ++
 plugins/UserInfoEx/src/mir_menuitems.cpp           |  651 +++++++
 plugins/UserInfoEx/src/mir_menuitems.h             |   47 +
 plugins/UserInfoEx/src/mir_string.cpp              |  167 ++
 plugins/UserInfoEx/src/mir_string.h                |   88 +
 plugins/UserInfoEx/src/psp_about.cpp               |  124 ++
 plugins/UserInfoEx/src/psp_anniversary.cpp         |  347 ++++
 plugins/UserInfoEx/src/psp_base.cpp                |  111 ++
 plugins/UserInfoEx/src/psp_base.h                  |   51 +
 plugins/UserInfoEx/src/psp_company.cpp             |   76 +
 plugins/UserInfoEx/src/psp_contact.cpp             |  347 ++++
 plugins/UserInfoEx/src/psp_general.cpp             |  211 +++
 plugins/UserInfoEx/src/psp_options.cpp             | 1570 ++++++++++++++++
 plugins/UserInfoEx/src/psp_options.h               |   43 +
 plugins/UserInfoEx/src/psp_origin.cpp              |  176 ++
 plugins/UserInfoEx/src/psp_profile.cpp             | 1464 +++++++++++++++
 plugins/UserInfoEx/src/resdefines.h                |    5 +
 plugins/UserInfoEx/src/resource.h                  |  333 ++++
 plugins/UserInfoEx/src/svc_avatar.cpp              |  247 +++
 plugins/UserInfoEx/src/svc_avatar.h                |   41 +
 plugins/UserInfoEx/src/svc_constants.cpp           |  436 +++++
 plugins/UserInfoEx/src/svc_constants.h             |  195 ++
 plugins/UserInfoEx/src/svc_contactinfo.cpp         |  798 ++++++++
 plugins/UserInfoEx/src/svc_contactinfo.h           |   36 +
 plugins/UserInfoEx/src/svc_email.cpp               |  409 ++++
 plugins/UserInfoEx/src/svc_email.h                 |   39 +
 plugins/UserInfoEx/src/svc_gender.cpp              |  284 +++
 plugins/UserInfoEx/src/svc_gender.h                |   40 +
 plugins/UserInfoEx/src/svc_homepage.cpp            |  337 ++++
 plugins/UserInfoEx/src/svc_homepage.h              |   38 +
 plugins/UserInfoEx/src/svc_phone.cpp               |  309 +++
 plugins/UserInfoEx/src/svc_phone.h                 |   38 +
 plugins/UserInfoEx/src/svc_refreshci.cpp           |  936 +++++++++
 plugins/UserInfoEx/src/svc_refreshci.h             |   36 +
 plugins/UserInfoEx/src/svc_reminder.cpp            | 1216 ++++++++++++
 plugins/UserInfoEx/src/svc_reminder.h              |  117 ++
 plugins/UserInfoEx/src/svc_timezone.cpp            |   83 +
 plugins/UserInfoEx/src/svc_timezone.h              |   51 +
 plugins/UserInfoEx/src/svc_timezone_old.cpp        |  645 +++++++
 plugins/UserInfoEx/src/svc_timezone_old.h          |  102 +
 plugins/UserInfoEx/src/version.h                   |   51 +
 106 files changed, 42156 insertions(+)
 create mode 100644 plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp
 create mode 100644 plugins/UserInfoEx/src/Flags/svc_countrylistext.h
 create mode 100644 plugins/UserInfoEx/src/Flags/svc_flags.cpp
 create mode 100644 plugins/UserInfoEx/src/Flags/svc_flags.h
 create mode 100644 plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp
 create mode 100644 plugins/UserInfoEx/src/Flags/svc_flagsicons.h
 create mode 100644 plugins/UserInfoEx/src/classMAnnivDate.cpp
 create mode 100644 plugins/UserInfoEx/src/classMAnnivDate.h
 create mode 100644 plugins/UserInfoEx/src/classMTime.cpp
 create mode 100644 plugins/UserInfoEx/src/classMTime.h
 create mode 100644 plugins/UserInfoEx/src/classPsTree.cpp
 create mode 100644 plugins/UserInfoEx/src/classPsTreeItem.cpp
 create mode 100644 plugins/UserInfoEx/src/commonheaders.cpp
 create mode 100644 plugins/UserInfoEx/src/commonheaders.h
 create mode 100644 plugins/UserInfoEx/src/ctrl_annivedit.cpp
 create mode 100644 plugins/UserInfoEx/src/ctrl_annivedit.h
 create mode 100644 plugins/UserInfoEx/src/ctrl_base.cpp
 create mode 100644 plugins/UserInfoEx/src/ctrl_base.h
 create mode 100644 plugins/UserInfoEx/src/ctrl_button.cpp
 create mode 100644 plugins/UserInfoEx/src/ctrl_button.h
 create mode 100644 plugins/UserInfoEx/src/ctrl_combo.cpp
 create mode 100644 plugins/UserInfoEx/src/ctrl_combo.h
 create mode 100644 plugins/UserInfoEx/src/ctrl_contact.cpp
 create mode 100644 plugins/UserInfoEx/src/ctrl_contact.h
 create mode 100644 plugins/UserInfoEx/src/ctrl_edit.cpp
 create mode 100644 plugins/UserInfoEx/src/ctrl_edit.h
 create mode 100644 plugins/UserInfoEx/src/ctrl_tzcombo.cpp
 create mode 100644 plugins/UserInfoEx/src/ctrl_tzcombo.h
 create mode 100644 plugins/UserInfoEx/src/dlg_anniversarylist.cpp
 create mode 100644 plugins/UserInfoEx/src/dlg_anniversarylist.h
 create mode 100644 plugins/UserInfoEx/src/dlg_msgbox.cpp
 create mode 100644 plugins/UserInfoEx/src/dlg_msgbox.h
 create mode 100644 plugins/UserInfoEx/src/dlg_propsheet.cpp
 create mode 100644 plugins/UserInfoEx/src/dlg_propsheet.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/classExImContactBase.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/classExImContactXML.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImINI.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImXML.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/svc_ExImport.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/tinystr.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/tinystr.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/tinyxml.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/tinyxml.h
 create mode 100644 plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp
 create mode 100644 plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp
 create mode 100644 plugins/UserInfoEx/src/init.cpp
 create mode 100644 plugins/UserInfoEx/src/mir_contactqueue.cpp
 create mode 100644 plugins/UserInfoEx/src/mir_contactqueue.h
 create mode 100644 plugins/UserInfoEx/src/mir_db.cpp
 create mode 100644 plugins/UserInfoEx/src/mir_db.h
 create mode 100644 plugins/UserInfoEx/src/mir_icolib.cpp
 create mode 100644 plugins/UserInfoEx/src/mir_icolib.h
 create mode 100644 plugins/UserInfoEx/src/mir_menuitems.cpp
 create mode 100644 plugins/UserInfoEx/src/mir_menuitems.h
 create mode 100644 plugins/UserInfoEx/src/mir_string.cpp
 create mode 100644 plugins/UserInfoEx/src/mir_string.h
 create mode 100644 plugins/UserInfoEx/src/psp_about.cpp
 create mode 100644 plugins/UserInfoEx/src/psp_anniversary.cpp
 create mode 100644 plugins/UserInfoEx/src/psp_base.cpp
 create mode 100644 plugins/UserInfoEx/src/psp_base.h
 create mode 100644 plugins/UserInfoEx/src/psp_company.cpp
 create mode 100644 plugins/UserInfoEx/src/psp_contact.cpp
 create mode 100644 plugins/UserInfoEx/src/psp_general.cpp
 create mode 100644 plugins/UserInfoEx/src/psp_options.cpp
 create mode 100644 plugins/UserInfoEx/src/psp_options.h
 create mode 100644 plugins/UserInfoEx/src/psp_origin.cpp
 create mode 100644 plugins/UserInfoEx/src/psp_profile.cpp
 create mode 100644 plugins/UserInfoEx/src/resdefines.h
 create mode 100644 plugins/UserInfoEx/src/resource.h
 create mode 100644 plugins/UserInfoEx/src/svc_avatar.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_avatar.h
 create mode 100644 plugins/UserInfoEx/src/svc_constants.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_constants.h
 create mode 100644 plugins/UserInfoEx/src/svc_contactinfo.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_contactinfo.h
 create mode 100644 plugins/UserInfoEx/src/svc_email.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_email.h
 create mode 100644 plugins/UserInfoEx/src/svc_gender.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_gender.h
 create mode 100644 plugins/UserInfoEx/src/svc_homepage.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_homepage.h
 create mode 100644 plugins/UserInfoEx/src/svc_phone.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_phone.h
 create mode 100644 plugins/UserInfoEx/src/svc_refreshci.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_refreshci.h
 create mode 100644 plugins/UserInfoEx/src/svc_reminder.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_reminder.h
 create mode 100644 plugins/UserInfoEx/src/svc_timezone.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_timezone.h
 create mode 100644 plugins/UserInfoEx/src/svc_timezone_old.cpp
 create mode 100644 plugins/UserInfoEx/src/svc_timezone_old.h
 create mode 100644 plugins/UserInfoEx/src/version.h

(limited to 'plugins/UserInfoEx/src')

diff --git a/plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp b/plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp
new file mode 100644
index 0000000000..fabb2749f2
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_countrylistext.cpp
@@ -0,0 +1,326 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright �2006-2007 H. 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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_countrylistext.cpp $
+Revision       : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "..\commonheaders.h"
+#include "svc_countrylistext.h"
+
+/************************* Services *******************************/
+
+static struct CountryListEntry countries[]={
+	{0   ,"Unspecified"},
+	{9999,"Other"},
+	{0xFFFF,"Unknown"},
+	{93  ,"Afghanistan"},
+	{355 ,"Albania"},
+	{213 ,"Algeria"},
+	{376 ,"Andorra"},
+	{244 ,"Angola"},
+	{1264,"Anguilla"},						/* change county code to NANP (from 101) */
+	{1268,"Antigua and Barbuda"},			/* change county code to NANP (from 1021) */
+//	{5902,"Antilles"},						/* removed: it is not a country, it's a group of islands from diffrent countries (all are included in the list)*/
+	{54  ,"Argentina"},
+	{374 ,"Armenia"},
+	{297 ,"Aruba"},
+	{61   ,"Australia"},
+	{6720 ,"Australia, Antarctic Territory"},		/* added country code 672(0)*/
+	{614  ,"Australia, Christmas Island"},			/* rename (from Christmas Island) and change to official county code 61(4) (from 672) */
+	{61891,"Australia, Cocos (Keeling) Islands"},	/* rename and change to official county code 61(891) (from 6102) */
+	{6723 ,"Australia, Norfolk Island"},			/* rename (from Norfolk Island) and change to official county code 672(3) (from 6722) */
+	{43  ,"Austria"},
+	{994 ,"Azerbaijan"},
+	{1242,"Bahamas"},						/* change county code to NANP (from 103) */
+	{973 ,"Bahrain"},
+	{880 ,"Bangladesh"},
+	{1246,"Barbados"},						/* change county code to NANP (from 103) */
+//	{120 ,"Barbuda"},						/* removed: it is not a country and no special island, see Antigua and Barbuda*/
+	{375 ,"Belarus"},
+	{32  ,"Belgium"},
+	{501 ,"Belize"},
+	{229 ,"Benin"},
+	{1441,"Bermuda"},						/* change county code to NANP (from 105) */
+	{975 ,"Bhutan"},
+	{591 ,"Bolivia"},
+	{387 ,"Bosnia and Herzegovina"},
+	{267 ,"Botswana"},
+	{55  ,"Brazil"},
+	{673 ,"Brunei"},
+	{359 ,"Bulgaria"},
+	{226 ,"Burkina Faso"},
+	{257 ,"Burundi"},
+	{855 ,"Cambodia"},
+	{237 ,"Cameroon"},
+	{1002,"Canada"},						/* change county code to NANP (from 107 to virtual 1(002) -> reflect NANP*/
+	{238 ,"Cape Verde Islands"},
+	{1345,"Cayman Islands"},				/* change county code to NANP (from 108) */
+	{236 ,"Central African Republic"},
+	{235 ,"Chad"},
+	{56  ,"Chile, Republic of"},
+	{86  ,"China"},
+//	{6101,"Cocos-Keeling Islands"},			/* removed (double): see Australia, Cocos (Keeling) Islands */
+	{57  ,"Colombia"},
+	{269 ,"Comoros"},						/* change county code (from 2691) */
+	{243 ,"Congo, Democratic Republic of (Zaire)"},
+	{242 ,"Congo, Republic of the"},
+	{682 ,"Cook Islands"},
+	{506 ,"Costa Rica"},
+	{225 ,"Cote d'Ivoire (Ivory Coast)"},
+	{385 ,"Croatia"},
+	{53  ,"Cuba"},
+	{357 ,"Greek, Republic of South Cyprus"},	/* rename coz Turkey, Republic of Northern Cyprus */
+	{420 ,"Czech Republic"},
+	{45  ,"Denmark"},
+	{246 ,"Diego Garcia"},
+	{253 ,"Djibouti"},
+	{1767,"Dominica"},						/* change county code to NANP (from 109) */
+	{1809,"Dominican Republic"},			/* change county code to NANP 809, 829, 849 (from 110) */
+	{593 ,"Ecuador"},
+	{20  ,"Egypt"},
+	{503 ,"El Salvador"},
+	{240 ,"Equatorial Guinea"},
+	{291 ,"Eritrea"},
+	{372 ,"Estonia"},
+	{251 ,"Ethiopia"},
+	{3883,"Europe"},						/* add county code  +388 3 official European Telephony Numbering Space*/
+	{298 ,"Faeroe Islands"},
+	{500 ,"Falkland Islands"},
+	{679 ,"Fiji"},
+	{358 ,"Finland"},
+	{33  ,"France"},
+	{5901,"French Antilles"},
+	{594 ,"French Guiana"},
+	{689 ,"French Polynesia"},
+	{241 ,"Gabon"},
+	{220 ,"Gambia"},
+	{995 ,"Georgia"},
+	{49  ,"Germany"},
+	{233 ,"Ghana"},
+	{350 ,"Gibraltar"},
+	{30  ,"Greece"},
+	{299 ,"Greenland"},
+	{1473,"Grenada"},						/* change county code to NANP (from 111) */
+	{590 ,"Guadeloupe"},
+	{1671,"Guam, US Territory of"},			/* change county code to NANP (from 671) */
+	{502 ,"Guatemala"},
+	{224 ,"Guinea"},
+	{245 ,"Guinea-Bissau"},
+	{592 ,"Guyana"},
+	{509 ,"Haiti"},
+	{504 ,"Honduras"},
+	{852 ,"Hong Kong"},
+	{36  ,"Hungary"},
+	{354 ,"Iceland"},
+	{91  ,"India"},
+	{62  ,"Indonesia"},
+	{98  ,"Iran (Islamic Republic of)"},
+	{964 ,"Iraq"},
+	{353 ,"Ireland"},
+	{972 ,"Israel"},
+	{39  ,"Italy"},
+	{1876,"Jamaica"},						/* change county code to NANP (from 112) */
+	{81  ,"Japan"},
+	{962 ,"Jordan"},
+	{705 ,"Kazakhstan"},
+	{254 ,"Kenya"},
+	{686 ,"Kiribati"},
+	{850 ,"Korea, North"},
+	{82  ,"Korea, South"},
+	{965 ,"Kuwait"},
+	{996 ,"Kyrgyzstan"},					/* change county code (from 706) */
+	{856 ,"Laos"},
+	{371 ,"Latvia"},
+	{961 ,"Lebanon"},
+	{266 ,"Lesotho"},
+	{231 ,"Liberia"},
+	{218 ,"Libyan Arab Jamahiriya"},
+	{423 ,"Liechtenstein"},					/* change county code (from 4101) */
+	{370 ,"Lithuania"},
+	{352 ,"Luxembourg"},
+	{853 ,"Macau"},
+	{389 ,"Macedonia, Republic of"},		/* rename coz war */
+	{261 ,"Madagascar"},
+	{265 ,"Malawi"},
+	{60  ,"Malaysia"},
+	{960 ,"Maldives"},
+	{223 ,"Mali"},
+	{356 ,"Malta"},
+	{692 ,"Marshall Islands"},
+	{596 ,"Martinique"},
+	{222 ,"Mauritania"},
+	{230 ,"Mauritius"},
+	{262 ,"Mayotte Island"},				/* change county code coz bug (from 269) */
+	{52  ,"Mexico"},
+	{691 ,"Micronesia, Federated States of"},
+	{373 ,"Moldova, Republic of"},
+	{377 ,"Monaco"},
+	{976 ,"Mongolia"},
+	{1664,"Montserrat"},					/* change county code to NANP (from 113) */
+	{212 ,"Morocco"},
+	{258 ,"Mozambique"},
+	{95  ,"Myanmar"},
+	{264 ,"Namibia"},
+	{674 ,"Nauru"},
+	{977 ,"Nepal"},
+	{31  ,"Netherlands"},
+	{599  ,"Netherlands Antilles"},			/* dissolved 2010 */
+	{5995 ,"St. Maarten"},					/* add new country in 2010 (from Netherlands Antilles) */
+	{5999 ,"Curacao"},						/* add new country in 2010 (from Netherlands Antilles) */
+	{5997 ,"Netherlands (Bonaire Island)"},	/* add new Part of Netherlands in 2010 (from Netherlands Antilles) */
+	{59946,"Netherlands (Saba Island)"},	/* add new Part of Netherlands in 2010 (from Netherlands Antilles) */
+	{59938,"Netherlands (St. Eustatius Island)"},	/* add new Part of Netherlands in 2010 (from Netherlands Antilles) */
+	//	{114 ,"Nevis"},						/* removed: it is not a country, it's part of Saint Kitts and Nevis*/
+	{687 ,"New Caledonia"},
+	{64  ,"New Zealand"},
+	{505 ,"Nicaragua"},
+	{227 ,"Niger"},
+	{234 ,"Nigeria"},
+	{683 ,"Niue"},
+	{1670,"Northern Mariana Islands, US Territory of"},	/* added NANP */
+	{47  ,"Norway"},
+	{968 ,"Oman"},
+	{92  ,"Pakistan"},
+	{680 ,"Palau"},
+	{507 ,"Panama"},
+	{675 ,"Papua New Guinea"},
+	{595 ,"Paraguay"},
+	{51  ,"Peru"},
+	{63  ,"Philippines"},
+	{48  ,"Poland"},
+	{351 ,"Portugal"},
+	{1939,"Puerto Rico"},				/* change county code to NANP 939, 787 (from 121) */
+	{974 ,"Qatar"},
+	{262 ,"Reunion Island"},
+	{40  ,"Romania"},
+//	{6701,"Rota Island"},				/* removed: it is not a country it is part of Northern Mariana Islands, US Territory of */
+	{7   ,"Russia"},
+	{250 ,"Rwanda"},
+	{1684,"Samoa (USA)"},				/* rename (from American Samoa) change county code to NANP (from 684) */
+	{685 ,"Samoa, Western"},			/* rename (from Western Samoa) */
+	{290 ,"Saint Helena"},				/* UK (St. Helena, Ascension and Tristan da Cunha) */
+	{247 ,"Ascension Island"},			/* UK (St. Helena, Ascension and Tristan da Cunha) */
+	{2897,"Tristan da Cunha"},			/* UK (St. Helena, Ascension and Tristan da Cunha) */
+//	{115 ,"Saint Kitts"},				/* removed: it is not a country it is part of Saint Kitts and Nevis*/
+	{1869,"Saint Kitts and Nevis"},		/* change county code to NANP (from 1141) */
+	{1758,"Saint Lucia"},				/* change county code to NANP (from 122) */
+	{508 ,"Saint Pierre and Miquelon"},
+	{1784,"Saint Vincent and the Grenadines"},	/* change county code to NANP (from 116) */
+//	{670 ,"Saipan Island"},				/* removed: it is not a country it is part of Northern Mariana Islands, US Territory of */
+	{378 ,"San Marino"},
+	{239 ,"Sao Tome and Principe"},
+	{966 ,"Saudi Arabia"},
+	{442 ,"Scotland"},
+	{221 ,"Senegal"},
+	{248 ,"Seychelles"},
+	{232 ,"Sierra Leone"},
+	{65  ,"Singapore"},
+	{421 ,"Slovakia"},
+	{386 ,"Slovenia"},
+	{677 ,"Solomon Islands"},
+	{252 ,"Somalia"},
+	{27  ,"South Africa"},
+	{34  ,"Spain"},
+	{3492,"Spain, Canary Islands"},		/*rename and change county code to 34(92) spain + canary code*/
+	{94  ,"Sri Lanka"},
+	{249 ,"Sudan"},
+	{597 ,"Suriname"},
+	{268 ,"Swaziland"},
+	{46  ,"Sweden"},
+	{41  ,"Switzerland"},
+	{963 ,"Syrian Arab Republic"},
+	{886 ,"Taiwan"},
+	{992 ,"Tajikistan"},				/* change county code (from 708) */
+	{255 ,"Tanzania"},
+	{66  ,"Thailand"},
+//	{6702,"Tinian Island"},				/* removed: it is not a country it is part of Northern Mariana Islands, US Territory of */
+	{670 ,"Timor, East"},				/* added (is part off Northern Mariana Islands but not US Territory*/
+	{228 ,"Togo"},
+	{690 ,"Tokelau"},
+	{676 ,"Tonga"},
+	{1868,"Trinidad and Tobago"},		/* change county code to NANP (from 1141) */
+	{216 ,"Tunisia"},
+	{90   ,"Turkey"},
+	{90392,"Turkey, Republic of Northern Cyprus"},	/* added (is diffrent from Greek part)*/
+	{993 ,"Turkmenistan"},				/* change county code (from 709) */
+	{1649,"Turks and Caicos Islands"},	/* change county code to NANP (from 118) */
+	{688 ,"Tuvalu"},
+	{256 ,"Uganda"},
+	{380 ,"Ukraine"},
+	{971 ,"United Arab Emirates"},
+	{44  ,"United Kingdom"},
+	{598 ,"Uruguay"},
+	{1   ,"USA"},
+	{998 ,"Uzbekistan"},				/* change county code (from 711) */
+	{678 ,"Vanuatu"},
+	{379 ,"Vatican City"},
+	{58  ,"Venezuela"},
+	{84  ,"Vietnam"},
+	{1284,"Virgin Islands (UK)"},		/* change county code to NANP (from 105) - rename coz Virgin Islands (USA) */
+	{1340,"Virgin Islands (USA)"},		/* change county code to NANP (from 123) */
+	{441 ,"Wales"},
+	{681 ,"Wallis and Futuna Islands"},
+	{967 ,"Yemen"},
+	{38  ,"Yugoslavia"},				/* added for old values like birth-country */
+	{381 ,"Serbia, Republic of"},		/* rename need (from Yugoslavia)*/
+	{383 ,"Kosovo, Republic of"},		/*change country code (from 3811),  rename need (from Yugoslavia - Serbia) */
+	{382 ,"Montenegro, Republic of"},	/* rename need (from Yugoslavia - Montenegro) */
+	{260 ,"Zambia"},
+	{263 ,"Zimbabwe"},
+};
+
+INT_PTR ServiceGetCountryByNumber(WPARAM wParam,LPARAM lParam)
+{
+	int i;
+	UNREFERENCED_PARAMETER(lParam);
+	for(i=0; i<SIZEOF(countries); ++i)
+		if ((int)wParam==countries[i].id)
+			return (INT_PTR)countries[i].szName;
+	return NULL;
+}
+
+INT_PTR ServiceGetCountryList(WPARAM wParam,LPARAM lParam)
+{
+	if ((int*)wParam==NULL || (void*)lParam==NULL) return 1;
+	*(int*)wParam=SIZEOF(countries);
+	*(struct CountryListEntry**)lParam=countries;
+	return 0;
+}
+
+/************************* Misc ***********************************/
+
+VOID InitCountryListExt()
+{
+	/* hack to replace built-in country list */
+	if (!myDestroyServiceFunction(MS_UTILS_GETCOUNTRYLIST))
+		CreateServiceFunction(MS_UTILS_GETCOUNTRYLIST,ServiceGetCountryList);
+	if (!myDestroyServiceFunction(MS_UTILS_GETCOUNTRYBYNUMBER))
+		CreateServiceFunction(MS_UTILS_GETCOUNTRYBYNUMBER,ServiceGetCountryByNumber);
+}
diff --git a/plugins/UserInfoEx/src/Flags/svc_countrylistext.h b/plugins/UserInfoEx/src/Flags/svc_countrylistext.h
new file mode 100644
index 0000000000..a49a289542
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_countrylistext.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright �2006-2007 H. 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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_countrylistext.h $
+Revision       : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef  _UINFOEX_COUNTRYLIST_H_INCLUDED_
+#define  _UINFOEX_COUNTRYLIST_H_INCLUDED_
+
+VOID		InitCountryListExt();
+INT_PTR		ServiceGetCountryByNumber(WPARAM wParam,LPARAM lParam);
+
+#endif /* _UINFOEX_COUNTRYLIST_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/Flags/svc_flags.cpp b/plugins/UserInfoEx/src/Flags/svc_flags.cpp
new file mode 100644
index 0000000000..8999df2cca
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_flags.cpp
@@ -0,0 +1,765 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright �2006-2007 H. 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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_flags.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "..\commonheaders.h"
+#include "svc_flags.h"
+#include "svc_flagsicons.h"
+#include "svc_countrylistext.h"
+
+#define M_ENABLE_SUBCTLS			(WM_APP+1)
+
+FLAGSOPTIONS	gFlagsOpts;
+
+/* Misc */
+int		nCountriesCount;
+struct	CountryListEntry *countries;
+static	HANDLE *phExtraImageList	= NULL;		//return value(s) from MS_CLIST_EXTRA_ADD_ICON
+static	HANDLE hExtraIconSvc		= INVALID_HANDLE_VALUE;
+/* hook */
+static HANDLE hRebuildIconsHook		= NULL;
+static HANDLE hApplyIconHook		= NULL;
+static HANDLE hMsgWndEventHook		= NULL;
+static HANDLE hIconsChangedHook		= NULL;
+static HANDLE hSettingChangedHook	= NULL;
+
+static HANDLE hExtraIconSvcHook		= NULL;
+static HANDLE hOptInitHook			= NULL;
+
+static int OnContactSettingChanged(WPARAM wParam,LPARAM lParam);
+
+/***********************************************************************************************************
+ * Buffered functions
+ ***********************************************************************************************************/
+
+struct BufferedCallData {
+	DWORD startTick;
+	UINT uElapse;
+	BUFFEREDPROC pfnBuffProc;
+	LPARAM lParam;
+	#ifdef _DEBUG
+	const char *pszProcName;
+	#endif
+};
+
+static UINT_PTR idBufferedTimer;
+static struct BufferedCallData *callList;
+static int nCallListCount;
+
+// always gets called in main message loop
+static void CALLBACK BufferedProcTimer(HWND hwnd, UINT msg, UINT idTimer, DWORD currentTick) {
+	int i;
+	struct BufferedCallData *buf;
+	UINT uElapsed, uElapseNext = USER_TIMER_MAXIMUM;
+	BUFFEREDPROC pfnBuffProc;
+	LPARAM lParam;
+	#ifdef _DEBUG
+	char szDbgLine[256];
+	const char *pszProcName;
+	#endif
+	UNREFERENCED_PARAMETER(msg);
+
+	for(i=0;i<nCallListCount;++i) {
+		/* find elapsed procs */
+		uElapsed=currentTick-callList[i].startTick; /* wraparound works */
+		if ((uElapsed+USER_TIMER_MINIMUM)>=callList[i].uElapse) { 
+			/* call elapsed proc */
+			pfnBuffProc=callList[i].pfnBuffProc;
+			lParam=callList[i].lParam;
+			#ifdef _DEBUG
+				pszProcName=callList[i].pszProcName;
+			#endif
+			/* resize storage array */
+			if ((i+1)<nCallListCount)
+				MoveMemory(&callList[i],&callList[i+1],((nCallListCount-i-1)*sizeof(struct BufferedCallData)));
+			--nCallListCount;
+			--i; /* reiterate current */
+			if(nCallListCount) {
+				buf=(struct BufferedCallData*)mir_realloc(callList,nCallListCount*sizeof(struct BufferedCallData));
+				if(buf!=NULL) callList=buf;
+			} else {
+				mir_free(callList);
+				callList=NULL;
+			}
+			#ifdef _DEBUG
+				mir_snprintf(szDbgLine,sizeof(szDbgLine),"buffered call: %s(0x%X)\n",pszProcName,lParam); /* all ascii */
+				OutputDebugStringA(szDbgLine);
+			#endif
+			CallFunctionAsync((void (CALLBACK *)(void*))pfnBuffProc,(void*)lParam); /* compatible */
+		}
+		/* find next timer delay */
+		else if ((callList[i].uElapse-uElapsed)<uElapseNext)
+			uElapseNext=callList[i].uElapse-uElapsed;
+	}
+
+	/* set next timer */
+	if(nCallListCount) {
+		#ifdef _DEBUG
+			mir_snprintf(szDbgLine,sizeof(szDbgLine),"next buffered timeout: %ums\n",uElapseNext); /* all ascii */
+			OutputDebugStringA(szDbgLine);
+		#endif
+		idBufferedTimer=SetTimer(hwnd, idBufferedTimer, uElapseNext, (TIMERPROC)BufferedProcTimer); /* will be reset */
+	} else {
+		KillTimer(hwnd,idTimer);
+		idBufferedTimer=0;
+		#ifdef _DEBUG
+			OutputDebugStringA("empty buffered queue\n");
+		#endif
+	}
+}
+
+// assumes to be called in context of main thread
+#ifdef _DEBUG
+void _CallFunctionBuffered(BUFFEREDPROC pfnBuffProc,const char *pszProcName,LPARAM lParam,BOOL fAccumulateSameParam,UINT uElapse)
+#else
+void _CallFunctionBuffered(BUFFEREDPROC pfnBuffProc,LPARAM lParam,BOOL fAccumulateSameParam,UINT uElapse)
+#endif
+{
+	struct BufferedCallData *data=NULL;
+	int i;
+
+	/* find existing */
+	for(i=0;i<nCallListCount;++i)
+		if(callList[i].pfnBuffProc==pfnBuffProc)
+			if (!fAccumulateSameParam || callList[i].lParam==lParam) {
+				data=&callList[i];
+				break;
+			}
+	/* append new */
+	if(data==NULL) {
+		/* resize storage array */
+		data=(struct BufferedCallData*)mir_realloc(callList,(nCallListCount+1)*sizeof(struct BufferedCallData));
+		if(data==NULL) return;
+		callList=data;
+		data=&callList[nCallListCount];
+		++nCallListCount;
+	}
+	/* set delay */
+	data->startTick=GetTickCount();
+	data->uElapse=uElapse;
+	data->lParam=lParam;
+	data->pfnBuffProc=pfnBuffProc;
+	#ifdef _DEBUG
+		{	char szDbgLine[256];
+			data->pszProcName=pszProcName;
+			mir_snprintf(szDbgLine,sizeof(szDbgLine),"buffered queue: %s(0x%X)\n",pszProcName,lParam); /* all ascii */
+			OutputDebugStringA(szDbgLine);
+			if (!idBufferedTimer) {
+				mir_snprintf(szDbgLine,sizeof(szDbgLine),"next buffered timeout: %ums\n",uElapse); /* all ascii */
+				OutputDebugStringA(szDbgLine);
+			}
+		}
+	#endif
+	/* set next timer */
+	if(idBufferedTimer) uElapse=USER_TIMER_MINIMUM; /* will get recalculated */
+	idBufferedTimer=SetTimer(NULL,idBufferedTimer,uElapse,(TIMERPROC)BufferedProcTimer);
+}
+
+// assumes to be called in context of main thread
+void PrepareBufferedFunctions()
+{
+	idBufferedTimer=0;
+	nCallListCount=0;
+	callList=NULL;
+}
+
+// assumes to be called in context of main thread
+void KillBufferedFunctions()
+{
+	if(idBufferedTimer) KillTimer(NULL,idBufferedTimer);
+	nCallListCount=0;
+	mir_free(callList); /* does NULL check */
+}
+
+/***********************************************************************************************************
+ * service functions
+ ***********************************************************************************************************/
+
+static INT_PTR ServiceDetectContactOriginCountry(WPARAM wParam,LPARAM lParam)
+{
+	WORD countryNumber;
+	char *pszProto = 
+	pszProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0);
+	/* UserinfoEx */
+	if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,USERINFO,SET_CONTACT_ORIGIN_COUNTRY,0))
+		return (INT_PTR)countryNumber;
+	else if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,USERINFO,SET_CONTACT_COUNTRY,0))
+		return (INT_PTR)countryNumber;
+	else if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,USERINFO,SET_CONTACT_COMPANY_COUNTRY,0))
+		return (INT_PTR)countryNumber;
+	/* fallback proto settings */
+	else if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,pszProto,"Country",0))
+		return (INT_PTR)countryNumber;
+	else if (countryNumber = (int)DB::Setting::GetWord((HANDLE)wParam,pszProto,"CompanyCountry",0))
+		return (INT_PTR)countryNumber;
+	/* fallback ip detect
+	else if(countryNumber==0xFFFF && DBGetContactSettingByte(NULL,"Flags","UseIpToCountry",SETTING_USEIPTOCOUNTRY_DEFAULT)) {
+		countryNumber=ServiceIpToCountry(DBGetContactSettingDword((HANDLE)wParam,pszProto,"RealIP",0),0);
+	}*/
+
+	return (INT_PTR)0xFFFF;
+}
+
+/***********************************************************************************************************
+ * Clist Extra Image functions
+ ***********************************************************************************************************/
+
+static void CALLBACK SetExtraImage(LPARAM lParam) {
+	/* get contact's country */
+	int countryNumber = ServiceDetectContactOriginCountry((WPARAM)lParam,0);;
+	//use ExtraIconsService ?
+	if (myGlobals.ExtraIconsServiceExist) {
+		EXTRAICON ico;
+		ico.cbSize		= sizeof(ico);
+		ico.hContact	= (HANDLE)lParam;
+		ico.hExtraIcon	= hExtraIconSvc;
+		ico.icoName		= (char*)0;		//preset
+		if(countryNumber!=0xFFFF || gFlagsOpts.bUseUnknownFlag) {
+			char szId[20];
+			mir_snprintf(szId, SIZEOF(szId), (countryNumber==0xFFFF)?"%s_0x%X":"%s_%i","flags",countryNumber); /* buffer safe */
+			ico.icoName	= szId;
+		}
+		CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+	}
+	//use Clist ExtraImageService
+	else if(gFlagsOpts.bShowExtraImgFlag) {
+		int index;
+		IconExtraColumn iec;
+		iec.cbSize		= sizeof(iec);
+		iec.ColumnType	= gFlagsOpts.idExtraColumn;
+		iec.hImage		= INVALID_HANDLE_VALUE;		//preset
+		/* get icon for contact */
+		if(phExtraImageList!=NULL) { /* too early? */
+			if(countryNumber!=0xFFFF || gFlagsOpts.bUseUnknownFlag) {
+				index = CountryNumberToIndex(countryNumber);
+				/* icon not add to clist extra image list? */
+				if(phExtraImageList[index]==INVALID_HANDLE_VALUE) {
+					HICON hIcon=LoadFlag(countryNumber);	// Returned HICON SHOULDN'T be destroyed, it is managed by IcoLib
+					if(hIcon!=NULL) {
+						phExtraImageList[index]=(HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON,(WPARAM)hIcon,0);
+						Skin_ReleaseIcon(hIcon); /* does NULL check */
+					}
+				}
+				iec.hImage=phExtraImageList[index];
+			}
+		}
+		//Set icon for contact at needed column
+		CallService(MS_CLIST_EXTRA_SET_ICON,(WPARAM)lParam,(LPARAM)&iec);
+	}
+}
+
+static void CALLBACK RemoveExtraImages(LPARAM lParam) {
+	register HANDLE hContact;
+	//use ExtraIconsService ?
+	if (myGlobals.ExtraIconsServiceExist) {
+		EXTRAICON ico;
+		ico.cbSize			= sizeof(ico);
+		ico.hExtraIcon		= hExtraIconSvc;
+		ico.icoName			= (char *)0;		/* invalidate icon for contact*/
+		/* enum all contacts */
+		for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact)) {
+			ico.hContact	= hContact;
+			CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+		}
+	}
+	//use Clist ExtraImageService
+	else {
+		IconExtraColumn iec;
+		iec.cbSize		= sizeof(iec);
+		iec.ColumnType	= gFlagsOpts.idExtraColumn;
+		iec.hImage		= INVALID_HANDLE_VALUE;
+		/* enum all contacts */
+		for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact)) {
+			/* invalidate icon for contact*/
+			CallService(MS_CLIST_EXTRA_SET_ICON,(WPARAM)hContact,(LPARAM)&iec);
+		}
+	}
+}
+
+// always call in context of main thread
+void EnsureExtraImages()		 //garantieren - sicherstellen - updaten
+{
+	register HANDLE hContact;
+	//use Clist ExtraImageService?
+	if (!myGlobals.ExtraIconsServiceExist) {
+		BYTE idExtraColumnNew = DB::Setting::GetByte(MODNAMEFLAGS,"ExtraImgFlagColumn",SETTING_EXTRAIMGFLAGCOLUMN_DEFAULT);
+		if(idExtraColumnNew != gFlagsOpts.idExtraColumn) {
+			/* clear previous column */
+			RemoveExtraImages(0);
+			gFlagsOpts.idExtraColumn = idExtraColumnNew;
+			/* clear new column */
+			RemoveExtraImages(0);
+		}
+	}
+	/* enum all contacts */
+	for (hContact = DB::Contact::FindFirst(); hContact != NULL; hContact = DB::Contact::FindNext(hContact)) {
+		/* update icon */
+		CallFunctionBuffered(SetExtraImage,(LPARAM)hContact,TRUE,EXTRAIMAGE_REFRESHDELAY);
+	}
+}
+
+static void CALLBACK UpdateExtraImages(LPARAM lParam) {
+	if (!lParam)
+		 RemoveExtraImages(0);
+	else EnsureExtraImages();
+
+/*	if (!myGlobals.ExtraIconsServiceExist && !gFlagsOpts.bShowExtraImgFlag)
+		 RemoveExtraImages();
+	else EnsureExtraImages();  */
+}
+
+//hookProc ME_CLIST_EXTRA_LIST_REBUILD
+static int OnCListRebuildIcons(WPARAM wParam,LPARAM lParam) {
+	OutputDebugStringA("REBUILD EXTRA\n");
+	//use ExtraIconsService ?
+	if(myGlobals.ExtraIconsServiceExist) return 0;			//delete ?
+	//use Clist ExtraImageService
+	if(phExtraImageList!=NULL) {
+		/* invalidate icons */
+		for(int i=0;i<nCountriesCount;++i)
+			phExtraImageList[i]=INVALID_HANDLE_VALUE;
+	}
+	/* update column */
+	gFlagsOpts.idExtraColumn = DB::Setting::GetByte(MODNAMEFLAGS,"ExtraImgFlagColumn",SETTING_EXTRAIMGFLAGCOLUMN_DEFAULT);
+	return 0;
+}
+
+//hookProc ME_CLIST_EXTRA_IMAGE_APPLY
+static int OnCListApplyIcons(WPARAM wParam,LPARAM lParam) {
+	//OutputDebugStringA("APPLY EXTRA\n");
+	if(myGlobals.ExtraIconsServiceExist || gFlagsOpts.bShowExtraImgFlag) 
+		SetExtraImage((LPARAM)wParam); /* unbuffered */
+	return 0;
+}
+
+//hookProc (ME_DB_CONTACT_SETTINGCHANGED) - workaround for missing event from ExtraIconSvc
+static int OnExtraIconSvcChanged(WPARAM wParam,LPARAM lParam) {
+	DBCONTACTWRITESETTING *dbcws=(DBCONTACTWRITESETTING*)lParam;
+	if ((HANDLE)wParam!=NULL)return 0;
+	if (!lstrcmpA(dbcws->szModule, "ExtraIcons") &&
+	   !lstrcmpA(dbcws->szSetting,"Slot_Flags")) {
+			BOOL bEnable;
+			switch (dbcws->value.type) {
+				case DBVT_BYTE:
+					bEnable = dbcws->value.bVal != (BYTE)-1;
+					break;
+				case DBVT_WORD:
+					bEnable = dbcws->value.wVal != (WORD)-1;
+					break;
+				case DBVT_DWORD:
+					bEnable = dbcws->value.dVal != (DWORD)-1;
+					break;
+				default:
+					bEnable = -1;
+					break;
+			}
+			if(bEnable == -1) {
+				return 0;
+			}
+			else if(bEnable && !hApplyIconHook) {
+					hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, OnCListApplyIcons);
+			}
+			else if (!bEnable && hApplyIconHook) {
+				UnhookEvent(hApplyIconHook); hApplyIconHook = NULL;
+			}
+			CallFunctionBuffered(UpdateExtraImages,(LPARAM)bEnable,FALSE,EXTRAIMAGE_REFRESHDELAY);
+	}
+	return 0;
+}
+
+VOID SvcFlagsEnableExtraIcons(BYTE bColumn, BOOLEAN bUpdateDB) {
+	if (!myGlobals.HaveCListExtraIcons) return;
+	gFlagsOpts.bShowExtraImgFlag = (bColumn!=((BYTE)-1));
+	if (bUpdateDB) {
+		if(gFlagsOpts.bShowExtraImgFlag) DB::Setting::WriteByte(MODNAMEFLAGS,"ExtraImgFlagColumn", bColumn);
+		DB::Setting::WriteByte(MODNAMEFLAGS,"ShowExtraImgFlag", bColumn!=(BYTE)-1);
+	}
+
+	// Flags is on
+	if (gFlagsOpts.bShowExtraImgFlag) {
+		//use ExtraIconsService ?
+		if(myGlobals.ExtraIconsServiceExist) {
+			if(hExtraIconSvc == INVALID_HANDLE_VALUE) {
+				char  szId[20];
+				//get local langID for descIcon (try to use user local Flag as icon)
+				DWORD langid = 0;
+				int r = GetLocaleInfo(
+					LOCALE_USER_DEFAULT,
+					LOCALE_ICOUNTRY | LOCALE_RETURN_NUMBER ,
+					(LPTSTR)&langid, sizeof(langid)/sizeof(TCHAR));
+				if (!CallService(MS_UTILS_GETCOUNTRYBYNUMBER,langid,0)) langid = 1;
+
+				EXTRAICON_INFO ico = {0};
+				ico.cbSize		= sizeof(ico);
+				ico.type		= EXTRAICON_TYPE_ICOLIB;
+				ico.name		= "Flags";
+				ico.description	= "Flags (uinfoex)";
+				mir_snprintf(szId, SIZEOF(szId), (langid==0xFFFF)?"%s_0x%X":"%s_%i","flags",langid); /* buffer safe */
+				ico.descIcon	= szId;
+				hExtraIconSvc=(HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+				if(hExtraIconSvc)
+					hExtraIconSvcHook	= HookEvent(ME_DB_CONTACT_SETTINGCHANGED,		OnExtraIconSvcChanged);
+
+			}
+		}
+		//use Clist ExtraImageService
+		else {
+			if(phExtraImageList == NULL){
+				phExtraImageList = (HANDLE*)mir_alloc(nCountriesCount*sizeof(HANDLE));
+				/* invalidate icons */
+				if(phExtraImageList!=NULL)
+					for(int i=0;i<nCountriesCount;++i)
+						phExtraImageList[i]=INVALID_HANDLE_VALUE;
+			}
+			if (!hRebuildIconsHook) {
+				hRebuildIconsHook	= HookEvent(ME_CLIST_EXTRA_LIST_REBUILD,	OnCListRebuildIcons);
+			}
+		}
+		//init hooks
+		if (!hApplyIconHook) {
+			hApplyIconHook		= HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY,		OnCListApplyIcons);
+		}
+		if (!hSettingChangedHook) {
+			hSettingChangedHook	= HookEvent(ME_DB_CONTACT_SETTINGCHANGED,	OnContactSettingChanged);
+		}
+	}
+	// Flags is off
+	else {
+		//use ExtraIconsService ?
+		if (myGlobals.ExtraIconsServiceExist) {
+			//nothing to do, until plugin has a hookable event for status
+			return;
+		}
+		//use Clist ExtraImageService
+		//unhook
+		if (hRebuildIconsHook) {
+			UnhookEvent(hRebuildIconsHook);		hRebuildIconsHook = NULL;
+		}
+		if (hApplyIconHook) {
+			UnhookEvent(hApplyIconHook);		hApplyIconHook = NULL;
+		}
+		if (hSettingChangedHook && !gFlagsOpts.bShowStatusIconFlag) {
+			UnhookEvent(hSettingChangedHook);	hSettingChangedHook = NULL;
+		}
+			//SvcFlagsApplyCListIcons();
+			//RemoveExtraImages();
+			CallFunctionBuffered(RemoveExtraImages,0,FALSE,EXTRAIMAGE_REFRESHDELAY);
+	}
+}
+
+/***********************************************************************************************************
+ * message winsow status icon functions
+ ***********************************************************************************************************/
+
+MsgWndData::MsgWndData(HWND hwnd, HANDLE hContact) {
+	m_hwnd		= hwnd;
+	m_hContact	= hContact;
+	m_contryID	= (int)ServiceDetectContactOriginCountry((WPARAM)m_hContact,0);
+	FlagsIconUpdate();
+}
+
+MsgWndData::~MsgWndData() {
+	FlagsIconUnset();			//check if realy need
+}
+
+void
+MsgWndData::FlagsIconSet() {
+	HICON hIcon				= NULL;
+	StatusIconData sid		= {0};
+	sid.cbSize				= sizeof(sid);
+	sid.szModule			= MODNAMEFLAGS;
+	/* ensure status icon is registered */
+	if (	m_contryID!=0xFFFF || gFlagsOpts.bUseUnknownFlag) {
+		/* copy icon as status icon API will call DestroyIcon() on it */
+		hIcon = LoadFlagIcon(m_contryID);
+		sid.hIcon			= (hIcon!=NULL)?CopyIcon(hIcon):NULL;
+		Skin_ReleaseIcon(hIcon); /* does NULL check */
+		hIcon = sid.hIcon;
+		sid.dwId			= (DWORD)m_contryID;
+		sid.hIconDisabled	= sid.hIcon/*NULL*/;
+		sid.szTooltip		= Translate((char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,m_contryID,0));
+		sid.flags			= 0;
+		if(CallService(MS_MSG_MODIFYICON,(WPARAM)m_hContact,(LPARAM)&sid) !=0) /* not yet registered? */
+			CallService(MS_MSG_ADDICON,0,(LPARAM)&sid);
+	}	
+	sid.hIcon				= NULL;
+	sid.szTooltip			= NULL;
+	sid.hIconDisabled		= NULL;
+	for(int i=0;i<nCountriesCount;++i) {
+		sid.dwId			= (DWORD)countries[i].id;
+		sid.flags			= (m_contryID==countries[i].id && hIcon!=NULL)? 0:MBF_HIDDEN;
+		CallService(MS_MSG_MODIFYICON,(WPARAM)m_hContact,(LPARAM)&sid);
+	}
+}
+
+void
+MsgWndData::FlagsIconUnset() {
+	StatusIconData sid		= {0};
+	sid.cbSize				= sizeof(sid);
+	sid.szModule			= MODNAMEFLAGS;
+	sid.dwId				= (DWORD)m_contryID;
+	sid.flags				= MBF_HIDDEN;
+	CallService(MS_MSG_MODIFYICON,(WPARAM)m_hContact,(LPARAM)&sid);
+	/* can't call MS_MSG_REMOVEICON here as the icon might be
+	 * in use by other contacts simultanously, removing them all at exit */
+}
+
+static int CompareMsgWndData(const MsgWndData* p1, const MsgWndData* p2)
+{
+	return (int)((INT_PTR)p1->m_hContact - (INT_PTR)p2->m_hContact);
+}
+static LIST<MsgWndData> gMsgWndList(10, CompareMsgWndData);
+
+static int CompareIconListData(const IconList* p1, const IconList* p2)
+{
+	return (int)((INT_PTR)p1->m_ID - (INT_PTR)p2->m_ID);
+}
+static OBJLIST<IconList> gIListMW(10, CompareIconListData);
+
+IconList::IconList(StatusIconData *sid) {
+	m_StatusIconData.cbSize			= sid->cbSize;
+	m_StatusIconData.szModule		= mir_strdup(sid->szModule);
+	m_StatusIconData.dwId			= sid->dwId;
+	m_StatusIconData.hIcon			= CopyIcon(sid->hIcon);
+	m_StatusIconData.hIconDisabled	= sid->hIconDisabled;
+	m_StatusIconData.flags			= sid->flags;
+	m_StatusIconData.szTooltip		= mir_strdup(sid->szTooltip);
+
+	m_ID = sid->dwId;
+	CallService(MS_MSG_ADDICON,0,(LPARAM)sid);
+
+}
+IconList::~IconList() {
+	mir_free(m_StatusIconData.szModule);
+	mir_free(m_StatusIconData.szTooltip);
+}
+// const char *pszName;			// [Optional] Name of an icon registered with icolib to be used in GUI.
+static __inline int MessageAPI_AddIcon(const char* pszName, const char* szModul/*StatusIconData *sid*/,int ID, int flags, const char* szTooltip)
+{
+	HICON hIcon = Skin_GetIcon(pszName);
+
+	StatusIconData sid		= {0};
+	sid.cbSize				= sizeof(sid);
+	sid.szModule			= (char*)szModul;
+	sid.dwId				= (DWORD)ID;
+	sid.hIcon				= (hIcon!=NULL)?CopyIcon(hIcon):NULL;
+	Skin_ReleaseIcon(hIcon); /* does NULL check */
+//	sid.hIconDisabled		= sid.hIcon/*NULL*/;
+	sid.flags				= 0;
+	sid.szTooltip			= Translate((char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,ID,0));
+
+	int res = -1;
+	IconList* p = new IconList(&sid);
+	if (!p->m_ID)delete p;
+	else res = gIListMW.insert(p);
+	if(res == -1)delete p;
+	return res;
+}
+
+void CALLBACK UpdateStatusIcons(LPARAM lParam) {
+	if (!lParam) {
+		/* enum all opened message windows */
+		for (int i = 0; i < gMsgWndList.getCount(); i++)
+			gMsgWndList[i]->FlagsIconUpdate();
+	}
+	else {
+		int i = gMsgWndList.getIndex((MsgWndData*)&lParam);
+		if (i!= -1) gMsgWndList[i]->FlagsIconUpdate();
+	}
+}
+
+//hookProc ME_MSG_WINDOWEVENT
+static int OnMsgWndEvent(WPARAM wParam,LPARAM lParam) {
+	MessageWindowEventData *msgwe=(MessageWindowEventData*)lParam;
+	/* sanity check */
+	if(msgwe->hContact==NULL)
+		return 0;
+
+	switch(msgwe->uType) {
+		case MSG_WINDOW_EVT_OPENING:
+			{
+				MsgWndData* msgwnd = gMsgWndList.find((MsgWndData*)&msgwe->hContact);
+				if(msgwnd == NULL) {
+					msgwnd = new MsgWndData(msgwe->hwndWindow, msgwe->hContact);
+					gMsgWndList.insert(msgwnd);
+				}
+			}
+			break;
+
+		case MSG_WINDOW_EVT_CLOSE:
+			{
+				int i = gMsgWndList.getIndex((MsgWndData*)&msgwe->hContact);
+				if (i != -1) {
+					delete gMsgWndList[i];
+					gMsgWndList.remove(i);
+				}
+			}
+			break;
+	}
+	return 0;
+}
+
+//hookProc ME_SKIN2_ICONSCHANGED
+static int OnStatusIconsChanged(WPARAM wParam,LPARAM lParam) {
+	if(myGlobals.MsgAddIconExist && gFlagsOpts.bShowStatusIconFlag)
+		CallFunctionBuffered(UpdateStatusIcons,0,FALSE,STATUSICON_REFRESHDELAY);
+	return 0;
+}
+
+/***********************************************************************************************************
+ * option page (not used yet)
+ ***********************************************************************************************************/
+static INT_PTR CALLBACK ExtraImgOptDlgProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+	return FALSE;
+}
+
+//hookProc ME_OPT_INITIALISE
+static int ExtraImgOptInit(WPARAM wParam,LPARAM lParam)
+{
+	OPTIONSDIALOGPAGE odp = { 0 };
+	return 0;
+}
+
+/***********************************************************************************************************
+ * misc functions
+ ***********************************************************************************************************/
+//hookProc ME_DB_CONTACT_SETTINGCHANGED
+static int OnContactSettingChanged(WPARAM wParam,LPARAM lParam) {
+	if ((HANDLE)wParam==NULL) return 0;
+	DBCONTACTWRITESETTING *dbcws=(DBCONTACTWRITESETTING*)lParam;
+
+	/* user details update */
+	if (/*!lstrcmpA(dbcws->szSetting,"RealIP") || */
+	    !lstrcmpA(dbcws->szSetting,SET_CONTACT_COUNTRY) ||
+	    !lstrcmpA(dbcws->szSetting,SET_CONTACT_ORIGIN_COUNTRY) ||
+	    !lstrcmpA(dbcws->szSetting,SET_CONTACT_COMPANY_COUNTRY))
+		{
+		/* Extra Image */
+		if(myGlobals.HaveCListExtraIcons)
+			CallFunctionBuffered(SetExtraImage,(LPARAM)wParam,TRUE,EXTRAIMAGE_REFRESHDELAY);
+		/* Status Icon */
+		if(hMsgWndEventHook) {
+			int i = gMsgWndList.getIndex((MsgWndData*)&wParam);
+			if (i != -1) {
+				gMsgWndList[i]->ContryIDchange((int)ServiceDetectContactOriginCountry(wParam,0));
+				gMsgWndList[i]->FlagsIconUpdate();
+			}
+		}
+	}
+	return 0;
+}
+
+
+/***********************************************************************************************************
+ * module loading & unloading
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads all required stuff for Flags.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+void SvcFlagsLoadModule()
+{
+	PrepareBufferedFunctions();
+	InitCountryListExt();	/* hack to replace core country list */
+	if(CallService(MS_UTILS_GETCOUNTRYLIST,(WPARAM)&nCountriesCount,(LPARAM)&countries))
+		nCountriesCount=0;
+	InitIcons();			/* load in iconlib */
+	//InitIpToCountry();	/* not implementet */
+	CreateServiceFunction(MS_FLAGS_DETECTCONTACTORIGINCOUNTRY,ServiceDetectContactOriginCountry);
+	//init settings
+	gFlagsOpts.bShowExtraImgFlag	= DB::Setting::GetByte(MODNAMEFLAGS,"ShowExtraImgFlag",		SETTING_SHOWEXTRAIMGFLAG_DEFAULT);
+	gFlagsOpts.bUseUnknownFlag		= DB::Setting::GetByte(MODNAMEFLAGS,"UseUnknownFlag",		SETTING_USEUNKNOWNFLAG_DEFAULT);
+	gFlagsOpts.idExtraColumn		= DB::Setting::GetByte(MODNAMEFLAGS,"ExtraImgFlagColumn",	SETTING_EXTRAIMGFLAGCOLUMN_DEFAULT);
+	gFlagsOpts.bShowStatusIconFlag	= DB::Setting::GetByte(MODNAMEFLAGS,"ShowStatusIconFlag",	SETTING_SHOWSTATUSICONFLAG_DEFAULT);
+
+	hOptInitHook		= HookEvent(ME_OPT_INITIALISE,ExtraImgOptInit);
+	hIconsChangedHook	= HookEvent(ME_SKIN2_ICONSCHANGED,OnStatusIconsChanged);
+}
+
+/**
+ * This function is called by Miranda just after loading all system modules.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+void SvcFlagsOnModulesLoaded() {
+	//use ExtraIconsService ?
+	if ( myGlobals.ExtraIconsServiceExist) {
+		SvcFlagsEnableExtraIcons(DB::Setting::GetByte(SET_CLIST_EXTRAICON_FLAGS2, 0), FALSE);
+	}
+	//use Clist ExtraImageService
+	else {
+		SvcFlagsEnableExtraIcons(gFlagsOpts.bShowExtraImgFlag? gFlagsOpts.idExtraColumn : -1, FALSE);
+	}
+	/* Status Icon */
+	if(myGlobals.MsgAddIconExist)
+		hMsgWndEventHook = HookEvent(ME_MSG_WINDOWEVENT, OnMsgWndEvent);
+}
+
+/**
+ * This function unloads the module.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+void SvcFlagsUnloadModule() {
+	KillBufferedFunctions();
+	//Uninit ExtraImg
+	UnhookEvent(hRebuildIconsHook);
+	UnhookEvent(hApplyIconHook);
+	UnhookEvent(hIconsChangedHook);
+	UnhookEvent(hExtraIconSvcHook);
+	mir_free(phExtraImageList);		/* does NULL check */
+	//Uninit message winsow
+	UnhookEvent(hMsgWndEventHook);
+	for(int i = 0; i < gMsgWndList.getCount(); i++) {
+		//this should not happen
+		delete gMsgWndList[i];
+		gMsgWndList.remove(i);
+	}
+	gMsgWndList.destroy();
+	gIListMW.destroy();
+	//Uninit misc
+	UnhookEvent(hSettingChangedHook);
+	UnhookEvent(hOptInitHook);
+	UninitIcons();
+
+	//UninitIpToCountry();			/* not implementet */
+}
+
diff --git a/plugins/UserInfoEx/src/Flags/svc_flags.h b/plugins/UserInfoEx/src/Flags/svc_flags.h
new file mode 100644
index 0000000000..e7f97ab963
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_flags.h
@@ -0,0 +1,102 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright �2006-2007 H. 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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_flags.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef  _UINFOEX_FLAGS_H_INCLUDED_
+#define  _UINFOEX_FLAGS_H_INCLUDED_
+
+#define EXTRAIMAGE_REFRESHDELAY		100		/* time for which setting changes are buffered */
+#define STATUSICON_REFRESHDELAY		100		/* time for which setting changes are buffered */
+
+typedef struct _FLAGSOPTIONS 
+{
+	BYTE	bShowExtraImgFlag;
+	BYTE	bUseUnknownFlag;
+	BYTE	idExtraColumn;
+//	BYTE	bUseIpToCountry;
+	BYTE	bShowStatusIconFlag;
+} FLAGSOPTIONS, *LPFLAGSOPTIONS;
+
+extern int nCountriesCount;
+extern struct CountryListEntry *countries;
+extern FLAGSOPTIONS	gFlagsOpts;
+
+class MsgWndData {
+	public:
+		HANDLE	m_hContact;
+		HWND	m_hwnd;
+		int		m_contryID;
+		
+		MsgWndData(HWND hwnd, HANDLE hContact);
+		~MsgWndData();
+
+		void FlagsIconSet();
+		void FlagsIconUnset();
+		void FlagsIconUpdate() {
+			gFlagsOpts.bShowStatusIconFlag ? FlagsIconSet():FlagsIconUnset();
+		};
+		void ContryIDchange(int ID) {
+			m_contryID = ID; FlagsIconUpdate();
+		};
+};
+
+class IconList {
+	public:
+		int				m_ID;
+		HANDLE			m_hIcon;		//register
+		HANDLE			m_hImage;		//return value from MS_CLIST_EXTRA_ADD_ICON	   -INVALID_HANDLE_VALUE;		//preset
+		BYTE			m_TypeFlag;
+		StatusIconData	m_StatusIconData;
+
+		IconList(StatusIconData* sid);
+//		IconList(HWND hwnd, HANDLE hContact);
+		~IconList();
+
+};
+
+typedef void (CALLBACK *BUFFEREDPROC)(LPARAM lParam);
+#ifdef _DEBUG
+	void _CallFunctionBuffered(BUFFEREDPROC pfnBuffProc,const char *pszProcName,LPARAM lParam,BOOL fAccumulateSameParam,UINT uElapse);
+	#define CallFunctionBuffered(proc,param,acc,elapse) _CallFunctionBuffered(proc,#proc,param,acc,elapse)
+#else
+	void _CallFunctionBuffered(BUFFEREDPROC pfnBuffProc,LPARAM lParam,BOOL fAccumulateSameParam,UINT uElapse);
+	#define CallFunctionBuffered(proc,param,acc,elapse) _CallFunctionBuffered(proc,param,acc,elapse)
+#endif
+
+void EnsureExtraImages();
+VOID SvcFlagsEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB);
+void CALLBACK UpdateStatusIcons(LPARAM lParam);
+
+void SvcFlagsLoadModule();
+void SvcFlagsOnModulesLoaded();
+void SvcFlagsUnloadModule();
+
+#endif /* _UINFOEX_FLAGS_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp b/plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp
new file mode 100644
index 0000000000..d7119cd75e
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_flagsicons.cpp
@@ -0,0 +1,456 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright �2006-2007 H. 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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_flagsicons.cpp $
+Revision       : $Revision: 206 $
+Last change on : $Date: 2010-09-28 18:43:50 +0400 (Вт, 28 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "..\commonheaders.h"
+#include "svc_flags.h"
+#include "svc_flagsicons.h"
+
+/************************* Bitmap Access **************************/
+
+static HANDLE *phIconHandles = NULL;
+
+static int CountryNumberToBitmapIndex(int countryNumber)
+{
+	/* country number indices (same order as in flags.bmp) */
+	const int BitmapIndexMap[239]={
+		0,	1,	7,	20,	27,	30,	31,	32,	33,	34,
+		36,	38,	39,	40,	41,	43,	44,	45,	46,	47,
+		48,	49,	51,	52,	53,	54,	55,	56,	57,	58,
+		60,	61,	62,	63,	64,	65,	66,	81,	82,	84,
+		86,	90,	91,	92,	93,	94,	95,	98,	212,	213,
+		216,	218,	220,	221,	222,	223,	224,	225,	226,	227,
+		228,	229,	230,	231,	232,	233,	234,	235,	236,	237,
+		238,	239,	240,	241,	242,	243,	244,	245,	246,	247,
+		248,	249,	250,	251,	252,	253,	254,	255,	256,	257,
+		258,	260,	261,	263,	264,	265,	266,	267,	268,	269,
+		290,	291,	297,	298,	299,	350,	351,	352,	353,	354,
+		355,	356,	357,	358,	359,	370,	371,	372,	373,	374,
+		375,	376,	377,	378,	379,	380,	381,	382,	383,	385,
+		386,	387,	389,	420,	421,	423,	441,	442,	500,	501,
+		502,	503,	504,	505,	506,	507,	508,	509,	591,	592,
+		593,	595,	597,	598,	599,	614,	670,	673,	674,	675,
+		676,	677,	678,	679,	680,	681,	682,	683,	685,	686,
+		688,	689,	690,	691,	692,	705,	850,	852,	853,	855,
+		856,	880,	886,	960,	961,	962,	963,	964,	965,	966,
+		967,	968,	971,	972,	973,	974,	975,	976,	977,	992,
+		993,	994,	995,	996,	998,	1002,	1242,	1246,	1264,	1268,
+		1284,	1340,	1345,	1441,	1473,	1649,	1664,	1670,	1671,	1684,
+		1758,	1767,	1784,	1809,	1868,	1869,	1876,	1939,	2897,	3492,
+		3883,	5995,	5999,	6720,	6723,	9999,	61891,	65535,	90392
+	};
+	/* shared flags by multiple countries */
+	switch(countryNumber) {
+		case 9999:              /* Other */
+		case 65535:             /* 0xFFFF,"Unknown" */
+			countryNumber=0;    /* Unspecified */
+			break;
+		case 262:               /* Reunion Island */
+		case 594:               /* French Guiana */
+		case 5901:              /* remove French Antilles */
+			countryNumber=33;   /* France */
+			break;
+		case 120:               /* remove Barbuda */
+			countryNumber=1268; /* Antigua and Barbuda */
+			break;
+		case 6702:              /* removed Tinian Island */
+		case 6701:              /* removed Rota Island */
+		case 670:               /* removed Saipan Island */
+			countryNumber=1670;	/* Northern Mariana Islands, US Territory of*/
+			break;
+		case 115:               /* removed Saint Kitts */
+		case 114:               /* removed Nevis */
+			countryNumber=1869; /* Saint Kitts and Nevis */
+			break;
+		case 247:               /* Ascension Island */
+			countryNumber=44;   /* United Kingdom */
+			break;
+		case 6720:              /* Australian Antarctic Territory */
+			countryNumber=61;   /* Australia */
+			break;
+		case 5997:              /* Netherlands (Bonaire Island)*/
+		case 59946:             /* Netherlands (Saba Island) */
+		case 59938:             /* Netherlands (St. Eustatius Island) */
+			countryNumber=599;  /* Netherlands Antilles (dissolved 2010) */
+			//countryNumber=31;   /* Netherlands */
+			break;
+		case 5995:              /* St. Maarten (new country in 2010 (from Netherlands Antilles) new country in 2010 (from Netherlands Antilles)) */
+			countryNumber=599;  /* Netherlands Antilles (dissolved 2010) */
+			break;
+		case 5999:              /* Curacao (new country in 2010 (from Netherlands Antilles) new country in 2010 (from Netherlands Antilles)) */
+			countryNumber=599;  /* Netherlands Antilles (dissolved 2010) */
+			break;
+		case 5399:              /* missing Guantanamo Bay */
+			countryNumber=1;    /* USA */
+	}
+	/* binary search in index array */
+	if (countryNumber > 0) {
+		int i,high/*,low=0*/;
+		high=SIZEOF(BitmapIndexMap);
+		/*old code need sortet BitmapIndexMap*/
+		/*if(countryNumber<=BitmapIndexMap[high])
+			while(low<=high) {
+				i=low+((high-low)/2);
+				// never happens
+				if(i<0 || i>=SIZEOF(BitmapIndexMap)) DebugBreak();
+				if(BitmapIndexMap[i]==countryNumber) return i;
+				if(countryNumber>BitmapIndexMap[i]) low=i+1;      
+				else high=i-1;
+			}*/
+		for ( i=0; i < high; i++ ) {
+			if(BitmapIndexMap[i]==countryNumber) return i;
+		}
+	}
+	/* Other,Unknown,Unspecified */
+	return 0;
+}
+
+/************************* Utils **********************************/
+
+HICON LoadFlag(int countryNumber)
+{
+	char szId[20],*szCountry;
+	/* create identifier */
+	szCountry=(char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,countryNumber,0);
+	if(szCountry == NULL)
+		szCountry=(char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,countryNumber=0xFFFF,0);
+
+	wsprintfA(szId,(countryNumber==0xFFFF)?"%s_0x%X":"%s_%i","flags",countryNumber); /* buffer safe */
+	return Skin_GetIcon(szId);
+}
+
+int CountryNumberToIndex(int countryNumber)
+{
+	int i,nf=0;
+	for(i=0;i<nCountriesCount;++i) {
+		if(countries[i].id==countryNumber) return i;
+		if(countries[i].id==0xFFFF) nf=i;
+	}
+	return nf; /* Unknown */
+}
+
+FIBITMAP* ConvertTo(FIBITMAP* dib, UINT destBits, bool greyscale)
+{
+	FIBITMAP* dib_res = NULL;
+	switch (destBits) {
+		case 8:
+			// convert to 8Bits
+			if(greyscale) {
+				dib_res = FIP->FI_ConvertTo8Bits(dib);
+			}
+			else {
+				FIBITMAP* dib_tmp = FIP->FI_ConvertTo24Bits(dib);
+				if(dib_tmp) {
+					dib_res = FIP->FI_ColorQuantize(dib_tmp, FIQ_WUQUANT/*FIQ_NNQUANT*/);
+					FIP->FI_Unload(dib_tmp);
+				}
+			} break;
+		case 16:
+			// convert to 16Bits
+			dib_res = FIP->FI_ConvertTo16Bits555(dib);
+			break;
+		case 24:
+			// convert to 24Bits
+			dib_res = FIP->FI_ConvertTo24Bits(dib);
+			break;
+		case 32:
+			// convert to 32Bits
+			dib_res = FIP->FI_ConvertTo32Bits(dib);
+			break;
+		default:
+			break;
+	}
+	FIP->FI_Unload(dib);
+	return dib_res;
+}
+
+FIBITMAP* LoadResource(UINT ID, LPTSTR lpType)
+{
+	FIBITMAP *dib      = NULL;
+	if(lpType) {
+		HRSRC	hResInfo	= FindResource(ghInst,MAKEINTRESOURCE(ID),lpType);
+		DWORD	ResSize		= SizeofResource(ghInst,hResInfo);
+		HGLOBAL	hRes		= LoadResource(ghInst,hResInfo);
+		BYTE*	buffer		= (BYTE*)LockResource(hRes);
+		if (buffer)
+		{
+			// attach the binary data to a memory stream
+			FIMEMORY *hmem = FIP->FI_OpenMemory(buffer, ResSize);
+			// get the file type
+			FREE_IMAGE_FORMAT fif = FIP->FI_GetFileTypeFromMemory(hmem, 0);
+			// load an image from the memory stream
+			dib = FIP->FI_LoadFromMemory(fif, hmem, 0);
+			// always close the memory stream
+			FIP->FI_CloseMemory(hmem);
+
+			UnlockResource(buffer);
+		}
+		FreeResource(hRes);
+	}
+	else {
+		HBITMAP hScrBM = 0;
+		if(NULL == (hScrBM = (HBITMAP)LoadImage(ghInst,MAKEINTRESOURCE(ID), IMAGE_BITMAP, 0, 0,LR_SHARED)))
+			return dib;
+		dib = FIP->FI_CreateDIBFromHBITMAP(hScrBM);
+		DeleteObject(hScrBM);
+	}
+	return dib;
+}
+
+/************************* Services *******************************/
+
+static INT_PTR ServiceLoadFlagIcon(WPARAM wParam,LPARAM lParam)
+{
+	/* return handle */
+	if ((BOOL)lParam) {
+		if(phIconHandles==NULL) return NULL;
+		return (INT_PTR)phIconHandles[CountryNumberToIndex((int)wParam)];
+	}
+	/* return icon */
+	return (INT_PTR)LoadFlag(wParam);
+}
+
+static INT_PTR ServiceCreateMergedFlagIcon(WPARAM wParam,LPARAM lParam)
+{
+	//TODO: use freeimage to create merget icon and add RGB(A) support
+	HICON hUpperIcon,hLowerIcon;
+	ICONINFO icoi;
+	BITMAP bm;
+	HDC hdc;
+	POINT aptTriangle[3];
+	HICON hIcon=NULL;
+	HRGN hrgn;
+	HBITMAP hbmPrev;
+	/* load both icons */
+	if(NULL == (hLowerIcon=(HICON)ServiceLoadFlagIcon((WPARAM)lParam,0))) return NULL;
+	hUpperIcon=(HICON)ServiceLoadFlagIcon(wParam,0);
+	/* merge them */
+	if(GetIconInfo(hLowerIcon,&icoi)) {
+		if(hUpperIcon!=NULL && GetObject(icoi.hbmColor,sizeof(bm),&bm)) {
+			hdc=CreateCompatibleDC(NULL);
+			if(hdc!=NULL) {
+				ZeroMemory(&aptTriangle,sizeof(aptTriangle));
+				aptTriangle[1].y=bm.bmHeight-1;
+				aptTriangle[2].x=bm.bmWidth-1;
+				hrgn=CreatePolygonRgn(aptTriangle,SIZEOF(aptTriangle),WINDING);
+				if(hrgn!=NULL) {
+					SelectClipRgn(hdc,hrgn);
+					DeleteObject(hrgn);
+					hbmPrev=(HBITMAP)SelectObject(hdc,icoi.hbmColor);
+					if(hbmPrev!=NULL) {  /* error on select? */
+						if(DrawIconEx(hdc,0,0,hUpperIcon,bm.bmWidth,bm.bmHeight,0,NULL,DI_NOMIRROR|DI_IMAGE)) {
+							if(SelectObject(hdc,icoi.hbmMask)!=NULL) /* error on select? */
+								DrawIconEx(hdc,0,0,hUpperIcon,bm.bmWidth,bm.bmHeight,0,NULL,DI_NOMIRROR|DI_MASK);
+						}
+						SelectObject(hdc,hbmPrev);
+					}
+					DeleteObject(hrgn);
+				}
+				DeleteDC(hdc);
+			}
+		}
+		/* create icon */
+		hIcon=CreateIconIndirect(&icoi);
+		DeleteObject(icoi.hbmColor);
+		DeleteObject(icoi.hbmMask);
+	}
+	return (INT_PTR)hIcon;
+}
+
+/************************* Misc ***********************************/
+
+VOID InitIcons()
+{
+	HIMAGELIST			himl		= {0};
+	HBITMAP				hScrBM		= 0;
+	HBITMAP				hbmMask		= 0;
+	FIBITMAP			*dib		= NULL,
+						*dib_ico	= NULL;
+	UINT				bitDest		= ILC_COLOR8,
+						bit			= 24;
+
+//	BOOL				res;
+//	FREE_IMAGE_FORMAT	fif			= FIP->FI_GetFIFFromFilename("dummy.bmp");
+
+	/* all those flag icons storing in a large 24bit opaque bitmap to reduce file size */
+	if(NULL == (dib = LoadResource(IDB_FLAGSPNG,_T("PNG"))))
+		return;
+
+	if		(IsWinVerXPPlus())		bitDest = bit = ILC_COLOR32;
+	else if	(IsWinVer2000Plus())	bitDest = ILC_COLOR24;
+	else if	(IsWinVerMEPlus())		bitDest = ILC_COLOR16;
+	/*else	preset					bitDest = ILC_COLOR8;*/
+
+	//we work always with 24bit if bitDest < xp+
+	//coz we cannot perform paste operations between palettized images,
+	//unless both src and dst images use the same palette.
+
+	if (FIP->FI_GetBPP(dib) != bit)
+		if (NULL == (dib = ConvertTo(dib, bit, 0))) return;
+
+	//create new dib
+	if (NULL == (dib_ico = FIP->FI_Allocate(FIP->FI_GetWidth(dib), 16, bit,0,0,0))) {
+		FIP->FI_Unload(dib);
+		return;
+	}
+
+//	res = FIP->FI_IsTransparent(dib_ico);
+	if(bit<32) {
+		//disable transparency
+		FIP->FI_SetTransparent(dib, FALSE);
+		FIP->FI_SetTransparent(dib_ico, FALSE);
+	}
+
+	UINT h = FIP->FI_GetHeight(dib_ico);
+	UINT w = FIP->FI_GetWidth(dib_ico);
+	UINT t = ((h - FIP->FI_GetHeight(dib))/2)+1;
+	UINT b = t + FIP->FI_GetHeight(dib);
+
+	//copy dib to new dib_ico (centered)
+	if(FIP->FI_Paste(dib_ico, dib, 0, t-1, 255+1)) {
+		FIP->FI_Unload(dib);	dib = NULL;
+		switch (bitDest) {
+			case 8:
+			case 16:
+			case 24:
+			{//transparency by 1bit monocrome icon mask (for bit < 32)
+				BYTE value;
+				FIBITMAP *dib_mask;
+				if(NULL == (dib_mask = FIP->FI_Allocate(w, h, 1, 0, 0, 0))) {
+					FIP->FI_Unload(dib_ico);
+					return;
+				}
+				for(unsigned y = 0; y < h; y++)
+					for(unsigned x = 0; x < w; x++) {
+						value = ((y<t || y>=b)? 0:1);
+						FIP->FI_SetPixelIndex(dib_mask, x, y, &value);
+					}
+				hbmMask = FIP->FI_CreateHBITMAPFromDIB(dib_mask);
+				FIP->FI_Unload(dib_mask);
+
+				//convert to target resolution
+				if (!hbmMask || !(dib_ico = ConvertTo(dib_ico, bitDest, 0))) {
+					FIP->FI_Unload(dib_ico);
+					if(hbmMask) DeleteObject(hbmMask);
+					return;
+				}
+			} break;
+			case 32:
+			{//transparency by alpha schannel
+				//Calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit)
+				int bytespp = FIP->FI_GetLine(dib_ico) / w;
+				//set alpha schannel
+				for(unsigned y = 0; y < h; y++) {
+					BYTE *bits = FIP->FI_GetScanLine(dib_ico, y);
+					for(unsigned x = 0; x < w; x++) {
+						bits[FI_RGBA_ALPHA] = (y<t || y>=b)? 0:255;
+						// jump to next pixel
+						bits += bytespp;
+					}
+				}
+			} break;
+			default:
+				FIP->FI_Unload(dib_ico);
+				return;
+		}
+		//FIP->FI_SaveU(fif,dib_ico,_T("d:\\CD-Archiv\\Miranda\\trunk\\miranda\\bin9\\Debug Unicode\\Plugins\\dib_ico.bmp"),0);
+	}
+	else {
+		FIP->FI_Unload(dib);
+		FIP->FI_Unload(dib_ico);
+		return;
+	}
+
+	hScrBM = FIP->FI_CreateHBITMAPFromDIB(dib_ico);
+	FIP->FI_Unload(dib_ico);
+
+	if (!hScrBM) {
+		DeleteObject(hbmMask);
+		return;
+	}
+
+	//create ImageList
+	himl = ImageList_Create(16, 16 , bitDest | ILC_MASK, 0, nCountriesCount);
+	ImageList_Add(himl, hScrBM, hbmMask);
+	DeleteObject(hScrBM);	hScrBM  = NULL;
+	DeleteObject(hbmMask);	hbmMask = NULL;
+
+	if(himl!=NULL) {
+		phIconHandles=(HANDLE*)mir_alloc(nCountriesCount*sizeof(HANDLE));
+		if(phIconHandles!=NULL) {
+			char szId[20];
+			int i,index;
+			SKINICONDESC skid;
+
+			/* register icons */
+			ZeroMemory(&skid,sizeof(skid));
+			skid.cbSize				= sizeof(skid);
+			skid.ptszSection		= LPGENT("Country Flags");
+			skid.pszName			= szId;			// name to refer to icon when playing and in db
+			//skid.pszDefaultFile	= NULL;			// default icon file to use
+			//skid.iDefaultIndex	= NULL;			// index of icon in default file
+			skid.cx					= GetSystemMetrics(SM_CXSMICON); 
+			skid.cy					= GetSystemMetrics(SM_CYSMICON);
+			skid.flags				= SIDF_SORTED|SIDF_TCHAR;
+
+			for(i=0;i<nCountriesCount;++i) {
+				skid.ptszDescription = mir_a2t(LPGEN(countries[i].szName));
+				/* create identifier */
+				wsprintfA(szId,(countries[i].id==0xFFFF)?"%s0x%X":"%s%i","flags_",countries[i].id); /* buffer safe */
+				index = CountryNumberToBitmapIndex(countries[i].id);
+				/* create icon */
+				skid.hDefaultIcon=ImageList_ExtractIcon(NULL, himl, index);
+				index = CountryNumberToIndex(countries[i].id);
+
+				phIconHandles[index] = Skin_AddIcon(&skid);
+				if(skid.hDefaultIcon!=NULL) DestroyIcon(skid.hDefaultIcon);
+				mir_free(skid.ptszDescription); skid.ptszDescription = NULL;
+			}
+		}
+		ImageList_Destroy(himl);
+	}
+
+	/* create services */
+	CreateServiceFunction(MS_FLAGS_LOADFLAGICON,ServiceLoadFlagIcon);
+	CreateServiceFunction(MS_FLAGS_CREATEMERGEDFLAGICON,ServiceCreateMergedFlagIcon);
+}
+
+VOID UninitIcons()
+{
+	for(int i=0;i<nCountriesCount;++i) {
+		/* create identifier */
+		char szId[20];
+		wsprintfA(szId,(countries[i].id==0xFFFF)?"%s0x%X":"%s%i","flags_",countries[i].id); /* buffer safe */
+		Skin_RemoveIcon(szId);
+	}
+	mir_free(phIconHandles);  /* does NULL check */
+}
diff --git a/plugins/UserInfoEx/src/Flags/svc_flagsicons.h b/plugins/UserInfoEx/src/Flags/svc_flagsicons.h
new file mode 100644
index 0000000000..7f29330a11
--- /dev/null
+++ b/plugins/UserInfoEx/src/Flags/svc_flagsicons.h
@@ -0,0 +1,43 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+part of this code based on:
+Miranda IM Country Flags Plugin Copyright �2006-2007 H. 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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/Flags/svc_flagsicons.h $
+Revision       : $Revision: 204 $
+Last change on : $Date: 2010-09-28 16:48:38 +0400 (Вт, 28 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef  _UINFOEX_FLAGSICONS_H_INCLUDED_
+#define  _UINFOEX_FLAGSICONS_H_INCLUDED_
+
+int		CountryNumberToIndex(int countryNumber);
+HICON	LoadFlag(int countryNumber);
+
+VOID InitIcons();
+//VOID SvcFlagsOnModulesLoaded();
+VOID UninitIcons();
+
+#endif /* _UINFOEX_FLAGSICONS_H_INCLUDED_ */
diff --git a/plugins/UserInfoEx/src/classMAnnivDate.cpp b/plugins/UserInfoEx/src/classMAnnivDate.cpp
new file mode 100644
index 0000000000..fb487f61d2
--- /dev/null
+++ b/plugins/UserInfoEx/src/classMAnnivDate.cpp
@@ -0,0 +1,836 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classMAnnivDate.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "svc_Reminder.h"
+
+/**
+ * name:	MAnnivDate
+ * class:	MAnnivDate
+ * desc:	default constructor
+ * param:	none
+ * return:	nothing
+ **/ 
+MAnnivDate::MAnnivDate()
+{
+	Clear();
+}
+
+/**
+ * name:	MAnnivDate
+ * class:	MAnnivDate
+ * desc:	constructor, which duplicates an existing anniversary date
+ * param:	mda	- anniversary date to copy
+ * return:	nothing
+ **/ 
+MAnnivDate::MAnnivDate(MAnnivDate &mda)
+{
+	SetDate(mda);
+}
+
+/**
+ * name:	Clear
+ * class:	MAnnivDate
+ * desc:	set all attributes to default value
+ * param:	none
+ * return:	nothing
+ **/ 
+VOID MAnnivDate::Clear()
+{
+	ZeroDate();
+	_wID = ANID_NONE;
+	_strDesc.clear();
+	_strModule.clear();
+	_wFlags = MADF_NONE;
+	_bRemind = BST_INDETERMINATE;
+	_wDaysEarlier = (WORD)-1;
+}
+
+/**
+ * name:	Set
+ * class:	MAnnivDate
+ * desc:	set new date
+ * param:	none
+ * return:	nothing
+ **/ 
+VOID MAnnivDate::SetDate(SYSTEMTIME &st)
+{
+	ZeroDate();
+	Year(st.wYear);
+	Month(st.wMonth);
+	Day(st.wDay);
+}
+
+/**
+ * name:	Set
+ * class:	MAnnivDate
+ * desc:	duplicates the given anniversary date class
+ * param:	none
+ * return:	nothing
+ **/ 
+VOID MAnnivDate::SetDate(MAnnivDate &mda)
+{
+	SetDate(mda.SystemTime());
+	_wID = mda.Id();
+	_strDesc = mda.Description();
+	_strModule = mda.Module();
+	_wFlags = mda.Flags();
+	_bRemind = mda.RemindOption();
+	_wDaysEarlier = mda.RemindOffset();
+}
+
+/**
+ * name:	IsValid
+ * class:	MAnnivDate
+ * desc:	compare the current date with the given one in st
+ * param:	st	- SYSTEMTIME to compare with
+ * return:	number of days the st differs from the class value
+ **/ 
+__inline BOOLEAN MAnnivDate::IsValid() const
+{
+	return (
+		Year() > 1600 &&
+		Month() > 0 && Month() < 13 &&
+		Day() > 0 && Day() <= DaysInMonth(Month())
+ );
+}
+
+/**
+ * name:	CompareDays
+ * class:	MAnnivDate
+ * desc:	compare the current date with the given one in st
+ * param:	mt	- MTime to compare with
+ * return:	number of days the mt differs from the class value
+ **/ 
+INT MAnnivDate::CompareDays(MTime mt) const
+{
+	mt.Year(Year());
+	return DayOfYear() - mt.DayOfYear();
+}
+
+/**
+ * name:	Compare
+ * class:	MAnnivDate
+ * desc:	compare the current date with the given one in st
+ * param:	st	- SYSTEMTIME to compare with
+ * return:	number of days the st differs from the class value
+ **/
+BOOLEAN MAnnivDate::IsEqual(const SYSTEMTIME &st) const
+{
+	return (
+		Day() == st.wDay &&
+		Month() == st.wMonth &&
+		Year() == st.wYear
+ );
+}
+
+/**
+ * name:	DateStamp
+ * class:	MAnnivDate
+ * desc:	return the current date encoded as an DWORD
+ * param:	nothing
+ * return:	DWORD encoded date
+ **/
+DWORD MAnnivDate::DateStamp() const
+{
+	DWORD dwStamp;
+
+	if (!IsValid()) return 0;
+
+	dwStamp	= (Day() << 24) & 0xFF000000;
+	dwStamp |= (Month() << 16) & 0x00FF0000;
+	dwStamp |= Year() & 0x0000FFFF;
+	return dwStamp;
+}
+
+/**
+ * name:	DateStamp
+ * class:	MAnnivDate
+ * desc:	set the date according to a datestamp
+ * param:	dwStamp	- the dword encoded date
+ * return:	nothing
+ **/
+VOID MAnnivDate::DateStamp(const DWORD dwStamp)
+{
+	Day((const WORD)((dwStamp & 0xFF000000) >> 24));
+	Month((const WORD)((dwStamp & 0x00FF0000) >> 16));
+	Year((const WORD)(dwStamp & 0x0000FFFF));
+}
+
+/**
+ * name:	Age
+ * class:	MAnnivDate
+ * desc:	calculates the age according to the date of the class and current date
+ * param:	pNow	- optional pointer to a MTime class to specify a certain time to use for age calculation
+ * return:	number of years the anniversary differs from now
+ **/ 
+INT MAnnivDate::Age(MTime *pNow)
+{
+	INT age = 0;
+	MTime now;
+
+	if (!IsValid()) return -1;
+
+	if (pNow) now = *pNow;
+	else now.GetLocalTime();
+
+	age = now.Year() - Year();
+	if (age > 1 && CompareDays(now) > 0)
+		age--;
+	return age;
+}
+
+/**
+ * name:	Zodiac
+ * class:	MAnnivDate
+ * desc:	returns the zodiac icon and text for the date
+ * param:	none
+ * return:	structure, holding zodiac information
+ **/ 
+MZodiac MAnnivDate::Zodiac()
+{
+	static const struct TZodiac {
+		const WORD startDays;
+		const WORD endDays;
+		LPCTSTR szZodiac;
+		LPCSTR szZodiacIcon;
+	} zodiac[] = {
+		{ 80,	110,	LPGENT("Aries")			, ICO_ZOD_ARIES			},	// Widder
+		{ 111,	140,	LPGENT("Taurus")		, ICO_ZOD_TAURUS		},	// Stier
+		{ 141,	172,	LPGENT("Gemini")		, ICO_ZOD_GEMINI		},	// Zwillinge
+		{ 173,	203,	LPGENT("Cancer")		, ICO_ZOD_CANCER		},	// Krebs
+		{ 204,	235,	LPGENT("Leo")			, ICO_ZOD_LEO			},	// L�we
+		{ 236,	266,	LPGENT("Virgo")			, ICO_ZOD_VIRGO			},	// Jungfrau
+		{ 267,	296,	LPGENT("Libra")			, ICO_ZOD_LIBRA			},	// Waage
+		{ 297,	326,	LPGENT("Scorpio")		, ICO_ZOD_SCORPIO		},	// Scorpion
+		{ 327,	355,	LPGENT("Sagittarius")	, ICO_ZOD_SAGITTARIUS	},	// Sch�tze
+		{ 356,	364,	LPGENT("Capricorn")		, ICO_ZOD_CAPRICORN		},	// Steinbock
+		{ 1,	19,		LPGENT("Capricorn")		, ICO_ZOD_CAPRICORN		},	// Steinbock
+		{ 20,	49,		LPGENT("Aquarius")		, ICO_ZOD_AQUARIUS		},	// Wassermann
+		{ 50,	79,		LPGENT("Pisces")		, ICO_ZOD_PISCES		},	// Fische
+		//{ 0,	0,		LPGENT("Unknown")		, ICO_ZOD_UNKNOWN		},	// not found
+		{ 0,	0,		NULL						, ""					}	// end of array
+	};
+	const WORD wDays = DayOfYear();
+	BYTE i;
+	MZodiac mZodiac;
+
+	for (i = 0; i < 13 && (wDays < zodiac[i].startDays || wDays > zodiac[i].endDays); i++);
+
+	mZodiac.hIcon = IcoLib_GetIcon(zodiac[i].szZodiacIcon);
+	mZodiac.pszName = zodiac[i].szZodiac;
+	return mZodiac;
+}
+
+/***********************************************************************************************************
+ * reading and writing options
+ ***********************************************************************************************************/
+
+/**
+ * name:	DBGetReminderOpts
+ * class:	MAnnivDate
+ * desc:	read reminder options for previously read date from database
+ * param:	hContact	- handle to a contact to read the date from
+ * return:	0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBGetReminderOpts(HANDLE hContact)
+{
+	if (!hContact || hContact == INVALID_HANDLE_VALUE)
+		return 1;
+	if (_wID == ANID_BIRTHDAY) {
+		_bRemind = DB::Setting::GetByte(hContact, USERINFO, SET_REMIND_BIRTHDAY_ENABLED, BST_INDETERMINATE);
+		_wDaysEarlier = DB::Setting::GetWord(hContact, USERINFO, SET_REMIND_BIRTHDAY_OFFSET, (WORD)-1);
+	}
+	else if (_wID <= ANID_LAST) {
+		CHAR pszSetting[MAXSETTING];
+
+		// read reminder option
+		mir_snprintf(pszSetting, MAXSETTING, "Anniv%dReminder", _wID);
+		_bRemind = DB::Setting::GetByte(hContact, Module(), pszSetting, BST_INDETERMINATE);
+		// read offset
+		mir_snprintf(pszSetting, MAXSETTING, "Anniv%dOffset", _wID);
+		_wDaysEarlier = DB::Setting::GetWord(hContact, Module(), pszSetting, (WORD)-1);
+	}
+	else {
+		_bRemind = BST_INDETERMINATE;
+		_wDaysEarlier = (WORD)-1;
+	}
+	return 0;
+}
+
+/**
+ * name:	DBWriteReminderOpts
+ * class:	MAnnivDate
+ * desc:	write reminder options for date to database
+ * param:	hContact	- handle to a contact to read the date from
+ * return:	0 on success, 1 otherwise
+ **/
+INT MAnnivDate::DBWriteReminderOpts(HANDLE hContact)
+{
+	if (!hContact || hContact == INVALID_HANDLE_VALUE)
+		return 1;
+	if (_wID == ANID_BIRTHDAY) {
+		if (_bRemind == BST_INDETERMINATE) DB::Setting::Delete(hContact, USERINFO, SET_REMIND_BIRTHDAY_ENABLED);
+		else DB::Setting::WriteByte(hContact, USERINFO, SET_REMIND_BIRTHDAY_ENABLED, _bRemind);
+
+		if (_wDaysEarlier == (WORD)-1) DB::Setting::Delete(hContact, USERINFO, SET_REMIND_BIRTHDAY_OFFSET);
+		else DB::Setting::WriteWord(hContact, USERINFO, SET_REMIND_BIRTHDAY_OFFSET, _wDaysEarlier);
+	}
+	else if (_wID <= ANID_LAST) {
+		CHAR pszSetting[MAXSETTING];
+		// read reminder option
+		mir_snprintf(pszSetting, MAXSETTING, "Anniv%dReminder", _wID);
+		if (_bRemind == BST_INDETERMINATE) DB::Setting::Delete(hContact, USERINFO, pszSetting);
+		else DB::Setting::WriteByte(hContact, USERINFO, pszSetting, _bRemind);
+		
+		// read offset
+		mir_snprintf(pszSetting, MAXSETTING, "Anniv%dOffset", _wID);
+		if (_wDaysEarlier == (WORD)-1) DB::Setting::Delete(hContact, USERINFO, pszSetting);
+		else DB::Setting::WriteWord(hContact, USERINFO, pszSetting, _wDaysEarlier);
+	}
+	return 0;
+}
+
+/***********************************************************************************************************
+ * reading, writing and deleting general date
+ ***********************************************************************************************************/
+
+/**
+ * name:	DBGetDate
+ * class:	MAnnivDate
+ * desc:	read a certain date from database
+ * param:	hContact	- handle to a contact to read the date from
+ *			pszModule	- module holding the date
+ *			szDay		- setting of the day to read
+ *			szMonth		- setting of the month to read 
+ *			szYear		- setting of the year to read
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBGetDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear)
+{
+	WORD wtmp;
+
+	ZeroDate();
+
+	wtmp = DB::Setting::GetWord(hContact, pszModule, szYear, 0);
+	if (wtmp < 1601) return 1;
+	Year(wtmp);
+
+	wtmp = DB::Setting::GetWord(hContact, pszModule, szMonth, 0);
+	if (wtmp > 0 && wtmp < 13) {
+		Month(wtmp);
+
+		wtmp = DB::Setting::GetWord(hContact, pszModule, szDay, 0);
+		if (wtmp > 0 && wtmp <= DaysInMonth(Month())) {
+			Day(wtmp);
+			// date was correctly read from db
+			_strModule = pszModule;
+			return 0;
+		}
+	}
+	ZeroDate();
+	return 1;
+}
+
+/**
+ * name:	DBWriteDate
+ * class:	MAnnivDate
+ * desc:	write a certain date from database
+ * param:	hContact	- handle to a contact to read the date from
+ *			pszModule	- module holding the date
+ *			szDay		- setting of the day to read
+ *			szMonth		- setting of the month to read 
+ *			szYear		- setting of the year to read
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBWriteDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear)
+{
+	return (
+		DB::Setting::WriteByte(hContact, pszModule, szDay, (BYTE)Day()) ||
+		DB::Setting::WriteByte(hContact, pszModule, szMonth, (BYTE)Month()) ||
+		DB::Setting::WriteWord(hContact, pszModule, szYear, Year())
+);
+}
+
+/**
+ * name:	DBDeleteDate
+ * class:	MAnnivDate
+ * desc:	delete a certain date from database
+ * param:	hContact	- handle to a contact to read the date from
+ *			pszModule	- module holding the date
+ *			szDay		- setting of the day to read
+ *			szMonth		- setting of the month to read 
+ *			szYear		- setting of the year to read
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBDeleteDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear) const
+{
+	INT ret;
+
+	ret = DB::Setting::Delete(hContact, pszModule, szDay);
+	ret &= DB::Setting::Delete(hContact, pszModule, szMonth);
+	ret &= DB::Setting::Delete(hContact, pszModule, szYear);
+	return ret;
+}
+
+/***********************************************************************************************************
+ * reading, writing and deleting general datestamp
+ ***********************************************************************************************************/
+
+/**
+ * name:	DBGetDateStamp
+ * class:	MAnnivDate
+ * desc:	Read a datestamp from database. A datestamp is an DWORD of the form <ddmmyyyy>.
+ * param:	hContact		- handle to a contact to read the datestamp from
+ *			pszModule		- module to read the datestamp from
+ *			pszSetting		- key used to identify the datestamp
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBGetDateStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting) 
+{
+	DBVARIANT dbv;
+
+	if (DB::Setting::GetAsIs(hContact, pszModule, pszSetting, &dbv))
+		return 1;
+	if (dbv.type != DBVT_DWORD) {
+		DB::Variant::Free(&dbv);
+		return 1;
+	}
+	DateStamp(dbv.dVal);
+	return IsValid() == 0;
+}
+
+/**
+ * name:	DBWriteDateStamp
+ * class:	MAnnivDate
+ * desc:	Write a datestamp to database. A datestamp is an DWORD of the form <ddmmyyyy>.
+ * param:	hContact		- handle to a contact to write the datestamp to
+ *			pszModule	 - module to write the datestamp to
+ *			pszSetting	- key used to save the datestamp
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBWriteDateStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting) 
+{
+	DWORD dwStamp = DateStamp();
+
+	if (hContact	 == INVALID_HANDLE_VALUE	||
+			pszModule	== 0 || *pszModule	== 0 ||
+			pszSetting == 0 || *pszSetting == 0 ||
+			dwStamp == 0)
+	{
+		return 1;
+	}
+	return DB::Setting::WriteDWord(hContact, pszModule, pszSetting, dwStamp);
+}
+
+/***********************************************************************************************************
+ * reading, writing and deleting birthday
+ ***********************************************************************************************************/
+
+/**
+ * name:	DBGetBirthDate
+ * class:	MAnnivDate
+ * desc:	try to read birthday date from all known modules
+ * param:	hContact		- handle to a contact to read the date from
+ *			pszProto		- basic protocol module
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBGetBirthDate(HANDLE hContact, LPSTR pszProto)
+{
+	Clear();
+
+	// try to get birthday from any custom module 
+	if (	!DBGetDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR) ||
+			!DBGetDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR) ||
+			!DBGetDate(hContact, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR) ||
+			!DBGetDate(hContact, USERINFO, SET_CONTACT_DOBD, SET_CONTACT_DOBM, SET_CONTACT_DOBY))
+	{
+		SetFlags(MADF_HASCUSTOM);
+	}
+	// if pszProto is set to NULL, this will be scaned only incase the birthday date has not been found yet
+	else if (pszProto || (pszProto = DB::Contact::Proto(hContact)) != NULL)
+	{
+		// try to get birthday from basic protocol
+		if (!DBGetDate(hContact, pszProto, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR)) 
+		{
+			SetFlags(MADF_HASPROTO);
+		}
+		// try to get birthday date from metacontact's subcontact
+		else if (DB::Module::IsMetaAndScan(pszProto))
+		{
+			const INT def = DB::MetaContact::SubDefNum(hContact);
+			HANDLE hSubContact;
+
+			// try to get setting from the default subcontact first
+			if (def > -1 && def < INT_MAX) 
+			{
+				hSubContact = DB::MetaContact::Sub(hContact, def);
+				if (hSubContact != NULL && !DBGetBirthDate(hSubContact, NULL)) 
+				{
+					RemoveFlags(MADF_HASCUSTOM);
+					SetFlags(MADF_HASMETA);
+				}
+			}
+
+			// scan all subcontacts for the setting
+			if (_wFlags == 0)
+			{
+				const INT cnt = DB::MetaContact::SubCount(hContact);
+
+				if (cnt < INT_MAX) 
+				{
+					INT i;
+					for (i = 0; i < cnt; i++) 
+					{
+						if (i != def)
+						{
+							hSubContact = DB::MetaContact::Sub(hContact, i);
+							if (hSubContact != NULL && !DBGetBirthDate(hSubContact, NULL)) 
+							{
+								RemoveFlags(MADF_HASCUSTOM);
+								SetFlags(MADF_HASMETA);
+								break;
+		}	}	}	}	}	}	
+	}
+
+	if (_wFlags != 0) 
+	{
+		_wID = ANID_BIRTHDAY;
+		_strDesc = TranslateT("Birthday");
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * name:	DBMoveBirthDate
+ * class:	MAnnivDate
+ * desc:	keep the database clean 
+ * param:	hContact		- handle to a contact to read the date from
+ *			bOld			- byte RemindBirthModule src
+ *			bNew			- byte RemindBirthModule dest
+ * return:	0 on success, 1 otherwise
+ **/ 
+// 
+INT MAnnivDate::DBMoveBirthDate(HANDLE hContact, BYTE bOld, BYTE bNew) {
+	Clear();
+	switch(bOld) {
+		case 0:		//MOD_MBIRTHDAY
+			if (!DBGetDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR)) {
+				if(DBWriteDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+					return 1;
+				DBDeleteDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+				DB::Setting::Delete(hContact, MOD_MBIRTHDAY, "BirthMode");
+				}
+			break;
+		case 1:		//USERINFO
+			if (!DBGetDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR)) {
+				if(DBWriteDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+					return 1;
+				DB::Setting::WriteByte(hContact, MOD_MBIRTHDAY, "BirthMode", 2);
+				DBDeleteDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+			}
+			break;
+		default:
+			return 1;
+			break;
+		}
+	return 0;
+}
+
+/**
+ * name:	DBWriteBirthDate
+ * class:	MAnnivDate
+ * desc:	write birthday date to desired module
+ * param:	hContact		- handle to a contact to read the date from
+ *			pszProto		- basic protocol module
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBWriteBirthDate(HANDLE hContact)
+{
+	INT rc = 0;
+	LPCSTR pszModule = SvcReminderGetMyBirthdayModule();
+
+	rc = DBWriteDate(hContact, pszModule, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+	if (!rc)
+	{
+		if (!mir_strcmp(pszModule, MOD_MBIRTHDAY))
+		{
+			DB::Setting::WriteByte(hContact, MOD_MBIRTHDAY, "BirthMode", 2);
+		}
+
+		if (
+				// only delete values from current contact's custom modules
+				!(_wFlags & (MADF_HASPROTO|MADF_HASMETA)) &&
+				// check whether user wants this feature
+				DB::Setting::GetByte(SET_REMIND_SECUREBIRTHDAY, TRUE) &&
+				!myGlobals.UseDbxTree)
+		{
+			// keep the database clean
+			
+			if (mir_strcmp(pszModule, MOD_MBIRTHDAY)!= 0)
+			{
+				DBDeleteDate(hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+				DB::Setting::Delete(hContact, MOD_MBIRTHDAY, "BirthMode");
+			}
+			else if (mir_strcmp(pszModule, USERINFO) !=0)
+			{
+				DBDeleteDate(hContact, USERINFO, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+			}
+			DBDeleteDate(hContact, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+			DBDeleteDate(hContact, USERINFO, SET_CONTACT_DOBD, SET_CONTACT_DOBM, SET_CONTACT_DOBY);
+		}
+
+		rc = DB::Setting::WriteWord(hContact, USERINFO, SET_CONTACT_AGE, Age());
+	}
+	return rc;
+}
+
+/**
+ * name:	DBDeleteBirthDate
+ * class:	MAnnivDate
+ * desc:	delete birthday date from desired module
+ * param:	hContact		- handle to a contact to read the date from
+ *			pszProto		- basic protocol module
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBDeleteBirthDate(HANDLE hContact)
+{
+	return DBDeleteDate(hContact, Module(), SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR);
+}
+
+/***********************************************************************************************************
+ * reading, writing and deleting anniversary
+ ***********************************************************************************************************/
+
+/**
+ * name:	DBGetAnniversaryDate
+ * class:	MAnnivDate
+ * desc:	try to read anniversary date from userinfo module
+ * param:	hContact		- handle to a contact to read the date from
+ *			pszProto		- basic protocol module
+ * return: 0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBGetAnniversaryDate(HANDLE hContact, WORD iIndex)
+{
+	CHAR szStamp[MAXSETTING];
+	DBVARIANT dbv;
+	INT rc;
+
+	Clear();
+
+	// read date and convert older versions
+	mir_snprintf(szStamp, SIZEOF(szStamp), "Anniv%dDate", iIndex);
+	rc = DBGetDateStamp(hContact, USERINFO, szStamp);
+	if (!rc)
+	{
+		_strModule = USERINFO;
+		_wFlags |= MADF_HASCUSTOM;
+		_wID = iIndex;
+		
+		// read description
+		mir_snprintf(szStamp, SIZEOF(szStamp), "Anniv%dDesc", iIndex);
+		if (!DB::Setting::GetTString(hContact, USERINFO, szStamp, &dbv)) 
+		{
+			_strDesc = dbv.ptszVal;
+			DB::Variant::Free(&dbv);
+		}
+	}
+	return rc;
+}
+
+/**
+ * name:	DBWriteAnniversaryDate
+ * class:	MAnnivDate
+ * desc:	write birthday date to desired module
+ * param:	hContact		- handle to a contact to read the date from
+ *			pszProto		- basic protocol module
+ * return:	0 on success, 1 otherwise
+ **/ 
+INT MAnnivDate::DBWriteAnniversaryDate(HANDLE hContact, WORD wIndex)
+{
+	INT ret = 0;
+
+	// date can only be written to db as anniversary if it is not marked as birthday
+	if (wIndex <= ANID_LAST && _wID != ANID_BIRTHDAY) 
+	{
+		CHAR	pszSetting[MAXSETTING];
+
+		_wID = wIndex;
+		
+		mir_snprintf(pszSetting, SIZEOF(pszSetting), "Anniv%dDate", wIndex);
+		if (!DBWriteDateStamp(hContact, USERINFO, pszSetting)) 
+		{
+			// write description
+			mir_snprintf(pszSetting, SIZEOF(pszSetting), "Anniv%dDesc", wIndex);
+			DB::Setting::WriteTString(hContact, USERINFO, pszSetting, (LPTSTR)Description());
+			return 0;
+		}
+		// delete date if written incompletely
+		DB::Setting::Delete(hContact, USERINFO, pszSetting);
+	}
+	return 1;
+}
+
+/***********************************************************************************************************
+ * automatic backup service
+ ***********************************************************************************************************/
+
+static WORD AskUser(HANDLE hContact, MAnnivDate *pOldCustomDate, MAnnivDate *pNewProtoDate)
+{
+	MSGBOX	MB;
+	TCHAR	 szMsg[MAXDATASIZE];
+	TCHAR	 szDate[MAX_PATH];
+	TCHAR	 szoldDate[MAX_PATH];
+
+	pOldCustomDate->DateFormat(szoldDate, SIZEOF(szoldDate));
+	pNewProtoDate->DateFormat(szDate, SIZEOF(szDate));
+	
+	mir_sntprintf(szMsg, SIZEOF(szMsg),
+		TranslateT("%s provides a new birthday via protocol.\nIt is %s. The old one was %s.\n\nDo you want to use this as the new birthday for this contact?"), 
+		DB::Contact::DisplayName(hContact), szDate, szoldDate
+ );
+
+	MB.cbSize = sizeof(MSGBOX);
+	MB.hParent = NULL;
+	MB.hiLogo = IcoLib_GetIcon(ICO_DLG_ANNIVERSARY);
+	MB.hiMsg = NULL;
+	MB.uType = MB_YESALLNO|MB_ICON_QUESTION|MB_INFOBAR|MB_NOPOPUP;
+	MB.ptszTitle = LPGENT("Update custom birthday");
+	MB.ptszInfoText = LPGENT("Keeps your custom birthday up to date.");
+	MB.ptszMsg = szMsg;
+	return MsgBoxService(NULL, (LPARAM)&MB);
+}
+
+/**
+ * name:	BackupBirthday
+ * class:	MAnnivDate
+ * desc:	tries to read birthday date from protocol and compares it with the classes date
+ * param:	hContact		- handle to a contact to read the date from
+ *			pszProto		- basic protocol module
+ * return:	0 if backup was done, 1 otherwise
+ **/ 
+INT MAnnivDate::BackupBirthday(HANDLE hContact, LPSTR pszProto, const BOOLEAN bDontIgnoreAnything, PWORD lastAnswer)
+{
+	if (hContact)
+	{
+		// This birthday is a protocol based or metasubcontact's anniversary and no custom information exist,
+		// so directly back it up under all circumstances!
+		if ((_wFlags & MADF_HASPROTO) || (_wFlags & MADF_HASMETA))
+		{
+			DBWriteDateStamp(hContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED);
+			DBWriteBirthDate(hContact);
+		}
+		// A custom birthday was set by user before and is not to be ignored
+		else if ((_wFlags & MADF_HASCUSTOM) && (bDontIgnoreAnything || !lastAnswer || (*lastAnswer != IDNONE)))
+		{
+			if (!pszProto)
+			{
+				pszProto = DB::Contact::Proto(hContact);
+			}
+			if (pszProto)
+			{
+				BOOLEAN bIsMeta = DB::Module::IsMeta(pszProto);
+				BOOLEAN bIsMetaSub = !bIsMeta && DB::MetaContact::IsSub(hContact);
+				BOOLEAN bWantBackup = FALSE;
+				MAnnivDate mdbNewProto;
+				MAnnivDate mdbIgnore;
+				HANDLE hSubContact;
+
+				const INT nSubContactCount = (bIsMeta) ? DB::MetaContact::SubCount(hContact) : 0;
+				
+				bWantBackup = !mdbNewProto.DBGetDate(hContact, pszProto, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR) 
+									 && !IsEqual(mdbNewProto.SystemTime())
+									 && (bDontIgnoreAnything || (DB::Setting::GetDWord(hContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED, 0) != mdbNewProto.DateStamp()))
+									 && !bIsMetaSub;
+
+				// allow backup only, if the custom setting differs from all meta subcontacts' protocol based settings, too.
+				for (INT i = 0; (i < nSubContactCount) && bWantBackup && bIsMeta; i++)
+				{
+					hSubContact = DB::MetaContact::Sub(hContact, i);
+					if (hSubContact && !mdbIgnore.DBGetDate(hSubContact, pszProto, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+					{
+						bWantBackup = bWantBackup 
+											 && !IsEqual(mdbIgnore.SystemTime()) 
+											 && (bDontIgnoreAnything || (DB::Setting::GetDWord(hSubContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED, 0) != mdbIgnore.DateStamp()));
+					}
+				}
+				if (bWantBackup)
+				{
+					if (!lastAnswer || *lastAnswer != IDALL)
+					{
+						WORD rc = AskUser(hContact, this, &mdbNewProto);
+						if (lastAnswer)
+						{
+							*lastAnswer = rc;
+						}
+						if (IDYES != rc && IDALL != rc) 
+						{
+							// special handling for metasubcontacts required?!
+							mdbNewProto.DBWriteDateStamp(hContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED);
+							bWantBackup = FALSE;
+						}
+					}
+					if (bWantBackup)
+					{
+						Set(mdbNewProto);
+						DBWriteDateStamp(hContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED);
+						DBWriteBirthDate(hContact);
+						
+						// update metasubcontacts
+						for (INT i = 0; i < nSubContactCount; i++)
+						{
+							hSubContact = DB::MetaContact::Sub(hContact, i);
+							if (hSubContact != NULL) 
+							{
+								if (!mdbIgnore.DBGetDate(hSubContact, DB::Contact::Proto(hSubContact), SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR))
+								{
+									mdbIgnore.DBWriteDateStamp(hSubContact, USERINFO, SET_REMIND_BIRTHDAY_IGNORED);
+								}
+								DBWriteBirthDate(hSubContact);
+							}
+						}
+						return 0;
+					}
+				}
+			}
+		}				
+		/*
+		else if (mir_stricmp(Module(), SvcReminderGetMyBirthdayModule()))
+		{
+			DBWriteBirthDate(hContact);
+		}
+		*/
+	}
+	return 1;
+}
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/classMAnnivDate.h b/plugins/UserInfoEx/src/classMAnnivDate.h
new file mode 100644
index 0000000000..4e2d447a13
--- /dev/null
+++ b/plugins/UserInfoEx/src/classMAnnivDate.h
@@ -0,0 +1,132 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classMAnnivDate.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#pragma once
+
+#define ANID_LAST		0xFFFC
+#define ANID_BIRTHDAY	0xFFFE
+#define ANID_NONE		0xFFFF
+
+struct MZodiac {
+	HICON	hIcon;
+	LPCTSTR	pszName;
+};
+
+class MAnnivDate : public MTime
+{
+public:
+	typedef enum {
+		MADF_NONE				= 0,
+		MADF_CHANGED			= 1,	// date has been edited (used, if date is used in controls)
+		MADF_HASPROTO			= 2,	// basic protocol module contains date information
+		MADF_HASCUSTOM			= 4,	// date is customized or read from a custom module 
+		MADF_HASMETA			= 8,	// date is read from a metacontact's subcontact
+		MADF_REMINDER_CHANGED	= 16	// reminder options have changed
+	} EFlags;
+
+private:
+	WORD	_wID;			// index to anniversary in database or ANID_BIRTHDAY
+	tstring	_strDesc;		// descripes the anniversary (e.g. birthday)
+	string	_strModule;		// the module the anniversary has been read from
+	WORD	_wFlags;		// the flags
+	BYTE	_bRemind;		// per user setting for reminder (0 - disabled, 1 - use local offset, 2 - use global offset)
+	WORD	_wDaysEarlier;	// number of days to the anniversary the user wants to be reminded of this anniversary
+
+	INT DBWriteDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear);
+	INT DBDeleteDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear) const;
+
+public:
+	MAnnivDate();
+	MAnnivDate(MAnnivDate &mda);
+
+	// basic access to attributes
+	__inline LPCTSTR	Description() const				{ return _strDesc.c_str(); };
+	__inline VOID		Description(LPCTSTR pszDesc)	{ if (pszDesc) _strDesc = pszDesc; };
+	__inline LPCSTR		Module() const					{ return _strModule.c_str(); };
+	__inline VOID		Module(LPCSTR pszModule)		{ if (pszModule) _strModule = pszModule; else _strModule.clear(); };
+	__inline BYTE		RemindOption() const			{ return _bRemind; };
+	__inline VOID		RemindOption(BYTE bRemind)		{ if (bRemind <= BST_INDETERMINATE) _bRemind = bRemind; };
+	__inline WORD		RemindOffset() const			{ return _wDaysEarlier; };
+	__inline VOID		RemindOffset(WORD wOffset)		{ _wDaysEarlier = wOffset; };
+	__inline WORD		Id() const						{ return _wID; };
+	__inline VOID		Id(WORD wId)					{ if (_wID == ANID_NONE) _wID = wId; };
+	
+	DWORD				DateStamp() const;
+	VOID				DateStamp(const DWORD dwStamp);
+	
+	// basic checks
+	__inline BOOLEAN IsValid() const;
+	__inline BOOLEAN IsChanged() const						{ return (_wFlags & MADF_CHANGED); };
+	__inline BOOLEAN IsReminderChanged() const				{ return (_wFlags & MADF_REMINDER_CHANGED); };
+	__inline BOOLEAN IsEqual(const MAnnivDate &mda) const	{ return IsEqual(mda.SystemTime()); };
+	BOOLEAN	IsEqual(const SYSTEMTIME &st) const;
+
+	// handling flags
+	__inline WORD	Flags() const			{ return _wFlags; };
+	__inline VOID	Flags(WORD wFlags)		{ _wFlags = wFlags; };
+	__inline VOID	SetFlags(WORD wFlag)	{ _wFlags |= wFlag; };
+	__inline VOID	RemoveFlags(WORD wFlag)	{ _wFlags &= ~wFlag; };
+
+	// return diffence of days, ignoring the date
+	INT	CompareDays(MTime mt) const;
+	
+	MZodiac	Zodiac();
+	INT		Age(MTime *pNow = NULL);
+	VOID	Clear();
+
+	// read date from database
+	INT DBGetDate(HANDLE hContact, LPCSTR pszModule, LPCSTR szDay, LPCSTR szMonth, LPCSTR szYear);
+	INT DBGetDateStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+	INT DBGetAnniversaryDate(HANDLE hContact, WORD iIndex);
+	INT DBGetBirthDate(HANDLE hContact, LPSTR pszProto = NULL);
+	INT DBGetReminderOpts(HANDLE hContact);
+
+	// write date to database
+	INT DBWriteDateStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+	INT DBWriteAnniversaryDate(HANDLE hContact, WORD wIndex);
+	INT DBWriteBirthDate(HANDLE hContact);
+	INT DBWriteReminderOpts(HANDLE hContact);
+
+	// delete date from database
+	INT DBDeleteBirthDate(HANDLE hContact);
+
+	INT DBMoveBirthDate(HANDLE hContact, BYTE bOld, BYTE bNew);
+	INT BackupBirthday (HANDLE hContact, LPSTR pszProto = NULL, const BOOLEAN bDontIgnoreAnything = FALSE, PWORD lastAnswer = NULL);
+
+	// setting values
+	VOID	SetDate(SYSTEMTIME &st);
+	VOID	SetDate(MAnnivDate &mda);
+
+	BOOLEAN operator == (const SYSTEMTIME &st) { return IsEqual(st); };
+	BOOLEAN operator == (const MAnnivDate &mda) { return IsEqual(mda); };
+
+	VOID operator = (SYSTEMTIME &st) { SetDate(st); };
+	VOID operator = (MAnnivDate &mda) { SetDate(mda); };
+};
diff --git a/plugins/UserInfoEx/src/classMTime.cpp b/plugins/UserInfoEx/src/classMTime.cpp
new file mode 100644
index 0000000000..3b1dd2e081
--- /dev/null
+++ b/plugins/UserInfoEx/src/classMTime.cpp
@@ -0,0 +1,468 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classMTime.cpp $
+Revision       : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "svc_Timezone.h"
+
+/******************************************************************************************
+ * class MTime
+ *
+ *****************************************************************************************/
+
+/*********************************************
+ * construction
+ *********************************************/
+
+MTime::MTime() 
+{
+	ZeroDate();
+}
+
+MTime::MTime(SYSTEMTIME &st, const BOOLEAN bIsLocal)
+{
+	_SysTime = st;
+	_isLocal = bIsLocal != FALSE;
+}
+
+MTime::MTime(FILETIME &ft, const BOOLEAN bIsLocal)
+{
+	ZeroDate();
+	Set(ft, bIsLocal);
+}
+
+MTime::MTime(LARGE_INTEGER &li, const BOOLEAN bIsLocal)
+{
+	ZeroDate();
+	Set(li, bIsLocal);
+}
+
+MTime::MTime(DWORD dwStamp)
+{
+	ZeroDate();
+	FromStampAsUTC(dwStamp);
+}
+
+MTime::MTime(const MTime& mtime)
+{
+	Set(mtime);
+}
+
+VOID	MTime::ZeroDate() 
+{
+	_isLocal = FALSE;
+	ZeroMemory(&_SysTime, sizeof(_SysTime));
+}
+
+/*********************************************
+ * validation / checks
+ *********************************************/
+
+BOOLEAN	MTime::IsValid() const
+{
+	return (
+		_SysTime.wYear > 1600 &&
+		_SysTime.wMonth > 0 && _SysTime.wMonth < 13 &&
+		_SysTime.wDay > 0 && _SysTime.wDay <= DaysInMonth(_SysTime.wMonth) &&
+		_SysTime.wHour < 25 && 
+		_SysTime.wMinute < 60 && 
+		_SysTime.wSecond < 60 );
+}
+
+BOOLEAN	MTime::IsLeapYear() const
+{
+	return (!(((_SysTime.wYear) % 4 != 0) || (((_SysTime.wYear) % 100 == 0) && ((_SysTime.wYear) % 400 != 0))));
+}
+
+LONG	MTime::Compare(const DWORD dwTimeStamp) const
+{
+	return (LONG)(TimeStamp() - dwTimeStamp);
+}
+
+/**
+ * name:	Compare
+ * desc:	compare a filetime with the value of current object and return difference as number of seconds
+ * param:	ft	- FILETIME to compare with
+ * return:	number of seconds the ft differs from the class value
+ **/ 
+LONG	MTime::Compare(const FILETIME &ft) const
+{
+	const FILETIME ft1 = FileTime();
+	return (LONG)((*(__int64*)&ft1 - *(__int64*)&ft) / 10000000i64);
+}
+
+/**
+ * name:	Compare
+ * desc:	compare a systemtime with the value of current object and return difference as number of seconds it handles some strange, too.
+ * param:	st	- SYSTEMTIME to compare with
+ * return:	number of seconds the st differs from the class value
+ **/ 
+LONG	MTime::Compare(SYSTEMTIME st) const
+{
+	FILETIME ft2;
+
+	//strange day-in-month thing
+	if (st.wYear == 0) {
+		if (Month() < st.wMonth) return -1;
+		if (Month() > st.wMonth) return 1;
+
+		MTime mtTmp(st, FALSE);
+
+		mtTmp.Year(Year());
+		mtTmp.Day(1);
+		mtTmp.Set(mtTmp.FileTime(), FALSE);	//gets the day of week of the first of the month
+		mtTmp.Day(1 + (7 + st.wDayOfWeek - mtTmp.DayOfWeek()) % 7);
+
+		//last wDayOfWeek in month
+		if (st.wDay == 5) {
+			mtTmp.Day(mtTmp.Day() + 7 * 3);	//can't be less than 4 of that day in the month
+			if (mtTmp.Day() + 7 <= mtTmp.DaysInMonth(st.wMonth - 1))
+				mtTmp.Day(mtTmp.Day() + 7);
+		}
+		else
+			mtTmp.Day(7 * (st.wDay - 1)); //nth of month
+
+		ft2 = mtTmp.FileTime();
+	}
+	else {
+		SystemTimeToFileTime(&st, &ft2);
+	}
+	return Compare(ft2);
+}
+
+/**
+ * name:	Compare
+ * desc:	compare a MTime with the value of current object and return difference as number of seconds it handles some strange, too.
+ * param:	mt	- MTime to compare with
+ * return:	number of seconds the st differs from the class value
+ **/ 
+LONG	MTime::Compare(const MTime &mt) const
+{
+	return Compare(mt.SystemTime());
+}
+
+/*********************************************
+ * conversions
+ *********************************************/
+
+VOID	MTime::UTCToLocal()
+{
+	if (!IsLocal()) {
+		TIME_ZONE_INFORMATION tzInfo;
+		
+		ZeroMemory(&tzInfo, sizeof(TIME_ZONE_INFORMATION));
+		GetTimeZoneInformation(&tzInfo);
+		UTCToTzSpecificLocal(&tzInfo);
+	}
+}
+
+VOID	MTime::UTCToTzSpecificLocal(INT tzh)
+{
+
+	TIME_ZONE_INFORMATION tzInfo;
+
+	if (IsLocal()) LocalToUTC();
+	ZeroMemory(&tzInfo, sizeof(TIME_ZONE_INFORMATION));
+
+	if (tzh > 24) tzh = 24;
+	if (tzh < -24)tzh = -24;
+
+	GetTimeZoneInformation(&tzInfo);
+	tzInfo.Bias = tzh * 30i64;
+	UTCToTzSpecificLocal(&tzInfo);
+}
+
+LONG	MTime::_Offset(TIME_ZONE_INFORMATION *tzi)
+{
+	LONG offset = tzi->Bias;
+	// daylight saving times
+	if (tzi->StandardDate.wMonth != 0) {
+		if (tzi->DaylightDate.wMonth < tzi->StandardDate.wMonth) {
+			//northern hemisphere
+			if (Compare(tzi->DaylightDate) < 0 || Compare(tzi->StandardDate) > 0) 
+				offset += tzi->StandardBias;
+			else
+				offset += tzi->DaylightBias;
+		 }
+		 else {
+			//southern hemisphere
+			if (Compare(tzi->StandardDate) < 0 || Compare(tzi->DaylightDate) > 0)
+				offset += tzi->DaylightBias;
+			else
+				offset += tzi->StandardBias;
+		 }
+	}
+	return offset;
+}
+
+VOID	MTime::UTCToTzSpecificLocal(TIME_ZONE_INFORMATION *tzi)
+{
+	LARGE_INTEGER liFiletime;
+
+	// do not transform to local twice
+	if (tzi && !_isLocal) {
+		liFiletime = LargeInt();
+		liFiletime.QuadPart -= _Offset(tzi) * 60 * 10000000i64;
+		Set(liFiletime, TRUE);
+	}
+}
+
+VOID	MTime::TzSpecificLocalToUTC(TIME_ZONE_INFORMATION *tzi)
+{
+	LARGE_INTEGER liFiletime;
+
+	// do not transform to utc twice
+	if (tzi && _isLocal) {
+		liFiletime = LargeInt();
+		liFiletime.QuadPart += _Offset(tzi) * 60 * 10000000i64;
+		Set(liFiletime, TRUE);
+	}
+}
+
+VOID	MTime::LocalToUTC()
+{
+	TIME_ZONE_INFORMATION tzInfo;
+	
+	GetTimeZoneInformation(&tzInfo);
+	TzSpecificLocalToUTC(&tzInfo);
+}
+
+/*********************************************
+ * return values
+ *********************************************/
+
+LARGE_INTEGER	MTime::LargeInt() const
+{
+	LARGE_INTEGER liFileTime = { 0 };
+
+	SystemTimeToFileTime(&_SysTime, (LPFILETIME)&liFileTime);
+	return liFileTime;
+}
+
+FILETIME		MTime::FileTime() const
+{
+	FILETIME ftFileTime;
+
+	SystemTimeToFileTime(&_SysTime, &ftFileTime);
+	return ftFileTime;
+}
+
+DWORD	MTime::TimeStamp() const
+{
+	LARGE_INTEGER li;
+
+	if (IsLocal()) {
+		MTime mt(*this);
+		mt.LocalToUTC();
+		li = mt.LargeInt();
+	}
+	else
+		li = LargeInt();
+
+	li.QuadPart /= 10000000i64;
+	li.QuadPart -= 11644473600i64;
+
+	if (li.QuadPart < 0)
+		return 0;
+
+	return (DWORD)li.QuadPart;
+}
+
+WORD	MTime::DaysInMonth(const WORD &wMonth)	const
+{
+	static const WORD wDaysInMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+	if (wMonth > 12) return 0;
+	return (IsLeapYear() && wMonth == 2) ? wDaysInMonth[wMonth] + 1 : wDaysInMonth[wMonth];
+}
+
+WORD	MTime::DaysInYear(BOOLEAN bIgnoreLeap)	const
+{
+	return ((!bIgnoreLeap && IsLeapYear()) ? 366 : 365); 
+};
+
+WORD	MTime::DayOfYear()	const
+{
+	WORD daysResult = 0;
+	WORD i;
+
+	for (i = 0; i < _SysTime.wMonth; i++)
+		daysResult += DaysInMonth(i);
+	daysResult += _SysTime.wDay;
+	return daysResult;
+}
+
+WORD	MTime::AdjustYear(const INT nDiffDays)
+{
+	const INT nDay = DayOfYear() + nDiffDays;
+
+	if (nDay > DaysInYear())
+		return _SysTime.wYear + 1;
+	else if (nDay < 0)
+		return _SysTime.wYear - 1;
+	return _SysTime.wYear;
+}
+
+WORD	MTime::TimeFormat(LPTSTR ptszTimeFormat, WORD cchTimeFormat)
+{
+	if (!ptszTimeFormat || !cchTimeFormat)
+		return 0;
+	if ((cchTimeFormat = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &_SysTime, NULL, ptszTimeFormat, cchTimeFormat)) == 0) {
+		*ptszTimeFormat = 0;
+		return 0;
+	}
+	return cchTimeFormat;
+}
+
+WORD	MTime::DateFormat(LPTSTR ptszTimeFormat, WORD cchTimeFormat)
+{
+	if (!ptszTimeFormat || !cchTimeFormat)
+		return 0;
+	if ((cchTimeFormat = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &_SysTime, NULL, ptszTimeFormat, cchTimeFormat)) == 0) {
+		*ptszTimeFormat = 0;
+		return 0;
+	}
+	return cchTimeFormat;
+}
+
+WORD	MTime::DateFormatLong(LPTSTR ptszTimeFormat, WORD cchTimeFormat)
+{
+	if (!ptszTimeFormat || !cchTimeFormat)
+		return 0;
+	if ((cchTimeFormat = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &_SysTime, NULL, ptszTimeFormat, cchTimeFormat)) == 0) {
+		*ptszTimeFormat = 0;
+		return 0;
+	}
+	return cchTimeFormat;
+}
+
+/*********************************************
+ * set class value
+ *********************************************/
+
+VOID	MTime::FromStampAsUTC(const DWORD dwTimeStamp)
+{
+	LARGE_INTEGER li;
+	li.QuadPart = (dwTimeStamp + 11644473600i64) * 10000000i64;
+	Set(li, FALSE);
+}
+
+VOID	MTime::FromStampAsLocal(const DWORD dwTimeStamp)
+{
+	FromStampAsUTC(dwTimeStamp);
+	UTCToLocal();
+}
+
+VOID	MTime::Set(LARGE_INTEGER liFileTime, const BOOLEAN bIsLocal)
+{
+	if (liFileTime.QuadPart < 0i64) liFileTime.QuadPart = 0;
+	FileTimeToSystemTime((LPFILETIME)&liFileTime, &_SysTime);
+	_isLocal = bIsLocal != FALSE;
+}
+
+VOID	MTime::Set(FILETIME &ftFileTime, const BOOLEAN bIsLocal)
+{
+	FileTimeToSystemTime(&ftFileTime, &_SysTime);
+	_isLocal = bIsLocal != FALSE;
+}
+
+VOID	MTime::Set(const MTime &mt)
+{
+	Set(mt.SystemTime(), mt.IsLocal());
+}
+
+VOID	MTime::Set(SYSTEMTIME &st, const BOOLEAN bIsLocal)
+{
+	memcpy(&_SysTime, &st, sizeof(SYSTEMTIME));
+	_isLocal = bIsLocal != FALSE;
+}
+
+VOID	MTime::GetTimeUTC()
+{
+	_isLocal = FALSE;
+	::GetSystemTime(&_SysTime);
+}
+
+VOID	MTime::GetLocalTime()
+{
+	_isLocal = TRUE;
+	::GetLocalTime(&_SysTime);
+}
+
+VOID	MTime::GetLocalTime(HANDLE hContact)
+{
+	TIME_ZONE_INFORMATION tzi;
+
+	GetTimeUTC();
+
+	if (!GetContactTimeZoneInformation((WPARAM)hContact, (LPARAM)&tzi)) {
+		UTCToTzSpecificLocal(&tzi);
+	}
+}
+
+/*********************************************
+ * read and write time to miranda's database
+ *********************************************/
+
+INT		MTime::DBGetStamp  (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+	DWORD dwTimeStamp;
+
+	if (hContact == INVALID_HANDLE_VALUE ||
+			pszModule == NULL || pszModule[0] == 0 ||
+			pszSetting == NULL || pszSetting[0] == 0)
+	{
+		ZeroDate();
+		return 1;
+	}
+
+	dwTimeStamp = DB::Setting::GetDWord(hContact, pszModule, pszSetting, 0);
+
+	if (dwTimeStamp == 0) {
+		ZeroDate();
+		return 1;
+	}
+	FromStampAsUTC(dwTimeStamp);
+	_isLocal = FALSE;
+	return 0;
+}
+
+INT		MTime::DBWriteStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+	if (hContact == INVALID_HANDLE_VALUE ||
+			pszModule == NULL || pszModule[0] == 0 ||
+			pszSetting == NULL || pszSetting[0] == 0)
+	{
+		return 1;
+	}
+	return DB::Setting::WriteDWord(hContact, pszModule, pszSetting, TimeStamp());
+}
diff --git a/plugins/UserInfoEx/src/classMTime.h b/plugins/UserInfoEx/src/classMTime.h
new file mode 100644
index 0000000000..9d30c4782e
--- /dev/null
+++ b/plugins/UserInfoEx/src/classMTime.h
@@ -0,0 +1,125 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classMTime.h $
+Revision       : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+class MTime {
+	SYSTEMTIME	_SysTime;
+	BOOLEAN		_isLocal;
+
+	LONG		_Offset(TIME_ZONE_INFORMATION *tzi);
+
+public:
+	// contruction
+	MTime();
+	MTime(SYSTEMTIME &st, const BOOLEAN bIsLocal);
+	MTime(FILETIME &ft, const BOOLEAN bIsLocal);
+	MTime(LARGE_INTEGER &li, const BOOLEAN bIsLocal);
+	MTime(DWORD dwStamp);
+	MTime(const MTime& mtime);
+
+	// checks
+	__inline BOOLEAN IsLocal() const		{ return _isLocal; };
+	BOOLEAN	IsValid() const;
+	BOOLEAN	IsLeapYear() const;
+
+	// compare by seconds
+	LONG	Compare(SYSTEMTIME st) const;
+	LONG	Compare(const FILETIME &ft) const;
+	LONG	Compare(const MTime &mt) const;
+	LONG	Compare(const DWORD dwTimeStamp) const;
+	
+	// get value from class
+	LARGE_INTEGER	LargeInt() const;
+	FILETIME		FileTime() const;
+	DWORD			TimeStamp() const;
+	SYSTEMTIME		SystemTime() const	{ return _SysTime; };
+	WORD			DaysInMonth(const WORD &wMonth) const;
+	WORD			DaysInYear(BOOLEAN bIgnoreLeap = FALSE) const;
+	WORD			DayOfYear() const;
+	WORD			AdjustYear(const INT nDiffDays);
+
+	WORD			TimeFormat(LPTSTR ptszTimeFormat, WORD cchTimeFormat);
+	WORD			TimeFormat(tstring& str);
+	WORD			DateFormat(LPTSTR ptszTimeFormat, WORD cchTimeFormat);
+	WORD			DateFormatLong(LPTSTR ptszTimeFormat, WORD cchTimeFormat);
+	
+	// return single attributes
+	__inline WORD	DayOfWeek()	const			{ return _SysTime.wDayOfWeek;	};
+	__inline WORD	Day()		const			{ return _SysTime.wDay;			};
+	__inline WORD	Month()		const			{ return _SysTime.wMonth;		};
+	__inline WORD	Year()		const			{ return _SysTime.wYear;		};
+	__inline WORD	Hour()		const			{ return _SysTime.wHour;		};
+	__inline WORD	Minute()	const			{ return _SysTime.wMinute;		};
+	__inline WORD	Second()	const			{ return _SysTime.wSecond;		};
+
+	// set single values
+	__inline VOID	Minute(const WORD wMinute)	{ if (wMinute <= 59) _SysTime.wMinute = wMinute; };
+	__inline VOID	Hour(const WORD wHour)		{ if (wHour <= 24) _SysTime.wHour = wHour; };
+	__inline VOID	Day(const WORD wDay)		{ if (wDay <= 31) _SysTime.wDay = wDay; };
+	__inline VOID	Month(const WORD wMonth)	{ if (wMonth <= 12) _SysTime.wMonth = wMonth; };
+	__inline VOID	Year(const WORD wYear)		{ _SysTime.wYear = wYear; };
+
+	// set value to class
+	VOID	ZeroDate();
+	VOID	FromStampAsUTC(const DWORD dwTimeStamp);
+	VOID	FromStampAsLocal(const DWORD dwTimeStamp);
+	VOID	Set(FILETIME &ftFileTime, const BOOLEAN bIsLocal);
+	VOID	Set(LARGE_INTEGER liFileTime, const BOOLEAN bIsLocal);
+	VOID	Set(SYSTEMTIME &st, const BOOLEAN bIsLocal);
+	VOID	Set(const MTime &mt);
+
+	// get current time
+	VOID	GetTimeUTC();
+	VOID	GetLocalTime();
+	VOID	GetLocalTime(HANDLE hContact);
+
+	// conversions
+	VOID	UTCToLocal();
+	VOID	UTCToTzSpecificLocal(INT tzh);
+	VOID	UTCToTzSpecificLocal(TIME_ZONE_INFORMATION *tzi);
+	VOID	LocalToUTC();
+	VOID	TzSpecificLocalToUTC(TIME_ZONE_INFORMATION *tzi);
+
+	// read and write from and to db
+	INT		DBGetStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+	INT		DBWriteStamp(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+
+	// operatoren
+	VOID operator = (DWORD& dwTimeStamp)			{ FromStampAsUTC(dwTimeStamp); };
+	VOID operator = (FILETIME &ftFileTime)			{ Set(ftFileTime, FALSE); };
+	VOID operator = (LARGE_INTEGER &liFileTime)		{ Set(liFileTime, FALSE); };
+	VOID operator = (SYSTEMTIME &st)				{ Set(st, FALSE); };
+	VOID operator = (const MTime &mt)				{ Set(mt); };
+};
+
+/**
+ * prototypes
+ **/
+VOID		UserTime_LoadModule(VOID);
diff --git a/plugins/UserInfoEx/src/classPsTree.cpp b/plugins/UserInfoEx/src/classPsTree.cpp
new file mode 100644
index 0000000000..ac88130d17
--- /dev/null
+++ b/plugins/UserInfoEx/src/classPsTree.cpp
@@ -0,0 +1,993 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classPsTree.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "dlg_propsheet.h"
+
+
+static WNDPROC DefEditProc;
+
+/***********************************************************************************************************
+ * construction and destruction
+ ***********************************************************************************************************/
+
+/**
+ * name:	CPsTree
+ * class:	CPsTree
+ * desc:	constructor
+ * param:	none
+ * return:	none
+ **/
+CPsTree::CPsTree(LPPS pPs)
+{
+	_hWndTree = NULL;
+	_hImages = NULL;
+
+	_pItems = NULL;
+	_numItems = 0;
+	_curItem = -1;
+	_dwFlags = 0;
+	_hLabelEdit = NULL;
+	_hDragItem = NULL;
+	_isDragging = FALSE;
+	_pPs = pPs;
+}
+
+/**
+ * name:	~CPsTree
+ * class:	CPsTree
+ * desc:	frees up all memory, used by the tree control
+ * return:	nothing
+ **/
+CPsTree::~CPsTree()
+{
+	if (_hLabelEdit)
+	{
+		DestroyWindow(_hLabelEdit);
+		_hLabelEdit = NULL;
+	}
+	if (_pItems) 
+	{
+		for (INT i = 0; i < _numItems; i++) 
+		{
+			if (_pItems[i])
+			{
+				delete _pItems[i];
+				_pItems[i] = NULL;
+			}
+		}
+		MIR_FREE(_pItems);
+		_pItems = NULL;
+		_numItems = NULL;
+	}
+	ImageList_Destroy(_hImages);
+	_hImages = NULL;
+}
+
+/**
+ * name:	CPsTree
+ * class:	CPsTree
+ * desc:	constructor
+ * param:	none
+ * return:	none
+ **/
+BOOLEAN CPsTree::Create(HWND hWndTree, CPsHdr* pPsh)
+{
+	BOOLEAN rc;
+
+	if (hWndTree && pPsh->_hImages && pPsh->_pPages && pPsh->_numPages)
+	{
+		_hWndTree = hWndTree;
+		_hImages = pPsh->_hImages;
+		_pItems = pPsh->_pPages;
+		_numItems = pPsh->_numPages;
+		_dwFlags = pPsh->_dwFlags;
+		
+		TreeView_SetImageList(_hWndTree, _hImages, TVSIL_NORMAL);
+		TreeView_SetItemHeight(_hWndTree, TreeView_GetItemHeight(_hWndTree) + 4);
+		SetUserData(_hWndTree, this);
+		rc = TRUE;
+	}
+	else
+	{
+		rc = FALSE;
+	}
+	return rc;
+}
+
+/**
+ * name:	AddDummyItem
+ * class:	CPsTree
+ * desc:	insert an empty tree item group
+ * param:	pszGroup	- utf8 encoded string of the item to add
+ * return:	index of the new item or -1 if failed to add
+ **/
+INT CPsTree::AddDummyItem(LPCSTR pszGroup)
+{
+	if (mir_stricmp(pszGroup, TREE_ROOTITEM)) 
+	{
+		CPsHdr psh;
+		psh._hContact = _pPs->hContact;
+		psh._pszProto = _pPs->pszProto;
+		psh._hImages	= _hImages;
+		psh._pPages	 = _pItems;
+		psh._numPages = _numItems;
+
+		OPTIONSDIALOGPAGE odp = { 0 };
+		odp.cbSize = sizeof(odp);
+		odp.hInstance = ghInst;
+		odp.flags = ODPF_TCHAR;
+		odp.ptszTitle = mir_utf8decodeT(pszGroup);
+		
+		INT_PTR rc = UserInfo_AddPage((WPARAM)&psh, &odp);
+		mir_free(odp.ptszTitle);
+		if (!rc) {
+			_pItems = psh._pPages;
+			_numItems = psh._numPages;
+			return _numItems - 1;
+		}
+	}
+	return -1;
+}
+
+/**
+ * name:	InitTreeItems()
+ * desc:	initialize the tree control's datastructure
+ * param:	needWidth	- width to expand the tree by
+ * return:	TRUE if initialization is ok, FALSE otherwise
+ **/
+BOOLEAN CPsTree::InitTreeItems(LPWORD needWidth)
+{
+	INT i;
+	DBVARIANT dbv;
+
+	if (!_hWndTree || !_pItems)
+	{
+		return FALSE;
+	}
+
+	if (!DB::Setting::GetUString(NULL, MODNAME, SET_LASTITEM, &dbv)) 
+	{
+		_curItem = FindItemIndexByName(dbv.pszVal);
+		DB::Variant::Free(&dbv);
+	}
+
+	// init the groups
+	if ((_dwFlags & PSTVF_GROUPS) || (!_pPs->hContact && myGlobals.CanChangeDetails)) 
+	{
+		LPSTR pszGroup;
+		
+		// set treeview styles
+		TreeView_SetIndent(_hWndTree, 3);
+		SetWindowLongPtr(_hWndTree, GWL_STYLE, GetWindowLongPtr(_hWndTree, GWL_STYLE)|TVS_HASBUTTONS);
+
+		// init the iParent member for all the items
+		for (i = 0; i < _numItems; i++) 
+		{
+			if (_pItems[i] && (pszGroup = _pItems[i]->ParentItemName()) != NULL) 
+			{
+				INT iParent = FindItemIndexByName(pszGroup);
+				
+				// need to add an empty parent item
+				if (iParent == -1) 
+				{
+					iParent = AddDummyItem(pszGroup);
+				}
+				_pItems[i]->Parent(iParent);
+				mir_free(pszGroup);
+			}
+		}
+	}
+
+	if (needWidth)
+	{
+		*needWidth = 0;
+	}
+	ShowWindow(_hWndTree, SW_HIDE);
+	for (i = 0; i < _numItems; i++)
+	{
+		if (_pItems[i]->State() != DBTVIS_INVISIBLE)
+		{
+			ShowItem(i, needWidth);
+		}
+	}
+	ShowWindow(_hWndTree, SW_SHOW);
+	return TRUE;
+}
+
+/***********************************************************************************************************
+ * finding items
+ ***********************************************************************************************************/
+
+/**
+ * name:	FindItemIndexByHandle
+ * class:	CPsTree
+ * desc:	returns the treeitem with specified handle
+ * param:	hItem		- handle of the treeview's treeitem
+ * return:	HTREEITEM if item exists or NULL otherwise
+ **/
+INT CPsTree::FindItemIndexByHandle(HTREEITEM hItem)
+{
+	INT i;
+	for (i = 0; i < _numItems; i++) 
+	{
+		if (_pItems[i] && hItem == _pItems[i]->Hti())
+		{
+			return i;
+		}
+	}
+	return -1;
+}
+
+/**
+ * name:	FindItemIndexByHandle
+ * class:	CPsTree
+ * desc:	returns the treeitem with specified handle
+ * param:	hItem		- handle of the treeview's treeitem
+ * return:	HTREEITEM if item exists or NULL otherwise
+ **/
+INT CPsTree::FindItemIndexByName(LPCSTR pszName)
+{
+	INT i;
+	for (i = 0; i < _numItems; i++) 
+	{
+		if (_pItems[i] && _pItems[i]->HasName(pszName))
+		{
+			return i;
+		}
+	}
+	return -1;
+}
+
+/**
+ * name:	FindItemByHandle
+ * class:	CPsTree
+ * desc:	returns the treeitem with specified handle
+ * param:	hItem		- handle of the treeview's treeitem
+ * return:	HTREEITEM if item exists or NULL otherwise
+ **/
+CPsTreeItem* CPsTree::FindItemByHandle(HTREEITEM hItem)
+{
+	INT i;
+
+	if ((i = FindItemIndexByHandle(hItem)) > -1)
+	{
+		return _pItems[i];
+	}
+	return NULL;
+}
+
+/**
+ * name:	FindItemByName
+ * class:	CPsTree
+ * desc:	returns the treeitem with specified name
+ * param:	pszName		- name of the item to search for
+ * return:	HTREEITEM if item exists or NULL otherwise
+ **/
+CPsTreeItem* CPsTree::FindItemByName(LPCSTR pszName)
+{
+	INT i;
+	
+	if ((i = FindItemIndexByName(pszName)) > -1) 
+	{
+		return _pItems[i];
+	}
+	return NULL;
+}
+
+/**
+ * name:	FindItemByHandle
+ * class:	CPsTree
+ * desc:	returns the treeitem with specified handle
+ * param:	hItem		- handle of the treeview's treeitem
+ * return:	HTREEITEM if item exists or NULL otherwise
+ **/
+CPsTreeItem* CPsTree::FindItemByResource(HINSTANCE hInst, INT idDlg)
+{
+	INT i;
+	for (i = 0; i < _numItems; i++) 
+	{
+		if (_pItems[i] && _pItems[i]->Inst() == hInst && _pItems[i]->DlgId() == idDlg) 
+		{
+			return _pItems[i];
+		}
+	}
+	return NULL;
+}
+
+
+/**
+ * name:	FindItemHandleByName
+ * class:	CPsTree
+ * desc:	returns the treeitem with specified name
+ * param:	pszName		- name of the item to search for
+ * return:	HTREEITEM if item exists or NULL otherwise
+ **/
+HTREEITEM CPsTree::FindItemHandleByName(LPCSTR pszName)
+{
+	INT i;
+
+	if ((i = FindItemIndexByName(pszName)) > -1)
+	{
+		return _pItems[i]->Hti();
+	}
+	return NULL;
+}
+
+/***********************************************************************************************************
+ * public methods
+ ***********************************************************************************************************/
+
+
+/**
+ * name:	HideItem
+ * class:	CPsTree
+ * desc:	is called if icolib's icons have changed
+ * param:	iPageIndex	- the index of the treeitem in the array.
+ * return:	nothing
+ **/
+VOID CPsTree::HideItem(const INT iPageIndex)
+{
+	if (IsIndexValid(iPageIndex)) 
+	{
+		TreeView_DeleteItem(_hWndTree, _pItems[iPageIndex]->Hti());
+		_pItems[iPageIndex]->Hti(0);
+		_dwFlags |= PSTVF_STATE_CHANGED;
+	}
+}
+
+/**
+ * name:	ShowItem
+ * class:	CPsTree
+ * desc:	displays on of the items in the treeview
+ * param:	iPageIndex	- the index of the treeitem in the array.
+ *			needWidth	- gives and takes the width, the treeview must have to show all items properly
+ * return:	TRUE if item was added successfully, FALSE otherwise
+ **/
+HTREEITEM CPsTree::ShowItem(const INT iPageIndex, LPWORD needWidth)
+{
+	TVINSERTSTRUCT tvii;
+	CPsTreeItem *pti;
+
+	// check parameters
+	if (!_hWndTree || 
+		!IsIndexValid(iPageIndex) || 
+		!(pti = _pItems[iPageIndex]) ||
+		!pti->Name() ||
+		!pti->Label())
+	{
+		MsgErr(GetParent(_hWndTree), LPGENT("Due to a parameter error, one of the treeitems can't be added!"));
+		return NULL;
+	}	
+	// item is visible at the moment
+	if ((tvii.itemex.hItem = pti->Hti()) == NULL)
+	{
+		RECT rc;
+		const INT iParent = pti->Parent();
+		
+		// init the rest of the treeitem
+		tvii.hParent = IsIndexValid(iParent) ? ShowItem(iParent, needWidth) : NULL;
+		tvii.hInsertAfter		= (_dwFlags & PSTVF_SORTTREE) ? TVI_SORT : TVI_LAST;
+		tvii.itemex.mask		= TVIF_TEXT|TVIF_PARAM|TVIF_STATE;
+		tvii.itemex.pszText		= pti->Label();
+		tvii.itemex.state		= pti->State() == DBTVIS_EXPANDED ? TVIS_EXPANDED : 0;
+		tvii.itemex.stateMask	= TVIS_EXPANDED;
+		tvii.itemex.lParam		= iPageIndex;
+		// set images
+		if ((tvii.itemex.iImage = tvii.itemex.iSelectedImage = pti->Image()) != -1)
+		{
+			tvii.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+		}
+		// insert item into tree if set visible
+		if ((tvii.itemex.hItem = TreeView_InsertItem(_hWndTree, &tvii)) == NULL) 
+		{
+			MsgErr(GetParent(_hWndTree), LPGENT("An fatal error occured on adding a property sheet page!\nDialog creation aborted!"));
+			return NULL;
+		}
+		pti->Hti(tvii.itemex.hItem);
+		// calculate width of treeview
+		if (needWidth && TreeView_GetItemRect(_hWndTree, pti->Hti(), &rc, TRUE) && rc.right > *needWidth)
+		{
+			*needWidth = (WORD)rc.right;
+		}
+	}
+	return tvii.itemex.hItem;
+}
+
+/**
+ * name:	MoveItem()
+ * class:	CPsTree
+ * desc:	moves a treeitem and its children to a new position
+ * param:	pPs					- datastructure of the propertysheetpage
+ *			hItem				- the HTREEITEM to move
+ *			hInsertAfter		- the HTREEITEM to insert hItem after
+ *			bAsChild			- tells, whether to try to add hItem as child of hInsertAfter or not
+ * return:	handle to new (moved) treeitem if successful or NULL otherwise
+ **/
+HTREEITEM CPsTree::MoveItem(HTREEITEM hItem, HTREEITEM hInsertAfter, BOOLEAN bAsChild)
+{
+	TVINSERTSTRUCT tvis;
+	HTREEITEM hParent, hChild, hNewItem;
+	INT iItemIndex;
+
+	if (!hItem || !hInsertAfter)
+		return NULL;
+	if (hItem == hInsertAfter)
+		return hItem;
+	
+	switch ((ULONG_PTR)hInsertAfter) {
+		case TVI_ROOT:
+		case TVI_FIRST:
+		case TVI_LAST:
+			hParent = NULL;
+			bAsChild = FALSE;
+			break;
+		default:
+			hParent = TreeView_GetParent(_hWndTree, hInsertAfter);
+			break;
+	}
+	// do not move a parent next to its own children!
+	if (hItem == hParent)
+		return hItem;
+	// get detailed information about the item to move
+	if (FAILED(iItemIndex = FindItemIndexByHandle(hItem)))
+		return hItem;
+	
+	// item should be inserted as the first child of an existing root item
+	if (bAsChild) { 
+		tvis.hParent = hInsertAfter;
+		tvis.hInsertAfter = (_dwFlags & PSTVF_SORTTREE) ? TVI_SORT : ((bAsChild == 2) ? TVI_LAST : TVI_FIRST);
+	}
+	// item should be inserted after an existing item
+	else {
+		tvis.hParent = hParent;
+		tvis.hInsertAfter = hInsertAfter;
+	}
+
+	// don't move subitems of a protocol to root as this would mean them not to be unique anymore
+	if (!_pPs->hContact && (_pItems[iItemIndex]->Flags() & PSPF_PROTOPREPENDED) && !tvis.hParent)
+		return hItem;
+	
+	// prepare the insert structure
+	tvis.itemex.mask = TVIF_PARAM|TVIF_TEXT;
+	tvis.itemex.state = TVIS_EXPANDED;
+	tvis.itemex.stateMask = TVIS_EXPANDED;
+	tvis.itemex.pszText = _pItems[iItemIndex]->Label();
+	tvis.itemex.lParam = (LPARAM)iItemIndex;
+	if ((tvis.itemex.iImage = tvis.itemex.iSelectedImage = _pItems[iItemIndex]->Image()) >= 0)
+		tvis.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+
+	// insert the item
+	if (!(hNewItem = TreeView_InsertItem(_hWndTree, &tvis)))
+		return hItem;
+	// update handle pointer in the page structure
+	_pItems[iItemIndex]->Hti(hNewItem);
+	// get the index of the parent
+	_pItems[iItemIndex]->Parent(FindItemIndexByHandle(tvis.hParent));
+	// move children
+	hInsertAfter = hNewItem;
+	while (hChild = TreeView_GetChild(_hWndTree, hItem)) {
+		MoveItem(hChild, hInsertAfter, 2);
+	}
+	// delete old tree
+	TreeView_DeleteItem(_hWndTree, hItem);
+	_dwFlags |= PSTVF_POS_CHANGED;
+
+	TreeView_SelectItem(_hWndTree, hNewItem);
+	TreeView_Expand(_hWndTree, hNewItem, TVE_EXPAND);
+	return hNewItem;
+}
+
+/**
+ * name:	SaveItemsState
+ * class:	CPsTree
+ * desc:	saves the tree's visible items to database
+ * param:	pszGroup		- name of the parent item of the current subtree
+ *			hRootItem		- the root of the current subtree
+ *			iItem			- index of the current item for position saving
+ * return:	0 on success or 1 otherwise
+ **/
+WORD CPsTree::SaveItemsState(LPCSTR pszGroup, HTREEITEM hRootItem, INT& iItem)
+{
+	TVITEMEX tvi;
+	WORD numErrors = 0;
+
+	tvi.mask = TVIF_CHILDREN|TVIF_STATE|TVIF_PARAM;
+	tvi.state = 0;
+	tvi.stateMask = TVIS_EXPANDED;
+	tvi.lParam = (LPARAM)-1;
+
+	// save all visible items
+	for (tvi.hItem = TreeView_GetChild(_hWndTree, hRootItem);
+			 TreeView_GetItem(_hWndTree, &tvi);
+			 tvi.hItem = TreeView_GetNextSibling(_hWndTree, tvi.hItem)) 
+	{
+		numErrors += _pItems[tvi.lParam]->DBSaveItemState(pszGroup, iItem++, tvi.state, _dwFlags);
+		if (tvi.cChildren) numErrors += SaveItemsState(_pItems[tvi.lParam]->Name(), tvi.hItem, iItem);
+	}
+	return numErrors;
+}
+
+/**
+ * name:	SaveState
+ * class:	CPsTree
+ * desc:	saves the current tree to database
+ * param:	none
+ * return:	nothing
+ **/
+VOID CPsTree::SaveState()
+{
+	CPsTreeItem *pti = CurrentItem();
+
+	if (_hWndTree && (_dwFlags & (PSTVF_LABEL_CHANGED|PSTVF_POS_CHANGED|PSTVF_STATE_CHANGED))) {
+		SHORT i;
+		INT iItem = 0;
+
+		// save all visible items
+		WORD numErrors = SaveItemsState(TREE_ROOTITEM, TVGN_ROOT, iItem);
+
+		// save all invisible items of the current subtree
+		for (i = 0; i < _numItems; i++) {
+			if (!_pItems[i]->Hti()) {
+				LPSTR pszGroup;
+
+				if (!IsIndexValid(_pItems[i]->Parent()) || !(pszGroup = _pItems[_pItems[i]->Parent()]->Name()))
+					pszGroup = TREE_ROOTITEM;
+				numErrors += _pItems[i]->DBSaveItemState(pszGroup, iItem++, DBTVIS_INVISIBLE, _dwFlags);
+			}
+		}
+		// remove changed flags
+		RemoveFlags(PSTVF_STATE_CHANGED|PSTVF_LABEL_CHANGED|PSTVF_POS_CHANGED);
+	}
+
+	// save current selected item
+	if (pti) DB::Setting::WriteUString(SET_LASTITEM, pti->Name());
+	else DB::Setting::Delete(NULL, MODNAME, SET_LASTITEM);
+}
+
+/**
+ * name:	DBResetState
+ * class:	CPsTree
+ * desc:	delete all treesettings from database
+ * param:	pszGroup		- name of the parent item of the current subtree
+ *			hRootItem		- the root of the current subtree
+ *			iItem			- index of the current item for position saving
+ * return: 0 on success or 1 otherwise
+ **/
+VOID CPsTree::DBResetState()
+{
+	DB::CEnumList	Settings;
+
+	if (!Settings.EnumSettings(NULL, MODNAME))
+	{
+		INT i;
+		LPSTR s;
+		LPCSTR p;
+		INT_PTR c;
+
+		p = (_pPs->pszProto[0]) ? _pPs->pszProto : "Owner";
+		c = mir_strlen(p);
+
+		for (i = 0; i < Settings.getCount(); i++)
+		{
+			s = Settings[i];
+
+			if (s && *s == '{' && !mir_strnicmp(s + 1, p, c)) 
+			{
+				DB::Setting::Delete(NULL, MODNAME, s);
+			}
+		}
+		// keep only these flags
+		_dwFlags &= PSTVF_SORTTREE|PSTVF_GROUPS;
+	}
+}
+
+/***********************************************************************************************************
+ * public methods for handling label editing
+ ***********************************************************************************************************/
+
+/**
+ * name:	TPropsheetTree_LabelEditProc()
+ * desc:	subclussproc of the label editcontrol
+ * return:	0
+ **/
+static LRESULT CALLBACK TPropsheetTree_LabelEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+	switch(uMsg)
+	{
+		case WM_KEYDOWN:
+			switch(wParam)
+			{
+				case VK_RETURN:
+					return ((CPsTree*)GetUserData(hwnd))->EndLabelEdit(TRUE);
+				case VK_TAB:
+				case VK_ESCAPE:
+					return ((CPsTree*)GetUserData(hwnd))->EndLabelEdit(FALSE);
+			}
+			break;
+		case WM_KILLFOCUS:
+			((CPsTree*)GetUserData(hwnd))->EndLabelEdit(FALSE);
+			break;
+		case WM_GETDLGCODE:
+			return DLGC_WANTALLKEYS | CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam );
+	}
+	return CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam);
+}
+
+/**
+ * name:	BeginLabelEdit
+ * class:	CPsTree
+ * desc:	begins the labeledit mode
+ * param:	hItem		- handle of the treeitm whose label to edit
+ * return:	0
+ **/
+INT CPsTree::BeginLabelEdit(HTREEITEM hItem)
+{
+	CPsTreeItem* pti;
+
+	// tree is readonly
+	if (DB::Setting::GetByte(SET_PROPSHEET_READONLYLABEL, 0))
+		return 0;
+
+	// get item text
+	if (!hItem && !(hItem = TreeView_GetSelection(_hWndTree)))
+		return 0;
+	if (pti = FindItemByHandle(hItem))
+		{
+		RECT rc, rcTree;
+
+		// create the edit control
+		GetClientRect(_hWndTree, &rcTree);
+		TreeView_GetItemRect(_hWndTree, hItem, &rc, TRUE);
+		_hLabelEdit = CreateWindowEx(WS_EX_NOPARENTNOTIFY|WS_EX_CLIENTEDGE,
+						_T( "EDIT" ),
+						pti->Label(),
+						WS_VISIBLE|ES_AUTOHSCROLL|WS_CHILD,
+						rc.left, rc.top,
+						rcTree.right - rc.left, rc.bottom - rc.top,
+						_hWndTree,
+						NULL,
+						ghInst,
+						NULL );
+		if(_hLabelEdit)
+		{
+			_hDragItem = hItem;
+			SetUserData(_hLabelEdit, this);
+			DefEditProc = (WNDPROC)SetWindowLongPtr(_hLabelEdit,GWLP_WNDPROC, (LONG_PTR)TPropsheetTree_LabelEditProc );
+			SendMessage(_hLabelEdit, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0 );
+			Edit_SetSel(_hLabelEdit, 0, -1);
+			Edit_LimitText(_hLabelEdit, MAX_TINAME);
+			SetFocus(_hLabelEdit);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ * name:	EndLabelEdit
+ * class:	CPsTree
+ * desc:	exits the labeledit mode
+ *			bSave		- tell whether to save changes or not
+ * return:	0
+ **/
+INT CPsTree::EndLabelEdit(const BOOLEAN bSave)
+{
+	TCHAR szEdit[MAX_TINAME];
+	TVITEM tvi;
+	WORD cchEdit;
+
+	if (bSave && (cchEdit = Edit_GetText(_hLabelEdit, szEdit, MAX_TINAME)) > 0)
+	{
+		tvi.hItem = _hDragItem;
+		tvi.mask = TVIF_TEXT|TVIF_HANDLE;
+		tvi.pszText = szEdit;
+		if (TreeView_SetItem(_hWndTree, &tvi))
+		{
+			CPsTreeItem* pti;
+			if (pti = FindItemByHandle(_hDragItem))
+			{
+				pti->Rename(szEdit);
+			}
+			_dwFlags |= PSTVF_LABEL_CHANGED;
+		}
+	}
+	DestroyWindow(_hLabelEdit);
+	_hLabelEdit = NULL;
+	_hDragItem = NULL;
+	return 0;
+}
+
+/***********************************************************************************************************
+ * public methods for handling other commands
+ ***********************************************************************************************************/
+
+VOID CPsTree::PopupMenu()
+{
+	HMENU hPopup;
+	MENUITEMINFO mii;
+	TVHITTESTINFO hti;
+	TVITEM tvi;
+	POINT pt;
+	INT iItem, i;
+
+	// init popup menu
+	if (!(hPopup = CreatePopupMenu()))
+		return;
+	ZeroMemory(&mii, sizeof(MENUITEMINFO));
+	mii.cbSize = sizeof(mii);
+	mii.fMask = MIIM_STRING|MIIM_ID;
+
+	// get cursor postion
+	GetCursorPos(&pt);
+	hti.pt = pt;
+	ScreenToClient(_hWndTree, &hti.pt);
+
+	tvi.mask = TVIF_PARAM|TVIF_CHILDREN;
+	// find treeitem under cursor
+	TreeView_HitTest(_hWndTree, &hti);
+	if (hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+		tvi.hItem = hti.hItem;
+		TreeView_GetItem(_hWndTree, &tvi);
+
+		if (!DB::Setting::GetByte(SET_PROPSHEET_READONLYLABEL, FALSE)) {
+			mii.dwTypeData = TranslateT("Rename Item");
+			mii.wID = 32001;
+			InsertMenuItem(hPopup, 0, FALSE, &mii);
+		}
+		
+		// do not allow hiding groups
+		if (tvi.cChildren) {
+			mii.fMask |= MIIM_STATE;
+			mii.fState = MFS_DISABLED;
+		}
+		mii.dwTypeData = TranslateT("Hide Item");
+		mii.wID = 32000;
+		InsertMenuItem(hPopup, 0, FALSE, &mii);
+	}
+	else {
+		// add hidden items to menu
+		mii.wID = 0;
+		for (i = 0; i < _numItems; i++) {
+			if (!_pItems[i]->Hti()) {
+				mii.dwTypeData = _pItems[i]->Label();
+				mii.wID = 100 + i;
+				InsertMenuItem(hPopup, 0, FALSE, &mii);
+			}
+		}
+		// add headline
+		if (mii.wID > 0) {
+			mii.fMask |= MIIM_STATE;
+			mii.fState = MFS_DISABLED;
+			mii.dwTypeData = TranslateT("Show Items:");
+			mii.wID = 0;
+			InsertMenuItem(hPopup, 0, TRUE, &mii);
+			mii.fMask |= MIIM_FTYPE;
+			mii.fType = MFT_SEPARATOR;
+			InsertMenuItem(hPopup, 1, TRUE, &mii);
+			InsertMenuItem(hPopup, ++i, TRUE, &mii);
+		}
+		mii.fMask &= ~(MIIM_FTYPE|MIIM_STATE);
+		mii.dwTypeData = TranslateT("Reset to defaults");
+		mii.wID = 32004;
+		InsertMenuItem(hPopup, ++i, TRUE, &mii);
+	}
+	// show the popup menu
+	iItem = TrackPopupMenu(hPopup, TPM_RETURNCMD, pt.x, pt.y, 0, _hWndTree, NULL);
+	DestroyMenu(hPopup);
+	
+	switch (iItem) {
+		// hide the item
+		case 32000:
+			HideItem(tvi.lParam);
+			break;
+		// rename the item
+		case 32001:
+			BeginLabelEdit(tvi.hItem);
+			break;
+		// reset current tree
+		case 32004:
+			DBResetState();
+			break;
+		// show a hidden item
+		default:
+			if ((iItem -= 100) >= 0 && ShowItem(iItem, NULL))
+				AddFlags(PSTVF_STATE_CHANGED|PSTVF_POS_CHANGED);
+			break;
+	}
+}
+
+/***********************************************************************************************************
+ * public event handlers
+ ***********************************************************************************************************/
+
+/**
+ * name:	OnIconsChanged
+ * class:	CPsTree
+ * desc:	is called if icolib's icons have changed
+ * param:	none
+ * return:	nothing
+ **/
+VOID CPsTree::OnIconsChanged()
+{
+	for (INT i = 0; i < _numItems; i++) {
+		_pItems[i]->OnIconsChanged(this);
+	}
+}
+
+/**
+ * name:	OnInfoChanged
+ * class:	CPsTree
+ * desc:	contact information have changed and pages need an update
+ * param:	none
+ * return:	TRUE if any page holds changed information
+ **/
+BOOLEAN CPsTree::OnInfoChanged()
+{
+	PSHNOTIFY pshn;
+	INT i;
+	BYTE bChanged = 0;
+
+	pshn.hdr.idFrom = 0;
+	pshn.hdr.code = PSN_INFOCHANGED;
+	for (i = 0; i < _numItems; i++) {
+		pshn.hdr.hwndFrom = _pItems[i]->Wnd();
+		if (pshn.hdr.hwndFrom != NULL) {
+			pshn.lParam = (LPARAM)_pItems[i]->hContact();
+			SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+			if (PSP_CHANGED == GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT))
+				bChanged |= 1;
+			else
+				_pItems[i]->RemoveFlags(PSPF_CHANGED);
+		}
+	}
+	return bChanged;
+}
+
+/**
+ * name:	OnSelChanging
+ * class:	CPsTree
+ * desc:	the displayed page is up to change
+ * param:	none
+ * return:	nothing
+ **/
+BOOLEAN CPsTree::OnSelChanging()
+{	 
+	CPsTreeItem *pti = CurrentItem();
+	
+	if (pti != NULL) {
+		TreeView_SetItemState(_hWndTree, pti->Hti(), 0, TVIS_SELECTED);
+		if (pti->Wnd() != NULL) {
+			PSHNOTIFY pshn;
+
+			pshn.hdr.code = PSN_KILLACTIVE;
+			pshn.hdr.hwndFrom = pti->Wnd();
+			pshn.hdr.idFrom = 0;
+			pshn.lParam = (LPARAM)pti->hContact();
+			if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+				SetWindowLongPtr(_pPs->hDlg, DWLP_MSGRESULT, TRUE);
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * name:	OnSelChanged
+ * class:	CPsTree
+ * desc:	it handles the change of displayed page
+ * param:	lpnmtv	-	notification structure
+ * return:	nothing
+ **/
+VOID CPsTree::OnSelChanged(LPNMTREEVIEW lpnmtv)
+{
+	CPsTreeItem *oldPage = CurrentItem();
+	CPsTreeItem *pti;
+
+	_curItem = (INT)lpnmtv->itemNew.lParam;
+	if (pti = CurrentItem()) {
+		if (pti->Wnd() == NULL) {
+			pti->CreateWnd(_pPs);
+		}
+	}
+	// hide old page even if new item has no valid one
+	if (oldPage && oldPage->Wnd() != NULL)
+		ShowWindow(oldPage->Wnd(), SW_HIDE);
+	if (pti)
+		ShowWindow(pti->Wnd(), SW_SHOW);
+	if (lpnmtv->action != TVC_BYMOUSE)
+		SetFocus(_hWndTree);
+}
+
+/**
+ * name:	OnCancel
+ * class:	CPsTree
+ * desc:	asks pages to reset their information
+ * param:	none
+ * return:	nothing
+ **/
+VOID CPsTree::OnCancel()
+{
+	PSHNOTIFY pshn;
+	INT i;
+
+	pshn.hdr.idFrom = 0;
+	pshn.hdr.code = PSN_RESET;
+	for (i = 0; i < _numItems; i++) {
+		pshn.hdr.hwndFrom = _pItems[i]->Wnd();
+		if (pshn.hdr.hwndFrom && (_pItems[i]->Flags() & PSPF_CHANGED)) {
+			pshn.lParam = (LPARAM)_pItems[i]->hContact();
+			SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn);
+		}
+	}
+}
+
+/**
+ * name:	OnApply
+ * class:	CPsTree
+ * desc:	saves settings of all pages
+ * param:	none
+ * return:	0 if ready to continue, 1 if need to abort
+ **/
+INT CPsTree::OnApply()
+{
+	CPsTreeItem *pti = CurrentItem();
+	
+	if (pti != NULL) {
+		PSHNOTIFY pshn;
+		INT i;
+
+		pshn.hdr.idFrom = 0;
+		pshn.hdr.code = PSN_KILLACTIVE;
+		pshn.hdr.hwndFrom = pti->Wnd();
+		if (!SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+			// save everything to database
+			pshn.hdr.code = PSN_APPLY;
+			for (i = 0; i < _numItems; i++) {
+				pshn.lParam = (LPARAM)_pItems[i]->hContact();
+				if (_pItems[i] && _pItems[i]->Wnd() && _pItems[i]->Flags() & PSPF_CHANGED) {
+					pshn.hdr.hwndFrom = _pItems[i]->Wnd();
+					if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn) == PSNRET_INVALID_NOCHANGEPAGE) {
+						CPsTreeItem *pti;
+						if (pti = CurrentItem())
+							ShowWindow(pti->Wnd(), SW_HIDE);
+						_curItem = i;
+						ShowWindow(_pItems[i]->Wnd(), SW_SHOW);
+						return 1;
+					}
+					_pItems[i]->RemoveFlags(PSPF_CHANGED);
+				}
+			}
+			return 0;
+		}
+	}
+	return 1;
+}
diff --git a/plugins/UserInfoEx/src/classPsTreeItem.cpp b/plugins/UserInfoEx/src/classPsTreeItem.cpp
new file mode 100644
index 0000000000..4f8b641691
--- /dev/null
+++ b/plugins/UserInfoEx/src/classPsTreeItem.cpp
@@ -0,0 +1,697 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/classPsTreeItem.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "dlg_propsheet.h"
+#include "m_skin.h"
+
+/**
+ * name:	BoldGroupTitlesEnumChildren()
+ * desc:	set font of groupboxes to bold
+ *
+ * return:	0
+ **/
+BOOL CALLBACK BoldGroupTitlesEnumChildren(HWND hWnd, LPARAM lParam)
+{
+	 TCHAR szClass[64];
+	 GetClassName(hWnd, szClass, 64);
+	 if (!mir_tcscmp(szClass, _T("Button")) && (GetWindowLongPtr(hWnd, GWL_STYLE) & 0x0F) == BS_GROUPBOX)
+			SendMessage(hWnd, WM_SETFONT, lParam, NULL);
+	 return TRUE;
+}
+
+/**
+ * name:	CPsTreeItem
+ * class:	CPsTreeItem
+ * desc:	default constructor
+ * param:	pPsh		- propertysheet's init structure
+ *			odp			- optiondialogpage structure with the info about the item to add
+ * return: nothing
+ **/
+CPsTreeItem::CPsTreeItem()
+{
+	_idDlg = NULL;
+	_pTemplate = NULL;
+	_hInst = NULL;
+	_pfnDlgProc = NULL;
+	_hWnd = NULL;
+	_dwFlags = NULL;
+	_hItem = NULL;			// handle to the treeview item
+	_iParent = -1;			// index to the parent item
+	_iImage = -1;			// index of treeview item's image
+	_bState = NULL;			// initial state of this treeitem
+	_pszName = NULL;		// original name, given by plugin (not customized)
+	_ptszLabel = NULL;
+	_pszProto = NULL;
+	_pszPrefix = NULL;
+	_hContact = NULL;
+}
+
+/**
+ * name:	~CPsTreeItem
+ * class:	CPsTreeItem
+ * desc:	default destructor
+ * param:	none
+ * return:	nothing
+ **/
+CPsTreeItem::~CPsTreeItem()
+{
+	if (_hWnd)		DestroyWindow(_hWnd);
+	if (_pszName)	mir_free(_pszName);
+	if (_ptszLabel)	mir_free(_ptszLabel);
+	if (_pszProto)	mir_free((LPVOID)_pszProto);
+}
+
+/**
+ * name:	PropertyKey
+ * class:	CPsTreeItem
+ * desc:	merge the treeitem's unique name with a prefix to form a setting string
+ * param:	pszProperty - the prefix to add
+ * return:	pointer to the setting string
+ **/
+LPCSTR CPsTreeItem::PropertyKey(LPCSTR pszProperty)
+{
+	static CHAR pszSetting[MAXSETTING];
+	mir_snprintf(pszSetting, SIZEOF(pszSetting), "{%s\\%s}_%s", _pszPrefix, _pszName, pszProperty);
+	return pszSetting;
+}
+
+/**
+ * name:	GlobalName
+ * class:	CPsTreeItem
+ * desc:	return item name without prepended protocol name
+ * param:	nothing
+ * return:	item name without protocol name
+ **/
+LPCSTR CPsTreeItem::GlobalName()
+{
+	LPSTR pgn = NULL;
+	
+	if (_dwFlags & PSPF_PROTOPREPENDED)
+	{
+		pgn = mir_strchr(_pszName, '\\');
+		if (pgn && pgn[1]) pgn++;
+	}
+	return (!pgn || !*pgn) ?_pszName : pgn;
+}
+
+/**
+ * name:	GlobalPropertyKey
+ * class:	CPsTreeItem
+ * desc:	merge the treeitem's unique name with a prefix to form a setting string
+ * param:	pszProperty - the prefix to add
+ * return:	pointer to the setting string
+ **/
+LPCSTR CPsTreeItem::GlobalPropertyKey(LPCSTR pszProperty)
+{
+	static CHAR pszSetting[MAXSETTING];
+	mir_snprintf(pszSetting, SIZEOF(pszSetting), "{Global\\%s}_%s", GlobalName(), pszProperty);
+	return pszSetting;
+}
+
+/**
+ * name:	IconKey
+ * class:	CPsTreeItem
+ * desc:	merge the treeitem's unique name with a prefix to form a setting string
+ * param:	pszProperty			- the prefix to add
+ * return:	pointer to the setting string
+ **/
+LPCSTR CPsTreeItem::IconKey()
+{
+	LPCSTR pszIconName = GlobalName();
+	if (pszIconName)
+	{
+		static CHAR pszSetting[MAXSETTING];
+		mir_snprintf(pszSetting, SIZEOF(pszSetting), MODNAME"_{%s}", pszIconName);
+		return pszSetting;
+	}
+	return NULL;
+}
+
+/**
+ * name:	ParentItemName()
+ * class:	CPsTreeItem
+ * desc:	returns the unique name for the parent item
+ * param:	nothing
+ * return:	length of group name
+ **/
+LPSTR CPsTreeItem::ParentItemName()
+{
+	DBVARIANT dbv;
+	LPSTR result;
+
+	// try to read the parent item from the database
+	if (!DB::Setting::GetAString(NULL, MODNAME, PropertyKey(SET_ITEM_GROUP), &dbv))
+	{
+		result = dbv.pszVal;
+	}
+	else 
+	{
+		const CHAR* p = mir_strrchr(_pszName, '\\');
+		
+		if (p) 
+		{
+			INT cchGroup = p - _pszName + 1;
+			result = mir_strncpy((LPSTR)mir_alloc(cchGroup), _pszName, cchGroup);
+		}
+		else
+		{
+			result = NULL;
+		}
+	}
+	return result;
+}
+
+/**
+ * name:	SetName
+ * class:	CPsTreeItem
+ * desc:	set the unique name for this item from a given title as it comes with OPTIONDIALOGPAGE
+ * param:	ptszTitle		- the title which is the base for the unique name
+ *			bIsUnicode		- if TRUE the title is unicode
+ * return:	0 on success, 1 to 4 indicating the failed operation
+ **/
+INT CPsTreeItem::Name(LPTSTR ptszTitle, const BOOLEAN bIsUnicode)
+{
+	// convert title to utf8
+	_pszName = (bIsUnicode) ? mir_utf8encodeW((LPWSTR)ptszTitle) : mir_utf8encode((LPSTR)ptszTitle);
+	if (_pszName)
+	{
+		// convert disallowed characters
+		for (DWORD i = 0; _pszName[i] != 0; i++) 
+		{
+			switch (_pszName[i]) 
+			{
+				case '{': _pszName[i] = '('; break;
+				case '}': _pszName[i] = ')'; break;
+			}
+		}
+	}
+	return _pszName == NULL;
+}
+
+/**
+ * name:	HasName
+ * class:	CPsTreeItem
+ * desc:	returns true, if current item has the name provided by the parameter
+ * params:	pszName	- the name to match the item with
+ * return:	nothing
+ **/
+BOOLEAN	CPsTreeItem::HasName(const LPCSTR pszName) const
+{ 
+	return !mir_stricmp(_pszName, pszName); 
+};
+
+/**
+ * name:	Rename
+ * class:	CPsTreeItem
+ * desc:	Rename label for the treeitem
+ * params:	pszLabel	- the desired new label
+ * return:	nothing
+ **/
+VOID CPsTreeItem::Rename(const LPTSTR pszLabel)
+{
+	if(pszLabel && *pszLabel)
+	{
+		LPTSTR pszDup = mir_tcsdup(pszLabel);
+		if(pszDup) 
+		{
+			if(_ptszLabel)
+			{
+				mir_free(_ptszLabel);
+			}
+			_ptszLabel = pszDup;
+			// convert disallowed characters
+			while(*pszDup)
+			{
+				switch(*pszDup)
+				{
+					case '{': *pszDup = '('; break;
+					case '}': *pszDup = ')'; break;
+				}
+				pszDup++;
+			}
+			AddFlags(PSTVF_LABEL_CHANGED);
+		}
+	}
+}
+
+/**
+ * name:	ItemLabel
+ * class:	CPsTreeItem
+ * desc:	returns the label for a given item. The returned value must be freed after use!
+ * param:	pszName		- uniquely identifiing string for a propertypage encoded with utf8 (e.g.: {group\item})
+ * return:	Label in a newly allocated piece of memory
+ **/
+INT CPsTreeItem::ItemLabel(const BOOLEAN bReadDBValue)
+{
+	DBVARIANT dbv;
+
+	// clear existing
+	if (_ptszLabel)
+	{
+		mir_free(_ptszLabel);
+	}
+
+	// try to get custom label from database
+	if (!bReadDBValue || DB::Setting::GetTString(NULL, MODNAME, GlobalPropertyKey(SET_ITEM_LABEL), &dbv) || (_ptszLabel = dbv.ptszVal) == NULL)
+	{
+		// extract the name
+		LPSTR pszName = mir_strrchr(_pszName, '\\');
+		if (pszName && pszName[1])
+		{
+			pszName++;
+		}
+		else
+		{
+			pszName = _pszName;
+		}
+
+		LPTSTR ptszLabel = mir_utf8decodeT(pszName);
+		if (ptszLabel) 
+		{
+			_ptszLabel = mir_tcsdup(TranslateTS(ptszLabel));
+			mir_free(ptszLabel);
+		}
+	}
+	// return nonezero if label is invalid
+	return _ptszLabel == NULL;
+}
+
+/**
+ * name:	ProtoIcon
+ * class:	CPsTreeItem
+ * desc:	check if current tree item name is a protocol name and return its icon if so
+ * params:	none
+ * return:	nothing
+ **/
+HICON CPsTreeItem::ProtoIcon()
+{
+	PROTOACCOUNT **pa;
+	INT ProtoCount, i;
+
+	if (!CallService(MS_PROTO_ENUMACCOUNTS, (WPARAM)&ProtoCount, (LPARAM)&pa))
+	{
+		if (_pszName)
+		{
+			for (i = 0; i < ProtoCount; i++) 
+			{
+				if (pa[i]->type == PROTOTYPE_PROTOCOL)
+				{
+					TCHAR *ptszName = mir_a2t(_pszName);
+					if (!mir_tcsnicmp(pa[i]->tszAccountName, ptszName, mir_tcslen(pa[i]->tszAccountName))) 
+					{
+						HICON hIco;
+						CHAR szIconID[MAX_PATH];
+
+						mir_snprintf(szIconID, SIZEOF(szIconID), "core_status_%s1", pa[i]->szModuleName);
+						hIco = IcoLib_GetIcon(szIconID);
+						if (!hIco)
+						{
+							hIco = (HICON)CallProtoService(pa[i]->szModuleName, PS_LOADICON, PLI_PROTOCOL, NULL);
+						}
+						MIR_FREE (ptszName);
+						return hIco;
+					}
+					MIR_FREE (ptszName);
+				}
+			}
+		}
+	}
+	return NULL;
+}
+
+/**
+ * name:	Icon
+ * class:	CPsTreeItem
+ * desc:	load the icon, add to icolib if required and add to imagelist of treeview
+ * params:	hIml			- treeview's imagelist to add the icon to
+ *			odp				- pointer to OPTIONSDIALOGPAGE providing the information about the icon to load
+ *			hDefaultIcon	- default icon to use
+ * return: nothing
+ **/
+INT CPsTreeItem::Icon(HIMAGELIST hIml, OPTIONSDIALOGPAGE *odp, BOOLEAN bInitIconsOnly)
+{
+	HICON hIcon;
+
+	// check parameter
+	if (!_pszName || !odp)
+		return 1;
+
+	// load the icon if no icolib is installed or creating the required settingname failed
+	LPCSTR pszIconName = IconKey();
+
+	// use icolib to handle icons
+	if (!(hIcon = IcoLib_GetIcon(pszIconName))) 
+	{
+		SKINICONDESC sid;
+
+		ZeroMemory(&sid, sizeof(sid));
+		sid.cbSize = sizeof(sid);
+		sid.flags = SIDF_ALL_TCHAR;
+		sid.cx = GetSystemMetrics(SM_CXSMICON);
+		sid.cy = GetSystemMetrics(SM_CYSMICON);
+		sid.pszName = (LPSTR)pszIconName;
+		sid.ptszDescription = _ptszLabel;
+		sid.ptszSection = LPGENT(SECT_TREE);
+
+		// the item to insert brings along an icon?
+		if (odp->flags & ODPF_ICON) {
+			// is it uinfoex item?
+			if (odp->hInstance == ghInst) {
+
+				// the pszGroup holds the iconfile for items added by uinfoex
+				sid.ptszDefaultFile = odp->ptszGroup;
+
+				// icon library exists?
+				if (sid.ptszDefaultFile) {
+					sid.iDefaultIndex = (INT_PTR)odp->hIcon;
+				}
+				// no valid icon library
+				else {
+					sid.hDefaultIcon = ImageList_GetIcon(hIml, 0, ILD_NORMAL);;
+					sid.iDefaultIndex = -1;
+				}
+			}
+			// default icon is delivered by the page to add
+			else {
+				sid.hDefaultIcon = (odp->hIcon) ? odp->hIcon : ImageList_GetIcon(hIml, 0, ILD_NORMAL);
+				sid.iDefaultIndex = -1;
+			}
+		}
+		// no icon to add, use default
+		else {
+			sid.iDefaultIndex = -1;
+			sid.hDefaultIcon = ProtoIcon();
+			if (!sid.hDefaultIcon) sid.hDefaultIcon = ImageList_GetIcon(hIml, 0, ILD_NORMAL);
+		}
+		// add file to icolib
+		Skin_AddIcon(&sid);
+
+		if (!bInitIconsOnly)
+			hIcon = IcoLib_GetIcon(pszIconName);
+	}
+	
+	if (!bInitIconsOnly && hIml) {
+		// set custom icon to image list
+		if (hIcon) return ((_iImage = ImageList_AddIcon(hIml, hIcon)) == -1);
+		_iImage = 0;
+	}
+	else
+		_iImage = -1;
+	return 0;
+}
+
+/**
+ * name:	OnAddPage
+ * class:	CPsTreeItem
+ * desc:	inits the treeitem's attributes
+ * params:	pPsh	- pointer to the property page's header structure
+ *			odp		- OPTIONSDIALOGPAGE structure with the information about the page to add
+ * return:	0 on success, 1 on failure
+ **/
+INT CPsTreeItem::Create(CPsHdr* pPsh, OPTIONSDIALOGPAGE *odp)
+{
+	INT err;
+	TCHAR szTitle[ MAXSETTING ];
+
+	// check parameter
+	if (pPsh && pPsh->_dwSize == sizeof(CPsHdr) && odp && PtrIsValid(odp->hInstance))
+	{
+		// instance value
+		_hInst = odp->hInstance;
+		_dwFlags = odp->flags;
+		_initParam = odp->dwInitParam;
+
+		// init page owning contact
+		_hContact = pPsh->_hContact;
+		_pszProto = mir_strdup(pPsh->_pszProto);
+
+		// global settings prefix for current contact (is dialog owning contact's protocol by default)
+		_pszPrefix = (pPsh->_pszPrefix) ? pPsh->_pszPrefix : "Owner";
+
+		if (pPsh->_dwFlags & PSF_PROTOPAGESONLY) 
+		{
+			if (_dwFlags & ODPF_USERINFOTAB)
+			{
+				mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s %d\\%s"), odp->ptszTitle, pPsh->_nSubContact+1, odp->ptszTab);
+			}
+			else
+			{
+				mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s %d"), odp->ptszTitle, pPsh->_nSubContact+1);
+			}
+		}
+		else
+		{
+			if (_dwFlags & ODPF_USERINFOTAB)
+			{
+				mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s\\%s"), odp->ptszTitle, odp->ptszTab);
+			}
+			else
+			{
+				mir_tcscpy(szTitle, odp->ptszTitle);
+			}
+		}
+		// set the unique utf8 encoded name for the item
+		if (err = Name(szTitle, (_dwFlags & ODPF_UNICODE) == ODPF_UNICODE)) 
+		{
+			MsgErr(NULL, LPGENT("Creating unique name for a page failed with %d and error code %d"), err, GetLastError());
+		}
+		// read label from database or create it
+		else if (err = ItemLabel(TRUE)) 
+		{
+			MsgErr(NULL, LPGENT("Creating the label for a page failed with %d and error code %d"), err, GetLastError());
+		}
+		else
+		{
+			// load icon for the item
+			Icon(pPsh->_hImages, odp, (pPsh->_dwFlags & PSTVF_INITICONS) == PSTVF_INITICONS);
+			
+			// the rest is not needed if only icons are loaded
+			if (pPsh->_dwFlags & PSTVF_INITICONS)
+			{
+				return 0;
+			}
+
+			// load custom order
+			if (!(pPsh->_dwFlags & PSTVF_SORTTREE)) 
+			{
+				_iPosition = (INT)DB::Setting::GetByte(PropertyKey(SET_ITEM_POS), odp->position);
+				if ((_iPosition < 0) && (_iPosition > 0x800000A))
+					_iPosition = 0;
+			}
+			// read visibility state
+			_bState =	DB::Setting::GetByte(PropertyKey(SET_ITEM_STATE), DBTVIS_EXPANDED);
+
+			// error for no longer supported dialog template type
+			if (((UINT_PTR)odp->pszTemplate & 0xFFFF0000)) 
+			{
+				MsgErr(NULL, LPGENT("The dialog template type is no longer supported"));
+			}
+			else
+			{
+				// fetch dialog resource id
+				_idDlg = (INT_PTR)odp->pszTemplate;
+				// dialog procedure
+				_pfnDlgProc = odp->pfnDlgProc;
+
+				// is dummy item?
+				if (!_idDlg	&& !_pfnDlgProc)
+					return 0;
+
+				if (_idDlg	&& _pfnDlgProc)
+				{
+					// lock the property pages dialog resource
+					_pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(_hInst, FindResource(_hInst, (LPCTSTR)(UINT_PTR)_idDlg, RT_DIALOG)));
+					if (_pTemplate)
+					{
+						return 0;
+					}
+				}
+			}
+		}
+	}
+	return 1;
+}
+
+/**
+ * name:	DBSaveItemState
+ * class:	CPsTreeItem
+ * desc:	saves the current treeitem to database
+ * param:	pszGroup			- name of the parent item
+ *			iItemPosition		- iterated index to remember the order of the tree
+ *			iState				- expanded|collapsed|invisible
+ *			dwFlags				- tells what to save
+ * return:	handle to new (moved) treeitem if successful or NULL otherwise
+ **/
+WORD CPsTreeItem::DBSaveItemState(LPCSTR pszGroup, INT iItemPosition, UINT iState, DWORD dwFlags)
+{
+	WORD numErrors = 0;
+
+	// save group
+	if ((dwFlags & PSTVF_GROUPS) && (dwFlags & PSTVF_POS_CHANGED)) {
+		numErrors += DB::Setting::WriteUString(PropertyKey(SET_ITEM_GROUP), (LPSTR)pszGroup);
+	}
+	// save label
+	if ((dwFlags & PSTVF_LABEL_CHANGED) && (_dwFlags & PSTVF_LABEL_CHANGED)) {
+		numErrors += DB::Setting::WriteTString(GlobalPropertyKey(SET_ITEM_LABEL), Label());
+	}
+	// save position
+	if ((dwFlags & PSTVF_POS_CHANGED) && !(dwFlags & PSTVF_SORTTREE)) {
+		numErrors += DB::Setting::WriteByte(PropertyKey(SET_ITEM_POS), iItemPosition);
+	}
+	// save state
+	if (dwFlags & PSTVF_STATE_CHANGED) {
+		numErrors += DB::Setting::WriteByte(PropertyKey(SET_ITEM_STATE), 
+			_hItem ? ((iState & TVIS_EXPANDED) ? DBTVIS_EXPANDED : DBTVIS_NORMAL) : DBTVIS_INVISIBLE);
+	}
+	RemoveFlags(PSTVF_STATE_CHANGED|PSTVF_LABEL_CHANGED|PSTVF_POS_CHANGED);
+	return numErrors;
+}
+
+/**
+ * name:	CreateWnd
+ * class:	CPsTreeItem
+ * desc:	create the dialog for the propertysheet page
+ * params:	pPs		- propertysheet's datastructure
+ *			hDlg	- windowhandle of the propertysheet
+ * return:	windowhandle of the dialog if successful
+ **/
+HWND CPsTreeItem::CreateWnd(LPPS pPs)
+{
+	if (pPs && !_hWnd && _pTemplate && _pfnDlgProc) 
+	{
+		_hWnd = CreateDialogIndirectParam(_hInst, _pTemplate, pPs->hDlg, _pfnDlgProc, (LPARAM)_hContact);
+		if (_hWnd != NULL) 
+		{
+			PSHNOTIFY pshn;
+
+			pshn.hdr.code = PSN_PARAMCHANGED;
+			pshn.hdr.hwndFrom = _hWnd;
+			pshn.hdr.idFrom = 0;
+			pshn.lParam = (LPARAM)_initParam;
+			SendMessage(_hWnd, WM_NOTIFY, 0, (LPARAM)&pshn);
+
+			// force child window (mainly for AIM property page)
+			SetWindowLongPtr(_hWnd, GWL_STYLE, (GetWindowLongPtr(_hWnd, GWL_STYLE) & ~(WS_POPUP|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME)) | WS_CHILD);
+			SetWindowLongPtr(_hWnd, GWL_EXSTYLE, GetWindowLongPtr(_hWnd, GWL_EXSTYLE) & ~(WS_EX_APPWINDOW|WS_EX_STATICEDGE|WS_EX_CLIENTEDGE));
+			SetParent(_hWnd, pPs->hDlg);
+
+			// move dialog into the display area
+			SetWindowPos(_hWnd, HWND_TOP, 
+				pPs->rcDisplay.left,	pPs->rcDisplay.top,
+				pPs->rcDisplay.right - pPs->rcDisplay.left,	
+				pPs->rcDisplay.bottom - pPs->rcDisplay.top,	FALSE);
+			// set bold titles
+			if (_dwFlags & ODPF_BOLDGROUPS)
+			{
+				EnumChildWindows(_hWnd, BoldGroupTitlesEnumChildren, (LPARAM)pPs->hBoldFont);
+			}
+						
+			// some initial notifications
+			OnInfoChanged();
+			OnPageIconsChanged();
+			return _hWnd;
+		}
+	}
+	return NULL;
+}
+
+/***********************************************************************************************************
+ * public event handlers
+ ***********************************************************************************************************/
+
+/**
+ * name:	OnInfoChanged
+ * class:	CPsTreeItem
+ * desc:	Notifies the propertypage of changed contact information
+ * params:	none
+ * return:	nothing
+ **/
+VOID CPsTreeItem::OnInfoChanged()
+{
+	if (_hWnd) {
+		PSHNOTIFY pshn;
+
+		pshn.hdr.code = PSN_INFOCHANGED;
+		pshn.hdr.hwndFrom = _hWnd;
+		pshn.hdr.idFrom = 0;
+		pshn.lParam = (LPARAM)_hContact;
+		if (PSP_CHANGED != SendMessage(_hWnd, WM_NOTIFY, 0, (LPARAM)&pshn)) {
+			_dwFlags &= ~PSPF_CHANGED;
+		}
+	}
+}
+
+/**
+ * name:	OnPageIconsChanged
+ * class:	CPsTreeItem
+ * desc:	Notifies the propertypage of changed icons in icolib
+ * params:	none
+ * return:	nothing
+ **/
+VOID CPsTreeItem::OnPageIconsChanged()
+{
+	if (_hWnd) {
+		PSHNOTIFY pshn;
+
+		pshn.hdr.code = PSN_ICONCHANGED;
+		pshn.hdr.hwndFrom = _hWnd;
+		pshn.hdr.idFrom = 0;
+		pshn.lParam = (LPARAM)_hContact;
+		SendMessage(_hWnd, WM_NOTIFY, 0, (LPARAM)&pshn);
+	}
+}
+
+/**
+ * name:	OnIconsChanged
+ * class:	CPsTreeItem
+ * desc:	Handles reloading icons if changed by icolib
+ * params:	none
+ * return:	nothing
+ **/
+VOID CPsTreeItem::OnIconsChanged(CPsTree *pTree)
+{
+	HICON hIcon;
+	RECT rc;
+
+	// update tree item icons
+	if (pTree->ImageList() && (hIcon = IcoLib_GetIcon(IconKey())) != NULL) {
+		_iImage = (_iImage > 0)
+			? ImageList_ReplaceIcon(pTree->ImageList(), _iImage, hIcon)
+			: ImageList_AddIcon(pTree->ImageList(), hIcon);
+		
+		if (_hItem && TreeView_GetItemRect(pTree->Window(), _hItem, &rc, 0)) {
+			InvalidateRect(pTree->Window(), &rc, TRUE);
+		}
+	}
+	// update pages icons
+	OnPageIconsChanged();
+}
+
diff --git a/plugins/UserInfoEx/src/commonheaders.cpp b/plugins/UserInfoEx/src/commonheaders.cpp
new file mode 100644
index 0000000000..608d7ddbfc
--- /dev/null
+++ b/plugins/UserInfoEx/src/commonheaders.cpp
@@ -0,0 +1,180 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/commonheaders.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+// global:
+HINSTANCE		ghInst		= NULL;
+TIME_API		tmi;					//timezone interface
+FI_INTERFACE	*FIP		= NULL;		//freeimage interface
+CLIST_INTERFACE *pcli		= NULL;
+
+MGLOBAL			myGlobals;
+pfnDwmIsCompositionEnabled	dwmIsCompositionEnabled;
+
+/**
+ * Calculates an unique DWORD number from a string.
+ * It's the same as used in langpack.
+ *
+ * @param		szStr	- string to calculate the hash value for
+ * @return	the unique id for the szStr
+ **/
+
+#if __GNUC__
+#define NOINLINEASM
+#endif
+
+DWORD hashSetting(LPCSTR szStr)
+{
+#if defined _M_IX86 && !defined _NUMEGA_BC_FINALCHECK && !defined NOINLINEASM
+	__asm
+	{
+		xor		edx,edx
+		xor		eax,eax
+		mov		esi,szStr
+		mov		al,[esi]
+		dec		esi
+		xor		cl,cl
+		lph_top:			//only 4 of 9 instructions in here don't use AL, so optimal pipe use is impossible
+		xor		edx,eax
+		inc		esi
+		and		cl,31
+		movzx	eax,byte ptr [esi]
+		add		cl,5
+		test	al,al
+		rol		eax,cl		//rol is u-pipe only, but pairable
+							//rol doesn't touch z-flag
+		jnz		lph_top		//5 clock tick loop. not bad.
+
+		xor		eax,edx
+	}
+#else
+	DWORD hash = 0;
+	int i;
+	int shift = 0;
+	for (i = 0; szStr[i]; i++)
+	{
+		hash ^= szStr[i] << shift;
+		if (shift > 24)
+		{
+			hash ^= (szStr[i] >> (32 - shift)) & 0x7F;
+		}
+		shift = (shift + 5) & 0x1F;
+	}
+	return hash;
+#endif
+}
+
+// MurmurHash2
+#ifdef _DEBUG
+#pragma optimize( "gt", on )
+#endif
+unsigned int __fastcall hash_M2(const void * key, unsigned int len)
+{
+	// 'm' and 'r' are mixing constants generated offline.
+	// They're not really 'magic', they just happen to work well.
+	const unsigned int m = 0x5bd1e995;
+	const int r = 24;
+
+	// Initialize the hash to a 'random' value
+	unsigned int h = len;
+
+	// Mix 4 bytes at a time into the hash
+	const unsigned char * data = (const unsigned char *)key;
+
+	while(len >= 4)
+	{
+		unsigned int k = *(unsigned int *)data;
+
+		k *= m;
+		k ^= k >> r;
+		k *= m;
+
+		h *= m;
+		h ^= k;
+
+		data += 4;
+		len -= 4;
+	}
+
+	// Handle the last few bytes of the input array
+	switch(len)
+	{
+	case 3: h ^= data[2] << 16;
+	case 2: h ^= data[1] << 8;
+	case 1: h ^= data[0];
+			h *= m;
+	};
+
+	// Do a few final mixes of the hash to ensure the last few
+	// bytes are well-incorporated.
+	h ^= h >> 13;
+	h *= m;
+	h ^= h >> 15;
+
+	return h;
+}
+
+unsigned int hashSettingW_M2(const char * key)
+{
+	if (key == NULL) return 0;
+	const unsigned int len = (unsigned int)wcslen((const wchar_t*)key);
+	char* buf = (char*)alloca(len + 1);
+	for (unsigned i = 0; i <= len ; ++i)
+		buf[i] = key[i << 1];
+	return hash_M2(buf, len);
+}
+
+unsigned int hashSetting_M2(const char * key)
+{
+	if (key == NULL) return 0;
+	const unsigned int len = (unsigned int)strlen((const char*)key);
+	return hash_M2(key, len);
+}
+
+unsigned int hashSetting_M2(const wchar_t * key)
+{
+	if (key == NULL) return 0;
+	const unsigned int len = (unsigned int)wcslen((const wchar_t*)key);
+	return hash_M2(key, len * sizeof(wchar_t));
+}
+
+#ifdef _DEBUG
+#pragma optimize( "", on )
+#endif
+
+INT_PTR myDestroyServiceFunction(const char * key) {
+	//DestroyServiceFunction always return 0 therfore we must call ServiceExists to enshure it is delete
+	if (!ServiceExists(key)) return 0;
+	DestroyServiceFunction((HANDLE)(INT_PTR)hashSetting(key));		//old hash
+	if (!ServiceExists(key)) return 0;
+	DestroyServiceFunction((HANDLE)(INT_PTR)hashSetting_M2(key));	//new MurmurHash2
+	if (!ServiceExists(key)) return 0;
+	return 1;
+}
diff --git a/plugins/UserInfoEx/src/commonheaders.h b/plugins/UserInfoEx/src/commonheaders.h
new file mode 100644
index 0000000000..b998884e65
--- /dev/null
+++ b/plugins/UserInfoEx/src/commonheaders.h
@@ -0,0 +1,234 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/commonheaders.h $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+/***********************************************************************************************************
+ * some compiler definitions
+ ***********************************************************************************************************/
+
+#define _WIN32_WINNT	0x0501
+#define _WIN32_IE			0x0500
+#define WIN32_LEAN_AND_MEAN
+
+#define _CRT_SECURE_NO_DEPRECATE 1
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 0
+
+#pragma warning(disable:4995)		// disable warning about strcpy, ... is old in VC2005
+#pragma warning(disable:4996)		// disable warning about strcpy, ... is old in VC2005
+
+/***********************************************************************************************************
+ * standard windows includes
+ ***********************************************************************************************************/
+
+#include <windows.h>
+#include <windowsx.h>
+#include <winnt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <commctrl.h>
+#include <commdlg.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <malloc.h>
+#include <string>
+#include <list>
+#include <Richedit.h>
+
+using namespace std;
+typedef std::basic_string<TCHAR>	tstring;
+
+/***********************************************************************************************************
+ * Miranda IM SDK includes and macros
+ ***********************************************************************************************************/
+
+#define MIRANDA_VER 0x0A00
+
+#include <newpluginapi.h>	// This must be included first
+#include <m_stdhdr.h>
+#include <m_button.h>
+#include <m_clui.h>
+#include <m_clist.h>
+#include <m_clistint.h>
+#include <m_cluiframes.h>
+#include <m_database.h>
+#include <m_genmenu.h>
+#include <m_hotkeys.h>
+#include <m_langpack.h>
+#include <m_protomod.h>
+#include <m_protosvc.h>
+#include <m_options.h>
+#include <m_contacts.h>
+#include <m_utils.h>
+#include <m_system.h>		// memory interface
+#include <m_system_cpp.h>	// list template
+#include <m_xml.h>			// XML API
+#include <m_timezones.h>	// timezone interface
+#include <m_imgsrvc.h>
+#include <m_message.h>
+#include <m_userinfo.h>
+#include <win2k.h>
+#include <msapi/vsstyle.h>
+#include <msapi/vssym32.h>
+
+/***********************************************************************************************************
+ * Used Plugins SDK includes and macros
+ ***********************************************************************************************************/
+
+#include <m_popup.h>
+#include "m_popup2.h"
+#include "m_flags.h"
+#include "m_metacontacts.h"
+#include "m_magneticwindows.h"
+#include "m_toptoolbar.h"
+#include "m_userinfoex.h"
+
+#include <m_extraicons.h>	//change this to match extraicons header location
+
+/***********************************************************************************************************
+ * UserInfoEx plugin includes and macros
+ ***********************************************************************************************************/
+
+#pragma intrinsic(memcmp, memcpy, memset, strcmp, strlen)
+
+#ifndef MIR_OK
+#define MIR_OK		0		// success value of a miranda service function
+#define MIR_FAIL	1		// general failure value of a miranda service function
+#endif
+
+#define MIRSUCCEEDED(f)		((f)==MIR_OK)
+#define MIRFAILED(f)		((f)!=MIR_OK)
+#define MIREXISTS(f)		((int)(f)!=CALLSERVICE_NOTFOUND)
+
+#define PtrIsValid(p)		(((p)!=0)&&(((HANDLE)(p))!=INVALID_HANDLE_VALUE))
+#define FREE(p)				{if (PtrIsValid(p)){free((VOID*)p);(p)=NULL;}}
+#define MIR_DELETE(p)		{LPVOID ptr = (LPVOID)(p);if (PtrIsValid(ptr)){delete(ptr);(ptr)=NULL;}}
+#define MIR_FREE(p)			{if (PtrIsValid(p)){mir_free((VOID*)p);(p)=NULL;}}
+
+#define GetUserData(p)		GetWindowLongPtr((p), GWLP_USERDATA)
+#define SetUserData(p, l)	SetWindowLongPtr((p), GWLP_USERDATA, (LONG_PTR) (l))
+
+#include "resource.h"
+#include "../IconPacks/default/src/icons.h"
+#include "../IconPacks/ice/src/icons.h"
+
+#include "svc_constants.h"
+
+#include "mir_contactqueue.h"
+#include "mir_db.h"
+#include "mir_string.h"
+#include "mir_icolib.h"
+#include "dlg_msgbox.h"
+#include "classMTime.h"
+#include "classMAnnivDate.h"
+
+/***********************************************************************************************************
+ * UserInfoEx global variables
+ ***********************************************************************************************************/
+
+typedef struct _MGLOBAL
+{
+	DWORD		mirandaVersion;					// mirandaVersion
+	BOOLEAN		CanChangeDetails : 1;			// is service to upload own contact information for icq present?
+	BOOLEAN		HaveCListExtraIcons : 1;		// are extra icons supported by current clist
+	BOOLEAN		ExtraIconsServiceExist : 1;		// Extra Icon plugin / service exist
+	BOOLEAN		MsgAddIconExist : 1;			// Messsage Window support status Icon
+	BOOLEAN		TzIndexExist : 1;				// Win Reg has Timzone Index Info
+	BOOLEAN		PopUpActionsExist : 1;			// Popup++ or MS_POPUP_REGISTERACTIONS exist
+	BOOLEAN		ShowPropsheetColours : 1;		// cached SET_PROPSHEET_SHOWCOLOURS database value
+	BOOLEAN		WantAeroAdaption : 1;			// reserved for later use
+	BOOLEAN		UseDbxTree : 1;					// use dbx_tree ?
+	LPCSTR		szMetaProto;
+
+} MGLOBAL, *LPMGLOBAL;
+
+extern HINSTANCE		ghInst;
+extern MGLOBAL			myGlobals;
+extern FI_INTERFACE* FIP;
+
+/***********************************************************************************************************
+ * MIRANDA_CPP_PLUGIN_API
+ ***********************************************************************************************************/
+
+/**
+ * These macros provide an interface for classes to use member
+ * function as services and event hooks.
+ *
+ * @note	This requires Miranda Core 0.8+!
+ *
+ **/
+#define MIRANDA_CPP_PLUGIN_API(CCoreClass) \
+	typedef INT (__cdecl CCoreClass::*EVENTHOOK)(WPARAM, LPARAM);	\
+	typedef INT (__cdecl CCoreClass::*EVENTHOOKPARAM)(WPARAM, LPARAM, LPARAM); \
+	typedef INT (__cdecl CCoreClass::*SERVICEFUNC)(WPARAM, LPARAM); \
+	typedef INT (__cdecl CCoreClass::*SERVICEFUNCPARAM)(WPARAM, LPARAM, LPARAM); \
+	\
+	HANDLE ThisHookEvent(const char* szEvent, EVENTHOOK pfnEvent) \
+	{	return (HANDLE) ::HookEventObj(szEvent, (MIRANDAHOOKOBJ) (*(PVOID*) &pfnEvent), (PVOID)this);} \
+	HANDLE ThisHookEventParam(const char* szEvent, EVENTHOOKPARAM pfnEvent, LPARAM lParam) \
+	{	return (HANDLE) ::HookEventObjParam(szEvent, (MIRANDAHOOKOBJPARAM) (*(PVOID*) &pfnEvent), (PVOID)this, lParam);	} \
+	\
+	HANDLE ThisCreateService(const char* szService, SERVICEFUNC pfnService) \
+	{	return (HANDLE) ::CreateServiceFunctionObj(szService, (MIRANDASERVICEOBJ) (*(PVOID*) &pfnService), (PVOID)this);	} \
+	HANDLE ThisCreateServiceParam(const char* szService, SERVICEFUNCPARAM pfnService, LPARAM lParam) \
+	{	return (HANDLE) ::CreateServiceFunctionObjParam(szService, (MIRANDASERVICEOBJPARAM) (*(PVOID*) &pfnService), (PVOID)this, lParam); } \
+
+/***********************************************************************************************************
+ * UserInfoEx common used functions
+ ***********************************************************************************************************/
+
+DWORD	hashSetting(LPCSTR szStr);					//old miranda hash
+
+unsigned int hashSetting_M2(const wchar_t * key);	//new Murma2 hash
+unsigned int hashSetting_M2(const char * key);		//new Murma2 hash
+unsigned int hashSettingW_M2(const char * key);		//new Murma2 hash
+
+INT_PTR	myDestroyServiceFunction(const char * key);
+
+static FORCEINLINE BOOL IsProtoOnline(LPSTR pszProto)
+{
+	return pszProto && pszProto[0] && CallProtoService(pszProto, PS_GETSTATUS, NULL, NULL) >= ID_STATUS_ONLINE;
+}
+static FORCEINLINE BOOL IsProtoLoaded(LPSTR pszProto)
+{
+	return (CallService(MS_PROTO_ISPROTOCOLLOADED, NULL, (LPARAM)pszProto) != NULL);
+}
+static FORCEINLINE BOOL IsProtoAccountEnabled(PROTOACCOUNT *pAcc)
+{
+	return ((pAcc->type == PROTOTYPE_PROTOCOL) && pAcc->bIsEnabled && IsProtoLoaded(pAcc->szModuleName));
+}
+
+typedef HRESULT (STDAPICALLTYPE *pfnDwmIsCompositionEnabled)(BOOL *);
+extern pfnDwmIsCompositionEnabled dwmIsCompositionEnabled;
+static FORCEINLINE BOOLEAN IsAeroMode()
+{
+	BOOL result;
+	return myGlobals.WantAeroAdaption && dwmIsCompositionEnabled && (dwmIsCompositionEnabled(&result) == S_OK) && result;
+}
diff --git a/plugins/UserInfoEx/src/ctrl_annivedit.cpp b/plugins/UserInfoEx/src/ctrl_annivedit.cpp
new file mode 100644
index 0000000000..db809906a1
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_annivedit.cpp
@@ -0,0 +1,636 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_annivedit.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_annivedit.h"
+#include "svc_Reminder.h"
+#include "psp_base.h"
+
+CBaseCtrl* CAnnivEditCtrl::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+{
+	CAnnivEditCtrl *ctrl = NULL;
+
+	ctrl = new CAnnivEditCtrl(hDlg, idCtrl, pszSetting);
+	if (ctrl)
+	{
+	}
+	return (ctrl);
+}
+
+CAnnivEditCtrl::CAnnivEditCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+	: CBaseCtrl(hDlg, idCtrl, pszSetting)
+{
+	_hwndDlg = hDlg;
+	_hBtnAdd = GetDlgItem(hDlg, BTN_ADD);
+	_hBtnDel = GetDlgItem(hDlg, BTN_DELETE);
+	_hBtnEdit = GetDlgItem(hDlg, BTN_EDIT);
+	_hBtnMenu = GetDlgItem(hDlg, BTN_MENU);
+	_hwndDate = GetDlgItem(hDlg, EDIT_ANNIVERSARY_DATE);
+	_ReminderEnabled = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED);
+
+	_pDates = NULL;
+	_curDate = 0;
+	_numDates = 0;
+	
+	// set button tooltips
+	SendMessage(_hBtnAdd, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Add a new anniversary"), MBF_TCHAR);
+	SendMessage(_hBtnDel, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete an existing anniversary"), MBF_TCHAR);
+
+	// limit textinput
+	SendDlgItemMessage(_hwndDlg, EDIT_REMIND, EM_LIMITTEXT, 2, 0);
+	SendDlgItemMessage(_hwndDlg, SPIN_REMIND, UDM_SETRANGE32, 0, 50);
+
+	// birthday is shown as an item in any case
+	{
+		MAnnivDate mdb;
+
+		mdb.Id(ANID_BIRTHDAY);
+		mdb.Description(TranslateT("Birthday"));
+		AddDate(mdb);
+	}
+}
+
+CAnnivEditCtrl::~CAnnivEditCtrl()
+{
+	WORD i;
+
+	if (_pDates != NULL) 
+	{
+		for (i = 0; i < _numDates; i++) 
+		{
+			delete _pDates[i];
+		}
+		mir_free(_pDates);
+		_pDates = NULL;
+	}
+}
+
+VOID CAnnivEditCtrl::Release()
+{
+	delete this;
+}
+
+
+/**
+ * name:	CAnnivEditCtrl
+ * class:	ItemValid
+ * desc:	tests whether the item pointed to by the wIndex is valid or not
+ * param:	wIndex	- index to desired item
+ * return:	TRUE if item is valid, FALSE otherwise
+ **/
+BOOLEAN CAnnivEditCtrl::ItemValid(WORD wIndex) const
+{
+	return (_pDates != NULL && wIndex < _numDates && _pDates[wIndex] != NULL);
+}
+
+/**
+ * name:	CAnnivEditCtrl
+ * class:	CurrentItemValid
+ * desc:	checks, whether currently selected item is valid
+ * param:	none
+ * return:	TRUE if item is valid, FALSE otherwise
+ **/
+BOOLEAN CAnnivEditCtrl::CurrentItemValid() const
+{
+	return ItemValid(_curDate);
+}
+
+/**
+ * name:	CAnnivEditCtrl
+ * class:	EnableReminderCtrl
+ * desc:	enables or disables reminder controls
+ * param:	none
+ * return:	TRUE if item is valid, FALSE otherwise
+ **/
+VOID CAnnivEditCtrl::EnableReminderCtrl(BOOLEAN bEnabled)
+{
+	bEnabled &= _ReminderEnabled != REMIND_OFF;
+	EnableWindow(GetDlgItem(_hwndDlg, RADIO_REMIND1), bEnabled);
+	EnableWindow(GetDlgItem(_hwndDlg, RADIO_REMIND2), bEnabled);
+	EnableWindow(GetDlgItem(_hwndDlg, RADIO_REMIND3), bEnabled);
+	EnableWindow(GetDlgItem(_hwndDlg, EDIT_REMIND), bEnabled);
+	EnableWindow(GetDlgItem(_hwndDlg, SPIN_REMIND), bEnabled);
+	EnableWindow(GetDlgItem(_hwndDlg, TXT_REMIND), bEnabled);
+}
+
+/**
+ * name:	EnableCurrentItem
+ * class:	CAnnivEditCtrl
+ * desc:	make control readonly if required
+ * param:	none
+ * return:	nothing
+ **/
+VOID CAnnivEditCtrl::EnableCurrentItem()
+{
+	MAnnivDate *pCurrent = Current();
+
+	if (pCurrent) {
+		HANDLE hContact;
+	
+		PSGetContact(_hwndDlg, hContact);
+
+		const BOOLEAN bEnabled
+			= !hContact ||
+				(pCurrent->Flags() & CTRLF_HASCUSTOM) || 
+				!(pCurrent->Flags() & (CTRLF_HASPROTO|CTRLF_HASMETA)) ||
+				!DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0);
+
+		EnableWindow(_hBtnEdit, bEnabled);
+		EnableWindow(_hBtnDel, bEnabled);
+		EnableWindow(_hwndDate, bEnabled);
+	}
+}
+
+/**
+ * name:	FindDateById
+ * class:	CAnnivEditCtrl
+ * desc:	returns an iterator to an item with the given id
+ * param:	wId		- id the returned item must have
+ * return:	if an date with the wId was found - iterator to item,
+ *			NULL otherwise
+ **/
+MAnnivDate* CAnnivEditCtrl::FindDateById(const WORD wId)
+{
+	WORD i;
+
+	if (_pDates != NULL) {
+		for (i = 0; i < _numDates; i++) {
+			if (_pDates[i]->Id() < ANID_NONE && _pDates[i]->Id() == wId) {
+				return _pDates[i];
+			}
+		}
+	}
+	return NULL;
+}
+
+/**
+ * name:	AddDate
+ * class:	CAnnivEditCtrl
+ * desc:	Add a new item to the array of dates
+ * param:	mda		- the date to add
+ * return:	0 on success, -1 on failure, 1 if the item to change was edited before and the new item was not set
+ **/
+INT_PTR CAnnivEditCtrl::AddDate(MAnnivDate &mda)
+{
+	MAnnivDate *pmda, **pmd;
+
+	// if a date with wID exists, replace it
+	if ((pmda = FindDateById(mda.Id())) != NULL) {
+		BOOLEAN bChanged = pmda->IsChanged(),
+			bRemindChanged = pmda->IsReminderChanged();
+
+		if (!bChanged) {
+			pmda->Set(mda);
+			pmda->Module(mda.Module());
+			pmda->Description(mda.Description());
+			pmda->Flags(mda.Flags());
+		}
+		if (!bRemindChanged) {
+			pmda->RemindOption(mda.RemindOption());
+			pmda->RemindOffset(mda.RemindOffset());
+		}
+		return bChanged || bRemindChanged;
+	}
+	if (mda.Id() == ANID_NONE)
+		mda.Id(_numDates - 1);
+
+	if (pmd = (MAnnivDate **)mir_realloc(_pDates, (_numDates + 1) * sizeof(pmda))) {
+		_pDates = pmd;
+		if (_pDates[_numDates] = new MAnnivDate(mda)) {
+			_numDates++;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ * name:	DeleteDate
+ * class:	CAnnivEditCtrl
+ * desc:	Delete the item on the position identified by wIndex
+ * param:	pDateCtrl	- pointer to the date control's data structure
+ *			wIndex		- index of the item to delete
+ * return:	0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DeleteDate(WORD wIndex)
+{
+	if (!ItemValid(wIndex)) return 1;
+	
+	// only delete values, but not the item
+	if (_pDates[wIndex]->Id() == ANID_BIRTHDAY) {
+		HANDLE	hContact;
+		LPCSTR	pszProto;
+
+		PSGetContact(_hwndDlg, hContact);
+		PSGetBaseProto(_hwndDlg, pszProto);
+
+		// protocol value exists?
+		if (_pDates[wIndex]->DBGetDate(hContact, pszProto, SET_CONTACT_BIRTHDAY, SET_CONTACT_BIRTHMONTH, SET_CONTACT_BIRTHYEAR)) {
+			_pDates[wIndex]->SetFlags(MAnnivDate::MADF_HASPROTO);
+		}
+		else {
+			_pDates[wIndex]->ZeroDate();
+		}
+
+		_pDates[wIndex]->RemindOption(BST_INDETERMINATE);
+		_pDates[wIndex]->RemindOffset((BYTE)-1);
+
+		_pDates[wIndex]->RemoveFlags(MAnnivDate::MADF_HASCUSTOM);
+		_pDates[wIndex]->SetFlags(MAnnivDate::MADF_CHANGED|MAnnivDate::MADF_REMINDER_CHANGED);
+	}
+	else {
+		delete _pDates[wIndex];
+		_numDates--;
+		if (wIndex < _numDates)
+			memmove(_pDates + wIndex, _pDates + wIndex + 1, (_numDates - wIndex) * sizeof(*_pDates));
+		ZeroMemory(_pDates + _numDates, sizeof(*_pDates));
+		if (_curDate >= _numDates)
+			_curDate = _numDates - 1;
+	}
+	SendMessage(GetParent(_hwndDlg), PSM_CHANGED, NULL, NULL);
+	SetCurSel(_curDate);
+	return 0;
+}
+
+/**
+ * name:	DateCtrl_DBGetBirthDay
+ * desc:
+ * param:
+ * return:	0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DBGetBirthDay(HANDLE hContact, LPCSTR pszProto)
+{
+	MAnnivDate mdb;
+
+	if (!mdb.DBGetBirthDate(hContact, (char *)pszProto)) {
+		mdb.DBGetReminderOpts(hContact);
+		return AddDate(mdb) > 0;
+	}
+	return 0;
+}
+
+/**
+ * name:	DateCtrl_DBGetBirthDay
+ * desc:
+ * param:
+ * return:	0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DBGetAnniversaries(HANDLE hContact)
+{
+	MAnnivDate mda;
+
+	WORD i;
+	BOOLEAN bChanged = FALSE;
+
+	for (i = 0; i < ANID_LAST && !mda.DBGetAnniversaryDate(hContact, i); i++) {
+		mda.DBGetReminderOpts(hContact);
+		switch (AddDate(mda)) {
+			case -1:
+				return bChanged;
+			case 1:
+				bChanged |= 1;
+				break;
+		}
+	}
+	return bChanged;
+}
+
+/**
+ * name:	DBWriteBirthDay
+ * class:	CAnnivEditCtrl
+ * desc:	writes the birthday for a contact to database
+ * param:	hContact - the contact to write the anniversaries to
+ * return:	0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DBWriteBirthDay(HANDLE hContact)
+{
+	MAnnivDate *pmdb;
+
+	if ((pmdb = FindDateById(ANID_BIRTHDAY)) == NULL)
+		return 1;
+		
+	if (pmdb->IsChanged()) {
+		// save birthday, to mBirthday module by default
+		if (pmdb->Flags() & pmdb->MADF_HASCUSTOM)
+			pmdb->DBWriteBirthDate(hContact);
+		else
+			pmdb->DBDeleteBirthDate(hContact);
+	}
+
+	if (pmdb->IsReminderChanged()) {
+		pmdb->DBWriteReminderOpts(hContact);
+	}
+	pmdb->RemoveFlags(MAnnivDate::MADF_CHANGED|MAnnivDate::MADF_REMINDER_CHANGED);
+	return 0;
+}
+
+/**
+ * name:	DBWriteAnniversaries
+ * class:	CAnnivEditCtrl
+ * desc:	write all anniversaries to the database
+ * param:	hContact - the contact to write the anniversaries to
+ * return:	0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::DBWriteAnniversaries(HANDLE hContact)
+{
+	const LPCSTR szPrefix[] = { "Reminder", "Offset", "Desc", "Day", "Month", "Year", "Stamp", "Date" };
+	CHAR szSet0[MAXSETTING];
+	WORD i, ret, ofs, wIndex = 0;
+
+	for (i = 0; i < _numDates; i++) {
+		if (
+			_pDates[i] != NULL &&
+			!_pDates[i]->DBWriteAnniversaryDate(hContact, wIndex) &&
+			!_pDates[i]->DBWriteReminderOpts(hContact)
+	)
+		{
+			wIndex++;
+		}
+	}
+	// delete reluctant items
+	do {
+		ofs = mir_snprintf(szSet0, SIZEOF(szSet0), "Anniv%d", wIndex);
+		ret = 1;
+		for (i = 0; i < SIZEOF(szPrefix); i++) {
+			mir_strncpy(szSet0 + ofs, szPrefix[i], SIZEOF(szSet0) - ofs);
+			ret &= DB::Setting::Delete(hContact, USERINFO, szSet0);
+		}
+	}
+	while (wIndex++ <= ANID_LAST && !ret);
+	return 0;
+}
+
+/**
+ * name:	SetCurSel
+ * class:	CAnnivEditCtrl
+ * desc:	shows the item, identified by wIndex
+ * param:	pDateCtrl	- pointer to the date control's data structure
+ *			wIndex		- index of the item to delete
+ * return:	0 on success 1 otherwise
+ **/
+INT_PTR CAnnivEditCtrl::SetCurSel(WORD wIndex)
+{
+	BOOLEAN bEnabled = ItemValid(wIndex);
+
+	EnableWindow(_hwndDate, bEnabled);
+	EnableWindow(_hBtnEdit, bEnabled);
+	EnableWindow(_hBtnDel, bEnabled && _pDates[wIndex]->IsValid());
+	if (!bEnabled) {
+		EnableReminderCtrl(FALSE);
+		return 1;
+	}
+	_curDate = wIndex;
+
+	// set date of date control
+	if (_pDates[wIndex]->IsValid()) {
+		SYSTEMTIME st = _pDates[wIndex]->SystemTime();
+		DateTime_SetSystemtime(_hwndDate, GDT_VALID, &st);
+		DateTime_SetFormat(_hwndDate, NULL);
+	}
+	else {
+		TCHAR szText[MAX_DESC];
+		mir_sntprintf(szText, MAX_DESC, _T("'%s'"), TranslateT("Unspecified"));
+		DateTime_SetSystemtime(_hwndDate, GDT_NONE, NULL);
+		DateTime_SetFormat(_hwndDate, szText);
+	}
+	// set edit button's caption
+	SetWindowText(_hBtnEdit, _pDates[wIndex]->Description());
+
+	// set reminder options
+	CheckDlgButton(_hwndDlg, RADIO_REMIND1, _pDates[wIndex]->RemindOption() == BST_INDETERMINATE);
+	CheckDlgButton(_hwndDlg, RADIO_REMIND2, _pDates[wIndex]->RemindOption() == BST_CHECKED);
+	CheckDlgButton(_hwndDlg, RADIO_REMIND3, _pDates[wIndex]->RemindOption() == BST_UNCHECKED);
+
+	OnReminderChecked();
+	EnableCurrentItem();
+	return 0;
+}
+
+/**
+ * name:	OnMenuPopup
+ * class:	CAnnivEditCtrl
+ * desc:	is called to show a popup menu for all anniversaries of a contact
+ * param:	none
+ * return:	nothing
+ **/
+VOID CAnnivEditCtrl::OnMenuPopup()
+{
+	POINT pt = { 0, 0 };
+	RECT rc;
+	MENUITEMINFO mii;
+	HMENU hMenu;
+	WORD i;
+
+	if (hMenu = CreatePopupMenu()) {
+		SetFocus(_hBtnMenu);
+
+		ZeroMemory(&mii, sizeof(MENUITEMINFO));
+		mii.cbSize = sizeof(MENUITEMINFO);
+		mii.fMask = MIIM_ID|MIIM_STRING|MIIM_STATE;
+
+		// insert the items
+		for (i = 0; i < _numDates; i++) {
+			mii.fState = _pDates[i]->IsValid() ? MFS_CHECKED : MFS_UNCHECKED;
+			mii.dwTypeData = (LPTSTR)_pDates[i]->Description();
+			mii.wID = WM_USER + i;
+			if (!InsertMenuItem(hMenu, i, TRUE, &mii)) {
+				DestroyMenu(hMenu);
+				return;
+			}
+		}
+		ClientToScreen(_hBtnMenu, &pt);
+		GetClientRect(_hBtnMenu, &rc);
+		i = TrackPopupMenuEx(hMenu, TPM_RIGHTALIGN|TPM_RETURNCMD, pt.x + rc.right, pt.y + rc.bottom, _hwndDlg, NULL);
+		DestroyMenu(hMenu);
+		SendMessage(_hBtnMenu, BM_SETCHECK, NULL, NULL);
+		if (i >= WM_USER) SetCurSel(i - WM_USER);
+	}
+}
+
+/**
+ * name:	OnMenuPopup
+ * class:	CAnnivEditCtrl
+ * desc:	is called to show a popup menu for all anniversaries of a contact
+ * param:	none
+ * return:	nothing
+ **/
+VOID CAnnivEditCtrl::OnDateChanged(LPNMDATETIMECHANGE lpChange)
+{
+	MAnnivDate *pCurrent = Current();
+
+	if (pCurrent && !pCurrent->IsEqual(lpChange->st))
+	{
+		HWND hPs = GetParent(_hwndDlg);
+
+		// save the new date to the structure
+		DateTime_SetFormat(_hwndDate, NULL);
+		pCurrent->Set(lpChange->st, TRUE);
+		pCurrent->SetFlags(MAnnivDate::MADF_CHANGED|MAnnivDate::MADF_HASCUSTOM);
+		
+		// notify parent of the change
+		SendMessage(hPs, PSM_CHANGED, NULL, NULL);
+		EnableWindow(_hBtnDel, TRUE);
+
+		// update the age and zodiac controls on the general propertysheetpage
+		if (pCurrent->Id() == ANID_BIRTHDAY) 
+		{
+			SetZodiacAndAge(pCurrent);
+		}
+	}
+}
+
+/**
+ * name:	OnRemindEditChanged
+ * class:	CAnnivEditCtrl
+ * desc:	is called, if reminder edit control was changed
+ * param:	none
+ * return:	nothing
+ **/
+VOID CAnnivEditCtrl::OnRemindEditChanged()
+{
+	MAnnivDate	*pCurrent = Current();
+
+	if (pCurrent) 
+	{
+		UINT iVal = GetDlgItemInt(_hwndDlg, EDIT_REMIND, NULL, FALSE);
+		if (iVal != pCurrent->RemindOffset() && IsDlgButtonChecked(_hwndDlg, RADIO_REMIND2) == BST_CHECKED) 
+		{
+			SendMessage(GetParent(_hwndDlg), PSM_CHANGED, NULL, NULL);
+			pCurrent->SetFlags(MAnnivDate::MADF_REMINDER_CHANGED);
+			pCurrent->RemindOffset(iVal);
+		}
+	}
+}
+
+/**
+ * name:	OnReminderChecked
+ * class:	CAnnivEditCtrl
+ * desc:	is called if reminder checkbox's state was changed
+ * param:	none
+ * return:	nothing
+ **/
+VOID CAnnivEditCtrl::OnReminderChecked()
+{
+	HANDLE hContact;
+	LPCSTR pszProto;
+	INT state;
+	TCHAR buf[6];
+	MAnnivDate *pCurrent = Current();
+
+	PSGetContact(_hwndDlg, hContact);
+	if (!hContact || !PSGetBaseProto(_hwndDlg, pszProto) || !pCurrent) 
+	{
+		EnableReminderCtrl(FALSE);
+	}
+	else
+	{
+		if (IsDlgButtonChecked(_hwndDlg, RADIO_REMIND1))
+		{
+			_itot(DB::Setting::GetByte(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET), buf, 10);
+			EnableWindow(GetDlgItem(_hwndDlg, EDIT_REMIND), FALSE);
+			EnableWindow(GetDlgItem(_hwndDlg, SPIN_REMIND), FALSE);
+			state = BST_INDETERMINATE;
+		}
+		else if (IsDlgButtonChecked(_hwndDlg, RADIO_REMIND2))
+		{
+			if (pCurrent->RemindOffset() == (BYTE)-1)
+			{
+				_itot(DB::Setting::GetByte(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET), buf, 10);
+			}
+			else
+			{
+				_itot(pCurrent->RemindOffset(), buf, 10);
+			}
+			EnableWindow(GetDlgItem(_hwndDlg, EDIT_REMIND), _ReminderEnabled);
+			EnableWindow(GetDlgItem(_hwndDlg, SPIN_REMIND), _ReminderEnabled);
+			state = BST_CHECKED;
+		}
+		else
+		{
+			*buf = 0;
+			EnableWindow(GetDlgItem(_hwndDlg, EDIT_REMIND), FALSE);
+			EnableWindow(GetDlgItem(_hwndDlg, SPIN_REMIND), FALSE);
+			state = BST_UNCHECKED;
+		}
+		if (pCurrent->RemindOption() != state) 
+		{
+			pCurrent->RemindOption(state);
+			if (!PspIsLocked(_hwndDlg)) 
+			{
+				pCurrent->SetFlags(MAnnivDate::MADF_REMINDER_CHANGED);
+				SendMessage(GetParent(_hwndDlg), PSM_CHANGED, NULL, NULL);
+			}
+		}
+		SetDlgItemText(_hwndDlg, EDIT_REMIND, buf);
+	}
+}
+
+VOID CAnnivEditCtrl::SetZodiacAndAge(MAnnivDate *mt)
+{
+	if (PtrIsValid(mt))
+	{
+		INT age;
+		MZodiac zod;
+
+		zod = mt->Zodiac();
+		if (zod.pszName != NULL)
+		{
+			ShowWindow(GetDlgItem(_hwndDlg, TEXT_ZODIAC), SW_SHOW);
+			SetDlgItemText(_hwndDlg, TEXT_ZODIAC, TranslateTS(zod.pszName));
+			SendDlgItemMessage(_hwndDlg, IDC_ZODIAC, STM_SETIMAGE, IMAGE_ICON, (LPARAM)zod.hIcon);
+		}
+		else
+		{
+			ShowWindow(GetDlgItem(_hwndDlg, IDC_ZODIAC), SW_HIDE);
+			ShowWindow(GetDlgItem(_hwndDlg, TEXT_ZODIAC), SW_HIDE);
+		}
+		if ((age = mt->Age()) > 0)
+		{
+			SetDlgItemInt(_hwndDlg, EDIT_AGE, age , FALSE);
+		}
+	}
+}
+
+BOOL CAnnivEditCtrl::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+	BOOL bChanged;
+	bChanged = DBGetBirthDay(hContact, pszProto);
+	bChanged |= DBGetAnniversaries(hContact);
+	SetCurSel(0);
+	SetZodiacAndAge(_pDates[0]);
+	return bChanged;
+}
+
+VOID CAnnivEditCtrl::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+	DBWriteBirthDay(hContact);
+	DBWriteAnniversaries(hContact);
+}
+
diff --git a/plugins/UserInfoEx/src/ctrl_annivedit.h b/plugins/UserInfoEx/src/ctrl_annivedit.h
new file mode 100644
index 0000000000..412afa8c0d
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_annivedit.h
@@ -0,0 +1,104 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_annivedit.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_CTRLANNIVEDIT_H_
+#define _UINFOEX_CTRLANNIVEDIT_H_
+
+#include "ctrl_base.h"
+
+#define MAX_DESC	50
+
+class CAnnivEditCtrl : public CBaseCtrl
+{
+	HWND	_hwndDlg;	 // owning dialog box
+	HWND	_hwndDate;	// date picker
+	HWND	_hBtnMenu;	// anniversary dropdown button
+	HWND	_hBtnEdit;	// edit anniversary button
+	HWND	_hBtnAdd;	 // add anniversary button
+	HWND	_hBtnDel;	 // delete anniversary button
+
+	BOOLEAN			 _ReminderEnabled;
+
+	MAnnivDate**	_pDates;
+	WORD					_numDates;
+	WORD					_curDate;
+
+	BOOLEAN ItemValid(WORD wIndex) const;
+	BOOLEAN CurrentItemValid() const;
+
+	INT_PTR DBGetBirthDay(HANDLE hContact, LPCSTR pszProto);
+	INT_PTR DBWriteBirthDay(HANDLE hContact);
+
+	INT_PTR DBGetAnniversaries(HANDLE hContact);
+	INT_PTR DBWriteAnniversaries(HANDLE hContact);
+
+	CAnnivEditCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+	~CAnnivEditCtrl();
+
+public:
+
+	MAnnivDate* Current() { return CurrentItemValid() ? _pDates[_curDate] : NULL; };
+	WORD				CurrentIndex() const { return _curDate; };
+	WORD				NumDates() const { return _numDates; };
+	BOOLEAN		 ReminderEnabled() const { return _ReminderEnabled; };
+
+	MAnnivDate* FindDateById(const WORD wId);
+
+	VOID				EnableCurrentItem();
+	VOID				EnableReminderCtrl(BOOLEAN bEnabled);
+
+	INT_PTR		 SetCurSel(WORD wIndex);
+
+	INT_PTR		 AddDate(MAnnivDate &mda);
+	INT_PTR		 DeleteDate(WORD wIndex);
+
+	VOID				SetZodiacAndAge(MAnnivDate *mt);
+
+	// notification handlers
+	VOID OnMenuPopup();
+	VOID OnDateChanged(LPNMDATETIMECHANGE lpChange);
+	VOID OnRemindEditChanged();
+	VOID OnReminderChecked();
+
+	/**
+	 * CBaseCtrl interface:
+	 **/
+	static FORCEINLINE CAnnivEditCtrl* GetObj(HWND hCtrl) 
+		{ return (CAnnivEditCtrl*) GetUserData(hCtrl); }
+	static FORCEINLINE CAnnivEditCtrl* GetObj(HWND hDlg, WORD idCtrl)
+		{ return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+	static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+
+	virtual VOID	Release();
+	virtual BOOL	OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+	virtual VOID	OnApply(HANDLE hContact, LPCSTR pszProto);
+};
+
+#endif /* _UINFOEX_CTRLANNIVEDIT_H_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_base.cpp b/plugins/UserInfoEx/src/ctrl_base.cpp
new file mode 100644
index 0000000000..8203a5c37d
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_base.cpp
@@ -0,0 +1,301 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_base.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_base.h"
+
+/***********************************************************************************************************
+ * Old methods for setting text color of dialog controls
+ ***********************************************************************************************************/
+
+COLORREF clrBoth = -1;
+COLORREF clrChanged = -1;
+COLORREF clrCustom = -1;
+COLORREF clrNormal = -1;
+COLORREF clrMeta = -1;
+
+VOID Ctrl_InitTextColours()
+{
+	clrBoth = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRBOTH, RGB(0, 160, 10));
+	clrChanged = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRCHANGED, RGB(190, 0, 0));
+	clrCustom = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRCUSTOM, RGB(0, 10, 130));
+	clrNormal = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRNORMAL, RGB(90, 90, 90));
+	clrMeta = DB::Setting::GetDWord(NULL, MODNAME, SET_PROPSHEET_CLRMETA, RGB(120, 40, 130));
+}
+
+INT_PTR CALLBACK Ctrl_SetTextColour(HDC hdc, WORD wFlags)
+{
+	// OLD stuff
+	SetTextColor(hdc, 
+		(wFlags & CTRLF_CHANGED) 
+			? clrChanged : ((wFlags & CTRLF_HASCUSTOM) && (wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA)))
+				? clrBoth : (wFlags & CTRLF_HASMETA)
+					? clrMeta : (wFlags & CTRLF_HASCUSTOM)
+						? clrCustom	: clrNormal
+ );
+	return (INT_PTR)GetStockObject(WHITE_BRUSH);
+}
+
+INT_PTR CALLBACK Ctrl_SetTextColour(HWND hCtrl, HDC hdc)
+{
+	if (hCtrl && DB::Setting::GetByte(SET_PROPSHEET_SHOWCOLOURS, 1)) 
+	{
+		LPCTRL pCtrl = (LPCTRL)GetUserData(hCtrl);
+		if (PtrIsValid(pCtrl))
+			return Ctrl_SetTextColour(hdc, pCtrl->wFlags);
+	}
+	return FALSE;
+}
+
+/***********************************************************************************************************
+ * Implementation of CBaseCtrl
+ ***********************************************************************************************************/
+
+/**
+ *
+ *
+ **/
+CBaseCtrl::CBaseCtrl()
+{
+	ZeroMemory(this, sizeof(*this));
+	_cbSize = sizeof(CBaseCtrl);
+}
+
+/**
+ *
+ *
+ **/
+CBaseCtrl::CBaseCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+{
+	ZeroMemory(this, sizeof(*this));
+	_cbSize = sizeof(CBaseCtrl);
+	_hwnd = GetDlgItem(hDlg, idCtrl);
+	if (!IsWindow(_hwnd)) throw;
+	_idCtrl = idCtrl;
+	_pszModule = USERINFO;
+	_pszSetting = pszSetting;
+	SetUserData(_hwnd, this);
+}
+
+/**
+ *
+ *
+ **/
+CBaseCtrl::CBaseCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting)
+{
+	ZeroMemory(this, sizeof(*this));
+	_cbSize		= sizeof(CBaseCtrl);
+	_hwnd		= GetDlgItem(hDlg, idCtrl);
+	if (!IsWindow(_hwnd)) throw;
+	_idCtrl		= idCtrl;
+	_pszModule	= pszModule;
+	_pszSetting	= pszSetting;
+	SetUserData(_hwnd, this);
+}
+
+
+/**
+ *
+ *
+ **/
+CBaseCtrl::~CBaseCtrl()
+{
+	SetUserData(_hwnd, NULL);
+	MIR_FREE(_pszValue);
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR CBaseCtrl::OnSetTextColour(HDC hdc)
+{
+	SetTextColor(hdc, 
+		(_Flags.B.hasChanged) 
+		? clrChanged : ((_Flags.B.hasCustom) && (_Flags.B.hasProto || _Flags.B.hasMeta))
+				? clrBoth : _Flags.B.hasMeta
+					? clrMeta : _Flags.B.hasCustom
+						? clrCustom	: clrNormal
+ );
+	return (INT_PTR)GetStockObject(WHITE_BRUSH);
+}
+
+/***********************************************************************************************************
+ * Implementation of CCtrlList
+ ***********************************************************************************************************/
+
+/**
+ *
+ *
+ **/
+CCtrlList* CCtrlList::CreateObj(HWND hOwnerDlg)
+{
+	Ctrl_InitTextColours();
+	return new CCtrlList(hOwnerDlg);
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR CCtrlList::sortFunc(CBaseCtrl *p1, CBaseCtrl *p2)
+{
+	return p1->_idCtrl - p2->_idCtrl;
+}
+
+/**
+ *
+ *
+ **/
+CCtrlList::CCtrlList(HWND hOwnerDlg)
+: LIST<CBaseCtrl>(10, (FTSortFunc) CCtrlList::sortFunc)
+{
+	_hOwnerDlg = hOwnerDlg; 
+	SetUserData(_hOwnerDlg, this);
+}
+
+/**
+ *
+ *
+ **/
+CCtrlList::~CCtrlList()
+{
+	INT_PTR i;
+
+	SetUserData(_hOwnerDlg, NULL);
+	// delete data
+	for (i = 0 ; i < count; i++)
+	{
+		delete (*this)[i];
+	}
+	// delete the list
+	LIST<CBaseCtrl>::destroy();
+}
+
+/**
+ *
+ *
+ **/
+VOID CCtrlList::Release()
+{ 
+	delete this; 
+}
+
+/**
+ *
+ *
+ **/
+VOID CCtrlList::OnReset()
+{
+	INT_PTR i;
+
+	for (i = 0; i < count; i++)
+	{
+		if (items[i]) 
+		{
+			items[i]->OnReset();
+		}
+	}
+}
+
+/**
+ *
+ *
+ **/
+BOOL CCtrlList::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+	BOOL bChanged = 0;
+	INT_PTR i;
+
+	for (i = 0; i < count; i++)
+	{
+		if (PtrIsValid(items[i]))
+		{
+			bChanged |= items[i]->OnInfoChanged(hContact, pszProto);
+		}
+	}
+	return bChanged;
+}
+
+/**
+ *
+ *
+ **/
+VOID CCtrlList::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+	INT_PTR i;
+
+	for (i = 0; i < count; i++)
+	{
+		if (PtrIsValid(items[i]))
+		{
+			items[i]->OnApply(hContact, pszProto);
+		}
+	}
+}
+
+/**
+ *
+ *
+ **/
+VOID CCtrlList::OnChangedByUser(WORD idCtrl, WORD wChangedMsg)
+{
+	// prefilter messages to avoid unessesary search operations
+	switch (wChangedMsg)
+	{
+	case EN_UPDATE:
+	case EN_CHANGE:
+	case CBN_SELCHANGE:
+		{
+			CBaseCtrl *pResult = CBaseCtrl::GetObj(_hOwnerDlg, idCtrl);
+			if (PtrIsValid(pResult) && (pResult->_cbSize == sizeof(CBaseCtrl)))
+			{
+				pResult->OnChangedByUser(wChangedMsg);
+			}
+		}
+	}
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR CCtrlList::OnSetTextColour(HWND hCtrl, HDC hdc)
+{
+	if (IsWindow(hCtrl) && myGlobals.ShowPropsheetColours)
+	{
+		CBaseCtrl* pCtrl = CBaseCtrl::GetObj(hCtrl);
+		if (PtrIsValid(pCtrl) && (pCtrl->_cbSize = sizeof(CBaseCtrl)))
+		{
+			return pCtrl->OnSetTextColour(hdc);
+		}
+	}
+	return FALSE;
+}
diff --git a/plugins/UserInfoEx/src/ctrl_base.h b/plugins/UserInfoEx/src/ctrl_base.h
new file mode 100644
index 0000000000..161f8aeba7
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_base.h
@@ -0,0 +1,238 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_base.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_BASE_INCLUDE_
+#define _UI_CTRL_BASE_INCLUDE_
+
+/***********************************************************************************************************
+ * old CTRL stuff (is to replace in the future
+ ***********************************************************************************************************/
+
+// control flags
+#define CTRLF_CHANGED	 1
+#define CTRLF_HASPROTO	2
+#define CTRLF_HASCUSTOM 4
+#define CTRLF_HASMETA	 8
+
+#define CTRLF_FIRST		16 // first free flag for derived controls
+
+// control types
+#define CTRL_ERROR					0
+#define CTRL_EDIT					 1
+#define CTRL_COMBOBOX_STAT	2
+#define CTRL_DATEPICKER		 3
+#define CTRL_LIST_PROFILE	 4
+#define CTRL_LIST_ITEM			7
+
+typedef struct TCtrlInfo {
+	BYTE	nType;
+	WORD	wFlags;
+} CTRL, *LPCTRL;
+
+// for compatibility with old styled controls
+VOID							Ctrl_InitTextColours();
+INT_PTR CALLBACK	Ctrl_SetTextColour(HDC hdc, WORD wFlags);
+
+/***********************************************************************************************************
+ * CBaseCtrl declaration
+ ***********************************************************************************************************/
+
+union CCtrlFlags 
+{
+	WORD W;
+	struct CBits 
+	{
+		BYTE	hasChanged	: 1;
+		BYTE	hasProto		: 1;
+		BYTE	hasCustom	 : 1;
+		BYTE	hasMeta		 : 1;
+		BYTE	bit_04	: 1;
+		BYTE	bit_05	: 1;
+		BYTE	bit_06	: 1;
+		BYTE	bit_07	: 1;
+		BYTE	bit_08	: 1;
+		BYTE	bit_09	: 1;
+		BYTE	bit_10	: 1;
+		BYTE	bit_11	: 1;
+		BYTE	bit_12	: 1;
+		BYTE	bit_13	: 1;
+		BYTE	bit_14	: 1;
+		BYTE	bit_15	: 1;
+	} B;
+};
+
+/**
+ * CBaseCtrl is an abstract baseic class, which provides a common
+ * interface for all kinds of controls. It has the task to ease
+ * up programming and avoid memory leaks.
+ **/
+class CBaseCtrl
+{
+public:
+	DWORD			_cbSize;
+
+	friend class CCtrlList;
+
+protected:
+	
+	CCtrlFlags		_Flags; 
+	HWND			_hwnd;
+	WORD			_idCtrl;
+	LPCSTR			_pszModule;
+	LPCSTR			_pszSetting;
+	LPTSTR			_pszValue;
+
+	/**
+	 * Private constructure is to force the class used as base class only.
+	 *
+	 * @param	 none
+	 *
+	 * @return	nothing
+	 **/
+	CBaseCtrl();
+	CBaseCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+	CBaseCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting);
+
+	/**
+	 * Private constructure is to force the class used as base class only.
+	 *
+	 * @param	 none
+	 *
+	 * @return	nothing
+	 **/
+	~CBaseCtrl();
+
+public:
+
+	/**
+	 *
+	 *
+	 **/
+	static FORCEINLINE CBaseCtrl* GetObj(HWND hCtrl) 
+		{ return (CBaseCtrl*) GetUserData(hCtrl); }
+
+	/**
+	 *
+	 *
+	 **/
+	static FORCEINLINE CBaseCtrl* GetObj(HWND hDlg, WORD idCtrl)
+		{ return GetObj(GetDlgItem(hDlg, idCtrl)); }
+	
+	/**
+	 *
+	 *
+	 **/
+	FORCEINLINE CCtrlFlags Flags() const { return _Flags; }
+
+	/**
+	 * This is a pure virtual method, which is the common interface 
+	 * for deleting an instance of this class.
+	 *
+	 * @param	 none
+	 *
+	 * @return	nothing
+	 **/
+	virtual VOID Release() { }
+
+	/**
+	 * This is a pure virtual method, which is the common interface 
+	 * to handle database changes. It reads the new values from
+	 * database and refreshes the content of the associated control.
+	 *
+	 * @param	 hContact	- the handle of the contact
+	 * @param	 pszProto	 - the contact's protocol module
+	 *
+	 * @retval	TRUE	- the content was updated
+	 * @retval	FALSE - content not updated
+	 **/
+	virtual BOOL OnInfoChanged(HANDLE hContact, LPCSTR pszProto) { return 0; }
+
+	/**
+	 * This is a pure virtual method, which is the common interface 
+	 * for applying changed content of a control to database.
+	 *
+	 * @param	 hContact	- the handle of the contact
+	 * @param	 pszProto	 - the contact's protocol module
+	 *
+	 * @return	nothing
+	 **/
+	virtual VOID OnApply(HANDLE hContact, LPCSTR pszProto) { }
+
+	/**
+	 * This is a pure virtual method, which is called to set the 
+	 * changed bit. This is to indicate a setting has changed and
+	 * therefore enable the apply button of the details dialog.
+	 *
+	 * @param	 none
+	 *
+	 * @return	nothing
+	 **/
+	virtual VOID OnChangedByUser(WORD wChangedMsg) { }
+
+	virtual VOID OnReset() { }
+
+	INT_PTR OnSetTextColour(HDC hdc);
+};
+
+/***********************************************************************************************************
+ * CCtrlList declaration
+ ***********************************************************************************************************/
+
+/**
+ * The CCtrlList class is a sorted list of all dialog controls, such as edit, combo, etc. 
+ * with a common data structure and interface, described by the abstract class CBaseCtrl.
+ * The CCtrlList class sends notification messages to all its or to all relevant members.
+ * This reduces the risk of forgetting some message handling.
+ **/
+class CCtrlList : public LIST<CBaseCtrl>
+{
+	HWND	_hOwnerDlg;
+
+	static INT_PTR sortFunc(CBaseCtrl *tz1, CBaseCtrl *tz2);
+
+	CCtrlList(HWND hOwnerDlg);
+	~CCtrlList();
+
+public:
+	
+	static CCtrlList* CreateObj(HWND hOwnerDlg);
+
+	static FORCEINLINE CCtrlList* GetObj(HWND hDlg) 
+		{ return (CCtrlList*)GetUserData(hDlg); }
+
+	VOID		Release();
+	VOID		OnReset();
+	BOOL		OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+	VOID		OnApply(HANDLE hContact, LPCSTR pszProto);
+	VOID		OnChangedByUser(WORD idCtrl, WORD wChangedMsg);
+	INT_PTR OnSetTextColour(HWND hCtrl, HDC hdc);
+};
+
+#endif /* _UI_CTRL_BASE_INCLUDE_ */
diff --git a/plugins/UserInfoEx/src/ctrl_button.cpp b/plugins/UserInfoEx/src/ctrl_button.cpp
new file mode 100644
index 0000000000..8278b5b07d
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_button.cpp
@@ -0,0 +1,708 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_button.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+// Used for our own cheap TrackMouseEvent
+#define BUTTON_POLLID			 100
+#define BUTTON_POLLDELAY		50
+
+#define MGPROC(x) GetProcAddress(themeAPIHandle,x)
+
+typedef struct TMBCtrl{
+	HWND		hwnd;
+	HANDLE		hThemeButton;
+	HANDLE		hThemeToolbar;
+
+	HICON		hIcon;
+	HICON		arrow;			// uses down arrow
+	HBITMAP		hBitmap;
+	HFONT		hFont;			// font
+
+	DWORD		dwStyle;	
+	BOOLEAN		bFocus;	
+	
+	INT			stateId;		// button state
+	INT			defbutton;		// default button
+	INT			pbState;
+	TCHAR		cHot;
+} BTNCTRL, *LPBTNCTRL;
+
+// External theme methods and properties
+static CRITICAL_SECTION csTips;
+static HWND hwndToolTips = NULL;
+static HMODULE	themeAPIHandle = NULL;
+
+// theme procedures
+static HANDLE	 (WINAPI *OpenThemeData)(HWND,LPCWSTR);
+static HRESULT	(WINAPI *CloseThemeData)(HANDLE);
+static BOOL		 (WINAPI *IsThemeBackgroundPartiallyTransparent)(HANDLE,INT,INT);
+static HRESULT	(WINAPI *DrawThemeParentBackground)(HWND,HDC,RECT *);
+static HRESULT	(WINAPI *DrawThemeBackground)(HANDLE,HDC,INT,INT,const RECT *,const RECT *);
+static HRESULT	(WINAPI *DrawThemeText)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,DWORD,const RECT *);
+static HRESULT	(WINAPI *GetThemeTextExtent)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,OPTIONAL const RECT*, RECT *);
+
+/**
+ * name:	ThemeSupport
+ * desc:	Loads the uxtheme functions, if supported by the os
+ * param:	none
+ * return:	TRUE if themes are supported, FALSE if not
+ **/
+static BOOLEAN __fastcall ThemeSupport() {
+	if (IsWinVerXPPlus()) {
+		if (!themeAPIHandle) {
+			themeAPIHandle = GetModuleHandleA("uxtheme");
+			if (themeAPIHandle) {
+				OpenThemeData = (HANDLE (WINAPI *)(HWND,LPCWSTR))MGPROC("OpenThemeData");
+				CloseThemeData = (HRESULT (WINAPI *)(HANDLE))MGPROC("CloseThemeData");
+				IsThemeBackgroundPartiallyTransparent = (BOOL (WINAPI *)(HANDLE,INT,INT))MGPROC("IsThemeBackgroundPartiallyTransparent");
+				DrawThemeParentBackground = (HRESULT (WINAPI *)(HWND,HDC,RECT *))MGPROC("DrawThemeParentBackground");
+				DrawThemeBackground = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,const RECT *,const RECT *))MGPROC("DrawThemeBackground");
+				DrawThemeText = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,DWORD,const RECT *))MGPROC("DrawThemeText");
+				GetThemeTextExtent = (HRESULT (WINAPI *)(HANDLE,HDC,INT,INT,LPCWSTR,INT,DWORD,OPTIONAL const RECT*, RECT *))MGPROC("GetThemeTextExtent");
+			}
+		}
+		// Make sure all of these methods are valid (i would hope either all or none work)
+		if (OpenThemeData
+			&& CloseThemeData
+			&& IsThemeBackgroundPartiallyTransparent
+			&& DrawThemeParentBackground
+			&& DrawThemeBackground
+			&& DrawThemeText
+			&& GetThemeTextExtent)
+		{
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * name:	DestroyTheme
+ * desc:	destroys theme data for buttons
+ * param:	ctl - BTNCTRL structure with the information about the theme to close
+ * return:	nothing
+ **/
+static VOID __fastcall DestroyTheme(BTNCTRL *ctl) {
+	if (ctl->hThemeButton) {
+		CloseThemeData(ctl->hThemeButton);
+		ctl->hThemeButton = NULL;
+	}
+	if (ctl->hThemeToolbar) {
+		CloseThemeData(ctl->hThemeToolbar);
+		ctl->hThemeToolbar = NULL;
+	}
+}
+
+/**
+ * name:	LoadTheme
+ * desc:	load theme data for buttons if supported by os
+ * param:	ctl - BTNCTRL structure with the information about the theme to load
+ * return:	nothing
+ **/
+static VOID __fastcall LoadTheme(BTNCTRL *ctl) {
+	if (ThemeSupport()) {
+		DestroyTheme(ctl);
+		ctl->hThemeButton = OpenThemeData(ctl->hwnd,L"BUTTON");
+		ctl->hThemeToolbar = OpenThemeData(ctl->hwnd,L"TOOLBAR");
+	}
+}
+
+/**
+ * name:	TBStateConvert2Flat
+ * desc:	convert button stateIDs
+ * param:	state - state id for the normal theme button
+ * return:	stateID for the flat theme button
+ **/
+static INT __fastcall TBStateConvert2Flat(INT state) {
+	switch (state) {
+		case PBS_NORMAL:		return TS_NORMAL;
+		case PBS_HOT:			 return TS_HOT;
+		case PBS_PRESSED:	 return TS_PRESSED;
+		case PBS_DISABLED:	return TS_DISABLED;
+		case PBS_DEFAULTED: return TS_NORMAL;
+	}
+	return TS_NORMAL;
+}
+
+/**
+ * name:	PaintIcon
+ * desc:	Draws the Icon of the button
+ * param:	ctl			- BTNCTRL structure for the button
+ *			hdcMem		- device context to draw to
+ *			ccText		- character count of the text of the button
+ *			rcClient	- rectangle of the whole button
+ *			rcText		- rectangle of the text to draw later on
+ * return:	nothing
+ **/
+static VOID __fastcall PaintIcon(BTNCTRL *ctl, HDC hdcMem, LPWORD ccText, LPRECT rcClient, LPRECT rcText)
+{
+	RECT rcImage;
+
+	// draw icon on the left of the button
+	if (ctl->hIcon) {
+		rcImage.right = GetSystemMetrics(SM_CXSMICON);
+		rcImage.bottom = GetSystemMetrics(SM_CYSMICON);
+		rcImage.left = (rcClient->right - rcClient->left) / 2 - ((rcImage.right + rcText->right + (*ccText > 0 ? 4 : 0) + (ctl->arrow ? rcImage.right : 0)) / 2);
+		rcImage.top = (rcClient->bottom - rcClient->top - rcImage.bottom) / 2;
+		rcImage.right += rcImage.left;
+		rcImage.bottom += rcImage.top;
+		
+		OffsetRect(rcText, rcImage.right + 4, 0);
+		if (ctl->stateId == PBS_PRESSED)	OffsetRect(&rcImage, 1, 1);
+
+		DrawState(hdcMem, NULL, NULL, (LPARAM)ctl->hIcon, 0, 
+			rcImage.left, rcImage.top, 
+			rcImage.right - rcImage.left, rcImage.bottom - rcImage.top,
+			IsWindowEnabled(ctl->hwnd) ? DST_ICON | DSS_NORMAL : DST_ICON | DSS_DISABLED);
+	}
+
+	// draw arrow on the right of the button
+	if (ctl->arrow) {
+		rcImage.right = GetSystemMetrics(SM_CXSMICON);
+		rcImage.left = (*ccText > 0 || ctl->hIcon) 
+					 ? rcClient->right - GetSystemMetrics(SM_CXSMICON) 
+					 : (rcClient->right - rcClient->left - rcImage.right) / 2;
+		rcImage.right += rcImage.left;
+		rcImage.bottom = GetSystemMetrics(SM_CYSMICON);
+		rcImage.top = (rcClient->bottom - rcClient->top - rcImage.bottom) / 2;
+		if (ctl->stateId == PBS_PRESSED)	OffsetRect(&rcImage, 1, 1);
+
+		DrawState(hdcMem, NULL, NULL, (LPARAM)ctl->arrow, 0, 
+			rcImage.left, rcImage.top, 
+			rcImage.right - rcImage.left, rcImage.bottom - rcImage.top,
+			IsWindowEnabled(ctl->hwnd) ? DST_ICON | DSS_NORMAL : DST_ICON | DSS_DISABLED);
+	}
+}
+
+/**
+ * name:	PaintThemeButton
+ * desc:	Draws the themed button
+ * param:	ctl			- BTNCTRL structure for the button
+ *			hdcMem		- device context to draw to
+ *			rcClient	- rectangle of the whole button
+ * return:	nothing
+ **/
+static VOID __fastcall PaintThemeButton(BTNCTRL *ctl, HDC hdcMem, LPRECT rcClient)
+{
+	RECT rcText = { 0, 0, 0, 0 };
+	WCHAR wszText[MAX_PATH] = { 0 };
+	WORD ccText;
+
+	// Draw the flat button
+	if ((ctl->dwStyle & MBS_FLAT) && ctl->hThemeToolbar) {
+		INT state = IsWindowEnabled(ctl->hwnd)
+				? (ctl->stateId == PBS_NORMAL && ctl->defbutton 
+					? PBS_DEFAULTED
+					: ctl->stateId)
+				: PBS_DISABLED;
+		if (IsThemeBackgroundPartiallyTransparent(ctl->hThemeToolbar, TP_BUTTON, TBStateConvert2Flat(state))) {
+			if (SUCCEEDED(DrawThemeParentBackground(ctl->hwnd, hdcMem, rcClient)))
+				DrawThemeParentBackground(GetParent(ctl->hwnd), hdcMem, rcClient);
+		}
+		DrawThemeBackground(ctl->hThemeToolbar, hdcMem, TP_BUTTON, TBStateConvert2Flat(state), rcClient, rcClient);
+	}
+	else {
+		// draw themed button background
+		if (ctl->hThemeButton) {
+			INT state = IsWindowEnabled(ctl->hwnd)
+				? (ctl->stateId == PBS_NORMAL && ctl->defbutton 
+					? PBS_DEFAULTED
+					: ctl->stateId)
+				: PBS_DISABLED;
+			if (IsThemeBackgroundPartiallyTransparent(ctl->hThemeButton, BP_PUSHBUTTON, state)) {
+				if (SUCCEEDED(DrawThemeParentBackground(ctl->hwnd, hdcMem, rcClient)))
+					DrawThemeParentBackground(GetParent(ctl->hwnd), hdcMem, rcClient);
+			}
+			DrawThemeBackground(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, state, rcClient, rcClient);
+		}
+	}
+	
+	// calculate text rect
+	{
+		RECT	sizeText;
+		HFONT	hOldFont;
+
+		ccText = GetWindowTextW(ctl->hwnd, wszText, sizeof(wszText) / sizeof(WCHAR));
+
+		if (ccText > 0) {
+			hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+			
+			GetThemeTextExtent(
+				ctl->hThemeButton,
+				hdcMem,
+				BP_PUSHBUTTON,
+				IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED,
+				wszText,
+				ccText,
+				DST_PREFIXTEXT,
+				NULL,
+				&sizeText);
+			
+			if (ctl->cHot) {
+				RECT rcHot;
+				
+				GetThemeTextExtent(ctl->hThemeButton,
+					hdcMem,
+					BP_PUSHBUTTON,
+					IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED,
+					L"&",
+					1,
+					DST_PREFIXTEXT,
+					NULL,
+					&rcHot);
+				
+				sizeText.right -= (rcHot.right - rcHot.left);
+			}
+			SelectObject(hdcMem, hOldFont);
+
+			rcText.left = (ctl->hIcon) ? 0 : (rcClient->right - rcClient->left - (sizeText.right - sizeText.left)) / 2;
+			rcText.top = (rcClient->bottom - rcClient->top - (sizeText.bottom - sizeText.top)) / 2;
+			rcText.right = rcText.left + (sizeText.right - sizeText.left);
+			rcText.bottom = rcText.top + (sizeText.bottom - sizeText.top);
+			if (ctl->stateId == PBS_PRESSED) {
+				OffsetRect(&rcText, 1, 1);
+			}
+		}
+	}
+	PaintIcon(ctl, hdcMem, &ccText, rcClient, &rcText);
+	// draw text
+	if (ccText > 0 && ctl->hThemeButton) { 
+		HFONT hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+		DrawThemeText(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, IsWindowEnabled(ctl->hwnd) ? ctl->stateId : PBS_DISABLED, wszText, ccText, DST_PREFIXTEXT, 0, &rcText);
+		SelectObject(hdcMem, hOldFont);
+	}
+}
+
+/**
+ * name:	PaintThemeButton
+ * desc:	Draws the none themed button
+ * param:	ctl			- BTNCTRL structure for the button
+ *			hdcMem		- device context to draw to
+ *			rcClient	- rectangle of the whole button
+ * return:	nothing
+ **/
+static VOID __fastcall PaintButton(BTNCTRL *ctl, HDC hdcMem, LPRECT rcClient)
+{
+	RECT rcText = { 0, 0, 0, 0 };
+	TCHAR szText[MAX_PATH] = { 0 };
+	WORD ccText;
+
+	// Draw the flat button
+	if (ctl->dwStyle & MBS_FLAT) {
+		HBRUSH hbr = NULL;
+		
+		if (ctl->stateId == PBS_PRESSED || ctl->stateId == PBS_HOT)
+			hbr = GetSysColorBrush(COLOR_3DLIGHT);
+		else {
+			HDC dc;
+			HWND hwndParent;
+
+			hwndParent = GetParent(ctl->hwnd);
+			if (dc = GetDC(hwndParent)) {
+				hbr = (HBRUSH)SendMessage(hwndParent, WM_CTLCOLORDLG, (WPARAM)dc, (LPARAM)hwndParent);
+				ReleaseDC(hwndParent, dc);
+			}
+		}
+		if (hbr) {
+			FillRect(hdcMem, rcClient, hbr);
+			DeleteObject(hbr);
+		}
+		if (ctl->stateId == PBS_HOT || ctl->bFocus) {
+			if (ctl->pbState) DrawEdge(hdcMem, rcClient, EDGE_ETCHED, BF_RECT|BF_SOFT);
+			else DrawEdge(hdcMem, rcClient, BDR_RAISEDOUTER, BF_RECT|BF_SOFT|BF_FLAT);
+		}
+		else
+		if (ctl->stateId == PBS_PRESSED)
+			DrawEdge(hdcMem, rcClient, BDR_SUNKENOUTER, BF_RECT|BF_SOFT);
+	}
+	else {
+		UINT uState = DFCS_BUTTONPUSH|((ctl->stateId == PBS_HOT) ? DFCS_HOT : 0)|((ctl->stateId == PBS_PRESSED) ? DFCS_PUSHED : 0);
+		if (ctl->defbutton&&ctl->stateId==PBS_NORMAL) uState |= DLGC_DEFPUSHBUTTON;
+		DrawFrameControl(hdcMem, rcClient, DFC_BUTTON, uState);
+		// Draw focus rectangle if button has focus
+		if (ctl->bFocus) {
+			RECT focusRect = *rcClient;
+			InflateRect(&focusRect, -3, -3);
+			DrawFocusRect(hdcMem, &focusRect);
+		}
+	}
+	// calculate text rect
+	{
+		SIZE	sizeText;
+		HFONT	hOldFont;
+
+		ccText = GetWindowText(ctl->hwnd, szText, SIZEOF(szText));
+
+		if (ccText > 0) {
+			hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+			GetTextExtentPoint32(hdcMem, szText, ccText, &sizeText);
+			if (ctl->cHot) {
+				SIZE sizeHot;
+				
+				GetTextExtentPoint32A(hdcMem, "&", 1, &sizeHot);
+				sizeText.cx -= sizeHot.cx;
+			}
+			SelectObject(hdcMem, hOldFont);
+
+			rcText.left = (ctl->hIcon) ? 0 : (rcClient->right - rcClient->left - sizeText.cx) / 2;
+			rcText.top = (rcClient->bottom - rcClient->top - sizeText.cy) / 2;
+			rcText.right = rcText.left + sizeText.cx;
+			rcText.bottom = rcText.top + sizeText.cy;
+			if (ctl->stateId == PBS_PRESSED)
+				OffsetRect(&rcText, 1, 1);
+		}
+	}
+	PaintIcon(ctl, hdcMem, &ccText, rcClient, &rcText);
+
+	// draw text
+	if (ccText > 0) { 
+		HFONT hOldFont;
+
+		hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+
+		SetBkMode(hdcMem, TRANSPARENT);
+		SetTextColor(hdcMem, 
+			IsWindowEnabled(ctl->hwnd) || !ctl->hThemeButton 
+			? ctl->stateId == PBS_HOT
+					? GetSysColor(COLOR_HOTLIGHT)
+					: GetSysColor(COLOR_BTNTEXT) 
+				: GetSysColor(COLOR_GRAYTEXT));
+
+		DrawState(hdcMem, NULL, NULL, (LPARAM)szText, 0, 
+			rcText.left, rcText.top, rcText.right - rcText.left, rcText.bottom - rcText.top,
+			IsWindowEnabled(ctl->hwnd) || ctl->hThemeButton ? DST_PREFIXTEXT | DSS_NORMAL : DST_PREFIXTEXT | DSS_DISABLED);
+		SelectObject(hdcMem, hOldFont);
+	}
+}
+
+/**
+ * name:	Button_WndProc
+ * desc:	window procedure for the button class
+ * param:	hwndBtn		- window handle to the button
+ *			uMsg		- message to handle
+ *			wParam		- message specific parameter
+ *			lParam		- message specific parameter
+ * return:	message specific
+ **/
+static LRESULT CALLBACK Button_WndProc(HWND hwndBtn, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+	LPBTNCTRL bct = (LPBTNCTRL)GetWindowLongPtr(hwndBtn, 0);
+	
+	switch (uMsg) {
+		case WM_NCCREATE:
+		{
+			LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam;
+
+			cs->style |= BS_OWNERDRAW;
+			if (!(bct = (LPBTNCTRL)mir_alloc(sizeof(BTNCTRL))))
+				return FALSE;
+			ZeroMemory(bct, sizeof(BTNCTRL));
+			bct->hwnd = hwndBtn;
+			bct->stateId = PBS_NORMAL;
+			bct->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+			bct->dwStyle = cs->style;
+			if (cs->style & MBS_DOWNARROW)
+				bct->arrow = IcoLib_GetIcon(ICO_BTN_DOWNARROW);
+			LoadTheme(bct);
+			SetWindowLongPtr(hwndBtn, 0, (LONG_PTR)bct);
+			if (cs->lpszName) SetWindowText(hwndBtn, cs->lpszName);
+			return TRUE;
+		}
+		case WM_DESTROY:
+			if (bct) {
+				EnterCriticalSection(&csTips);
+				if (hwndToolTips) {
+					TOOLINFO ti;
+
+					ZeroMemory(&ti, sizeof(ti));
+					ti.cbSize = sizeof(ti);
+					ti.uFlags = TTF_IDISHWND;
+					ti.hwnd = bct->hwnd;
+					ti.uId = (UINT_PTR)bct->hwnd;
+					if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
+						SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+					}
+					if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) {
+						DestroyWindow(hwndToolTips);
+						hwndToolTips = NULL;
+					}
+				}
+				LeaveCriticalSection(&csTips);
+				DestroyTheme(bct);
+				mir_free(bct);
+			}
+			SetWindowLongPtr(hwndBtn, 0, NULL);
+			break;
+		case WM_SETTEXT:
+			bct->cHot = 0;
+			if ((LPTSTR)lParam) {
+				LPTSTR tmp = (LPTSTR)lParam;
+				
+				while (*tmp) {
+					if (*tmp=='&' && *(tmp+1)) {
+						bct->cHot = _totlower(*(tmp+1));
+						break;
+					}
+					tmp++;
+				}
+				InvalidateRect(bct->hwnd, NULL, TRUE);
+			}
+			break;
+		case WM_SYSKEYUP:
+			if (bct->stateId != PBS_DISABLED && bct->cHot && bct->cHot == _totlower((TCHAR)wParam)) {
+				if (bct->dwStyle & MBS_PUSHBUTTON) {
+					if (bct->pbState) bct->pbState = 0;
+					else bct->pbState = 1;
+					InvalidateRect(bct->hwnd, NULL, TRUE);
+				}
+				else
+					SetFocus(hwndBtn);
+				SendMessage(GetParent(hwndBtn), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndBtn), BN_CLICKED), (LPARAM)hwndBtn);
+				return 0;
+			}
+			break;
+		case WM_THEMECHANGED: 
+		{
+			// themed changed, reload theme object
+			LoadTheme(bct);
+			InvalidateRect(bct->hwnd, NULL, TRUE); // repaint it
+			break;
+		}
+		case WM_SETFONT: // remember the font so we can use it later
+			bct->hFont = (HFONT)wParam; // maybe we should redraw?
+			break;
+		case WM_NCPAINT:
+		case WM_PAINT:
+		{
+			PAINTSTRUCT ps;
+			HDC hdcPaint;
+			HDC hdcMem;
+			HBITMAP hbmMem;
+			HDC hOld;
+			RECT rcClient;
+			
+			if (hdcPaint = BeginPaint(hwndBtn, &ps)) {
+				GetClientRect(bct->hwnd, &rcClient);
+				hdcMem = CreateCompatibleDC(hdcPaint);
+				hbmMem = CreateCompatibleBitmap(hdcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
+				hOld = (HDC)SelectObject(hdcMem, hbmMem);
+
+				// If its a push button, check to see if it should stay pressed
+				if ((bct->dwStyle & MBS_PUSHBUTTON) && bct->pbState) bct->stateId = PBS_PRESSED;
+
+				if ((bct->dwStyle & MBS_FLAT) && bct->hThemeToolbar || bct->hThemeButton)
+					PaintThemeButton(bct, hdcMem, &rcClient);
+				else
+					PaintButton(bct, hdcMem, &rcClient);
+
+				BitBlt(hdcPaint, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcMem, 0, 0, SRCCOPY);
+				SelectObject(hdcMem, hOld);
+				DeleteObject(hbmMem);
+				DeleteDC(hdcMem);				
+				EndPaint(hwndBtn, &ps);
+			}
+			return 0;
+		}
+		case BM_SETIMAGE:
+			if (wParam == IMAGE_ICON) {
+				bct->hIcon = (HICON)lParam;
+				bct->hBitmap = NULL;
+				InvalidateRect(bct->hwnd, NULL, TRUE);
+			}
+			else if (wParam == IMAGE_BITMAP) {
+				bct->hIcon = NULL;
+				bct->hBitmap = (HBITMAP)lParam;
+				InvalidateRect(bct->hwnd, NULL, TRUE);
+			}
+			else if (wParam == NULL && lParam == NULL) {
+				bct->hIcon = NULL;
+				bct->hBitmap = NULL;
+				InvalidateRect(bct->hwnd, NULL, TRUE);
+			}
+			break;
+		case BM_SETCHECK:
+			if (!(bct->dwStyle & MBS_PUSHBUTTON)) break;
+			if (wParam == BST_CHECKED) {
+				bct->pbState = 1;
+								bct->stateId = PBS_PRESSED;
+			}
+			else if (wParam == BST_UNCHECKED) {
+				bct->pbState = 0;
+								bct->stateId = PBS_NORMAL;
+			}
+			InvalidateRect(bct->hwnd, NULL, TRUE);
+			break;
+		case BM_GETCHECK:
+			if (bct->dwStyle & MBS_PUSHBUTTON) return bct->pbState ? BST_CHECKED : BST_UNCHECKED;
+			return 0;
+		case BUTTONSETDEFAULT:
+			bct->defbutton = (wParam != 0);
+			InvalidateRect(bct->hwnd, NULL, TRUE);
+			break;
+		case BUTTONADDTOOLTIP:
+		{
+			if (!wParam) break;
+						EnterCriticalSection(&csTips);
+			if (!hwndToolTips) {
+				hwndToolTips = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL);
+			}
+
+			if (lParam == MBF_UNICODE) {
+				TOOLINFOW ti;
+
+				ZeroMemory(&ti, sizeof(TOOLINFOW));
+				ti.cbSize = sizeof(TOOLINFOW);
+				ti.uFlags = TTF_IDISHWND;
+				ti.hwnd = bct->hwnd;
+				ti.uId = (UINT_PTR)bct->hwnd;
+				if (SendMessage(hwndToolTips, TTM_GETTOOLINFOW, 0, (LPARAM)&ti)) {
+					SendMessage(hwndToolTips, TTM_DELTOOLW, 0, (LPARAM)&ti);
+				}
+				ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
+				ti.uId = (UINT_PTR)bct->hwnd;
+				ti.lpszText=(LPWSTR)wParam;
+				SendMessage(hwndToolTips, TTM_ADDTOOLW, 0, (LPARAM)&ti);
+			}
+			else {
+				TOOLINFOA ti;
+
+				ZeroMemory(&ti, sizeof(TOOLINFOA));
+				ti.cbSize = sizeof(TOOLINFOA);
+				ti.uFlags = TTF_IDISHWND;
+				ti.hwnd = bct->hwnd;
+				ti.uId = (UINT_PTR)bct->hwnd;
+				if (SendMessage(hwndToolTips, TTM_GETTOOLINFOA, 0, (LPARAM)&ti)) {
+					SendMessage(hwndToolTips, TTM_DELTOOLA, 0, (LPARAM)&ti);
+				}
+				ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
+				ti.uId = (UINT_PTR)bct->hwnd;
+				ti.lpszText=(LPSTR)wParam;
+				SendMessage(hwndToolTips, TTM_ADDTOOLA, 0, (LPARAM)&ti);
+			}
+						LeaveCriticalSection(&csTips);
+			break;
+		}
+		case BUTTONTRANSLATE:
+		{
+			TCHAR szButton[MAX_PATH];
+			GetWindowText(bct->hwnd, szButton, MAX_PATH);
+			SetWindowText(bct->hwnd, TranslateTS(szButton));
+			break;
+		}
+		case WM_SETFOCUS: // set keybord bFocus and redraw
+			bct->bFocus = 1;
+			InvalidateRect(bct->hwnd, NULL, TRUE);
+			break;
+		case WM_KILLFOCUS: // kill bFocus and redraw
+			bct->bFocus = 0;
+			InvalidateRect(bct->hwnd, NULL, TRUE);
+			break;
+		case WM_WINDOWPOSCHANGED:
+			InvalidateRect(bct->hwnd, NULL, TRUE);
+			break;
+		case WM_ENABLE: // windows tells us to enable/disable
+			bct->stateId = wParam ? PBS_NORMAL : PBS_DISABLED;
+			InvalidateRect(bct->hwnd, NULL, TRUE);
+			break;
+		case WM_MOUSELEAVE: // faked by the WM_TIMER
+			if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
+				bct->stateId = PBS_NORMAL;
+				InvalidateRect(bct->hwnd, NULL, TRUE);
+			}
+			break;
+		case WM_LBUTTONDOWN:
+			if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
+				bct->stateId = PBS_PRESSED;
+				InvalidateRect(bct->hwnd, NULL, TRUE);
+			}
+			break;
+		case WM_LBUTTONUP:
+			if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
+				BOOLEAN bPressed = bct->stateId == PBS_PRESSED;
+
+				if (bct->dwStyle & MBS_PUSHBUTTON) {
+					if (bct->pbState) bct->pbState = 0;
+					else bct->pbState = 1;
+				}
+				bct->stateId = PBS_HOT;
+
+				// Tell your daddy you got clicked, if mouse is still over the button.
+				if ((bct->dwStyle & MBS_PUSHBUTTON) || bPressed)
+					SendMessage(GetParent(hwndBtn), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndBtn), BN_CLICKED), (LPARAM)hwndBtn);
+				InvalidateRect(bct->hwnd, NULL, TRUE);
+			}
+			break;
+		case WM_MOUSEMOVE:
+			if (bct->stateId == PBS_NORMAL) {
+				bct->stateId = PBS_HOT;
+				InvalidateRect(bct->hwnd, NULL, TRUE);
+			}
+			// Call timer, used to start cheesy TrackMouseEvent faker
+			SetTimer(hwndBtn, BUTTON_POLLID, BUTTON_POLLDELAY, NULL);
+			break;
+		case WM_TIMER: // use a timer to check if they have did a mouseout
+						if (wParam == BUTTON_POLLID) {
+					RECT rc;
+					POINT pt;
+
+					GetWindowRect(hwndBtn, &rc);
+					GetCursorPos(&pt);
+					if (!PtInRect(&rc, pt)) { // mouse must be gone, trigger mouse leave
+						PostMessage(hwndBtn, WM_MOUSELEAVE, 0, 0L);
+						KillTimer(hwndBtn, BUTTON_POLLID);
+					}
+						}
+			break;
+		case WM_ERASEBKGND:
+			return 1;
+	}
+	return DefWindowProc(hwndBtn, uMsg, wParam, lParam);
+}
+
+VOID CtrlButtonUnloadModule() 
+{
+	DeleteCriticalSection(&csTips);
+	UnregisterClass(UINFOBUTTONCLASS, ghInst);
+}
+
+VOID CtrlButtonLoadModule()
+{
+	WNDCLASSEX wc;
+	
+	ZeroMemory(&wc, sizeof(wc));
+	wc.cbSize				 = sizeof(wc);
+	wc.lpszClassName	= UINFOBUTTONCLASS;
+	wc.lpfnWndProc		= Button_WndProc;
+	wc.hCursor				= LoadCursor(NULL, IDC_ARROW);
+	wc.cbWndExtra		 = sizeof(LPBTNCTRL);
+	wc.style					= CS_GLOBALCLASS;
+	RegisterClassEx(&wc);
+	InitializeCriticalSection(&csTips);
+}
+
diff --git a/plugins/UserInfoEx/src/ctrl_button.h b/plugins/UserInfoEx/src/ctrl_button.h
new file mode 100644
index 0000000000..5dd99bb4f6
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_button.h
@@ -0,0 +1,36 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_button.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_BOTTONS_H_INCLUDED_
+#define _UINFOEX_BOTTONS_H_INCLUDED_ 1
+
+VOID CtrlButtonLoadModule();
+VOID CtrlButtonUnloadModule();
+
+#endif /* _UINFOEX_BOTTONS_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_combo.cpp b/plugins/UserInfoEx/src/ctrl_combo.cpp
new file mode 100644
index 0000000000..6cdd5bdd2c
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_combo.cpp
@@ -0,0 +1,277 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_combo.cpp $
+Revision       : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+
+/**
+ * This static method creates an object for the CCombo class and returns its pointer.
+ * If combobox is filled with items from pList.
+ *
+ * @param		hDlg			- HWND of the owning propertysheet page
+ * @param		idCtrl			- the ID of the control to associate with this class's instance
+ * @param		pszSetting		- the database setting to be handled by this class
+ * @param		bDBDataType		- datatype of of the associated database setting (WORD, DWORD, ...)
+ * @param		pList			- pointer to a LPIDSTRLIST list, which holds the items to insert into the combobox.
+ * @param		nListCount		- number of items in the list
+ *
+ * @return	pointer of the newly created CCombo object.
+ **/
+CBaseCtrl* CCombo::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount)
+{
+	return new CCombo(hDlg, idCtrl, pszSetting, bDBDataType, pList, nListCount);
+}
+
+/**
+ * This is the constructor. If combobox is filled with items from pList.
+ *
+ * @param		hDlg			- HWND of the owning propertysheet page
+ * @param		idCtrl			- the ID of the control to associate with this class's instance
+ * @param		pszSetting		- the database setting to be handled by this class
+ * @param		bDBDataType		- datatype of of the associated database setting (WORD, DWORD, ...)
+ * @param		pList			- pointer to a LPIDSTRLIST list, which holds the items to insert into the combobox.
+ * @param		nListCount		- number of items in the list
+ *
+ * @return	nothing
+ **/
+CCombo::CCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount)
+: CBaseCtrl(hDlg, idCtrl, pszSetting)
+{
+	_curSel = CB_ERR;
+	_pList = pList;
+	_nList = nListCount;
+	_bDataType = bDBDataType;
+
+	// fill in data
+	if (_pList && (_nList > 0))
+	{
+		for (INT i = 0; i < _nList; i++)
+		{
+			AddItem(_pList[i].ptszTranslated, (LPARAM)&_pList[i]);
+		}
+	}
+}
+
+/**
+ * This method searches for an item which matches the given nID.
+ *
+ * @param		nID				- the id of the desired object.
+ *
+ * @retval	CB_ERR	- item not found
+ * @retval	0...n		- index of the combobox item
+ **/
+INT CCombo::Find(INT nID) const
+{
+	INT i;
+	LPIDSTRLIST pd;
+
+	for (i = ComboBox_GetCount(_hwnd) - 1; i >= 0; i--)
+	{
+		pd = (LPIDSTRLIST)ComboBox_GetItemData(_hwnd, i);
+		if (PtrIsValid(pd) && (pd->nID == nID))
+		{
+			break;
+		}
+	}
+	return i;
+}
+
+/**
+ * This method searches for an item which matches the given label.
+ *
+ * @param		ptszItemLabel	- The translated label of the desired item.
+ *
+ * @retval	CB_ERR	- item not found
+ * @retval	0...n		- index of the combobox item
+ **/
+INT CCombo::Find(LPTSTR ptszItemLabel) const
+{
+	return ComboBox_FindStringExact(_hwnd, 0, ptszItemLabel);
+}
+
+/**
+ * Adds a string and associated item data to a combobox.
+ *
+ * @param	 pszText			- the text to add to the combobox
+ * @param	 lParam				- item data to accociate with the new item
+ *
+ * @return	zero-based index to new item or CB_ERR on failure
+ **/
+INT_PTR CCombo::AddItem(LPCTSTR pszText, LPARAM lParam)
+{
+	INT_PTR added = ComboBox_AddString(_hwnd, pszText);
+	if (SUCCEEDED(added)) 
+	{
+		if (PtrIsValid(lParam) && FAILED(ComboBox_SetItemData(_hwnd, added, lParam)))
+		{
+			ComboBox_DeleteString(_hwnd, added);
+			added = CB_ERR;
+		}
+	}
+	return added;
+}
+
+/**
+ * This functions removes the user data from a combobox.
+ *
+ * @param		hCtrl			- HWND of the combobox
+ *
+ * @return	nothing
+ **/
+VOID CCombo::Release()
+{
+	delete this;
+}
+
+/**
+ * This method selects the combobox item which matches the contact's setting, associated with.
+ *
+ * @param		hContact	- HANDLE of the contact
+ * @param		pszProto	- the contact's protocol
+ *
+ * @return	nothing
+ **/
+BOOL CCombo::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+	if (!_Flags.B.hasChanged)
+	{
+		DBVARIANT dbv;
+		LPIDSTRLIST pItem = NULL;
+		INT iVal = CB_ERR;
+
+		_Flags.B.hasCustom = _Flags.B.hasProto = _Flags.B.hasMeta = 0;
+		_Flags.W |= DB::Setting::GetTStringCtrl(hContact, USERINFO, USERINFO, pszProto, _pszSetting, &dbv);
+		EnableWindow(_hwnd, !hContact || _Flags.B.hasCustom || !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0));	
+
+		if (_Flags.B.hasCustom || _Flags.B.hasProto || _Flags.B.hasMeta)
+		{
+			switch (dbv.type) 
+			{
+			case DBVT_BYTE:		iVal = Find((INT)dbv.bVal);		break;
+			case DBVT_WORD:		iVal = Find((INT)dbv.wVal);		break;
+			case DBVT_DWORD:	iVal = Find((INT)dbv.dVal);		break;
+			case DBVT_TCHAR:
+				iVal = Find(TranslateTS(dbv.ptszVal));
+				if (iVal == CB_ERR) {
+					// other
+					iVal = Find(_pList[_nList - 1].nID);
+				}
+			}
+		}
+		if (iVal == CB_ERR)
+		{
+			// unspecified
+			iVal = Find(_pList[0].nID);
+		}
+		DB::Variant::Free(&dbv);
+		ComboBox_SetCurSel(_hwnd, iVal);
+		_curSel = ComboBox_GetCurSel(_hwnd);
+		SendMessage(GetParent(_hwnd), WM_COMMAND, MAKEWPARAM( (WORD)this->_idCtrl, (WORD)CBN_SELCHANGE), (LPARAM)_hwnd);
+	}
+	return _Flags.B.hasChanged;
+}
+
+/**
+ * This method writes the combobox's item
+ *
+ * @param		hContact		- HANDLE of the contact
+ * @param		pszProto		- the contact's protocol
+ *
+ * @return	nothing
+ **/
+VOID CCombo::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+	if (_Flags.B.hasChanged)
+	{
+		LPCSTR pszModule = hContact ? USERINFO : pszProto;
+
+		if ((_Flags.B.hasCustom || !hContact) && (_curSel != CB_ERR))
+		{
+			LPIDSTRLIST pd;
+
+			pd = (LPIDSTRLIST)SendMessage(_hwnd, CB_GETITEMDATA, _curSel, 0);
+			if (pd != NULL)
+			{
+				switch (_bDataType)
+				{
+				case DBVT_BYTE:
+					DB::Setting::WriteByte(hContact, pszModule, _pszSetting, pd->nID);
+					break;
+				case DBVT_WORD:
+					DB::Setting::WriteWord(hContact, pszModule, _pszSetting, pd->nID);
+					break;
+				case DBVT_DWORD:
+					DB::Setting::WriteDWord(hContact, pszModule, _pszSetting, pd->nID);
+					break;
+				case DBVT_ASCIIZ:
+				case DBVT_WCHAR:
+					DB::Setting::WriteAString(hContact, pszModule, _pszSetting, (LPSTR)pd->pszText);
+				}
+				if (!hContact)
+				{
+					_Flags.B.hasCustom = 0;
+					_Flags.B.hasProto = 1;
+				}
+				_Flags.B.hasChanged = 0;
+			}
+		}
+		if (_Flags.B.hasChanged)
+		{
+			DB::Setting::Delete(hContact, pszModule, _pszSetting);
+			_Flags.B.hasChanged = 0;
+			OnInfoChanged(hContact, pszProto);
+		}
+		InvalidateRect(_hwnd, NULL, TRUE);
+	}
+}
+
+/**
+ * The user changed combobox selection, so mark it changed.
+ *
+ * @return	nothing
+ **/
+VOID CCombo::OnChangedByUser(WORD wChangedMsg)
+{
+	if (wChangedMsg == CBN_SELCHANGE)
+	{
+		INT c = ComboBox_GetCurSel(_hwnd);
+
+		if (_curSel != c)
+		{
+			if (!_Flags.B.hasChanged)
+			{
+				_Flags.B.hasChanged = 1;
+				_Flags.B.hasCustom = 1;
+				SendMessage(GetParent(GetParent(_hwnd)), PSM_CHANGED, 0, 0);
+			}
+			_curSel = c;
+		}
+	}
+}
+	
diff --git a/plugins/UserInfoEx/src/ctrl_combo.h b/plugins/UserInfoEx/src/ctrl_combo.h
new file mode 100644
index 0000000000..84bd4e23a6
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_combo.h
@@ -0,0 +1,80 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_combo.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_COMBO_INCLUDE_
+#define _UI_CTRL_COMBO_INCLUDE_
+
+#include "ctrl_base.h"
+
+/**
+ *
+ **/
+class CCombo : public CBaseCtrl
+{
+	INT					_curSel;
+	LPIDSTRLIST _pList;
+	INT					_nList;
+	BYTE				_bDataType;
+	
+	/**
+	 * Private constructure is to force to use static member 'Create' 
+	 * as the default way of attaching a new object to the window control.
+	 *
+	 * @param	 none
+	 *
+	 * @return	nothing
+	 **/
+	CCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount);
+
+	INT Find(INT nIndex) const;
+	INT Find(LPTSTR ptszItemLabel) const;
+
+	INT_PTR AddItem(LPCTSTR pszText, LPARAM lParam);
+
+public:
+
+	/**
+	 *
+	 *
+	 **/
+	static FORCEINLINE CCombo* GetObj(HWND hCtrl) 
+		{ return (CCombo*) GetUserData(hCtrl); }
+	static FORCEINLINE CCombo* GetObj(HWND hDlg, WORD idCtrl)
+		{ return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+	static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE bDBDataType, LPIDSTRLIST pList, INT nListCount);
+
+	virtual VOID	Release();
+	virtual BOOL	OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+	virtual VOID	OnApply(HANDLE hContact, LPCSTR pszProto);
+	virtual VOID	OnChangedByUser(WORD wChangedMsg);
+};
+
+#endif /* _UI_CTRL_COMBO_INCLUDE_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_contact.cpp b/plugins/UserInfoEx/src/ctrl_contact.cpp
new file mode 100644
index 0000000000..8b13ad9256
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_contact.cpp
@@ -0,0 +1,1539 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_contact.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "ctrl_base.h"
+#include "ctrl_contact.h"
+
+#define MAX_CAT			64
+
+//#define BTN_MENU		(WM_USER+1)
+//#define BTN_EDIT		(WM_USER+2)
+#define EDIT_VALUE		(WM_USER+3)
+
+#define CBEXM_MENIITEMFIRST (WM_USER+100)	// caution: after this message there must be room for all menuitem commnds
+											// so it should be the last message with the highest value
+
+typedef struct TCbExItem
+{
+	WORD	wMask;
+	WORD	wFlags;
+	DWORD	dwID;
+	TCHAR	szCat[MAX_CAT];
+	LPTSTR	pszVal;
+	LPCSTR	pszIcon;
+	HICON	hIcon;
+} CBEXITEMINTERN, *LPCBEXITEMINTERN;
+
+typedef struct TComboEx 
+{
+	LPCBEXITEMINTERN	pItems;
+	INT			numItems;
+	INT			numOther;
+	INT			iSelectedItem;
+
+	BOOLEAN		bIsChanged;
+	BOOLEAN		bLocked;
+	BOOLEAN		bIsEditChanged;
+
+	HINSTANCE	hInstance;
+	HFONT		hFont;
+	HWND		hEdit;
+	HWND		hBtnMenu;
+	HWND		hBtnEdit;
+	HWND		hBtnAdd;
+	HWND		hBtnDel;
+	RECT		rect;
+} CBEX, *LPCBEX;
+
+
+static INT compareProc(LPCVOID cbi1, LPCVOID cbi2)
+{
+	return _tcscmp(((LPCBEXITEMINTERN)cbi1)->szCat, ((LPCBEXITEMINTERN)cbi2)->szCat);
+}
+
+static INT CheckPhoneSyntax(LPTSTR pszSrc, LPTSTR szNumber, WORD cchNumber, INT& errorPos)
+{
+	INT lenNum = 0;
+	BOOLEAN	hasLeftBreaket = FALSE,
+			hasRightBreaket = FALSE;
+
+	if (!szNumber || !pszSrc || !*pszSrc || !cchNumber) return 0;
+	*szNumber = 0;
+	errorPos = -1;
+	
+	if (*pszSrc != '+') {
+		errorPos = 2; // set cursor after first digit
+		*(szNumber + lenNum++) = '+';
+	}
+	else
+		*(szNumber + lenNum++) = *(pszSrc++);
+
+	for (; lenNum < cchNumber - 1 && *pszSrc != 0; pszSrc++) {
+		switch (*pszSrc) {
+			case '(':
+				if (hasLeftBreaket) {
+					if (errorPos == -1) errorPos = lenNum;
+					break;
+				}
+				if (*(szNumber + lenNum - 1) != ' ') {
+					*(szNumber + lenNum++) = ' ';
+					if (errorPos == -1) errorPos = lenNum + 1;
+				}
+				*(szNumber + lenNum++) = *pszSrc;
+				hasLeftBreaket = TRUE;
+				break;
+
+			case ')':
+				if (hasRightBreaket) {
+					if (errorPos == -1) errorPos = lenNum;
+					break;
+				}
+				*(szNumber + lenNum++) = *pszSrc;
+				if (*(pszSrc + 1) != ' ') {
+					*(szNumber + lenNum++) = ' ';
+					if (errorPos == -1) errorPos = lenNum;
+				}
+				hasRightBreaket = TRUE;
+				break;
+
+			case ' ':
+				if (*(szNumber + lenNum - 1) != ' ') {
+					*(szNumber + lenNum++) = *pszSrc;
+				}
+				else
+					if (errorPos == -1) errorPos = lenNum;
+				break;
+
+			default:
+				if (*pszSrc >= '0' && *pszSrc <= '9' || *pszSrc == '-') {
+					*(szNumber + lenNum++) = *pszSrc;
+				}
+				// remember first error position
+				else if (errorPos == -1) errorPos = lenNum;
+				break;
+		}
+	}
+	*(szNumber + lenNum) = 0;
+	return lenNum;
+}
+
+/**
+ * name:	 DlgProc_EditEMail()
+ * desc:	 dialog procedure
+ *
+ * return:	 0 or 1
+ **/
+static INT_PTR CALLBACK DlgProc_EMail(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	switch (msg) 
+	{
+	case WM_INITDIALOG:
+	{
+		LPCBEXITEM cbi = (LPCBEXITEM)lParam;
+
+		if (!cbi) return FALSE;
+		SetUserData(hDlg, lParam);
+			
+		SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)IcoLib_GetIcon(ICO_DLG_EMAIL));
+		if (DB::Setting::GetByte(SET_ICONS_BUTTONS, 1))
+		{
+			SendDlgItemMessage(hDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_OK));
+			SendDlgItemMessage(hDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_CANCEL));
+		}
+
+		if (*cbi->pszVal) SetWindowText(hDlg, LPGENT("Edit E-Mail"));
+		TranslateDialogDefault(hDlg);
+		SendDlgItemMessage(hDlg, EDIT_CATEGORY, EM_LIMITTEXT, cbi->ccCat - 1, 0);
+		SendDlgItemMessage(hDlg, EDIT_EMAIL, EM_LIMITTEXT, cbi->ccVal - 1, 0);
+		SetDlgItemText(hDlg, EDIT_CATEGORY, cbi->pszCat);
+		SetDlgItemText(hDlg, EDIT_EMAIL, cbi->pszVal);
+		EnableWindow(GetDlgItem(hDlg, EDIT_CATEGORY), !(cbi->wFlags & CBEXIF_CATREADONLY));
+		EnableWindow(GetDlgItem(hDlg, IDOK), *cbi->pszVal);
+		// translate Userinfo buttons
+		{
+			TCHAR szButton[MAX_PATH];
+			HWND hBtn;
+			
+			hBtn = GetDlgItem(hDlg, IDOK);
+			GetWindowText(hBtn, szButton, MAX_PATH);
+			SetWindowText(hBtn, TranslateTS(szButton));
+			hBtn = GetDlgItem(hDlg, IDCANCEL);
+			GetWindowText(hBtn, szButton, MAX_PATH);
+			SetWindowText(hBtn, TranslateTS(szButton));
+		}
+		return TRUE;
+	}
+
+	case WM_CTLCOLORSTATIC:
+		SetBkColor((HDC)wParam, RGB(255, 255, 255));
+		return (INT_PTR)GetStockObject(WHITE_BRUSH);
+
+	case WM_COMMAND:
+		switch (LOWORD(wParam)) 
+		{
+			if (HIWORD(wParam) == BN_CLICKED) 
+			{
+				case IDOK:
+				{
+					LPCBEXITEM cbi = (LPCBEXITEM)GetUserData(hDlg);
+
+					if (cbi->pszVal && cbi->ccVal > 0)
+						GetDlgItemText(hDlg, EDIT_EMAIL, cbi->pszVal, cbi->ccVal);
+					if (cbi->pszCat && cbi->ccCat > 0)
+						GetDlgItemText(hDlg, EDIT_CATEGORY, cbi->pszCat, cbi->ccCat);
+				}
+				case IDCANCEL:
+					EndDialog(hDlg, LOWORD(wParam));
+					break;
+			}
+			case EDIT_EMAIL:
+				if (HIWORD(wParam) == EN_UPDATE) {
+					TCHAR szText[MAXDATASIZE];
+					LPTSTR pszAdd, pszDot;
+					LPCBEXITEM cbi = (LPCBEXITEM)GetUserData(hDlg);
+
+					if (PtrIsValid(cbi)) {
+						GetWindowText((HWND)lParam, szText, SIZEOF(szText));
+						EnableWindow(GetDlgItem(hDlg, IDOK), 
+							((pszAdd = _tcschr(szText, '@')) && 
+							*(pszAdd + 1) != '.' &&
+							(pszDot = _tcschr(pszAdd, '.')) &&
+							*(pszDot + 1) &&
+							_tcscmp(szText, cbi->pszVal)));
+					}
+				}
+				break;
+		}
+		break;
+	}
+	return FALSE;
+}
+
+/**
+ * name:	 DlgProc_EditPhone()
+ * desc:	 dialog procedure
+ *
+ * return:	 0 or 1
+ **/
+INT_PTR CALLBACK DlgProc_Phone(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	switch (msg) {
+		case WM_INITDIALOG:
+		{
+			LPCBEXITEM cbi = (LPCBEXITEM)lParam;
+			UINT i, item, countryCount;
+			LPIDSTRLIST	pCountries;
+			HWND hCombo = GetDlgItem(hDlg, EDIT_COUNTRY);
+
+			if (!cbi) return FALSE;
+			SetUserData(hDlg, lParam);
+
+			SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)IcoLib_GetIcon(ICO_DLG_PHONE));
+			if (DB::Setting::GetByte(SET_ICONS_BUTTONS, 1))
+			{
+				SendDlgItemMessage(hDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_OK));
+				SendDlgItemMessage(hDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_CANCEL));
+			}
+
+			// translate Userinfo buttons
+			{
+				TCHAR szButton[MAX_PATH];
+				HWND hBtn;
+				
+				hBtn = GetDlgItem(hDlg, IDOK);
+				GetWindowText(hBtn, szButton, MAX_PATH);
+				SetWindowText(hBtn, TranslateTS(szButton));
+				hBtn = GetDlgItem(hDlg, IDCANCEL);
+				GetWindowText(hBtn, szButton, MAX_PATH);
+				SetWindowText(hBtn, TranslateTS(szButton));
+			}
+			if (*cbi->pszVal) SetWindowText(hDlg, LPGENT("Edit Phone Number"));
+			if (cbi->wFlags & CBEXIF_SMS) CheckDlgButton(hDlg, CHECK_SMS, BST_CHECKED);
+			TranslateDialogDefault(hDlg);
+							
+			EnableWindow(GetDlgItem(hDlg, IDOK), *cbi->pszVal);
+			SendDlgItemMessage(hDlg, EDIT_AREA, EM_LIMITTEXT, 31, 0);
+			SendDlgItemMessage(hDlg, EDIT_NUMBER, EM_LIMITTEXT, 63, 0);
+			SendDlgItemMessage(hDlg, EDIT_CATEGORY, EM_LIMITTEXT, cbi->ccCat - 1, 0);
+			SendDlgItemMessage(hDlg, EDIT_PHONE, EM_LIMITTEXT, cbi->ccVal - 1, 0);
+			
+			GetCountryList(&countryCount, &pCountries);
+			for (i = 0; i < countryCount; i++) {
+				if (pCountries[i].nID == 0 || pCountries[i].nID == 0xFFFF) continue;
+				item = SendMessage(hCombo, CB_ADDSTRING, NULL, (LPARAM)pCountries[i].ptszTranslated);
+				SendMessage(hCombo, CB_SETITEMDATA, item, pCountries[i].nID);
+			}
+
+			SetDlgItemText(hDlg, EDIT_PHONE, cbi->pszVal);
+			SetDlgItemText(hDlg, EDIT_CATEGORY, cbi->pszCat);
+			EnableWindow(GetDlgItem(hDlg, EDIT_CATEGORY), !(cbi->wFlags & CBEXIF_CATREADONLY));
+			return TRUE;
+		}
+
+		case WM_CTLCOLORSTATIC:
+			SetBkColor((HDC)wParam, RGB(255, 255, 255));
+			return (INT_PTR)GetStockObject(WHITE_BRUSH);
+
+		case WM_COMMAND:
+		{
+			static INT noRecursion=0;
+						
+			switch (LOWORD(wParam)) {
+				if (HIWORD(wParam) == BN_CLICKED) {
+					case IDOK:
+					{	 
+						LPCBEXITEM cbi = (LPCBEXITEM)GetUserData(hDlg);
+						TCHAR szText[MAXDATASIZE];
+						INT errorPos;
+
+						if (!GetDlgItemText(hDlg, EDIT_PHONE, szText, MAXDATASIZE) || !CheckPhoneSyntax(szText, cbi->pszVal, cbi->ccVal, errorPos) || errorPos > -1) {
+							MsgErr(hDlg, TranslateT("The phone number should start with a + and consist of\nnumbers, spaces, brackets and hyphens only."));
+							/*
+							EDITBALLOONTIP btt;
+
+							btt.cbStruct = SIZEOF(btt);
+
+							btt.pszTitle = TranslateW(L"Syntax Error");
+							btt.pszText = TranslateW(L"The phone number should start with a + and consist of\nnumbers, spaces, brackets and hyphens only.");
+
+							SendDlgItemMessage(hDlg, EDIT_PHONE, EM_SHOWBALLOONTIP, NULL, (LPARAM)&btt);
+							SetDlgItemText(hDlg, EDIT_PHONE, cbi->pszVal);
+							SendDlgItemMessage(hDlg, EDIT_PHONE, EM_SETSEL, errorPos, errorPos);
+							*/
+							break;
+						}
+						// save category string
+						GetDlgItemText(hDlg, EDIT_CATEGORY, cbi->pszCat, cbi->ccCat);
+
+						// save SMS flag
+						if (IsDlgButtonChecked(hDlg, CHECK_SMS) != ((cbi->wFlags & CBEXIF_SMS) == CBEXIF_SMS)) {
+							cbi->wFlags ^= CBEXIF_SMS;
+						}
+					}
+					//fall through
+					case IDCANCEL:
+						EndDialog(hDlg, wParam);
+						break;
+				}
+								
+				case EDIT_COUNTRY:
+					if (HIWORD(wParam) != CBN_SELCHANGE) break;
+
+				case EDIT_AREA:
+				case EDIT_NUMBER:
+					if (LOWORD(wParam) != EDIT_COUNTRY && HIWORD(wParam) != EN_CHANGE) break;
+					if (noRecursion) break;
+					EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
+					{	 
+						TCHAR szPhone[MAXDATASIZE], szArea[32], szData[64];
+						INT	 nCurSel = SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_GETCURSEL, 0, 0);	 
+						UINT	nCountry = (nCurSel != CB_ERR) ? SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_GETITEMDATA, nCurSel, 0) : 0;
+
+						GetDlgItemText(hDlg, EDIT_AREA, szArea, SIZEOF(szArea));
+						GetDlgItemText(hDlg, EDIT_NUMBER, szData, SIZEOF(szData));
+						mir_sntprintf(szPhone, MAXDATASIZE, _T("+%u (%s) %s"), nCountry, szArea, szData);
+						noRecursion = 1;
+						SetDlgItemText(hDlg, EDIT_PHONE, szPhone);
+						noRecursion = 0;
+					}
+					break;
+
+				case EDIT_PHONE:
+					if (HIWORD(wParam) != EN_UPDATE) break;
+					if (noRecursion) break;
+					noRecursion = 1;
+					{	 
+						TCHAR szText[MAXDATASIZE], *pText, *pArea, *pNumber;
+						INT isValid = 1;
+						GetDlgItemText(hDlg, EDIT_PHONE, szText, SIZEOF(szText));
+						if (szText[0] != '+') isValid = 0;
+						if (isValid) {
+							INT i,country = _tcstol(szText + 1, &pText, 10);
+							if (pText - szText > 4)
+								isValid = 0;
+							else {
+								for (i = SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_GETCOUNT, 0, 0) - 1; i >= 0; i--) {
+									if (country == SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_GETITEMDATA, i, 0)) {
+										SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_SETCURSEL, i, 0);
+										break;
+									}
+								}
+							}
+							if (i < 0) isValid=0;
+						}
+						if (isValid) {
+							pArea = pText + _tcscspn(pText, _T("0123456789"));
+							pText = pArea + _tcsspn(pArea, _T("0123456789"));
+							if (*pText) {
+								*pText = '\0';
+								pNumber = pText + 1 + _tcscspn(pText + 1, _T("0123456789"));
+								SetDlgItemText(hDlg, EDIT_NUMBER, pNumber);
+							}
+							SetDlgItemText(hDlg, EDIT_AREA, pArea);
+						}
+						if (!isValid) {
+							SendDlgItemMessage(hDlg, EDIT_COUNTRY, CB_SETCURSEL, -1, 0);
+							SetDlgItemText(hDlg, EDIT_AREA, _T(""));
+							SetDlgItemText(hDlg, EDIT_NUMBER, _T(""));
+						}
+					}
+					noRecursion = 0;
+					EnableWindow(GetDlgItem(hDlg, IDOK), GetWindowTextLength(GetDlgItem(hDlg, EDIT_PHONE)));
+					break;
+			}
+			break;
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * name:	CtrlContactWndProc
+ * desc:	window procedure for the extended combobox class
+ * param:	hwnd	- handle to a extended combobox window
+ *			msg		- message to handle
+ *			wParam	- message specific
+ *			lParam	- message specific
+ * return:	message specific
+ **/
+static LRESULT CALLBACK CtrlContactWndProc(HWND hwnd, UINT msg,	WPARAM wParam, LPARAM lParam) 
+{
+	LPCBEX	cbex = (LPCBEX)GetWindowLongPtr(hwnd, 0);
+
+	switch (msg) {
+
+		/**
+		 * name:	WM_NCCREATE
+		 * desc:	is called to initiate the window creation
+		 * param:	wParam - not used
+		 *			lParam - pointer to a CREATESTRUCT
+		 *
+		 * return:	FALSE on error, TRUE if initialisation was ok
+		 **/
+		case WM_NCCREATE:
+		{
+			LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam;
+
+			if (!(cbex = (LPCBEX)mir_calloc(1*sizeof(CBEX))))
+				return FALSE;
+			SetWindowLongPtr(hwnd, 0, (LONG_PTR)cbex);
+			cbex->bLocked = 1;
+			cbex->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+			cbex->hInstance = cs->hInstance;
+			cbex->iSelectedItem = -1;
+			cbex->rect.left = cs->x;
+			cbex->rect.top = cs->y;
+			cbex->rect.right = cs->x + cs->cx;
+			cbex->rect.bottom = cs->y + cs->cy;
+			return TRUE;
+		}
+
+		/**
+		 * name:	WM_NCCREATE
+		 * desc:	is called to create all subitems
+		 * param:	wParam - not used
+		 *			lParam - not used
+		 *
+		 * return:	FALSE on error, TRUE if initialisation was ok
+		 **/
+		case WM_CREATE:
+		{
+			WORD wHeight = (WORD)(cbex->rect.bottom - cbex->rect.top);
+			WORD wWidth = 130;
+			WORD x = 0;
+
+			if (!(cbex->hBtnEdit = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
+					UINFOBUTTONCLASS, 
+					_T("none"),
+					WS_VISIBLE|WS_CHILD|WS_TABSTOP, 0, 0,
+					wWidth, wHeight,
+					hwnd,
+					NULL,
+					cbex->hInstance, NULL))) {
+				cbex->bLocked = 0;
+				return FALSE;
+			}
+			x += wWidth + 2;
+			wWidth = wHeight;
+			if (!(cbex->hBtnMenu = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
+					UINFOBUTTONCLASS,
+					NULL,
+					WS_VISIBLE|WS_CHILD|WS_TABSTOP|MBS_PUSHBUTTON|MBS_DOWNARROW,
+					x, 0,
+					wWidth, wHeight,
+					hwnd,
+					NULL,
+					cbex->hInstance, NULL))) {
+				DestroyWindow(cbex->hBtnEdit);
+				cbex->bLocked = 0;
+				return FALSE;
+			}
+			x += wWidth + 2;
+			wWidth = (WORD)(cbex->rect.right - cbex->rect.left - x - (2 * (wHeight + 2)));
+			if (!(cbex->hEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
+					_T("Edit"), 
+					NULL,
+					WS_VISIBLE|WS_CHILD|WS_TABSTOP|ES_AUTOHSCROLL,
+					x, 1,
+					wWidth,	wHeight - 2,
+					hwnd,
+					NULL,
+					cbex->hInstance, NULL))) {
+				DestroyWindow(cbex->hBtnEdit);
+				DestroyWindow(cbex->hBtnMenu);
+				cbex->bLocked = 0;
+				return FALSE;
+			}
+			x += wWidth + 2;
+			wWidth = wHeight;
+			if (!(cbex->hBtnAdd = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
+					UINFOBUTTONCLASS,
+					NULL,
+					WS_VISIBLE|WS_CHILD|WS_TABSTOP|MBS_FLAT,
+					x, 0,
+					wWidth, wHeight,
+					hwnd,
+					NULL,
+					cbex->hInstance, NULL))) {
+				DestroyWindow(cbex->hBtnEdit);
+				DestroyWindow(cbex->hBtnMenu);
+				DestroyWindow(cbex->hEdit);
+				cbex->bLocked = 0;
+				return FALSE;
+			}
+			x += wWidth + 2;
+			if (!(cbex->hBtnDel = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
+					UINFOBUTTONCLASS,
+					NULL,
+					WS_VISIBLE|WS_CHILD|WS_TABSTOP|MBS_FLAT,
+					x, 0,
+					wWidth, wHeight,
+					hwnd,
+					NULL,
+					cbex->hInstance, NULL))) {
+				DestroyWindow(cbex->hBtnEdit);
+				DestroyWindow(cbex->hBtnMenu);
+				DestroyWindow(cbex->hEdit);
+				DestroyWindow(cbex->hBtnAdd);
+				cbex->bLocked = 0;
+				return FALSE;
+			}
+
+			// set ids
+			SetWindowLongPtr(cbex->hBtnEdit, GWLP_ID, BTN_EDIT);
+			SetWindowLongPtr(cbex->hBtnMenu, GWLP_ID, BTN_MENU);
+			SetWindowLongPtr(cbex->hEdit, GWLP_ID, EDIT_VALUE);
+			SetWindowLongPtr(cbex->hBtnAdd, GWLP_ID, BTN_ADD);
+			SetWindowLongPtr(cbex->hBtnDel, GWLP_ID, BTN_DEL);
+			// set fonts & maximum edit control charachters
+			SendMessage(cbex->hEdit, WM_SETFONT, (WPARAM)cbex->hFont, NULL);
+			SendMessage(cbex->hEdit, EM_LIMITTEXT, (WPARAM)MAXDATASIZE, NULL);
+			// add tooltips
+			SendMessage(cbex->hBtnMenu, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Choose the item to display."), MBF_TCHAR);
+			SendMessage(cbex->hBtnEdit, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Edit the currently displayed item."), MBF_TCHAR);
+			SendMessage(cbex->hBtnAdd, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Add a new custom item."), MBF_TCHAR);
+			SendMessage(cbex->hBtnDel, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete the selected item."), MBF_TCHAR);
+			// reload icons
+			CtrlContactWndProc(hwnd, WM_SETICON, NULL, NULL);
+			cbex->bLocked = 0;
+			return TRUE;
+		}
+
+		/**
+		 * name:	WM_DESTROY
+		 * desc:	default destroy message, so clear up memory
+		 * param:	wParam - not used
+		 *			lParam - not used
+		 * return:	return value of DefWindowProc
+		 **/
+		case WM_DESTROY:
+			CtrlContactWndProc(hwnd, CBEXM_DELALLITEMS, NULL, NULL);
+			DestroyWindow(cbex->hBtnEdit);
+			DestroyWindow(cbex->hBtnMenu);
+			DestroyWindow(cbex->hBtnAdd);
+			DestroyWindow(cbex->hBtnDel);
+			DestroyWindow(cbex->hEdit);
+			MIR_FREE(cbex);
+			break;
+
+		/**
+		 * name:	WM_CTLCOLOREDIT
+		 * desc:	is called on a paint message for a dialog item to determine its colour scheme
+		 * param:	wParam - pointer to a HDC
+		 *			lParam - pointer to a HWND
+		 * return:	a brush
+		 **/
+		case WM_CTLCOLOREDIT:
+			if (!DB::Setting::GetByte(SET_PROPSHEET_SHOWCOLOURS, 1) || (HWND)lParam != cbex->hEdit || !cbex->pItems || cbex->iSelectedItem < 0) 
+				break;
+			return Ctrl_SetTextColour((HDC)wParam, cbex->pItems[cbex->iSelectedItem].wFlags);
+
+		case WM_CTLCOLORSTATIC:
+			if ((HWND)lParam == cbex->hEdit)
+					return (BOOL)GetSysColor(COLOR_WINDOW);
+			return FALSE;
+		/**
+		 * name:	WM_SETICON
+		 * desc:	updates the icons of this control
+		 * param:	wParam - not used
+		 *			lParam - not used
+		 * return:	always 0
+		 **/
+		case WM_SETICON:
+		{
+			HICON hIcon;
+			INT i;
+
+			hIcon = IcoLib_GetIcon(ICO_BTN_ADD);
+			SendMessage(cbex->hBtnAdd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+			SendMessage(cbex->hBtnAdd, WM_SETTEXT, NULL, (LPARAM)(hIcon ? _T("") : _T("+")));
+
+			hIcon = IcoLib_GetIcon(ICO_BTN_DELETE);
+			SendMessage(cbex->hBtnDel, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+			SendMessage(cbex->hBtnDel, WM_SETTEXT, NULL, (LPARAM)(hIcon ? _T("") : _T("-")));
+
+			if (cbex->pItems && cbex->numItems > 0) {
+				for (i = 0; i < cbex->numItems; i++) {
+					cbex->pItems[i].hIcon = IcoLib_GetIcon(cbex->pItems[i].pszIcon);
+				}
+				if (cbex->iSelectedItem >= 0 && cbex->iSelectedItem < cbex->numItems)
+					SendMessage(cbex->hBtnEdit, BM_SETIMAGE, IMAGE_ICON, (LPARAM)cbex->pItems[cbex->iSelectedItem].hIcon);
+			}
+			return 0;
+		}
+
+		case WM_COMMAND:
+			switch (LOWORD(wParam)) {
+				/**
+				 * name:	BTN_MENU
+				 * desc:	the button to dropdown the list to show all items is pressed
+				 **/
+				case BTN_MENU:
+					if (HIWORD(wParam) == BN_CLICKED) {
+						POINT pt = { 0, 0 };
+						RECT rc;
+						MENUITEMINFO mii;
+						INT i, nItems;
+						HMENU hMenu;
+
+						if (!(hMenu = CreatePopupMenu())) return 0;
+						SetFocus((HWND)lParam);
+
+						ZeroMemory(&mii, sizeof(MENUITEMINFO));
+						mii.cbSize = sizeof(MENUITEMINFO);
+						mii.fMask = MIIM_ID|MIIM_STRING|MIIM_FTYPE|MIIM_STATE;
+						mii.fType = MFT_STRING;
+
+						// insert the items
+						for (i = nItems = 0; i < cbex->numItems; i++) {
+							if ((cbex->pItems[i].wFlags & CBEXIF_DELETED) || *cbex->pItems[i].szCat == 0) continue;
+							mii.fState = (cbex->pItems[i].pszVal && *cbex->pItems[i].pszVal) ? MFS_CHECKED : MFS_UNCHECKED;
+							mii.wID = CBEXM_MENIITEMFIRST + i;
+							mii.dwTypeData = cbex->pItems[i].szCat;
+							if (!InsertMenuItem(hMenu, i, TRUE, &mii)) {
+								DestroyMenu(hMenu);
+								return 0;
+							}
+							nItems++;
+						}
+						// add separator between default and custom items
+						if (nItems > 3) {
+							mii.fMask = MIIM_FTYPE;
+							mii.fType = MFT_SEPARATOR;
+							mii.wID = 0;
+							mii.dwItemData = 0;
+							InsertMenuItem(hMenu, 3, TRUE, &mii);
+						}
+						ClientToScreen((HWND)lParam, &pt);
+						GetClientRect((HWND)lParam, &rc);
+						i = TrackPopupMenuEx(hMenu, TPM_RIGHTALIGN|TPM_RETURNCMD, pt.x + rc.right, pt.y + rc.bottom, hwnd, NULL);
+						SendMessage(cbex->hBtnMenu, BM_SETCHECK, NULL, NULL);
+						if (i >= CBEXM_MENIITEMFIRST && i < CBEXM_MENIITEMFIRST + cbex->numItems) {
+							CtrlContactWndProc(hwnd, CBEXM_SETCURSEL, (WPARAM)i - CBEXM_MENIITEMFIRST, NULL);
+						}
+						DestroyMenu(hMenu);
+						return 0;
+					}
+					break;
+
+				/**
+				 * name:	BTN_ADD
+				 * desc:	the button to add a new entry is pressed
+				 **/
+				case BTN_ADD:
+					if (HIWORD(wParam) == BN_CLICKED) {
+						DLGPROC dlgProc;
+						WORD dlgID;
+						TCHAR szCat[MAX_CAT] = { 0 };
+						TCHAR szVal[MAXDATASIZE] = { 0 };
+						CBEXITEM cbi;
+						HWND hDlgDetails;
+
+						SetFocus((HWND)lParam);
+						if (!(hDlgDetails = GetParent(GetParent(hwnd)))) return 1;
+						if (SendMessage(hDlgDetails, PSM_ISLOCKED, NULL, NULL)) return 0;
+						
+						switch (GetWindowLongPtr(hwnd, GWLP_ID)) {
+							case EDIT_PHONE:
+								dlgID = IDD_ADDPHONE;
+								dlgProc = (DLGPROC)DlgProc_Phone;
+								cbi.pszIcon = ICO_BTN_CUSTOMPHONE;
+								break;
+							case EDIT_EMAIL:
+								dlgID = IDD_ADDEMAIL;
+								dlgProc = (DLGPROC)DlgProc_EMail;
+								cbi.pszIcon = ICO_BTN_EMAIL;
+								break;
+							default:
+								return 1;
+						}
+							
+						cbi.iItem = -1;
+						cbi.wMask = CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS|CBEXIM_ICONTEXT;
+						cbi.pszCat = szCat;
+						cbi.pszVal = szVal;
+						cbi.ccCat = MAX_CAT;
+						cbi.ccVal = MAXDATASIZE;
+						cbi.wFlags = 0;
+						cbi.dwID = 0;
+
+						if (DialogBoxParam(ghInst, MAKEINTRESOURCE(dlgID), GetParent(hwnd), dlgProc, (LPARAM)&cbi) == IDOK) {
+							HANDLE hContact = NULL;
+							
+							SendMessage(hDlgDetails, PSM_GETCONTACT, NULL, (LPARAM)&hContact);
+							if (hContact) cbi.wFlags |= CTRLF_HASCUSTOM;
+							cbi.wFlags |= CTRLF_CHANGED;
+							if (SendMessage(hwnd, CBEXM_ADDITEM, NULL, (LPARAM)&cbi) > CB_ERR) {
+								SendMessage(hDlgDetails, PSM_CHANGED, NULL, NULL);
+								cbex->bIsChanged = TRUE;
+								SendMessage(hwnd, CBEXM_SETCURSEL, cbex->numItems - 1, NULL);
+							}
+						}
+						return 0;
+					}
+					break;
+
+				/**
+				 * name:	BTN_EDIT
+				 * desc:	the button to edit an existing entry is pressed
+				 **/
+				case BTN_EDIT:
+					if (HIWORD(wParam) == BN_CLICKED) {
+						DLGPROC dlgProc;
+						WORD dlgID;
+						TCHAR szCat[MAX_CAT] = { 0 };
+						TCHAR szVal[MAXDATASIZE] = { 0 };
+						CBEXITEM cbi;
+						HWND hDlgDetails;
+
+						SetFocus((HWND)lParam);
+						if (!(hDlgDetails = GetParent(GetParent(hwnd)))) return 1;
+						if (SendMessage(hDlgDetails, PSM_ISLOCKED, NULL, NULL)) return 0;
+						if (!cbex->pItems || cbex->iSelectedItem == -1) return 0;
+
+						switch (GetWindowLongPtr(hwnd, GWLP_ID)) {
+							case EDIT_PHONE:
+								dlgID = IDD_ADDPHONE;
+								dlgProc = (DLGPROC)DlgProc_Phone;
+								break;
+							case EDIT_EMAIL:
+								dlgID = IDD_ADDEMAIL;
+								dlgProc = (DLGPROC)DlgProc_EMail;
+								break;
+							default:
+								return 1;
+						}
+						cbi.iItem = cbex->iSelectedItem;
+						cbi.dwID = 0;
+						cbi.wMask = CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS;
+						cbi.pszCat = szCat;
+						cbi.pszVal = szVal;
+						cbi.ccCat = MAX_CAT;
+						cbi.ccVal = MAXDATASIZE;
+						if (!CtrlContactWndProc(hwnd, CBEXM_GETITEM, NULL, (LPARAM)&cbi)) {
+							MsgErr(hwnd, LPGENT("CRITICAL: Unable to edit current entry!\nThis should not happen!"));
+							return 1;
+						}
+
+						if (DialogBoxParam(ghInst, MAKEINTRESOURCE(dlgID), GetParent(hwnd), dlgProc, (LPARAM)&cbi) == IDOK) {
+							HANDLE hContact;
+
+							SendMessage(hDlgDetails, PSM_GETCONTACT, NULL, (LPARAM)&hContact);
+							if (hContact) cbi.wFlags |= CTRLF_HASCUSTOM;
+							cbi.wFlags |= CTRLF_CHANGED;
+							SendMessage(hwnd, CBEXM_SETITEM, NULL, (LPARAM)&cbi);
+							SendMessage(hDlgDetails, PSM_CHANGED, NULL, NULL);
+							cbex->bIsChanged = TRUE;
+						}
+						return 0;
+					}
+					break;
+
+				/**
+				 * name:	BTN_DEL
+				 * desc:	the button to delete an existing entry is pressed
+				 **/
+				case BTN_DEL:
+					if (HIWORD(wParam) == BN_CLICKED) {
+						HWND hDlgDetails;
+						MSGBOX mBox;
+						TCHAR szMsg[MAXDATASIZE];
+					
+						SetFocus((HWND)lParam);
+						if (!(hDlgDetails = GetParent(GetParent(hwnd))) ||
+							 SendMessage(hDlgDetails, PSM_ISLOCKED, NULL, NULL) ||
+							 !cbex->pItems ||
+							 cbex->iSelectedItem < 0 ||
+							 cbex->iSelectedItem >= cbex->numItems ||
+							 FAILED(mir_sntprintf(szMsg, MAXDATASIZE, TranslateT("Do you really want to delete the current selected item?\n\t%s\n\t%s"),
+								cbex->pItems[cbex->iSelectedItem].szCat, cbex->pItems[cbex->iSelectedItem].pszVal))
+				 )
+						{
+							 return 1;
+						}
+						mBox.cbSize = sizeof(MSGBOX);
+						mBox.hParent = hDlgDetails;
+						mBox.hiLogo = IcoLib_GetIcon(ICO_DLG_PHONE);
+						mBox.uType = MB_YESNO|MB_ICON_QUESTION|MB_NOPOPUP;
+						mBox.ptszTitle = TranslateT("Delete");
+						mBox.ptszMsg = szMsg;
+						if (IDYES == MsgBoxService(NULL, (LPARAM)&mBox)) {
+							// clear value for standard entry
+							if (cbex->pItems[cbex->iSelectedItem].wFlags & CBEXIF_CATREADONLY) {
+								MIR_FREE(cbex->pItems[cbex->iSelectedItem].pszVal);
+								SetWindowText(cbex->hEdit, _T(""));
+								cbex->pItems[cbex->iSelectedItem].wFlags &= ~CBEXIF_SMS;
+								cbex->pItems[cbex->iSelectedItem].wFlags |= CTRLF_CHANGED;
+							}
+							// clear values for customized database entry
+							else 
+							if (cbex->pItems[cbex->iSelectedItem].dwID != 0) {
+								MIR_FREE(cbex->pItems[cbex->iSelectedItem].pszVal);
+								*cbex->pItems[cbex->iSelectedItem].szCat = 0;
+								cbex->pItems[cbex->iSelectedItem].wFlags |= CTRLF_CHANGED|CBEXIF_DELETED;
+								CtrlContactWndProc(hwnd, CBEXM_SETCURSEL, cbex->iSelectedItem - 1, FALSE);
+							}
+							// delete default entry
+							else
+								CtrlContactWndProc(hwnd, CBEXM_DELITEM, NULL, cbex->iSelectedItem);
+
+							SendMessage(hDlgDetails, PSM_CHANGED, NULL, NULL);
+							cbex->bIsChanged = TRUE;
+						}
+						return 0;
+					}
+					break;
+
+				/**
+				 * name:	EDIT_VALUE
+				 * desc:	the edit control wants us to act
+				 **/
+				case EDIT_VALUE:
+					switch (HIWORD(wParam)) {
+						case EN_UPDATE:
+						{
+							TCHAR szVal[MAXDATASIZE] = { 0 };
+							INT ccVal;
+							HANDLE hContact;
+							HWND hDlgDetails = GetParent(GetParent(hwnd));
+							
+							EnableWindow(cbex->hBtnDel, GetWindowTextLength(cbex->hEdit) > 0);
+
+							if (SendMessage(hDlgDetails, PSM_ISLOCKED, NULL, NULL) ||
+								cbex->bLocked || 
+								!cbex->pItems || 
+								cbex->iSelectedItem < 0 || 
+								cbex->iSelectedItem >= cbex->numItems) return 1;
+
+							// get the edit control's text value and check it for syntax
+							switch (GetWindowLongPtr(hwnd, GWLP_ID)) {
+								case EDIT_PHONE:
+								{
+									INT errorPos;
+									TCHAR szEdit[MAXDATASIZE];
+
+									if (ccVal = GetWindowText(cbex->hEdit, szEdit, MAXDATASIZE)) {
+										if (!(ccVal = CheckPhoneSyntax(szEdit, szVal, MAXDATASIZE, errorPos)) || errorPos > -1) {
+											SetWindowText(cbex->hEdit, szVal);
+											SendMessage(cbex->hEdit, EM_SETSEL, errorPos, errorPos);
+										}
+									}
+									break;
+								}
+								case EDIT_EMAIL:
+									ccVal = GetWindowText(cbex->hEdit, szVal, MAXDATASIZE);
+									break;
+								default:
+									ccVal = GetWindowText(cbex->hEdit, szVal, MAXDATASIZE);
+									break;
+							}
+							
+							SendMessage(hDlgDetails, PSM_GETCONTACT, NULL, (LPARAM)&hContact);
+							if ((cbex->pItems[cbex->iSelectedItem].wFlags & CTRLF_CHANGED) && !(hContact && (cbex->pItems[cbex->iSelectedItem].wFlags & CTRLF_HASCUSTOM))) return 0;
+							
+							if (*szVal == 0 || !cbex->pItems[cbex->iSelectedItem].pszVal || _tcscmp(szVal, cbex->pItems[cbex->iSelectedItem].pszVal)) {
+								cbex->pItems[cbex->iSelectedItem].wFlags |= CTRLF_CHANGED;
+								cbex->pItems[cbex->iSelectedItem].wFlags |= (hContact ? CTRLF_HASCUSTOM : CTRLF_HASPROTO);
+								cbex->bIsChanged = TRUE;
+								InvalidateRect((HWND)lParam, NULL, TRUE);
+								SendMessage(hDlgDetails, PSM_CHANGED, NULL, NULL);
+							}
+							return 0;
+						}
+						case EN_KILLFOCUS:
+						{
+							INT ccText;
+							
+							if (!cbex->pItems || cbex->iSelectedItem < 0 || cbex->iSelectedItem >= cbex->numItems) return 1;
+							if (!(cbex->pItems[cbex->iSelectedItem].wFlags & CTRLF_CHANGED)) return 0;
+
+							if ((ccText = GetWindowTextLength(cbex->hEdit)) <= 0) {
+								if (cbex->pItems[cbex->iSelectedItem].wFlags & CBEXIF_CATREADONLY) {
+									MIR_FREE(cbex->pItems[cbex->iSelectedItem].pszVal);
+									SetWindowText(cbex->hEdit, _T(""));
+									cbex->pItems[cbex->iSelectedItem].wFlags &= ~CBEXIF_SMS;
+								}
+								else
+									CtrlContactWndProc(hwnd, CBEXM_DELITEM, NULL, cbex->iSelectedItem);
+								SendMessage(GetParent(GetParent(hwnd)), PSM_CHANGED, NULL, NULL);
+								cbex->bIsChanged = TRUE;
+							}
+							else
+							if (cbex->pItems[cbex->iSelectedItem].pszVal = (LPTSTR)mir_realloc(cbex->pItems[cbex->iSelectedItem].pszVal, (ccText + 2) * sizeof(TCHAR))) {
+								cbex->pItems[cbex->iSelectedItem].pszVal[ccText + 1] = 0;
+								GetWindowText(cbex->hEdit, cbex->pItems[cbex->iSelectedItem].pszVal, ccText + 1);
+							}
+							return 0;
+						}
+					}
+					break;
+			}
+			break;
+
+		/**
+		 * name:	CBEXM_ADDITEM
+		 * desc:	add a item to the control
+		 * param:	wParam - not used
+		 *			lParam - (LPCBEXITEM)&item
+		 * return:	CB_ERR on failure, new item index if successful
+		 **/
+		case CBEXM_ADDITEM:
+		{
+			LPCBEXITEM	pItem = (LPCBEXITEM)lParam;
+
+			if (!pItem) return FALSE;
+
+			// if an item with the id of pItem exists, change it instead of adding a new one
+			// but only if it has not been changed by the user yet.
+			if ((pItem->wMask & CBEXIM_ID) && cbex->pItems && pItem->dwID != 0) {
+				INT iIndex;
+				
+				for (iIndex = 0; iIndex < cbex->numItems; iIndex++) {
+					if (cbex->pItems[iIndex].dwID == pItem->dwID) {
+						pItem->iItem = iIndex;
+						if (cbex->pItems[iIndex].wFlags & CTRLF_CHANGED)
+							pItem->wFlags |= CTRLF_CHANGED;
+						else
+							CtrlContactWndProc(hwnd, CBEXM_SETITEM, 0, lParam);
+						return iIndex;
+					}
+				}
+			}
+
+			// add a new item to the combobox
+			if (!(cbex->pItems = (LPCBEXITEMINTERN)mir_realloc(cbex->pItems, (cbex->numItems + 1) * sizeof(CBEXITEMINTERN)))) {
+				cbex->numItems = 0;
+				return CB_ERR;
+			}
+		
+			// set the ID
+			cbex->pItems[cbex->numItems].dwID = (pItem->wMask & CBEXIM_ID) ? pItem->dwID : 0;
+
+			// set category string
+			if (!pItem->pszCat || !pItem->pszCat[0] || !mir_tcsncpy(cbex->pItems[cbex->numItems].szCat, pItem->pszCat, MAX_CAT)) {
+				mir_sntprintf(cbex->pItems[cbex->numItems].szCat, MAX_CAT, _T("%s %d"), TranslateT("Other"), ++cbex->numOther);
+			}
+
+			// set value string
+			if ((pItem->wMask & CBEXIM_VAL) && pItem->pszVal && pItem->pszVal[0])
+				cbex->pItems[cbex->numItems].pszVal = mir_tstrdup(pItem->pszVal);
+			else
+				cbex->pItems[cbex->numItems].pszVal = NULL;
+			// set icon
+			if ((pItem->wMask & CBEXIM_ICONTEXT) && pItem->pszIcon) {
+				cbex->pItems[cbex->numItems].pszIcon = pItem->pszIcon;
+				cbex->pItems[cbex->numItems].hIcon = IcoLib_GetIcon(pItem->pszIcon);
+			}
+			// set flags
+			cbex->pItems[cbex->numItems].wFlags = (pItem->wMask & CBEXIM_CAT) ? pItem->wFlags : 0;
+
+			cbex->numItems++;
+			return cbex->numItems;
+		}
+
+		/**
+		 * name:	CBEXM_SETITEM
+		 * desc:	Set an item's information of the control.
+		 *			If iItem member of CBEXITEM is -1, the currently selected item is changed.
+		 * param:	wParam - not used
+		 *			lParam - (LPCBEXITEM)&item
+		 * return:	CB_ERR on failure, new item index if successful
+		 **/
+		case CBEXM_SETITEM:
+		{
+			LPCBEXITEM	pItem = (LPCBEXITEM)lParam;
+
+			if (!PtrIsValid(pItem) || !pItem->wMask || !PtrIsValid(cbex->pItems)) return FALSE;
+			if (pItem->iItem == -1) pItem->iItem = cbex->iSelectedItem;
+			if (pItem->iItem < 0 || pItem->iItem >= cbex->numItems) return FALSE;
+
+			// set new category string
+			if (pItem->wMask & CBEXIM_CAT) {
+				// set category string
+				if (!pItem->pszCat || !pItem->pszCat[0] || !mir_tcsncpy(cbex->pItems[pItem->iItem].szCat, pItem->pszCat, SIZEOF(cbex->pItems[pItem->iItem].szCat))) 
+					mir_sntprintf(cbex->pItems[pItem->iItem].szCat, MAX_CAT, _T("%s %d\0"), TranslateT("Other"), ++cbex->numOther);
+				if (pItem->iItem == cbex->iSelectedItem)
+					SetWindowText(cbex->hBtnEdit, cbex->pItems[pItem->iItem].szCat);
+			}
+			// set new value
+			if (pItem->wMask & CBEXIM_VAL) {
+				MIR_FREE(cbex->pItems[pItem->iItem].pszVal);
+				if (pItem->pszVal && pItem->pszVal[0])
+					cbex->pItems[pItem->iItem].pszVal = mir_tstrdup(pItem->pszVal);
+				if (pItem->iItem == cbex->iSelectedItem)
+					SetWindowText(cbex->hEdit, cbex->pItems[pItem->iItem].pszVal ? cbex->pItems[pItem->iItem].pszVal : _T(""));
+			}
+
+			// set icon
+			if ((pItem->wMask & CBEXIM_ICONTEXT) && pItem->pszIcon) {
+				cbex->pItems[pItem->iItem].pszIcon = pItem->pszIcon;
+				cbex->pItems[pItem->iItem].hIcon = IcoLib_GetIcon(pItem->pszIcon);
+				if (pItem->iItem == cbex->iSelectedItem)
+					SendMessage(cbex->hBtnEdit, BM_SETIMAGE, IMAGE_ICON, (LPARAM)cbex->pItems[pItem->iItem].hIcon);
+			}
+			if (pItem->wMask & CBEXIM_FLAGS) {
+				cbex->pItems[pItem->iItem].wFlags = pItem->wFlags;
+				CtrlContactWndProc(hwnd, CBEXM_ENABLEITEM, NULL, NULL);
+			}
+			return TRUE;
+		}
+
+		/**
+		 * name:	CBEXM_GETITEM
+		 * desc:	Get an item from the control.
+		 *			If iItem member of CBEXITEM is -1, the currently selected item is returned.
+		 * param:	wParam - not used
+		 *			lParam - (LPCBEXITEM)&item
+		 * return:	CB_ERR on failure, new item index if successful
+		 **/
+		case CBEXM_GETITEM:
+		{
+			LPCBEXITEM	pItem = (LPCBEXITEM)lParam;
+
+			if (!pItem || !cbex->pItems) return FALSE;
+			
+			// try to find item by id
+			if ((pItem->wMask & CBEXIM_ID) && pItem->dwID != 0) {
+				INT i;
+
+				for (i = 0; i < cbex->numItems; i++) {
+					if (cbex->pItems[i].dwID == pItem->dwID)
+						break;
+				}
+				pItem->iItem = i;
+			}
+			else
+			if (pItem->iItem == -1) pItem->iItem = cbex->iSelectedItem;
+			if (pItem->iItem < 0 || pItem->iItem >= cbex->numItems) return FALSE;
+			
+			// return only currently selected itemindex
+			if (!pItem->wMask) return TRUE;
+			// return the unique id
+			if (pItem->wMask & CBEXIM_ID) 
+				pItem->dwID = cbex->pItems[pItem->iItem].dwID;
+			// return category string
+			if ((pItem->wMask & CBEXIM_CAT) && pItem->pszCat) {
+				if (*cbex->pItems[pItem->iItem].szCat != 0)
+					mir_tcsncpy(pItem->pszCat, cbex->pItems[pItem->iItem].szCat, pItem->ccCat - 1);
+				else
+					*pItem->pszCat = 0;
+			}
+			// return value string
+			if ((pItem->wMask & CBEXIM_VAL) && pItem->pszVal) {
+				if (cbex->pItems[pItem->iItem].pszVal)
+					mir_tcsncpy(pItem->pszVal, cbex->pItems[pItem->iItem].pszVal, pItem->ccVal - 1);
+				else
+					*pItem->pszVal = 0;
+			}
+			// return the icon
+			if (pItem->wMask & CBEXIM_ICONTEXT)
+				pItem->pszIcon = cbex->pItems[pItem->iItem].pszIcon;
+			// return the flags
+			if (pItem->wMask & CBEXIM_FLAGS)
+				pItem->wFlags = cbex->pItems[pItem->iItem].wFlags;
+			return TRUE;
+		}
+
+		/**
+		 * name:	CBEXM_DELITEM
+		 * desc:	delete an item from the control
+		 * param:	wParam - not used
+		 *			lParam - item index
+		 * return:	CB_ERR on failure, new item index if successful
+		 **/
+		case CBEXM_DELITEM:
+		{
+			if (!cbex->pItems || (INT)lParam < 0 || (INT)lParam >= cbex->numItems || (cbex->pItems[lParam].wFlags & CBEXIF_CATREADONLY))
+				return FALSE;	
+			MIR_FREE(cbex->pItems[(INT)lParam].pszVal);
+			memmove(cbex->pItems + (INT)lParam, 
+				cbex->pItems + (INT)lParam + 1,
+				(cbex->numItems - (INT)lParam - 1) * sizeof(CBEXITEMINTERN));
+			cbex->numItems--;
+			ZeroMemory(cbex->pItems + cbex->numItems, sizeof(CBEXITEMINTERN));
+			CtrlContactWndProc(hwnd, CBEXM_SETCURSEL, lParam - 1, FALSE);
+			return TRUE;
+		}
+
+		/**
+		 * name:	CBEXM_DELITEM
+		 * desc:	delete an item from the control
+		 * param:	wParam - not used
+		 *			lParam - item index
+		 * return:	CB_ERR on failure, new item index if successful
+		 **/
+		case CBEXM_DELALLITEMS:
+		{
+			INT i;
+
+			if (PtrIsValid(cbex)) {
+				if (PtrIsValid(cbex->pItems)) {
+					for (i = 0; i < cbex->numItems; i++) {
+						MIR_FREE(cbex->pItems[i].pszVal);
+					}
+					MIR_FREE(cbex->pItems);
+					cbex->pItems = NULL;
+				}
+				cbex->numItems = 0;
+				cbex->iSelectedItem = -1;
+				SetWindowText(cbex->hEdit, _T(""));
+				SetWindowText(cbex->hBtnEdit, _T(""));
+				SendMessage(cbex->hBtnEdit, WM_SETICON, NULL, NULL);
+			}
+			return TRUE;
+		}
+
+		/**
+		 * name:	CBEXM_ENABLEITEM
+		 * desc:	enables or disables the current item
+		 * param:	wParam - not used
+		 *			lParam - not used
+		 * return:	always 0
+		 **/
+		case CBEXM_ENABLEITEM:
+			if (cbex->iSelectedItem >= 0 && cbex->iSelectedItem < cbex->numItems) {
+				HANDLE hContact;
+				BOOLEAN bEnabled;
+				
+				PSGetContact(GetParent(hwnd), hContact);
+
+				bEnabled	= !hContact ||
+							(cbex->pItems[cbex->iSelectedItem].wFlags & CTRLF_HASCUSTOM) || 
+							!(cbex->pItems[cbex->iSelectedItem].wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA)) ||
+							!DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0);
+
+				EnableWindow(cbex->hBtnEdit, bEnabled);
+				EnableWindow(cbex->hBtnDel, bEnabled && GetWindowTextLength(cbex->hEdit) > 0);
+				EnableWindow(cbex->hEdit, bEnabled);
+			}
+			break;
+
+		/**
+		 * name:	CBEXM_ISCHANGED
+		 * desc:	returns whether the control contains changed values or not
+		 * param:	wParam - not used
+		 *			lParam - not used
+		 * return:	TRUE if control was changed, FALSE if nothing was edited
+		 **/
+		case CBEXM_ISCHANGED:
+			return cbex->bIsChanged;
+
+		/**
+		 * name:	CBEXM_RESETCHANGED
+		 * desc:	resets changed flag to FALSE
+		 * param:	wParam - not used
+		 *			lParam - not used
+		 * return:	always FALSE
+		 **/
+		case CBEXM_RESETCHANGED:
+			cbex->bIsChanged = 0;
+			return 0;
+
+		/**
+		 * name:	CBEXM_SETCURSEL
+		 * desc:	selects a certain item
+		 * param:	wParam - index of the item to select
+		 *			lParam - (BOOLEAN)bValid - if TRUE, the next item with a value is selected
+		 * return:	always FALSE
+		 **/
+		case CBEXM_SETCURSEL:
+		{
+			INT i;
+
+			if (!cbex->pItems) return 1;
+			if ((INT)wParam < 0 || (INT)wParam >= cbex->numItems) wParam = max(cbex->iSelectedItem, 0);
+			cbex->bLocked = 1;
+			
+			if ((BOOLEAN)lParam == TRUE) {
+				INT i = (INT)wParam;
+
+				cbex->iSelectedItem = (INT)wParam;
+				while (i < cbex->numItems) {
+					if (cbex->pItems[i].pszVal && *cbex->pItems[i].pszVal) {
+						cbex->iSelectedItem = i;
+						break;
+					}
+					i++;
+				}
+			}
+			else {
+				// search for the next none deleted item
+				for (i = (INT)wParam; i < cbex->numItems && *cbex->pItems[i].szCat == 0; i++);
+				if (i == cbex->numItems && (INT)wParam > 0) {
+					for (i = 0; i < (INT)wParam && *cbex->pItems[i].szCat == 0; i++);
+					cbex->iSelectedItem = i == (INT)wParam ? 0 : i;
+				}
+				else
+					cbex->iSelectedItem = i;
+
+			}
+			SetWindowText(cbex->hBtnEdit, cbex->pItems[cbex->iSelectedItem].szCat);
+			SetWindowText(cbex->hEdit, cbex->pItems[cbex->iSelectedItem].pszVal ? cbex->pItems[cbex->iSelectedItem].pszVal : _T(""));
+			SendMessage(cbex->hBtnEdit, BM_SETIMAGE, IMAGE_ICON, (LPARAM)cbex->pItems[cbex->iSelectedItem].hIcon);
+			CtrlContactWndProc(hwnd, CBEXM_ENABLEITEM, NULL, NULL);
+			cbex->bLocked = 0;
+			return 0;
+		}
+		case CBEXM_SORT:
+			if (cbex->numItems > 4) {
+				qsort(cbex->pItems + 3, cbex->numItems - 3, sizeof(CBEXITEMINTERN), compareProc);
+			}
+			return 0;
+
+		case WM_ERASEBKGND:
+			return 1;
+
+		case WM_SETFOCUS:
+			SetFocus(cbex->hEdit);
+			SendMessage(cbex->hEdit, EM_SETSEL, 0, (LPARAM)-1);
+			return 0;
+	}
+	return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+/**
+ * name:	CtrlContactUnLoadModule
+ * desc:	calls required operations to clean up used memory and objects
+ * param:	wParam - not used
+ *			lParam - not used
+ * return:	always 0
+ **/
+INT CtrlContactUnLoadModule()
+{
+	UnregisterClass(UINFOCOMBOEXCLASS, ghInst);
+	return 0;
+}
+
+/**
+ * name:	CtrlContactLoadModule
+ * desc:	registers window class and does some other initializations
+ * param:	none
+ * return:	always 0
+ **/
+INT CtrlContactLoadModule()
+{
+	WNDCLASSEX wc;
+	
+	ZeroMemory(&wc, sizeof(wc));
+	wc.cbSize				 = sizeof(wc);
+	wc.lpszClassName	= UINFOCOMBOEXCLASS;
+	wc.lpfnWndProc		= CtrlContactWndProc;
+	wc.hCursor				= LoadCursor(NULL, IDC_ARROW);
+	wc.cbWndExtra		 = sizeof(LPCBEX);
+	wc.hbrBackground	= (HBRUSH)GetStockObject(COLOR_WINDOW);
+	wc.style					= CS_GLOBALCLASS;
+	RegisterClassEx(&wc);
+	return 0;
+}
+
+ 
+/**
+ * name:	CtrlContactAddItemFromDB
+ * desc:	add a item read from db to the combobox
+ * param:	hCtrl			- windowhandle to extended combobox control
+ *			hIcon			- icon to use for custom items
+ *			szItem			- category text for the item
+ *			wForcedFlags	- force flag for each new entry
+ *			hContact		- handle to contact whose settings to add
+ *			pszModule		- primary module to search the setting in
+ *			pszProto		- contacts protocol module
+ *			szSettingVal	- value holding setting
+ * return:	TRUE	- if the item is not updated, because its changed flag is set
+ *			FALSE	- if item is added or updated successfully
+ **/
+INT CtrlContactAddItemFromDB(
+				HWND hCtrl,
+				LPCSTR szIcon,
+				LPTSTR szItem,
+				HANDLE hContact,
+				LPCSTR pszModule,
+				LPCSTR pszProto,
+				LPCSTR szSettingVal)
+{
+	DBVARIANT dbv;
+	CBEXITEM cbi;
+	LPTSTR sms;
+
+	cbi.pszVal = NULL;
+	cbi.dwID = hashSetting(szSettingVal);
+	cbi.wFlags = CBEXIF_CATREADONLY|DB::Setting::GetTStringCtrl(hContact, pszModule, pszModule, pszProto, szSettingVal, &dbv);
+	if (dbv.type >= DBVT_WCHAR) {
+		// no value read from database
+		if (cbi.wFlags == CBEXIF_CATREADONLY) {
+			cbi.pszVal = NULL;
+		}
+		// check the database value
+		else {
+			cbi.pszVal = dbv.ptszVal;
+			if (sms = _tcsstr(cbi.pszVal, _T(" SMS\0"))) {
+				cbi.wFlags |= CBEXIF_SMS;
+				*sms = 0;
+			}
+		}
+	}
+	cbi.pszCat = szItem;
+	cbi.iItem = -1;
+	cbi.wMask = CBEXIM_ALL;
+	cbi.pszIcon = szIcon;
+	SendMessage(hCtrl, CBEXM_ADDITEM, NULL, (LPARAM)&cbi);
+	DB::Variant::Free(&dbv);
+	return (cbi.wFlags & CTRLF_CHANGED) == CTRLF_CHANGED;
+}
+
+/**
+ * name:	CtrlContactAddMyItemsFromDB
+ * desc:	add a item read from db to the combobox
+ * param:	hCtrl			- windowhandle to extended combobox control
+ *			hIcon			- icon to use for custom items
+ *			wForcedFlags	- force flag for each new entry
+ *			hContact		- handle to contact whose settings to add
+ *			pszModule		- primary module to search the setting in
+ *			pszProto		- contacts protocol module
+ *			szFormatCat		- format for the category holding setting
+ *			szFormatVal		- format for the value holding setting
+ * return:	TRUE	- if one of the items was not updated, because its changed flag is set
+ *			FALSE	- if all items were added or updated successfully
+ **/
+INT CtrlContactAddMyItemsFromDB(
+				HWND hCtrl,
+				LPCSTR szIcon,
+				WORD wForcedFlags,
+				HANDLE hContact,
+				LPCSTR pszModule,
+				LPCSTR pszProto,
+				LPCSTR szFormatCat,
+				LPCSTR szFormatVal)
+{
+	CBEXITEM cbi;
+	DBVARIANT dbv;
+	CHAR pszSetting[MAXSETTING];
+	WORD i;
+	LPTSTR sms;
+	INT bAnyItemIsChanged = 0;
+
+	ZeroMemory(&cbi, sizeof(cbi));
+	cbi.iItem = -1;
+	cbi.wMask = CBEXIM_ALL;
+	cbi.pszIcon = szIcon;
+
+	for (i = 0;
+		SUCCEEDED(mir_snprintf(pszSetting, MAXSETTING, szFormatVal, i)) &&
+		(cbi.wFlags = DB::Setting::GetTStringCtrl(hContact, pszModule, pszModule, pszProto, pszSetting, &dbv));
+		i++)
+	{
+		// read value
+		cbi.dwID = hashSetting(pszSetting);
+		cbi.pszVal = dbv.ptszVal;
+		dbv.type = DBVT_DELETED;
+		dbv.ptszVal = NULL;
+
+		// read category
+		if (SUCCEEDED(mir_snprintf(pszSetting, MAXSETTING, szFormatCat, i))) {
+			if (cbi.wFlags & CTRLF_HASCUSTOM) {
+				if (DB::Setting::GetTString(hContact, pszModule, pszSetting, &dbv))
+					dbv.type = DBVT_DELETED;
+			}
+			else
+			if (cbi.wFlags & CTRLF_HASPROTO) {
+				if (DB::Setting::GetTString(hContact, pszProto, pszSetting, &dbv))
+					dbv.type = DBVT_DELETED;
+			}
+
+			if (dbv.type > DBVT_DELETED && dbv.ptszVal && *dbv.ptszVal) {
+				cbi.pszCat = dbv.ptszVal;
+				dbv.type = DBVT_DELETED;
+				dbv.ptszVal = NULL;
+			}
+		}
+		if (sms = _tcsstr(cbi.pszVal, _T(" SMS"))) {
+			cbi.wFlags |= CBEXIF_SMS;
+			*sms = 0;
+		}
+		cbi.wFlags |= wForcedFlags;
+		if (CB_ERR == SendMessage(hCtrl, CBEXM_ADDITEM, NULL, (LPARAM)&cbi)) 
+			break;
+		bAnyItemIsChanged |= (cbi.wFlags & CTRLF_CHANGED) == CTRLF_CHANGED;
+		if (cbi.pszCat) { 
+			mir_free(cbi.pszCat);
+			cbi.pszCat = NULL; 
+		}
+		if (cbi.pszVal) {
+			mir_free(cbi.pszVal);
+			cbi.pszVal = NULL;
+		}
+	}
+	SendMessage(hCtrl, CBEXM_SORT, NULL, NULL);
+	return bAnyItemIsChanged;
+}
+
+/**
+ * name:	CtrlContactWriteItemToDB
+ * desc:	write a item from combobox to database
+ * param:	none
+ * return:	always 0
+ **/
+INT CtrlContactWriteItemToDB(
+				HWND hCtrl,
+				HANDLE hContact,
+				LPCSTR pszModule,
+				LPCSTR pszProto,
+				LPCSTR pszSetting)
+{
+	TCHAR szVal[MAXDATASIZE];
+	CBEXITEM cbi;
+
+	if (!CtrlContactWndProc(hCtrl, CBEXM_ISCHANGED, NULL, NULL)) return 1;
+
+	cbi.wMask = CBEXIM_ID|CBEXIM_VAL|CBEXIM_FLAGS;
+	cbi.pszVal = szVal;
+	cbi.ccVal = MAXDATASIZE - 4;
+	cbi.iItem = 0;
+	cbi.dwID = hashSetting(pszSetting);
+	if (!CtrlContactWndProc(hCtrl, CBEXM_GETITEM, NULL, (LPARAM)&cbi)) return 1;
+	if (!(cbi.wFlags & CTRLF_CHANGED)) return 0;
+	if (!hContact && !(pszModule = pszProto)) return 1;
+	if (!*szVal)
+		DB::Setting::Delete(hContact, pszModule, pszSetting);
+	else {
+		if (cbi.wFlags & CBEXIF_SMS) {
+			mir_tcsncat(szVal, _T(" SMS"), SIZEOF(szVal));
+		}
+		if (DB::Setting::WriteTString(hContact, pszModule, pszSetting, szVal)) return 1;
+	}
+	cbi.wFlags &= ~CTRLF_CHANGED;
+	cbi.wMask = CBEXIM_FLAGS;
+	CtrlContactWndProc(hCtrl, CBEXM_SETITEM, NULL, (LPARAM)&cbi);
+	InvalidateRect(GetDlgItem(hCtrl, EDIT_VALUE), NULL, TRUE);
+	return 0;
+}
+
+/**
+ * name:	CtrlContactWriteMyItemsToDB
+ * desc:	write a list of custom items from combobox to database
+ * param:	none
+ * return:	always 0
+ **/
+INT CtrlContactWriteMyItemsToDB(
+				HWND hCtrl,
+				INT iFirstItem,
+				HANDLE hContact,
+				LPCSTR pszModule,
+				LPCSTR pszProto,
+				LPCSTR szFormatCat,
+				LPCSTR szFormatVal)
+{
+	CHAR pszSetting[MAXSETTING];
+	TCHAR szCat[MAX_CAT];
+	TCHAR szVal[MAXDATASIZE];
+	LPTSTR pszOther;
+	CBEXITEM cbi;
+	INT_PTR ccOther;
+	INT i = 0;
+
+	if (!CtrlContactWndProc(hCtrl, CBEXM_ISCHANGED, NULL, NULL)) return 1;
+	if (!hContact && !(pszModule = pszProto)) return 1;
+	
+	pszOther = TranslateT("Other");
+	ccOther = _tcslen(pszOther);
+	cbi.wMask = CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS;
+	cbi.pszCat = szCat;
+	cbi.ccCat = MAX_CAT;
+	cbi.pszVal = szVal;
+	cbi.ccVal = MAXDATASIZE - 4;
+	cbi.iItem = iFirstItem;
+	cbi.dwID = 0;
+	
+	while (CtrlContactWndProc(hCtrl, CBEXM_GETITEM, NULL, (LPARAM)&cbi) && cbi.iItem < 50) {
+		if (!(cbi.wFlags & CBEXIF_DELETED) && *szVal) {
+			if (cbi.wFlags & CBEXIF_SMS) {
+				mir_tcsncat(szVal, _T(" SMS"), SIZEOF(szVal));
+			}
+			mir_snprintf(pszSetting, MAXSETTING, szFormatCat, i);
+			if (*szCat && _tcsncmp(szCat, pszOther, ccOther)) {
+				if (DB::Setting::WriteTString(hContact, pszModule, pszSetting, szCat)) return 1;
+			}
+			else
+				DB::Setting::Delete(hContact, pszModule, pszSetting);
+			mir_snprintf(pszSetting, MAXSETTING, szFormatVal, i);
+			if (DB::Setting::WriteTString(hContact, pszModule, pszSetting, szVal)) return 1;
+			cbi.wFlags &= ~CTRLF_CHANGED;
+			cbi.wMask = CBEXIM_FLAGS;
+			CtrlContactWndProc(hCtrl, CBEXM_SETITEM, NULL, (LPARAM)&cbi);
+			cbi.wMask = CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS;
+			i++;
+		}
+		
+		cbi.iItem++;
+	}
+	DB::Setting::DeleteArray(hContact, pszModule, szFormatCat, i);
+	DB::Setting::DeleteArray(hContact, pszModule, szFormatVal, i);
+	InvalidateRect(GetDlgItem(hCtrl, EDIT_VALUE), NULL, TRUE);
+	return 0;
+}
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_contact.h b/plugins/UserInfoEx/src/ctrl_contact.h
new file mode 100644
index 0000000000..3a3536e523
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_contact.h
@@ -0,0 +1,86 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_contact.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_INCLUDE_
+#define _UI_CTRL_INCLUDE_
+
+/**
+ *	ctrlContact.cpp
+ **/
+
+/* contact control	v0.1.0.6+
+*/
+#define UINFOCOMBOEXCLASS	_T("UInfoComboExWndClass")
+
+// messages
+#define CBEXM_ADDITEM			 (WM_USER+4)
+#define CBEXM_GETITEM			 (WM_USER+5)
+#define CBEXM_SETITEM			 (WM_USER+6)
+#define CBEXM_DELITEM			 (WM_USER+7)
+#define CBEXM_DELALLITEMS	 (WM_USER+8)
+#define CBEXM_ISCHANGED		 (WM_USER+9)
+#define CBEXM_RESETCHANGED	(WM_USER+10)
+#define CBEXM_SETCURSEL		 (WM_USER+11)
+#define CBEXM_SORT					(WM_USER+12)
+#define CBEXM_ENABLEITEM		(WM_USER+13)
+
+// item masks
+#define CBEXIM_FLAGS				0x0001
+#define CBEXIM_CAT					0x0002
+#define CBEXIM_VAL					0x0004
+#define CBEXIM_ICONTEXT		 0x0008
+#define CBEXIM_ID					 0x0010
+#define CBEXIM_ALL					(CBEXIM_ID|CBEXIM_ICONTEXT|CBEXIM_CAT|CBEXIM_VAL|CBEXIM_FLAGS)
+
+#define CBEXIF_CATREADONLY	(CTRLF_FIRST)
+#define CBEXIF_SMS					(CTRLF_FIRST * 2)
+#define CBEXIF_DELETED			(CTRLF_FIRST * 4)
+
+typedef struct TComboExItem
+{
+	WORD		wMask;		// determines which element of this structure is valid
+	WORD		wFlags;	 // standard control flags
+	INT		 iItem;		// position of the item in the data array
+	DWORD	 dwID;		 // unique number for each setting read from db to identify it, new entries have dwID = 0
+	LPTSTR	pszCat;	 // pointer to a descriptive category string to set or retrieve for the data entry
+	WORD		ccCat;
+	LPTSTR	pszVal;
+	WORD		ccVal;
+	LPCSTR	pszIcon;
+} CBEXITEM, *LPCBEXITEM;
+
+INT CtrlContactLoadModule();
+INT CtrlContactUnLoadModule();
+INT CtrlContactAddItemFromDB(HWND hCtrl, LPCSTR szIcon, LPTSTR szItem, HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR szSettingVal);
+INT CtrlContactAddMyItemsFromDB(HWND hCtrl, LPCSTR szIcon, WORD wForcedFlags, HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR szFormatCat, LPCSTR szFormatVal);
+INT CtrlContactWriteItemToDB(HWND hCtrl, HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR pszSetting);
+INT CtrlContactWriteMyItemsToDB(HWND hCtrl, INT iFirstItem, HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR szFormatCat, LPCSTR szFormatVal);
+
+#endif /* _UI_CTRL_INCLUDE_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_edit.cpp b/plugins/UserInfoEx/src/ctrl_edit.cpp
new file mode 100644
index 0000000000..19e018f8cb
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_edit.cpp
@@ -0,0 +1,381 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_edit.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_edit.h"
+
+/**
+ * This function creates a CEditCtrl object. 
+ *
+ * @param	hDlg			- HWND of the owning propertysheet page
+ * @param	idCtrl			- the ID of the dialog control to associate with this class's instance
+ * @param	pszSetting		- the database setting to be handled by this class
+ * @param	dbType			- the expected data type of the setting
+ *
+ * @return	This static method returns the pointer of the created object.
+ **/
+CBaseCtrl* CEditCtrl::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE dbType)
+{
+	CEditCtrl *ctrl = NULL;
+
+	ctrl = new CEditCtrl(hDlg, idCtrl, USERINFO, pszSetting);
+	if (ctrl) {
+		ctrl->_dbType = dbType;
+	}
+	return (ctrl);
+}
+
+/**
+ * This function creates a CEditCtrl object. 
+ *
+ * @param	hDlg			- HWND of the owning propertysheet page
+ * @param	idCtrl			- the ID of the dialog control to associate with this class's instance
+ * @param	pszModule		- the database module to be handled by this class
+ * @param	pszSetting		- the database setting to be handled by this class
+ * @param	dbType			- the expected data type of the setting
+ *
+ * @return	This static method returns the pointer of the created object.
+ **/
+CBaseCtrl* CEditCtrl::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting, BYTE dbType)
+{
+	CEditCtrl *ctrl = NULL;
+
+	ctrl = new CEditCtrl(hDlg, idCtrl, pszModule, pszSetting);
+	if (ctrl) {
+		ctrl->_dbType = dbType;
+	}
+	return (ctrl);
+}
+
+/**
+ *
+ *
+ **/
+CEditCtrl::CEditCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting)
+	: CBaseCtrl(hDlg, idCtrl, pszModule, pszSetting)
+{
+	SendDlgItemMessage(hDlg, idCtrl, EM_LIMITTEXT, 0x7fFFffFF, 0L);
+}
+
+/**
+ * This method deletes the class object
+ * and all allocated memory of its members.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID CEditCtrl::Release()
+{
+	delete this;
+}
+
+/*
+ * 
+ *
+ */
+VOID CEditCtrl::OnReset()
+{
+}
+
+
+/**
+ * This method controls the changed bit for the control.
+ *
+ * @param	hCtrl			- HWND of the combobox
+ * @param	hContact		- HANDLE of the contact whose timezone to select
+ * @param	pszProto		- the contact's protocol
+ *
+ * @return	nothing
+ **/
+BOOL CEditCtrl::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+	if (!_Flags.B.hasChanged)
+	{
+		DBVARIANT dbv;
+		TCHAR szText[64];
+
+		_Flags.B.hasCustom = _Flags.B.hasProto = _Flags.B.hasMeta = 0;
+		_Flags.W |= DB::Setting::GetTStringCtrl(hContact, _pszModule, _pszModule, pszProto, _pszSetting, &dbv);
+	
+		EnableWindow(_hwnd, 
+			!hContact || _Flags.B.hasCustom || !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0));	
+
+		MIR_FREE(_pszValue);
+		switch (dbv.type) 
+		{
+		case DBVT_BYTE:
+			_itot_s(dbv.bVal, szText, SIZEOF(szText), 10);
+			SetWindowText(_hwnd, szText);
+			_pszValue = mir_tcsdup(szText);
+			break;
+
+		case DBVT_WORD:
+			_itot_s(dbv.wVal, szText, SIZEOF(szText), 10);
+			SetWindowText(_hwnd, szText);
+			_pszValue = mir_tcsdup(szText);
+			break;
+
+		case DBVT_DWORD:
+			_itot_s(dbv.dVal, szText, SIZEOF(szText), 10);
+			SetWindowText(_hwnd, szText);
+			_pszValue = mir_tcsdup(szText);
+			break;
+
+		case DBVT_TCHAR:
+			if (dbv.ptszVal)
+			{
+				SetWindowText(_hwnd, dbv.ptszVal);
+				_pszValue = dbv.ptszVal;
+				break;
+			}
+		
+		default:
+			SetWindowText(_hwnd, _T(""));
+			DB::Variant::Free(&dbv);
+			break;
+		}
+		_Flags.B.hasChanged = 0;
+	}
+	return _Flags.B.hasChanged;
+}
+
+/**
+ * This method writes the control's information to database
+ *
+ * @param	hContact		- HANDLE of the contact whose timezone to select
+ * @param	pszProto		- the contact's protocol
+ *
+ * @return	nothing
+ **/
+VOID CEditCtrl::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+	if (_Flags.B.hasChanged)
+	{
+		const char* pszModule = hContact ? _pszModule : pszProto;
+
+		if (_Flags.B.hasCustom || !hContact)
+		{
+			DWORD cch = GetWindowTextLength(_hwnd);
+
+			if (cch > 0)
+			{
+				LPTSTR val = (LPTSTR)mir_alloc((cch + 1) * sizeof(TCHAR));
+
+				if (GetWindowText(_hwnd, val, cch + 1) > 0) 
+				{
+					DBVARIANT dbv;
+
+					dbv.type = _dbType;
+					switch (_dbType) 
+					{
+					case DBVT_BYTE:
+						dbv.bVal = (BYTE)_tcstol(val, NULL, 10);
+						break;
+
+					case DBVT_WORD:
+						dbv.wVal = (WORD)_tcstol(val, NULL, 10);
+						break;
+
+					case DBVT_DWORD:
+						dbv.dVal = (DWORD)_tcstol(val, NULL, 10);
+						break;
+
+					case DBVT_TCHAR:
+						dbv.ptszVal = val;
+						break;
+
+					default:
+						dbv.type = DBVT_DELETED;
+
+					}
+					if (dbv.type != DBVT_DELETED)
+					{
+						if (!DB::Setting::WriteVariant(hContact, pszModule, _pszSetting, &dbv))
+						{
+							if (!hContact)
+							{
+								_Flags.B.hasCustom = 0;
+								_Flags.B.hasProto = 1;
+							}
+							_Flags.B.hasChanged = 0;
+							
+							// save new value
+							MIR_FREE(_pszValue);
+							_pszValue = val;
+							val = NULL;
+						}
+					}
+				}
+				MIR_FREE(val);
+			}
+		}
+		if (_Flags.B.hasChanged)
+		{
+			DB::Setting::Delete(hContact, pszModule, _pszSetting);
+
+			_Flags.B.hasChanged = 0;
+
+			OnInfoChanged(hContact, pszProto);
+		}			
+		InvalidateRect(_hwnd, NULL, TRUE);
+	}
+}
+
+/**
+ * The user changed information stored in the control.
+ *
+ * @return	nothing
+ **/
+VOID CEditCtrl::OnChangedByUser(WORD wChangedMsg)
+{
+	if ((wChangedMsg == EN_UPDATE) || (wChangedMsg == EN_CHANGE))
+	{
+		const int	cch = GetWindowTextLength(_hwnd);
+
+		_Flags.B.hasChanged = mir_tcslen(_pszValue) != cch;
+		_Flags.B.hasCustom = (cch > 0);
+
+		if (!_Flags.B.hasChanged && _Flags.B.hasCustom) {
+			BOOLEAN		need_free = 0;
+			LPTSTR		szText;
+		
+			__try 
+			{
+				szText = (LPTSTR)alloca((cch + 1) * sizeof(TCHAR));
+			}
+			__except(EXCEPTION_EXECUTE_HANDLER) 
+			{
+				szText = (LPTSTR)mir_alloc((cch + 1) * sizeof(TCHAR));
+				need_free = 1;
+			}
+
+			GetWindowText(_hwnd, szText, cch + 1);
+			_Flags.B.hasChanged = mir_tcscmp(_pszValue, szText);
+
+			if (need_free) {
+				MIR_FREE(szText);
+			}
+		}
+		InvalidateRect(_hwnd, NULL, TRUE);
+
+		if (_Flags.B.hasChanged) {
+			SendMessage(GetParent(GetParent(_hwnd)), PSM_CHANGED, 0, 0);
+		}
+	}
+}
+
+/**
+ * Opens the url given in a editbox in the users default browser
+ **/
+VOID CEditCtrl::OpenUrl()
+{
+	INT lenUrl = 1 + Edit_GetTextLength(_hwnd);
+	LPSTR szUrl;
+	BOOLEAN need_free = 0;
+
+	__try 
+	{
+		szUrl = (LPSTR)alloca((8 + lenUrl) * sizeof(CHAR));
+	}
+	__except(EXCEPTION_EXECUTE_HANDLER) 
+	{
+		szUrl = (LPSTR)mir_alloc((8 + lenUrl) * sizeof(CHAR));
+		need_free = 1;
+	}
+	if (szUrl && (GetWindowTextA(_hwnd, szUrl, lenUrl) > 0))
+	{
+		CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+	}
+	if (need_free)
+	{
+		MIR_FREE(szUrl);
+	}
+}
+
+LRESULT CEditCtrl::LinkNotificationHandler(ENLINK* lnk)
+{
+	switch (lnk->msg)
+	{
+	case WM_SETCURSOR:
+		{
+			SetCursor(LoadCursor(NULL, IDC_HAND));
+			SetWindowLongPtr(GetParent(_hwnd), DWLP_MSGRESULT, TRUE);
+		}
+		return TRUE;
+
+	case WM_LBUTTONUP: 
+		{
+			if (lnk)
+			{
+				TEXTRANGE tr;
+				BOOLEAN need_free = 0;
+
+				// do not call function if user selected some chars of the url string
+				SendMessage(_hwnd, EM_EXGETSEL, NULL, (LPARAM) &tr.chrg);
+				if (tr.chrg.cpMax == tr.chrg.cpMin)
+				{
+					// retrieve the url string
+					tr.chrg = lnk->chrg;
+
+					__try 
+					{
+						tr.lpstrText = (LPTSTR)alloca((tr.chrg.cpMax - tr.chrg.cpMin + 8) * sizeof(TCHAR));
+					}
+					__except(EXCEPTION_EXECUTE_HANDLER) 
+					{
+						tr.lpstrText = (LPTSTR)mir_alloc((tr.chrg.cpMax - tr.chrg.cpMin + 8) * sizeof(TCHAR));
+						need_free = 1;
+					}
+					if (tr.lpstrText && (SendMessage(_hwnd, EM_GETTEXTRANGE, NULL, (LPARAM)&tr) > 0))
+					{
+						if (_tcschr(tr.lpstrText, '@') != NULL && _tcschr(tr.lpstrText, ':') == NULL && _tcschr(tr.lpstrText, '/') == NULL) 
+						{
+							 MoveMemory(tr.lpstrText + (7*sizeof(TCHAR)), tr.lpstrText, (tr.chrg.cpMax - tr.chrg.cpMin + 1)*sizeof(TCHAR));
+							 CopyMemory(tr.lpstrText, _T("mailto:"), (7*sizeof(TCHAR)));
+						}
+
+						LPSTR pszUrl = mir_t2a(tr.lpstrText);
+						if (pszUrl)
+						{
+							CallService(MS_UTILS_OPENURL, 1, (LPARAM)pszUrl);
+							mir_free(pszUrl);
+						}
+					}
+					if (need_free)
+					{
+						MIR_FREE(tr.lpstrText);
+					}
+				}
+			}
+		}
+	}
+	return FALSE;
+}
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_edit.h b/plugins/UserInfoEx/src/ctrl_edit.h
new file mode 100644
index 0000000000..15a69a5160
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_edit.h
@@ -0,0 +1,80 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_edit.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_EDIT_INCLUDE_
+#define _UI_CTRL_EDIT_INCLUDE_
+
+#include "ctrl_base.h"
+
+/**
+ *
+ **/
+class CEditCtrl : public CBaseCtrl
+{
+	BYTE	_dbType;
+	
+	/**
+	 * Private constructure is to force to use static member 'Create' 
+	 * as the default way of attaching a new object to the window control.
+	 *
+	 * @param	 hCtrl		 - the window handle of the control to 
+	 *											associate this class's instance with
+	 * @param	 pszSetting - the database setting to be handled by this control
+	 *
+	 * @return	nothing
+	 **/
+	CEditCtrl(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting);
+
+public:
+
+	/**
+	 *
+	 *
+	 **/
+	static FORCEINLINE CEditCtrl* GetObj(HWND hCtrl) 
+		{ return (CEditCtrl*) GetUserData(hCtrl); }
+	static FORCEINLINE CEditCtrl* GetObj(HWND hDlg, WORD idCtrl)
+		{ return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+	static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting, BYTE dbType);
+	static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszModule, LPCSTR pszSetting, BYTE dbType);
+
+	virtual VOID	Release();
+	virtual VOID	OnReset();
+	virtual BOOL	OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+	virtual VOID	OnApply(HANDLE hContact, LPCSTR pszProto);
+	virtual VOID	OnChangedByUser(WORD wChangedMsg);
+
+	VOID		OpenUrl();
+	LRESULT LinkNotificationHandler(ENLINK* lnk);
+	
+};
+
+#endif /* _UI_CTRL_EDIT_INCLUDE_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ctrl_tzcombo.cpp b/plugins/UserInfoEx/src/ctrl_tzcombo.cpp
new file mode 100644
index 0000000000..26dfbfee32
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_tzcombo.cpp
@@ -0,0 +1,306 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_tzcombo.cpp $
+Revision       : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "svc_timezone.h"
+#include "ctrl_tzcombo.h"
+
+
+static INT_PTR EnumNamesProc(CTimeZone *pTimeZone, INT index, LPARAM lParam)
+{
+	if (pTimeZone && pTimeZone->ptszDisplay)
+	{
+		INT added = ComboBox_AddString((HWND)lParam, pTimeZone->ptszDisplay);
+		if (SUCCEEDED(added)) 
+		{
+			if (FAILED(ComboBox_SetItemData((HWND)lParam, added, pTimeZone))) 
+			{
+				ComboBox_DeleteString((HWND)lParam, added);
+			}
+		}
+	}
+	return 0;
+}
+
+/**
+ * This functions fills a combobox given by @hCtrl with
+ * all items of the global timezone manager
+ *
+ * @param	hDlg			- HWND of the owning propertysheet page
+ * @param	idCtrl			- the ID of the control to associate with this class's instance
+ * @param	pszSetting		- the database setting to be handled by this class
+ *
+ * @return	CTzCombo*
+ **/
+CBaseCtrl* CTzCombo::CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+{
+	CTzCombo *ctrl = NULL;
+	HWND hCtrl = GetDlgItem(hDlg, idCtrl);
+
+	ctrl = new CTzCombo(hDlg, idCtrl, pszSetting);
+	if (ctrl) {
+		//use new core tz interface
+		if(tmi.prepareList) {
+			//set the adress of our timezone handle as itemdata
+			//caller can obtain the handle htz to extract all relevant information
+			ctrl->_curSel = 0;
+			tmi.prepareList(NULL, hCtrl, TZF_PLF_CB);
+		}
+		//fallback use old UIEX method
+		else {
+			ctrl->_curSel = ComboBox_AddString(hCtrl, TranslateT("<Unspecified>"));
+			if (SUCCEEDED(ctrl->_curSel)) {
+				ComboBox_SetItemData(hCtrl, ctrl->_curSel, NULL);
+			}
+			ComboBox_SetCurSel(hCtrl, ctrl->_curSel);
+			EnumTimeZones(EnumNamesProc, (LPARAM)hCtrl);
+		}
+	}
+	return (ctrl);
+}
+
+/**
+ *
+ *
+ **/
+CTzCombo::CTzCombo() : CBaseCtrl()
+{
+	_curSel = CB_ERR;
+}
+
+/**
+ *
+ *
+ **/
+CTzCombo::CTzCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting)
+	: CBaseCtrl(hDlg, idCtrl, pszSetting)
+{
+	_curSel = CB_ERR;
+}
+
+/**
+ * This method does a binary search in the sorted
+ * ComboBox for the item index. (old UIEX method)
+ *
+ * @param	pTimeZone		- CTimeZone compobox item data.
+ *
+ * @retval	CB_ERR			- item not found
+ * @retval	0...n			- index of the combobox item
+ **/
+INT CTzCombo::Find(CTimeZone *pTimeZone) const
+{
+	INT nItemIndex;
+	INT nItemCount = ComboBox_GetCount(_hwnd);
+
+	for (nItemIndex = 0; nItemIndex < nItemCount; nItemIndex++)
+	{
+		if (pTimeZone == (CTimeZone *)ComboBox_GetItemData(_hwnd, nItemIndex))
+			return nItemIndex;
+	}
+	return CB_ERR;
+}
+
+/**
+ * This method does a binary search in the sorted
+ * ComboBox for the item index. (new core tz interface)
+ *
+ * @param	pTimeZone		- LPTIME_ZONE_INFORMATION to find.
+ *
+ * @retval	CB_ERR			- item not found
+ * @retval	0...n			- index of the combobox item
+ **/
+INT CTzCombo::Find(LPTIME_ZONE_INFORMATION pTimeZone) const
+{
+	INT nItemIndex;
+	INT nItemCount = ComboBox_GetCount(_hwnd);
+
+	for (nItemIndex = 0; nItemIndex < nItemCount; nItemIndex++) {
+		if (pTimeZone == tmi.getTzi((HANDLE)ComboBox_GetItemData(_hwnd, nItemIndex)))
+			return nItemIndex;
+	}
+	return CB_ERR;
+}
+
+/**
+ * This functions removes the user data from a combobox.
+ *
+ * @param	hCtrl	-	HWND of the combobox
+ *
+ * @return	nothing
+ **/
+VOID CTzCombo::Release()
+{
+	delete this;
+}
+
+/**
+ * This functions selects the combobox item which matches the contact's timezone.
+ *
+ * @param	hCtrl		- HWND of the combobox
+ * @param	hContact	- HANDLE of the contact whose timezone to select
+ * @param	pszProto	- the contact's protocol (not used by new core tz interface)
+ *
+ * @return	_Flags.B.hasChanged member
+ **/
+BOOL CTzCombo::OnInfoChanged(HANDLE hContact, LPCSTR pszProto)
+{
+	if (!_Flags.B.hasChanged) {
+		//use new core tz interface to change the cbbox
+		if(tmi.storeListResults) {
+			_curSel = CB_ERR;
+//			_curSel = tmi.selectListItem(hContact, _hwnd, TZF_PLF_CB);
+										//dident work well, coz no fallback to proto setting.
+										//we use saver way by getTziByContact
+			LPTIME_ZONE_INFORMATION pTimeZone;
+			pTimeZone = tmi.getTziByContact(hContact);
+			ComboBox_SetCurSel(_hwnd, Find(pTimeZone));
+			_curSel = ComboBox_GetCurSel(_hwnd);
+		}
+		//fallback use old UIEX method
+		else {
+			CTimeZone *pTimeZone;
+			_curSel		= CB_ERR;
+			_Flags.W	= GetContactTimeZoneCtrl(hContact, pszProto, &pTimeZone);
+			if (_Flags.W) {
+				ComboBox_SetCurSel(_hwnd, Find(pTimeZone));
+				_curSel = ComboBox_GetCurSel(_hwnd);
+			}
+			if (_curSel == CB_ERR) {
+				ComboBox_SetCurSel(_hwnd, NULL);
+				_curSel = ComboBox_GetCurSel(_hwnd);
+			}
+		}
+		SendMessage(GetParent(_hwnd), WM_TIMER, 0, 0);
+	}
+	return _Flags.B.hasChanged;
+}
+
+/**
+ * This method writes the combobox's item as the contact's timezone.
+ *
+ * @param		hContact	- HANDLE of the contact whose timezone to select
+ * @param		pszProto	- the contact's protocol (not used by new core tz interface)
+ *
+ * @return	nothing
+ **/
+VOID CTzCombo::OnApply(HANDLE hContact, LPCSTR pszProto)
+{
+	if (_Flags.B.hasChanged)
+	{
+		const char* pszModule = hContact ? USERINFO : pszProto;
+		if (_Flags.B.hasCustom || !hContact) {
+			//use new core tz interface
+			if(tmi.storeListResults) {
+				tmi.storeListResults(hContact, _hwnd, TZF_PLF_CB);
+				if (!hContact) {
+					_Flags.B.hasCustom = 0;
+					_Flags.B.hasProto = 1;
+				}
+				_Flags.B.hasChanged = 0;
+			}
+			//fallback use old UIEX method
+			else {
+				const CTimeZone* pTimeZone = (CTimeZone*)ComboBox_GetItemData(_hwnd, _curSel);
+				if (PtrIsValid(pTimeZone)) {
+					DB::Setting::WriteTString(hContact, USERINFO, SET_CONTACT_TIMEZONENAME, pTimeZone->ptszName);
+					DB::Setting::WriteByte(hContact, pszModule, SET_CONTACT_TIMEZONE, pTimeZone->ToMirandaTimezone());
+
+					if (!hContact) {
+						_Flags.B.hasCustom = 0;
+						_Flags.B.hasProto = 1;
+					}
+					_Flags.B.hasChanged = 0;
+				}
+			}
+		}
+
+		if (_Flags.B.hasChanged)
+		{
+			DB::Setting::Delete(hContact, USERINFO, SET_CONTACT_TIMEZONENAME);
+			DB::Setting::Delete(hContact, USERINFO, SET_CONTACT_TIMEZONEINDEX);
+			DB::Setting::Delete(hContact, pszModule, SET_CONTACT_TIMEZONE);
+
+			_Flags.B.hasChanged = 0;
+			OnInfoChanged(hContact, pszProto);
+		}
+		InvalidateRect(_hwnd, NULL, TRUE);
+	}
+}
+
+/**
+ * The user changed combobox selection, so mark it changed.
+ *
+ * @return	nothing
+ **/
+VOID CTzCombo::OnChangedByUser(WORD wChangedMsg)
+{
+	if (wChangedMsg == CBN_SELCHANGE) {
+		INT c = ComboBox_GetCurSel(_hwnd);
+
+		if (_curSel != c) {
+			if (!_Flags.B.hasChanged) {
+				_Flags.B.hasChanged = 1;
+				_Flags.B.hasCustom = 1;
+				SendMessage(GetParent(GetParent(_hwnd)), PSM_CHANGED, 0, 0);
+			}
+			_curSel = c;
+			SendMessage(GetParent(_hwnd), WM_TIMER, 0, 0);
+		}
+	}
+}
+
+/**
+ * This method fills @szTime with the current time
+ * according to the combobox's selected timezone.
+ *
+ * @param		szTime		- string to fill with the current time
+ * @param		cchTime		- number of characters the string can take
+ *
+ * @return	nothing
+ **/
+VOID CTzCombo::GetTime(LPTSTR szTime, WORD cchTime)
+{
+	//use new core tz interface
+	if(tmi.printDateTime) {
+		tmi.printDateTime((HANDLE)ComboBox_GetItemData(_hwnd, _curSel), _T("t"), szTime, cchTime, 0);
+	}
+	//fallback use old UIEX method
+	else {
+		const CTimeZone *pTimeZone = (CTimeZone*)ComboBox_GetItemData(_hwnd, _curSel);
+		if (PtrIsValid(pTimeZone)) {
+			MTime now;
+			TIME_ZONE_INFORMATION tzi = *pTimeZone;
+
+			now.GetTimeUTC();
+			now.UTCToTzSpecificLocal(&tzi);
+			now.TimeFormat(szTime, cchTime);
+		}
+		else mir_tcscpy(szTime, _T("--:--"));
+	}
+}
diff --git a/plugins/UserInfoEx/src/ctrl_tzcombo.h b/plugins/UserInfoEx/src/ctrl_tzcombo.h
new file mode 100644
index 0000000000..3034f57481
--- /dev/null
+++ b/plugins/UserInfoEx/src/ctrl_tzcombo.h
@@ -0,0 +1,69 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ctrl_tzcombo.h $
+Revision       : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_CTRL_TZ_COMBO_INCLUDE_
+#define _UI_CTRL_TZ_COMBO_INCLUDE_
+
+#include "ctrl_base.h"
+#include "svc_timezone.h"
+#include "svc_timezone_old.h"
+
+/**
+ *
+ **/
+class CTzCombo : public CBaseCtrl
+{
+	INT _curSel;											//selectet combo index
+	
+	CTzCombo();
+	CTzCombo(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+
+	INT Find(CTimeZone *pTimeZone) const;					//old UIEX method
+	INT Find(LPTIME_ZONE_INFORMATION pTimeZone) const;		//new core tz interface
+
+public:
+
+	static FORCEINLINE CTzCombo* GetObj(HWND hCtrl) 
+		{ return (CTzCombo*) GetUserData(hCtrl); }
+	static FORCEINLINE CTzCombo* GetObj(HWND hDlg, WORD idCtrl)
+		{ return GetObj(GetDlgItem(hDlg, idCtrl)); }
+
+	static CBaseCtrl* CreateObj(HWND hDlg, WORD idCtrl, LPCSTR pszSetting);
+
+	virtual VOID	Release();
+//	virtual VOID	OnReset() {};
+	virtual BOOL	OnInfoChanged(HANDLE hContact, LPCSTR pszProto);
+	virtual VOID	OnApply(HANDLE hContact, LPCSTR pszProto);
+	virtual VOID	OnChangedByUser(WORD wChangedMsg);
+
+	VOID			GetTime(LPTSTR szTime, WORD cchTime);
+};
+
+#endif /* _UI_CTRL_TZ_COMBO_INCLUDE_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/dlg_anniversarylist.cpp b/plugins/UserInfoEx/src/dlg_anniversarylist.cpp
new file mode 100644
index 0000000000..1b0cd689da
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_anniversarylist.cpp
@@ -0,0 +1,1120 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_anniversarylist.cpp $
+Revision       : $Revision: 202 $
+Last change on : $Date: 2010-09-24 23:46:57 +0400 (Пт, 24 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "svc_gender.h"
+#include "svc_reminder.h"
+#include "dlg_anniversarylist.h"
+
+#include "m_message.h"
+#include "m_email.h"
+
+#define IsLeap(wYear)	(!(((wYear) % 4 != 0) || (((wYear) % 100 == 0) && ((wYear) % 400 != 0))))
+
+class CAnnivList;
+
+static CAnnivList *gpDlg = NULL;
+
+/***********************************************************************************************************
+ * class CAnnivList
+ ***********************************************************************************************************/
+
+class CAnnivList
+{
+	HWND		_hDlg;
+	HWND		_hList;
+	SIZE		_sizeMin;
+	RECT		_rcWin;
+	SHORT		_sortOrder;
+	INT			_sortHeader;
+	INT			_curSel;
+	INT			_numRows;
+	BYTE		_bRemindEnable;
+	HANDLE		_mHookExit;
+	bool		_wmINIT;
+
+	typedef INT (CALLBACK* CMPPROC)(LPARAM, LPARAM	LPARAM);
+
+	enum EColumn 
+	{
+		COLUMN_ETA = 0,
+		COLUMN_CONTACT,
+		COLUMN_PROTO,
+		COLUMN_AGE,
+		COLUMN_DESC,
+		COLUMN_DATE,
+	};
+
+	enum EFilter 
+	{
+		FILTER_ALL = 0,
+		FILTER_BIRTHDAY,
+		FILTER_ANNIV,
+		FILTER_DISABLED_REMINDER
+	};
+
+	struct CFilter 
+	{
+		WORD	wDaysBefore;
+		BYTE	bFilterIndex;
+		LPSTR	pszProto;
+		LPTSTR	pszAnniv;
+
+		CFilter() 
+		{
+			wDaysBefore		= (WORD)-1;
+			bFilterIndex	= 0;
+			pszProto		= NULL;
+			pszAnniv		= NULL;
+		}
+	} _filter;
+
+	struct CItemData 
+	{
+		HANDLE		_hContact;
+		MAnnivDate*	_pDate;
+		WORD		_wDaysBefore;
+		BYTE		_wReminderState;
+
+		CItemData(HANDLE hContact, MAnnivDate &date) 
+		{
+			_hContact = hContact;
+			_wReminderState = date.RemindOption();
+			_wDaysBefore = date.RemindOffset();
+			_pDate = new MAnnivDate(date);
+		}
+
+		~CItemData() 
+		{
+			if (_pDate) 
+			{
+				// save changes
+				if (_wReminderState != _pDate->RemindOption() || _wDaysBefore != _pDate->RemindOffset()) 
+				{
+					_pDate->RemindOffset(_wDaysBefore);
+					_pDate->RemindOption(_wReminderState);
+					_pDate->DBWriteReminderOpts(_hContact);
+				}
+				delete _pDate;
+				_pDate = NULL;
+			}
+		}
+	};
+
+	/**
+	 * This class handles the movement of child controls on size change.
+	 **/
+	class CAnchor 
+	{
+	public:
+		enum EAnchor
+		{
+			ANCHOR_LEFT		= 1,
+			ANCHOR_RIGHT	= 2,
+			ANCHOR_TOP		= 4,
+			ANCHOR_BOTTOM	= 8,
+			ANCHOR_ALL		= ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_TOP | ANCHOR_BOTTOM
+		};
+
+	private:
+		WINDOWPOS*	_wndPos;
+		HDWP		_hdWnds;
+		RECT		_rcParent;
+
+		VOID _ScreenToClient(HWND hWnd, LPRECT rc) 
+		{
+			POINT pt = { rc->left, rc->top };
+			
+			ScreenToClient(hWnd, &pt);
+			rc->right	+= pt.x - rc->left;
+			rc->bottom	+= pt.y - rc->top;
+			rc->left	 = pt.x;
+			rc->top		 = pt.y;
+		}
+
+		VOID _MoveWindow(HWND hWnd, INT anchors) 
+		{
+			if (!(_wndPos->flags & SWP_NOSIZE)) 
+			{
+				RECT rcc = _CalcPos(hWnd, anchors);
+				MoveWindow(hWnd, rcc.left, rcc.top, rcc.right - rcc.left, rcc.bottom - rcc.top, FALSE);
+			}
+		}
+
+		RECT _CalcPos(HWND hWnd, INT anchors) 
+		{
+			RECT rcc;
+
+			GetWindowRect(hWnd, &rcc);
+			_ScreenToClient(_wndPos->hwnd, &rcc);
+			if (!(_wndPos->flags & SWP_NOSIZE)) 
+			{
+				// calculate difference between new and old size
+				const INT cx = _wndPos->cx - _rcParent.right + _rcParent.left;
+				const INT cy = _wndPos->cy - _rcParent.bottom + _rcParent.top;
+
+				if (cx != 0 || cy != 0) 
+				{
+					// move client rect points to the desired new position
+					if (!(anchors & ANCHOR_LEFT) || (anchors & ANCHOR_RIGHT))
+					{
+						rcc.right += cx;
+					}
+					if (!(anchors & ANCHOR_TOP) || (anchors & ANCHOR_BOTTOM))
+					{
+						rcc.bottom += cy;
+					}
+					if ((anchors & ANCHOR_RIGHT) && (!(anchors & ANCHOR_LEFT)))
+					{
+						rcc.left += cx;
+					}
+					if ((anchors & ANCHOR_BOTTOM) && (!(anchors & ANCHOR_TOP)))
+					{
+						rcc.top += cy;
+					}
+				}
+			}
+			return rcc;
+		}
+
+	public:
+		CAnchor(WINDOWPOS* wndPos, SIZE minSize) 
+		{
+			GetWindowRect(wndPos->hwnd, &_rcParent);
+			if (wndPos->cx < minSize.cx) {
+				wndPos->cx = minSize.cx;
+			}
+			if (wndPos->cy < minSize.cy) {
+				wndPos->cy = minSize.cy;
+			}
+			_wndPos = wndPos;
+			_hdWnds = BeginDeferWindowPos(2);
+		}
+
+		~CAnchor() 
+		{
+			EndDeferWindowPos(_hdWnds);
+		}
+
+		VOID MoveCtrl(WORD idCtrl, INT anchors) 
+		{
+			if (!(_wndPos->flags & SWP_NOSIZE)) 
+			{
+				HWND hCtrl = GetDlgItem(_wndPos->hwnd, idCtrl);
+				RECT rcc = _CalcPos(hCtrl, anchors);
+				_hdWnds = DeferWindowPos(
+						_hdWnds,					//HDWP hWinPosInfo
+						hCtrl,						//HWND hWnd
+						HWND_NOTOPMOST,				//hWndInsertAfter
+						rcc.left,					//int x
+						rcc.top,					//int y
+						rcc.right  - rcc.left,
+						rcc.bottom - rcc.top,
+						SWP_NOZORDER				//UINT uFlags
+						);
+			}
+		}
+	};
+
+	/**
+	 * This compare function is used by ListView_SortItemsEx to sort the listview.
+	 *
+	 * @param		iItem1	- index of the first item
+	 * @param		iItem2	- index of the second item
+	 * @param		pDlg	- pointer to the class' object
+	 *
+	 * @return	This function returns a number indicating comparison result.
+	 **/
+	static INT CALLBACK cmpProc(INT iItem1, INT iItem2, CAnnivList* pDlg)
+	{
+		INT result;
+
+		if (pDlg) {
+			TCHAR szText1[MAX_PATH];
+			TCHAR szText2[MAX_PATH];
+
+			szText1[0] = szText2[0] = 0;
+			switch (pDlg->_sortHeader) {
+				case COLUMN_CONTACT:
+				case COLUMN_PROTO:
+				case COLUMN_DESC:
+					{
+						ListView_GetItemText(pDlg->_hList, iItem1, pDlg->_sortHeader, szText1, MAX_PATH);
+						ListView_GetItemText(pDlg->_hList, iItem2, pDlg->_sortHeader, szText2, MAX_PATH);
+						result = pDlg->_sortOrder * mir_tcscmp(szText1, szText2);
+					} break;
+
+				case COLUMN_AGE:
+				case COLUMN_ETA:
+					{
+						ListView_GetItemText(pDlg->_hList, iItem1, pDlg->_sortHeader, szText1, MAX_PATH);
+						ListView_GetItemText(pDlg->_hList, iItem2, pDlg->_sortHeader, szText2, MAX_PATH);
+						result = pDlg->_sortOrder * (_ttoi(szText1) - _ttoi(szText2));
+					} break;
+
+				case COLUMN_DATE: 
+					{
+						CItemData	*id1 = pDlg->ItemData(iItem1),
+									*id2 = pDlg->ItemData(iItem2);
+
+						if (PtrIsValid(id1) && PtrIsValid(id2)) {
+							result = pDlg->_sortOrder * id1->_pDate->Compare(*id2->_pDate);
+							break;
+						}
+					}
+				default:
+					{
+						result = 0;
+					}
+			}
+		}
+		else {
+			result = 0;
+		}
+		return result;
+	}
+
+	/**
+	 * This static method is the window procedure for the dialog.
+	 *
+	 * @param		hDlg	- handle of the dialog window
+	 * @param		uMsg	- message to handle
+	 * @param		wParam	- message dependend parameter
+	 * @param		lParam	- message dependend parameter
+	 *
+	 * @return	depends on message
+	 **/
+	static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 
+	{
+		CAnnivList *pDlg = (CAnnivList*)GetUserData(hDlg);
+		
+		switch (uMsg) 
+		{
+		case WM_INITDIALOG:
+			{
+				INT i = 0;
+				HWND hCtrl;
+				HICON hIcon;
+				RECT rc;
+
+				// link the class to the window handle
+				pDlg = (CAnnivList*)lParam;
+				if (!pDlg) {
+					break;
+				}
+				SetUserData(hDlg, lParam);
+				pDlg->_hDlg = hDlg;
+
+				// init pointer listview control
+				pDlg->_hList = GetDlgItem(hDlg, EDIT_ANNIVERSARY_DATE);
+				if (!pDlg->_hList) {
+					break;
+				}
+
+				// set icons
+				hIcon = IcoLib_GetIcon(ICO_DLG_ANNIVERSARY);
+				SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)hIcon);
+				SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
+
+				// insert columns into the listboxes
+				ListView_SetExtendedListViewStyle(pDlg->_hList, LVS_EX_FULLROWSELECT);
+
+				// add columns
+				if (pDlg->AddColumn(CAnnivList::COLUMN_ETA, LPGENT("ETA"), 40) ||
+						pDlg->AddColumn(CAnnivList::COLUMN_CONTACT, LPGENT("Contact"), 160) ||
+						pDlg->AddColumn(CAnnivList::COLUMN_PROTO, LPGENT("Proto"), 50) ||
+						pDlg->AddColumn(CAnnivList::COLUMN_AGE, LPGENT("Age/Nr."), 40) ||
+						pDlg->AddColumn(CAnnivList::COLUMN_DESC, LPGENT("Anniversary"), 100) ||
+						pDlg->AddColumn(CAnnivList::COLUMN_DATE, LPGENT("Date"), 80)) 
+				{
+					break;
+				}
+
+				TranslateDialogDefault(hDlg);
+				
+				// save minimal size
+				GetWindowRect(hDlg, &rc);
+				pDlg->_sizeMin.cx = rc.right - rc.left;
+				pDlg->_sizeMin.cy = rc.bottom - rc.top;
+				
+				// restore position and size
+				Utils_RestoreWindowPosition(hDlg, NULL, MODNAME, "AnnivDlg_");
+
+				//save win pos
+				GetWindowRect(hDlg, &pDlg->_rcWin);
+
+				// add filter strings
+				if (hCtrl = GetDlgItem(hDlg, COMBO_VIEW)) {
+					ComboBox_AddString(hCtrl, TranslateT("All contacts"));
+					ComboBox_AddString(hCtrl, TranslateT("Birthdays only"));
+					ComboBox_AddString(hCtrl, TranslateT("Anniversaries only"));
+					ComboBox_AddString(hCtrl, TranslateT("Disabled reminder"));
+					ComboBox_SetCurSel(hCtrl, pDlg->_filter.bFilterIndex);
+				}
+
+				// init reminder groups
+				pDlg->_bRemindEnable = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED) != REMIND_OFF;
+				if (hCtrl = GetDlgItem(hDlg, CHECK_REMIND)) {
+					Button_SetCheck(hCtrl, pDlg->_bRemindEnable ? BST_INDETERMINATE : BST_UNCHECKED);
+					EnableWindow(hCtrl, pDlg->_bRemindEnable);
+				}
+
+				CheckDlgButton(hDlg, CHECK_POPUP, DB::Setting::GetByte(SET_ANNIVLIST_POPUP, FALSE));
+				// set number of days to show contact in advance
+				SetDlgItemInt(hDlg, EDIT_DAYS, pDlg->_filter.wDaysBefore , FALSE);
+				if (hCtrl = GetDlgItem(hDlg, CHECK_DAYS)) {
+					Button_SetCheck(hCtrl, DB::Setting::GetByte(SET_ANNIVLIST_FILTER_DAYSENABLED, FALSE));
+					DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_DAYS, BN_CLICKED), (LPARAM)hCtrl);
+				}
+
+				pDlg->_wmINIT = false;
+			}
+			return TRUE;
+
+		/**
+		 * set propertysheet page's background white in aero mode
+		 **/
+		case WM_CTLCOLORSTATIC:
+		case WM_CTLCOLORDLG:
+			if (IsAeroMode())
+				return (INT_PTR)GetStockBrush(WHITE_BRUSH);
+			break;
+
+		case WM_NOTIFY:
+			{
+				switch (((LPNMHDR)lParam)->idFrom) 
+				{
+				case EDIT_ANNIVERSARY_DATE:
+					{
+						switch (((LPNMHDR)lParam)->code) 
+						{
+						/*
+						 * handle changed selection
+						 */
+						case LVN_ITEMCHANGED:
+							{
+								CItemData* pid;
+								HWND hCheck;
+
+								pDlg->_curSel = ((LPNMLISTVIEW)lParam)->iItem;
+								pid = pDlg->ItemData(pDlg->_curSel);
+								if (pid && pDlg->_bRemindEnable && (hCheck = GetDlgItem(hDlg, CHECK_REMIND))) {
+									SetDlgItemInt(hDlg, EDIT_REMIND, pid->_wDaysBefore, FALSE);
+									Button_SetCheck(hCheck, pid->_wReminderState);
+									DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_REMIND, BN_CLICKED), (LPARAM)hCheck);
+								}
+							} break;
+
+						/*
+						 * resort the list
+						 */
+						case LVN_COLUMNCLICK:
+							{
+								LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
+
+								if (pDlg->_sortHeader == pnmv->iSubItem) {
+									pDlg->_sortOrder *= -1;
+								}
+								else {
+									pDlg->_sortOrder = 1;
+									pDlg->_sortHeader = pnmv->iSubItem;
+								}
+								ListView_SortItemsEx(pDlg->_hList, (CMPPROC)cmpProc, pDlg);
+							} break;
+
+						/*
+						 * show contact menu
+						 */
+						case NM_RCLICK:
+							{
+								CItemData* pid = pDlg->ItemData(pDlg->_curSel);
+								if (pid) {
+									HMENU hPopup = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) pid->_hContact, 0);
+									if (hPopup) {
+										POINT pt;
+										GetCursorPos(&pt);
+										TrackPopupMenu(hPopup, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hDlg, NULL);
+										DestroyMenu(hPopup);
+									}
+								}
+							} break;
+
+						/*
+						 * handle double click on contact: show message dialog
+						 */
+						case NM_DBLCLK:
+							{
+								CItemData* pid = pDlg->ItemData(((LPNMITEMACTIVATE)lParam)->iItem);
+								if (pid) {
+									CallService(MS_MSG_SENDMESSAGE,(WPARAM)pid->_hContact, NULL);
+								}
+							}
+						}
+					}
+				}
+			} break;
+
+		case WM_COMMAND:
+			{
+				if (PtrIsValid(pDlg)) {
+					CItemData* pid = pDlg->ItemData(pDlg->_curSel);
+
+					// process contact menu command
+					if (pid && CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM)pid->_hContact)) {
+						break;
+					}
+
+					switch (LOWORD(wParam))
+					{
+
+					/*
+					 * enable/disable reminder checkbox is clicked
+					 */
+					case CHECK_REMIND:
+						{
+							if (pDlg->_bRemindEnable && HIWORD(wParam) == BN_CLICKED) {
+								BOOLEAN checkState = Button_GetCheck((HWND)lParam);
+
+								EnableWindow(GetDlgItem(hDlg, EDIT_REMIND), checkState == BST_CHECKED);
+								EnableWindow(GetDlgItem(hDlg, SPIN_REMIND), checkState == BST_CHECKED);
+								EnableWindow(GetDlgItem(hDlg, TXT_REMIND5), checkState == BST_CHECKED);
+								if (pid && pid->_wReminderState != checkState) {
+									pid->_wReminderState = checkState;
+								}
+							}
+						} break;
+
+					/*
+					 * number of days to remind in advance is edited
+					 */
+					case EDIT_REMIND:
+						{
+							if (pid && pDlg->_bRemindEnable && HIWORD(wParam) == EN_CHANGE) {
+								WORD wDaysBefore = GetDlgItemInt(hDlg, LOWORD(wParam), NULL, FALSE);
+								if (pid->_wReminderState == BST_CHECKED && pid->_wDaysBefore != wDaysBefore) {
+									pid->_wDaysBefore = wDaysBefore;
+								}
+							}
+						} break;
+
+					/*
+					 * the filter to display only contacts which have an anniversary in a certain 
+					 * period of time is enabled/disabled
+					 */
+					case CHECK_DAYS:
+						{
+							if (HIWORD(wParam) == BN_CLICKED) {
+								BOOLEAN isChecked = Button_GetCheck((HWND)lParam);
+								EnableWindow(GetDlgItem(hDlg, EDIT_DAYS), isChecked);
+								EnableWindow(GetDlgItem(hDlg, TXT_DAYS), isChecked);
+								pDlg->_filter.wDaysBefore = isChecked ? GetDlgItemInt(hDlg, EDIT_DAYS, NULL, FALSE) : (WORD)-1;
+								pDlg->RebuildList();
+							}
+						} break;
+
+					/*
+					 * the number of days a contact must have an anniversary in advance to be displayed is edited
+					 */
+					case EDIT_DAYS:
+						{
+							if (HIWORD(wParam) == EN_CHANGE) {
+								WORD wNewDays = GetDlgItemInt(hDlg, LOWORD(wParam), NULL, FALSE);
+								if (wNewDays != pDlg->_filter.wDaysBefore) {
+									pDlg->_filter.wDaysBefore = wNewDays;
+									pDlg->RebuildList();
+								}
+							}
+						} break;
+
+					/*
+					 * the filter selection of the filter combobox has changed
+					 */
+					case COMBO_VIEW:
+						{
+							if (HIWORD(wParam) == CBN_SELCHANGE) {
+								pDlg->_filter.bFilterIndex = ComboBox_GetCurSel((HWND)lParam);
+								pDlg->RebuildList();
+							}
+						}
+					}
+				}
+			} break;
+
+		case WM_DRAWITEM:
+			return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+
+		case WM_MEASUREITEM:
+			return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam);
+
+		case WM_WINDOWPOSCHANGING:
+			{
+				if (PtrIsValid(pDlg)) {
+					WINDOWPOS* wndPos = (WINDOWPOS*)lParam;
+					if (!pDlg->_wmINIT && (wndPos->cx != 0 || wndPos->cy != 0)) {
+						//win pos change
+						if ( (wndPos->cx == pDlg->_rcWin.right  - pDlg->_rcWin.left) && 
+							(wndPos->cy == pDlg->_rcWin.bottom - pDlg->_rcWin.top)) {
+							//win pos change (store new pos)
+							GetWindowRect(hDlg, &pDlg->_rcWin);
+						}
+						//win size change
+						else {
+							// l change
+							if (	(wndPos->cx < pDlg->_sizeMin.cx) && (wndPos->x > pDlg->_rcWin.left)) {
+								wndPos->x  = wndPos->x + wndPos->cx - pDlg->_sizeMin.cx;
+								wndPos->cx = pDlg->_sizeMin.cx;
+							}
+							// r change
+							else if (wndPos->cx < pDlg->_sizeMin.cx) {
+								wndPos->cx = pDlg->_sizeMin.cx;
+							}
+
+							// t change
+							if (	(wndPos->cy < pDlg->_sizeMin.cy) && (wndPos->y > pDlg->_rcWin.top)) {
+								wndPos->y  = wndPos->y + wndPos->cy - pDlg->_sizeMin.cy;
+								wndPos->cy = pDlg->_sizeMin.cy;
+							}
+							// b change
+							else if (wndPos->cy < pDlg->_sizeMin.cy) {
+								wndPos->cy = pDlg->_sizeMin.cy;
+							}
+
+							pDlg->_rcWin.left	= wndPos->x;
+							pDlg->_rcWin.right	= wndPos->x + wndPos->cx;
+							pDlg->_rcWin.top	= wndPos->y;
+							pDlg->_rcWin.bottom	= wndPos->y + wndPos->cy;
+						}
+					}
+
+					CAnchor anchor(wndPos, pDlg->_sizeMin);
+					INT anchorPos = CAnchor::ANCHOR_LEFT | CAnchor::ANCHOR_RIGHT | CAnchor::ANCHOR_TOP;
+
+					anchor.MoveCtrl(IDC_HEADERBAR, anchorPos);
+					anchor.MoveCtrl(GROUP_STATS, anchorPos);
+
+					// birthday list
+					anchor.MoveCtrl(EDIT_ANNIVERSARY_DATE, CAnchor::ANCHOR_ALL);
+
+					anchorPos = CAnchor::ANCHOR_RIGHT | CAnchor::ANCHOR_BOTTOM;
+
+					// filter group
+					anchor.MoveCtrl(GROUP_FILTER, anchorPos);
+					anchor.MoveCtrl(COMBO_VIEW, anchorPos);
+					anchor.MoveCtrl(CHECK_DAYS, anchorPos);
+					anchor.MoveCtrl(EDIT_DAYS, anchorPos);
+					anchor.MoveCtrl(TXT_DAYS, anchorPos);
+
+					// filter group
+					anchor.MoveCtrl(GROUP_REMINDER, anchorPos);
+					anchor.MoveCtrl(CHECK_REMIND, anchorPos);
+					anchor.MoveCtrl(EDIT_REMIND, anchorPos);
+					anchor.MoveCtrl(SPIN_REMIND, anchorPos);
+					anchor.MoveCtrl(TXT_REMIND6, anchorPos);
+					anchor.MoveCtrl(CHECK_POPUP, anchorPos);
+				}
+			} break;
+
+		/**
+		 * This message is sent if eighter the user clicked on the close button or
+		 * Miranda fires the ME_SYSTEM_SHUTDOWN event.
+		 **/
+		case WM_CLOSE:
+			{
+				DestroyWindow(hDlg);
+			} break;
+
+		/**
+		 * If the anniversary list is destroyed somehow, the data class must be
+		 * deleted, too.
+		 **/
+		case WM_DESTROY:
+			{
+				if (PtrIsValid(pDlg)) {
+					SetUserData(hDlg, NULL);
+					delete pDlg;
+				}
+				MagneticWindows_RemoveWindow(hDlg);
+			} break;
+		}
+		return FALSE;
+	}
+
+	/**
+	 * This method adds a column to the listview.
+	 *
+	 * @param	iSubItem		- desired column index
+	 * @param	pszText			- the header text
+	 * @param	defaultWidth	- the default witdth
+	 *
+	 * @retval	0 if successful
+	 * @retval	1 if failed
+	 **/
+	BOOLEAN AddColumn(INT iSubItem, LPCTSTR pszText, INT defaultWidth) 
+	{
+		LVCOLUMN lvc;
+		CHAR pszSetting[MAXSETTING];
+
+		mir_snprintf(pszSetting, SIZEOF(pszSetting), "AnnivDlg_Col%d", iSubItem);
+		lvc.cx = DB::Setting::GetWord(pszSetting, defaultWidth);
+		lvc.mask = LVCF_WIDTH|LVCF_TEXT;
+		lvc.iSubItem = iSubItem;
+		lvc.pszText = TranslateTS(pszText);
+		return ListView_InsertColumn(_hList, lvc.iSubItem++, &lvc) == -1;
+	}
+
+	/**
+	 * This method sets the subitem text for the current contact
+	 *
+	 * @param	iItem			- index of the current row
+	 * @param	iSubItem		- column to set text for
+	 * @param	pszText			- text to insert
+	 *
+	 * @retval	TRUE if successful
+	 * @retval	FALSE if failed
+	 **/
+	BOOLEAN AddSubItem(INT iItem, INT iSubItem, LPTSTR pszText) 
+	{
+		LVITEM lvi;
+		if (iSubItem > 0) 
+		{
+			lvi.iItem = iItem;
+			lvi.iSubItem = iSubItem;
+			lvi.pszText = pszText;
+			lvi.mask = LVIF_TEXT;
+			return ListView_SetItem(_hList, &lvi);
+		}
+		return FALSE;
+	}
+
+	/**
+	 * This method adds a row to the listview.
+	 *
+	 * @param	pszText			- text to insert
+	 * @param	lParam			- pointer to the rows data
+	 *
+	 * @retval	TRUE if successful
+	 * @retval	FALSE if failed
+	 **/	
+	BOOLEAN AddItem(LPTSTR pszText, LPARAM lParam)
+	{
+		LVITEM lvi;
+
+		if (!pszText) {
+			return FALSE;
+		}
+		lvi.iItem = 0;
+		lvi.iSubItem = 0;
+		lvi.pszText = pszText;
+		lvi.mask = LVIF_TEXT|TVIF_PARAM;
+		lvi.lParam = lParam;
+		return ListView_InsertItem(_hList, &lvi);
+	}
+
+	/**
+	 * This method adds a row to the listview.
+	 *
+	 * @param	hContact		- contact to add the line for
+	 * @param	pszProto		- contact's protocol
+	 * @param	ad				- anniversary to add
+	 * @param	mtNow			- current time
+	 * @param	wDaysBefore		- number of days in advance to remind the user of the anniversary
+	 *
+	 * @retval	TRUE if successful
+	 * @retval	FALSE if failed
+	 **/
+	BOOLEAN AddRow(HANDLE hContact, LPCSTR pszProto, MAnnivDate &ad, MTime &mtNow, WORD wDaysBefore) 
+	{
+		TCHAR szText[MAX_PATH];
+		INT diff, iItem = -1;
+		CItemData *pdata;
+	
+		//
+		// first column: ETA
+		//
+		diff = ad.CompareDays(mtNow);
+		if (diff < 0) 
+		{
+			diff += IsLeap(mtNow.Year() + 1) ? 366 : 365;
+		}
+		// is filtered
+		if (diff <= _filter.wDaysBefore) 
+		{
+			// read reminder options for the contact
+			ad.DBGetReminderOpts(hContact);
+			if ((_filter.bFilterIndex != FILTER_DISABLED_REMINDER) || (ad.RemindOption() == BST_UNCHECKED))
+			{
+				// set default offset if required
+				if (ad.RemindOffset() == (WORD)-1)
+				{
+					ad.RemindOffset(wDaysBefore);
+					
+					// create data object
+					pdata = new CItemData(hContact, ad);
+					if (!pdata) 
+					{
+						return FALSE;
+					}
+					// add item
+					iItem = AddItem(_itot(diff, szText, 10), (LPARAM)pdata);
+					if (iItem == -1) 
+					{
+						delete pdata;
+						return FALSE;
+					}
+
+					// second column: contact name
+					AddSubItem(iItem, COLUMN_CONTACT, DB::Contact::DisplayName(hContact));
+
+					// third column: protocol
+					TCHAR* ptszProto = mir_a2t(pszProto);
+					AddSubItem(iItem, COLUMN_PROTO, ptszProto);
+					mir_free(ptszProto);
+
+					// forth line: age
+					AddSubItem(iItem, COLUMN_AGE, _itot(ad.Age(&mtNow), szText, 10));
+
+					// fifth line: anniversary
+					AddSubItem(iItem, COLUMN_DESC, (LPTSTR)ad.Description());
+
+					// sixth line: date
+					ad.DateFormat(szText, SIZEOF(szText));
+					AddSubItem(iItem, COLUMN_DATE, szText);
+					
+					_numRows++;
+				}
+			}
+		}
+		return TRUE;
+	}
+
+	/**
+	 * This method clears the list and adds contacts again, according to the current filter settings.
+	 **/
+	VOID RebuildList() 
+	{
+		HANDLE		hContact;
+		LPSTR		pszProto;
+		MTime		mtNow;
+		MAnnivDate	ad;
+		INT			i	= 0;
+		DWORD		age	= 0;
+		WORD		wDaysBefore = DB::Setting::GetWord(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET);
+		WORD		numMale				= 0;
+		WORD		numFemale			= 0;
+		WORD		numContacts			= 0;
+		WORD		numBirthContacts	= 0;
+
+		ShowWindow(_hList, SW_HIDE);
+		DeleteAllItems();
+		mtNow.GetLocalTime();
+
+		// insert the items into the list
+		for (hContact = DB::Contact::FindFirst();
+				hContact;
+				hContact = DB::Contact::FindNext(hContact))
+		{
+			// ignore meta subcontacts here, as they are not interesting.
+			if (!DB::MetaContact::IsSub(hContact)) {
+				// filter protocol
+				pszProto = DB::Contact::Proto(hContact);
+				if (pszProto) {
+					numContacts++;
+					switch (GenderOf(hContact, pszProto))
+					{
+					case 'M': {
+							numMale++;
+						} break;
+
+					case 'F': {
+							numFemale++; 
+						}
+					}
+
+					if (!ad.DBGetBirthDate(hContact, pszProto)) {
+						age += ad.Age(&mtNow);
+						numBirthContacts++;
+
+						// add birthday
+						if ((_filter.bFilterIndex != FILTER_ANNIV) && (!_filter.pszProto || !strcmpi(pszProto, _filter.pszProto))) {
+							AddRow(hContact, pszProto, ad, mtNow, wDaysBefore);
+						}
+					}
+
+					// add anniversaries
+					if (	_filter.bFilterIndex != FILTER_BIRTHDAY && 
+							(!_filter.pszProto || !strcmpi(pszProto, _filter.pszProto))) 
+					{
+						for (i = 0; !ad.DBGetAnniversaryDate(hContact, i); i++) {
+							if (!_filter.pszAnniv || !_tcsicmp(_filter.pszAnniv, ad.Description())) {
+								AddRow(hContact, pszProto, ad, mtNow, wDaysBefore);
+							}
+						}
+					}
+				}
+			}
+		}
+		ListView_SortItemsEx(_hList, (CMPPROC)cmpProc, this);
+		ShowWindow(_hList, SW_SHOW);
+
+		// display statistics
+		SetDlgItemInt(_hDlg, TXT_NUMBIRTH, numBirthContacts, FALSE);
+		SetDlgItemInt(_hDlg, TXT_NUMCONTACT, numContacts, FALSE);
+		SetDlgItemInt(_hDlg, TXT_FEMALE, numFemale, FALSE);
+		SetDlgItemInt(_hDlg, TXT_MALE, numMale, FALSE);
+		SetDlgItemInt(_hDlg, TXT_AGE, numBirthContacts > 0 ? max(0, (age - (age % numBirthContacts)) / numBirthContacts) : 0, FALSE);
+	}
+
+	/**
+	 * This method deletes all items from the listview
+	 **/	
+	VOID DeleteAllItems() 
+	{
+		CItemData *pid;
+		
+		for (INT i = 0; i < _numRows; i++) 
+		{
+			pid = ItemData(i);
+			if (pid) 
+			{
+				delete pid;
+			}
+		}
+		ListView_DeleteAllItems(_hList);
+		_numRows = 0;
+	}
+
+	/**
+	 * This method returns the data structure accociated with a list item.
+	 *
+	 * @param	iItem			- index of the desired item
+	 *
+	 * @return	pointer to the data strucutre on success or NULL otherwise.
+	 **/
+	CItemData* ItemData(INT iItem) 
+	{
+		if (_hList && iItem >= 0 && iItem < _numRows) 
+		{
+			LVITEM lvi;
+		
+			lvi.mask = LVIF_PARAM;
+			lvi.iItem = iItem;
+			lvi.iSubItem = 0;
+			if (ListView_GetItem(_hList, &lvi) && PtrIsValid(lvi.lParam))
+			{
+				return (CItemData*)lvi.lParam;
+			}
+		}
+		return NULL;
+	}
+
+	/**
+	 * This method loads all filter settings from db
+	 **/	
+	VOID LoadFilter() 
+	{
+		_filter.wDaysBefore = DB::Setting::GetWord(SET_ANNIVLIST_FILTER_DAYS, 9);
+		_filter.bFilterIndex = DB::Setting::GetByte(SET_ANNIVLIST_FILTER_INDEX, 0);
+	}
+
+	/**
+	 * This method saves all filter settings to db
+	 **/	
+	VOID SaveFilter() 
+	{
+		if (_hDlg) {
+			DB::Setting::WriteWord(SET_ANNIVLIST_FILTER_DAYS, (WORD)GetDlgItemInt(_hDlg, EDIT_DAYS, NULL, FALSE));
+			DB::Setting::WriteByte(SET_ANNIVLIST_FILTER_DAYSENABLED, (BYTE)Button_GetCheck(GetDlgItem(_hDlg, CHECK_DAYS)));
+			DB::Setting::WriteByte(SET_ANNIVLIST_FILTER_INDEX, (BYTE)ComboBox_GetCurSel(GetDlgItem(_hDlg, EDIT_DAYS)));
+		}
+	}
+
+public:
+
+	/**
+	 * This is the default constructor.
+	 **/
+	CAnnivList() 
+	{
+		_hList			= NULL;
+		_sortHeader		= 0;
+		_sortOrder		= 1;
+		_curSel			= -1;
+		_numRows		= 0;
+		_wmINIT			= true;
+		_rcWin.left		= _rcWin.right = _rcWin.top = _rcWin.bottom = 0;
+		LoadFilter();
+
+		_hDlg = CreateDialogParam(ghInst, MAKEINTRESOURCE(IDD_ANNIVERSARY_LIST), NULL, (DLGPROC)DlgProc, (LPARAM)this);
+		if (_hDlg) {
+				MagneticWindows_AddWindow(_hDlg);
+				_mHookExit = HookEventMessage(ME_SYSTEM_PRESHUTDOWN, _hDlg, WM_CLOSE);
+		}
+		else {
+			_mHookExit = NULL;
+			delete this;
+		}
+	}
+
+	/**
+	 * This is the default destructor.
+	 **/
+	~CAnnivList() 
+	{
+		// delete the shutdown hook
+		if (_mHookExit) {
+			UnhookEvent(_mHookExit);
+			_mHookExit = NULL;
+		}
+
+		// close window if required
+		if (_hDlg) {
+			// save list state
+			if (_hList) {
+				CHAR pszSetting[MAXSETTING];
+				INT c, cc = Header_GetItemCount(ListView_GetHeader(_hList));
+
+				for (c = 0; c < cc; c++) {
+					mir_snprintf(pszSetting, MAXSETTING, "AnnivDlg_Col%d", c);
+					DB::Setting::WriteWord(pszSetting, (WORD)ListView_GetColumnWidth(_hList, c));
+				}
+				DeleteAllItems();
+			}
+			// remember popup setting
+			DB::Setting::WriteByte(SET_ANNIVLIST_POPUP, (BYTE)IsDlgButtonChecked(_hDlg, CHECK_POPUP));
+			// save window position, size and column widths
+			Utils_SaveWindowPosition(_hDlg, NULL, MODNAME, "AnnivDlg_");
+			SaveFilter();
+
+			// if the window did not yet retrieve a WM_DESTROY message, do it right now.
+			if (PtrIsValid(GetUserData(_hDlg))) {
+				SetUserData(_hDlg, NULL);
+				DestroyWindow(_hDlg);
+			}
+		}	
+		gpDlg = NULL;
+	}
+
+	/**
+	 * name:	BringToFront
+	 * class:	CAnnivList
+	 * desc:	brings anniversary list to the top
+	 * param:	none
+	 * return:	nothing
+	 **/
+	VOID BringToFront()
+	{
+		ShowWindow(_hDlg, SW_RESTORE);
+		SetForegroundWindow(_hDlg);
+		SetFocus(_hDlg);
+	}
+
+}; // class CAnnivList
+
+/***********************************************************************************************************
+ * service handlers
+ ***********************************************************************************************************/
+
+/**
+ * This is the service function that is called list all anniversaries.
+ *
+ * @param	wParam	- not used
+ * @param	lParam	- not used
+ *
+ * @return	always 0
+ **/
+INT_PTR DlgAnniversaryListShow(WPARAM wParam, LPARAM lParam) 
+{
+	if (!gpDlg) {
+		try 
+		{
+			myGlobals.WantAeroAdaption = DB::Setting::GetByte(SET_PROPSHEET_AEROADAPTION, TRUE);
+			gpDlg = new CAnnivList();
+		}
+		catch(...) 
+		{
+			delete gpDlg;
+			gpDlg = NULL;
+		}
+	} 
+	else {
+		gpDlg->BringToFront();
+	}
+	return 0;
+}
+
+/***********************************************************************************************************
+ * loading and unloading module
+ ***********************************************************************************************************/
+
+#define TBB_IDBTN		"AnnivList"
+#define TBB_ICONAME		TOOLBARBUTTON_ICONIDPREFIX TBB_IDBTN TOOLBARBUTTON_ICONIDPRIMARYSUFFIX
+
+/**
+ * This function is called by the ME_TTB_MODULELOADED event.
+ * It adds a set of buttons to the TopToolbar plugin.
+ *
+ * @param	wParam	- none
+ *
+ * @return	nothing
+ **/
+
+VOID DlgAnniversaryListOnTopToolBarLoaded()
+{
+	TTBButton ttb = {0};
+	ttb.cbSize = sizeof(ttb);
+	ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP;
+	ttb.pszService = MS_USERINFO_REMINDER_LIST;
+	ttb.hIconHandleUp = Skin_GetIconHandle(ICO_COMMON_ANNIVERSARY);
+	ttb.name = ttb.pszTooltipUp = LPGEN("Anniversary list");
+	TopToolbar_AddButton(&ttb);
+}
+
+/**
+ * This function initially loads all required stuff for the anniversary list.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID DlgAnniversaryListLoadModule()
+{
+	CreateServiceFunction(MS_USERINFO_REMINDER_LIST, DlgAnniversaryListShow);
+
+	HOTKEYDESC hk = { 0 };
+	hk.cbSize			= sizeof(HOTKEYDESC);
+	hk.pszSection		= MODNAME;
+	hk.pszName			= "AnniversaryList";
+	hk.pszDescription	= LPGEN("Popup Anniversary list");
+	hk.pszService		= MS_USERINFO_REMINDER_LIST;
+	Hotkey_Register(&hk);
+}
diff --git a/plugins/UserInfoEx/src/dlg_anniversarylist.h b/plugins/UserInfoEx/src/dlg_anniversarylist.h
new file mode 100644
index 0000000000..e7ff89e99c
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_anniversarylist.h
@@ -0,0 +1,43 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_anniversarylist.h $
+Revision       : $Revision: 202 $
+Last change on : $Date: 2010-09-24 23:46:57 +0400 (Пт, 24 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _DLGANNIVERSARYLIST_H_
+#define _DLGANNIVERSARYLIST_H_
+
+#define SET_ANNIVLIST_POPUP					"AnLstPopup"
+#define SET_ANNIVLIST_FILTER_DAYSENABLED	"AnLstFltDaysEnabled"
+#define SET_ANNIVLIST_FILTER_DAYS			"AnLstFltDays"
+#define SET_ANNIVLIST_FILTER_INDEX			"AnLstFltIndex"
+
+INT_PTR		DlgAnniversaryListShow(WPARAM wParam, LPARAM lParam);
+VOID		DlgAnniversaryListOnTopToolBarLoaded();
+VOID		DlgAnniversaryListLoadModule();
+
+#endif /* _DLGANNIVERSARYLIST_H_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/dlg_msgbox.cpp b/plugins/UserInfoEx/src/dlg_msgbox.cpp
new file mode 100644
index 0000000000..5f55802e3d
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_msgbox.cpp
@@ -0,0 +1,856 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_msgbox.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include <Commdlg.h>
+#include <m_clui.h>
+#include <m_skin.h>
+
+typedef struct _MSGPOPUPDATA
+{
+	POPUPACTION	pa[3];
+	HWND		hDialog;
+}
+MSGPOPUPDATA, *LPMSGPOPUPDATA;
+
+/**
+ * This helper function moves and resizes a dialog box's control element.
+ * 
+ * @param	hDlg		- the dialog box's window handle
+ * @param	idCtrl		- the identication number of the control to move
+ * @param	dx			-�number of pixels to horizontal move the control 
+ * @param	dy			- number of pixels to vertical move the control 
+ * @param	dw			- number of pixels to horizontal resize the control 
+ * @param	dh			- number of pixels to vertical resize the control 
+ *
+ * @return	nothing
+ **/
+static FORCEINLINE VOID MoveCtrl(HWND hDlg, INT idCtrl, INT dx, INT dy, INT dw, INT dh)
+{
+	RECT ws;
+	HWND hCtrl = GetDlgItem(hDlg, idCtrl);
+	GetWindowRect(hCtrl, &ws);
+	OffsetRect(&ws, dx, dy);
+	MoveWindow(hCtrl, ws.left, ws.top,	ws.right - ws.left + dw, ws.bottom - ws.top + dh, FALSE);
+}
+
+/**
+ * This function loads the icon to display for the current message.
+ *
+ * @param	pMsgBox		- pointer to a MSGBOX structure, with information about the message to display.
+ *
+ * @retval	HICON		- The function returns an icon to display with the message.
+ * @retval	NULL		- There is no icon for the message.
+ **/
+static HICON MsgLoadIcon(LPMSGBOX pMsgBox)
+{
+	HICON hIcon;
+
+	// load the desired status icon
+	switch (pMsgBox->uType & MB_ICONMASK)
+	{
+	
+	// custom icon defined by caller function
+	case MB_ICON_OTHER:
+		{
+			hIcon = pMsgBox->hiMsg;
+		}
+		break;
+	
+	// default windows icons
+	case MB_ICON_ERROR:
+	case MB_ICON_QUESTION:
+	case MB_ICON_WARNING:
+	case MB_ICON_INFO:
+		{
+			LPCTSTR ico[] = { 0, IDI_ERROR, IDI_QUESTION, IDI_WARNING, IDI_INFORMATION };
+			hIcon = LoadIcon(NULL, ico[MB_ICON_INDEX(pMsgBox->uType)]);
+		}
+		break;
+
+	// no icon
+	default:
+		{
+			hIcon = NULL;
+		}
+	}
+	return hIcon;
+}
+
+/**
+ * This function fills a given POPUPACTION structure with the data of a given message id,
+ * which is normally used by the message box. This is required to let the user interact 
+ * with a popup in the same way as with a normal message dialog box.
+ *
+ * @param	pa			- reference to a POPUPACTION structure to fill
+ * @param	id			- the message id
+ * @param	result		- This parameter is passed to the POPUPACTION structure as is.
+ *
+ * @return	nothing
+ **/
+static VOID MakePopupAction(POPUPACTION &pa, INT id)
+{
+	pa.cbSize = sizeof(POPUPACTION);
+	pa.flags = PAF_ENABLED;
+	pa.wParam = MAKEWORD(id, BN_CLICKED);
+	pa.lParam = 0;
+
+	switch (id)
+	{
+	case IDOK:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+			mir_strcpy(pa.lpzTitle, MODNAME"/Ok");
+		}
+		break;
+
+	case IDCLOSE:
+	case IDCANCEL:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+			mir_strcpy(pa.lpzTitle, MODNAME"/Cancel");
+		}
+		break;
+
+	case IDABORT:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+			mir_strcpy(pa.lpzTitle, MODNAME"/Abort");
+		}
+		break;
+
+	case IDRETRY:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+			mir_strcpy(pa.lpzTitle, MODNAME"/Retry");
+		}
+		break;
+
+	case IDIGNORE:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+			mir_strcpy(pa.lpzTitle, MODNAME"/Ignore");
+		}
+		break;
+
+	case IDYES:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+			mir_strcpy(pa.lpzTitle, MODNAME"/Yes");
+		}
+		break;
+
+	case IDNO:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+			mir_strcpy(pa.lpzTitle, MODNAME"/No");
+		}
+		break;
+	
+	case IDHELP:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+			mir_strcpy(pa.lpzTitle, MODNAME"/Help");
+		}
+		break;
+
+	case IDALL:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+			mir_strcpy(pa.lpzTitle, MODNAME"/All");
+		}
+		break;
+
+	case IDNONE:
+		{
+			pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+			mir_strcpy(pa.lpzTitle, MODNAME"/None");
+		}
+	}
+}
+
+/**
+ * This is the message procedure for my nice looking message box
+ *
+ * @param	hDlg		- window handle
+ * @param	uMsg		- message to handle
+ * @param	wParam		- message specific parameter
+ * @param	lParam		- message specific parameter
+ *
+ * @return	TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
+ **/
+static INT_PTR CALLBACK MsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	static INT retOk = IDOK;
+	static INT retAll = IDALL;
+	static INT retNon = IDNONE;
+	static INT retCancel = IDCANCEL;
+
+	switch (uMsg)
+	{
+	case WM_INITDIALOG:
+		{
+			LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+			if (PtrIsValid(pMsgBox))
+			{
+				INT icoWidth = 0;
+				INT InfoBarHeight = 0;
+				HFONT hNormalFont;
+
+				hNormalFont = (HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0);
+				if (pMsgBox->uType & MB_INFOBAR)
+				{
+					LOGFONT lf;
+
+					// set bold font for name in description area
+					GetObject(hNormalFont, sizeof(lf), &lf);
+					lf.lfWeight = FW_BOLD;
+					hNormalFont = CreateFontIndirect(&lf);
+					
+					// set infobar's textfont
+					SendDlgItemMessage(hDlg, TXT_NAME, WM_SETFONT, (WPARAM)hNormalFont, 0);
+
+					// set infobar's logo icon
+					SendDlgItemMessage(hDlg, ICO_DLGLOGO, STM_SETIMAGE, IMAGE_ICON, 
+						(LPARAM)((pMsgBox->hiLogo) ? pMsgBox->hiLogo : IcoLib_GetIcon(ICO_DLG_DETAILS)));
+
+					// anable headerbar
+					ShowWindow(GetDlgItem(hDlg, TXT_NAME), SW_SHOW);
+					ShowWindow(GetDlgItem(hDlg, ICO_DLGLOGO), SW_SHOW);
+				}
+				else
+				{
+					RECT rc;
+					GetClientRect(GetDlgItem(hDlg, TXT_NAME), &rc);
+					InfoBarHeight = rc.bottom;
+					
+					if (pMsgBox->hiLogo)
+					{
+						SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)pMsgBox->hiLogo);
+					}
+				}
+
+				// draw the desired status icon
+				HICON hIcon = MsgLoadIcon(pMsgBox);
+				if (hIcon)
+				{
+					SendDlgItemMessage(hDlg, ICO_MSGDLG, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+				}
+				else
+				{
+					RECT ws;
+					GetWindowRect(GetDlgItem(hDlg, ICO_MSGDLG), &ws);
+					icoWidth = ws.right - ws.left;
+					ShowWindow(GetDlgItem(hDlg, ICO_MSGDLG), SW_HIDE);
+				}
+
+				// resize the messagebox and reorganize the buttons
+				if (HDC hDC = GetDC(hDlg))
+				{
+					POINT mpt = {0,0};
+					RECT	ws = {0,0,0,0};
+					INT	 txtWidth,
+								txtHeight,
+								needX, needY;
+					RECT	rcDlg;
+					SIZE	ts;
+					LPTSTR h, rs;
+
+					SelectObject(hDC, hNormalFont);
+
+					for (rs = h = pMsgBox->ptszMsg, txtHeight = 0, txtWidth = 0; h; h++) 
+					{
+						if (*h == '\n' || *h == '\0') 
+						{
+							GetTextExtentPoint32(hDC, rs, h - rs, &ts);
+							if (ts.cx > txtWidth)
+							{
+								txtWidth = ts.cx;
+							}
+							txtHeight += ts.cy;
+							if (*h == '\0')
+							{
+								break;
+							}
+							rs = h + 1;
+						}
+					}
+					ReleaseDC(hDlg, hDC);
+				
+					// calc new dialog size
+					GetWindowRect(hDlg, &rcDlg);
+					GetWindowRect(GetDlgItem(hDlg, TXT_MESSAGE), &ws);
+					needX = txtWidth - (ws.right - ws.left) - icoWidth;
+					needY = max(0, txtHeight - (ws.bottom - ws.top) + 5);
+					rcDlg.left -= needX/2; rcDlg.right += needX/2;
+					rcDlg.top -= (needY-InfoBarHeight)/2; rcDlg.bottom += (needY-InfoBarHeight)/2;
+					
+					// resize dialog window
+					MoveWindow(hDlg, 
+								rcDlg.left, rcDlg.top, 
+								rcDlg.right - rcDlg.left, 
+								rcDlg.bottom - rcDlg.top,
+								FALSE);
+					ClientToScreen(hDlg, &mpt);
+
+					MoveCtrl(hDlg, STATIC_WHITERECT, -mpt.x, -mpt.y, needX, needY - InfoBarHeight); 
+					MoveCtrl(hDlg, TXT_NAME, -mpt.x, -mpt.y, needX, 0); 
+					MoveCtrl(hDlg, ICO_DLGLOGO, -mpt.x + needX, -mpt.y, 0, 0); 
+					MoveCtrl(hDlg, ICO_MSGDLG, -mpt.x, -mpt.y - InfoBarHeight, 0, 0); 
+					MoveCtrl(hDlg, TXT_MESSAGE, -mpt.x - icoWidth, -mpt.y - InfoBarHeight, needX, needY); 
+					MoveCtrl(hDlg, STATIC_LINE2, -mpt.x, -mpt.y + needY - InfoBarHeight, needX, 0); 
+
+					//
+					// Do pushbutton positioning
+					//
+					{
+						RECT rcOk, rcAll, rcNone, rcCancel;
+						LONG okWidth, caWidth, allWidth, noneWidth, dlgMid;
+
+						// get button rectangles
+						GetWindowRect(GetDlgItem(hDlg, IDOK), &rcOk);
+						OffsetRect(&rcOk, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+						GetWindowRect(GetDlgItem(hDlg, IDALL), &rcAll);
+						OffsetRect(&rcAll, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+						GetWindowRect(GetDlgItem(hDlg, IDNONE), &rcNone);
+						OffsetRect(&rcNone, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+						GetWindowRect(GetDlgItem(hDlg, IDCANCEL), &rcCancel);
+						OffsetRect(&rcCancel, -mpt.x, -mpt.y + needY - InfoBarHeight);
+						 
+						okWidth = rcOk.right - rcOk.left;
+						allWidth = rcAll.right - rcAll.left;
+						noneWidth = rcNone.right - rcNone.left;
+						caWidth = rcCancel.right - rcCancel.left;
+						dlgMid = (rcDlg.right - rcDlg.left) / 2;
+
+						// load button configuration
+						switch (MB_TYPE(pMsgBox->uType))
+						{
+
+						case MB_OK:
+							{
+								rcOk.left = dlgMid - (okWidth / 2);
+								rcOk.right = rcOk.left + okWidth;
+								ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+							}
+							break;
+
+						case MB_OKCANCEL:
+							{
+								retOk = IDRETRY;
+								SetDlgItemText(hDlg, IDOK, LPGENT("OK"));
+								retCancel = IDCANCEL;
+								SetDlgItemText(hDlg, IDCANCEL, LPGENT("Cancel"));
+								rcOk.left = dlgMid - okWidth - 10;
+								rcOk.right = rcOk.left + okWidth;
+								rcCancel.left = dlgMid + 10;
+								rcCancel.right = rcCancel.left + caWidth;
+								ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+							}
+							break;
+
+						case MB_RETRYCANCEL:
+							{
+								retOk = IDRETRY;
+								SetDlgItemText(hDlg, IDOK, LPGENT("Retry"));
+								retCancel = IDCANCEL;
+								SetDlgItemText(hDlg, IDCANCEL, LPGENT("Cancel"));
+								rcOk.left = dlgMid - okWidth - 10;
+								rcOk.right = rcOk.left + okWidth;
+								rcCancel.left = dlgMid + 10;
+								rcCancel.right = rcCancel.left + caWidth;
+								ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+							}
+							break;
+
+						case MB_YESNO:
+							{
+								retOk = IDYES;
+								SetDlgItemText(hDlg, IDOK, LPGENT("Yes"));
+								retCancel = IDNO;
+								SetDlgItemText(hDlg, IDCANCEL, LPGENT("No"));
+								rcOk.left = dlgMid - okWidth - 10;
+								rcOk.right = rcOk.left + okWidth;
+								rcCancel.left = dlgMid + 10;
+								rcCancel.right = rcCancel.left + caWidth;
+								ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+							}
+							break;
+
+						case MB_ABORTRETRYIGNORE:
+							{
+								retOk = IDABORT;
+								SetDlgItemText(hDlg, IDOK, LPGENT("Abord"));
+								retAll = IDABORT;
+								SetDlgItemText(hDlg, IDALL, LPGENT("Retry"));
+								retCancel = IDCANCEL;
+								SetDlgItemText(hDlg, IDCANCEL, LPGENT("Ignore"));
+								rcAll.left = dlgMid - (allWidth / 2);
+								rcAll.right = rcAll.left + allWidth;
+								rcOk.left = rcAll.left - okWidth - 5;
+								rcOk.right = rcOk.left + okWidth;
+								rcCancel.left = rcAll.right + 5;
+								rcCancel.right = rcCancel.left + caWidth;
+								ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+							}
+							break;
+
+						case MB_YESNOCANCEL:
+							{
+								retOk = IDYES;
+								SetDlgItemText(hDlg, IDOK, LPGENT("Yes"));
+								retAll = IDNO;
+								SetDlgItemText(hDlg, IDALL, LPGENT("No"));
+								retCancel = IDCANCEL;
+								SetDlgItemText(hDlg, IDCANCEL, LPGENT("Cancel"));
+								rcAll.left = dlgMid - (allWidth / 2);
+								rcAll.right = rcAll.left + allWidth;
+								rcOk.left = rcAll.left - okWidth - 5;
+								rcOk.right = rcOk.left + okWidth;
+								rcCancel.left = rcAll.right + 5;
+								rcCancel.right = rcCancel.left + caWidth;
+								ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+							}
+							break;
+
+						case MB_YESALLNO:
+							{
+								retOk = IDYES;
+								SetDlgItemText(hDlg, IDOK, LPGENT("Yes"));
+								retAll = IDALL;
+								SetDlgItemText(hDlg, IDALL, LPGENT("All"));
+								//retNon = IDNONE;
+								SetDlgItemText(hDlg, IDNONE, LPGENT("None"));
+								retCancel = IDNO;
+								SetDlgItemText(hDlg, IDCANCEL, LPGENT("No"));
+								rcCancel.right = rcDlg.right - rcDlg.left - 10;
+								rcCancel.left = rcCancel.right - caWidth;
+								rcNone.right = rcCancel.left - 5; 
+								rcNone.left = rcNone.right - noneWidth;
+								rcAll.right = rcNone.left - 5;
+								rcAll.left = rcAll.right - allWidth;
+								rcOk.right = rcAll.left - 5;
+								rcOk.left = rcOk.right - okWidth;
+								// show buttons
+								ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDNONE), SW_SHOW);
+								ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+							}
+							break;
+
+						default:
+							{
+								rcOk.left = dlgMid - (okWidth / 2);
+								rcOk.right = rcOk.left + okWidth;
+							}
+						}
+						// move ok button
+						MoveWindow(GetDlgItem(hDlg, IDOK), 
+							rcOk.left, rcOk.top, 
+							rcOk.right - rcOk.left, rcOk.bottom - rcOk.top,
+							FALSE);
+						// move all button
+						MoveWindow(GetDlgItem(hDlg, IDALL), 
+							rcAll.left, rcAll.top, 
+							rcAll.right - rcAll.left, rcAll.bottom - rcAll.top,
+							FALSE);
+						// move none button
+						MoveWindow(GetDlgItem(hDlg, IDNONE), 
+							rcNone.left, rcNone.top, 
+							rcNone.right - rcNone.left, rcNone.bottom - rcNone.top,
+							FALSE);
+						// move cancel button
+						MoveWindow(GetDlgItem(hDlg, IDCANCEL), 
+							rcCancel.left, rcCancel.top, 
+							rcCancel.right - rcCancel.left, rcCancel.bottom - rcCancel.top,
+							FALSE);
+					} // end* Do pushbutton positioning
+				} // end* resize the messagebox and reorganize the buttons
+
+				// set text's
+				SetWindowText(hDlg, pMsgBox->ptszTitle);
+				SetDlgItemText(hDlg, TXT_NAME, pMsgBox->ptszInfoText);
+				SetDlgItemText(hDlg, TXT_MESSAGE, pMsgBox->ptszMsg);
+
+				TranslateDialogDefault(hDlg);
+				return TRUE;
+			} // end* PtrIsValid(pMsgBox)
+		} // end* WM_INITDIALOG:
+		break;
+
+	case WM_CTLCOLORSTATIC:
+		{
+			switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) 
+			{
+			case STATIC_WHITERECT:
+			case ICO_DLGLOGO:
+			case ICO_MSGDLG:
+			case TXT_MESSAGE:
+			case TXT_NAME:
+				{
+					SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+					return GetSysColor(COLOR_WINDOW);
+				}
+			}
+		}
+		break;
+
+	case WM_COMMAND:
+		{
+			switch (LOWORD(wParam)) 
+			{
+			case IDOK:
+				EndDialog(hDlg, retOk);
+				break;
+			case IDCANCEL:
+				EndDialog(hDlg, retCancel);
+				break;
+			case IDALL:
+				EndDialog(hDlg, retAll);
+				break;
+			case IDNONE:
+				EndDialog(hDlg, retNon);
+			}
+		}
+		break;
+
+	case WM_DESTROY:
+		{
+			DeleteObject((HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0));
+		}
+		break;
+	}
+	return FALSE;
+}
+
+
+/**
+ * Dummi modal MsgBox for popup,
+ * this set call function in wait stait and do not freece miranda main thread
+ * the window is outside the desktop
+ */
+static INT_PTR CALLBACK MsgBoxPop(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg)
+	{
+	case WM_INITDIALOG:
+		{
+			POPUPDATAT_V2	pd;
+			LPMSGPOPUPDATA	pmpd;
+			LPMSGBOX		pMsgBox = (LPMSGBOX)lParam;
+
+			MoveWindow(hDlg,-10,-10,0,0,FALSE);
+			pmpd = (LPMSGPOPUPDATA)mir_alloc(sizeof(MSGPOPUPDATA));
+			if (pmpd)
+				{
+					ZeroMemory(&pd, sizeof(pd));
+					pd.cbSize = sizeof(POPUPDATAT_V2);
+					pd.lchContact = NULL; //(HANDLE)wParam;
+					// icon
+					pd.lchIcon = MsgLoadIcon(pMsgBox);
+					mir_tcsncpy(pd.lptzContactName, pMsgBox->ptszTitle, SIZEOF(pd.lptzContactName));
+					mir_tcsncpy(pd.lptzText, pMsgBox->ptszMsg, SIZEOF(pd.lptzText));
+				
+					// CALLBAC Proc
+					pd.PluginWindowProc = (WNDPROC)PopupProc;
+					// 
+					pd.PluginData = pmpd;
+
+					pd.iSeconds = -1;
+
+					pd.hNotification = NULL;
+					pd.lpActions = pmpd->pa;
+
+					// set color of popup
+					switch (pMsgBox->uType & MB_ICONMASK)
+					{
+					case MB_ICON_ERROR:
+						{
+							pd.colorBack = RGB(200,	10,	 0);
+							pd.colorText = RGB(255, 255, 255);
+						}
+						break;
+
+					case MB_ICON_WARNING:
+						{
+							pd.colorBack = RGB(200, 100,	 0);
+							pd.colorText = RGB(255, 255, 255);
+						}
+						break;
+
+					default:
+						{
+							if (pMsgBox->uType & MB_CUSTOMCOLOR)
+							{
+								pd.colorBack = pMsgBox->colorBack;
+								pd.colorText = pMsgBox->colorText;
+							}
+						}
+					}
+
+					// handle for MakePopupAction
+					pmpd->hDialog = hDlg;
+
+					// active buttons
+					switch (MB_TYPE(pMsgBox->uType))
+					{
+					case MB_OK:
+						{
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDOK);
+						}
+						break;
+
+					case MB_OKCANCEL:
+						{
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDOK);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDCANCEL);
+						}
+						break;
+
+					case MB_RETRYCANCEL:
+						{
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDRETRY);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDCANCEL);
+						}
+						break;
+
+					case MB_YESNO:
+						{
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDYES);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDNO);
+						}
+						break;
+
+					case MB_ABORTRETRYIGNORE:
+						{
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDABORT);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDRETRY);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDIGNORE);
+						}
+						break;
+
+					case MB_YESNOCANCEL:
+						{
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDYES);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDNO);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDCANCEL);
+						}
+						break;
+
+					case MB_YESALLNO:
+						{
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDYES);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDALL);
+							MakePopupAction(pmpd->pa[pd.actionCount++], IDNO);
+						}
+						break;
+
+					} // end* switch
+
+					// create popup
+					CallService(MS_POPUP_ADDPOPUPT, (WPARAM) &pd, APF_NEWDATA);
+					if (MB_TYPE(pMsgBox->uType) == MB_OK)
+					{
+						EndDialog(hDlg, IDOK);
+					}
+				} // end*if (pmpd)
+			break;
+		} // end* WM_INITDIALOG:
+	} // end* switch (uMsg)
+	return FALSE;
+}
+
+/**
+ * This is the message procedure for popup
+ *
+ * @param	hDlg		- window handle
+ * @param	uMsg		- message to handle
+ * @param	wParam		- message specific parameter
+ * @param	lParam		- message specific parameter
+ *
+ * @return	TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
+ **/
+static INT_PTR CALLBACK PopupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg)
+	{
+		case UM_POPUPACTION:
+		{
+			if (HIWORD(wParam) == BN_CLICKED)
+			{
+				LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
+
+				if (pmpd)
+				{
+					switch (LOWORD(wParam))
+					{
+					case IDOK:
+					case IDCANCEL:
+					case IDABORT:
+					case IDRETRY:
+					case IDIGNORE:
+					case IDYES:
+					case IDNO:
+					case IDALL:
+					case IDNONE:
+						{
+							if (IsWindow(pmpd->hDialog))
+								EndDialog(pmpd->hDialog, LOWORD(wParam));
+						}
+					break;
+
+					default:
+						{
+							if (IsWindow(pmpd->hDialog))
+								EndDialog(pmpd->hDialog, IDCANCEL);
+						}
+					}
+				}
+				PUDeletePopUp(hDlg);
+			}
+		}
+		break;
+
+		case UM_FREEPLUGINDATA:
+		{
+			LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
+			if (pmpd > 0) {
+				MIR_FREE(pmpd);
+			}
+			return TRUE; //TRUE or FALSE is the same, it gets ignored.
+		}
+		break;
+
+		default:
+		break;
+	}
+	return DefWindowProc(hDlg, uMsg, wParam, lParam);
+}
+
+/**
+ * This is the service function for external plugins to use the nice messagebox 
+ *
+ * @param	wParam		- HANDLE hContact which can display an avatar for popups
+ * @param	lParam		- MSGBOX structure holding parameters
+ *
+ * @return	The function returns the ID of the clicked button (IDOK, IDCANCEL, ...)
+ *			or -1 on error.
+ **/
+INT_PTR MsgBoxService(WPARAM wParam, LPARAM lParam)
+{
+	INT rc = -1;
+	LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+	// check input
+	if (PtrIsValid(pMsgBox) && pMsgBox->cbSize == sizeof(MSGBOX)) 
+	{
+		// Shall the MessageBox displayed as popup?
+		if (!(pMsgBox->uType & (MB_INFOBAR|MB_NOPOPUP)) &&					// message box can be a popup?
+				ServiceExists(MS_POPUP_ADDPOPUPT) &&						// popups exist?
+				myGlobals.PopUpActionsExist == 1 &&							// popup support ext stuct?
+				(DB::Setting::GetDWord(NULL, "PopUp","Actions", 0) & 1) &&	// popup++ actions on?
+				DB::Setting::GetByte(SET_POPUPMSGBOX, DEFVAL_POPUPMSGBOX)	// user likes popups?
+			)
+		{
+			rc = DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOXDUMMI), pMsgBox->hParent, (DLGPROC)MsgBoxPop, lParam);
+		}
+		else
+		{
+			rc = DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOX), pMsgBox->hParent, (DLGPROC)MsgBoxProc, lParam);
+		}
+	}
+	return rc;
+}
+
+/**
+ * name:	MsgBox
+ * desc:	calls a messagebox 
+ * param:	
+ **/
+INT_PTR CALLBACK MsgBox(HWND hParent, UINT uType, LPTSTR pszTitle, LPTSTR pszInfo, LPTSTR pszFormat, ...)
+{
+	MSGBOX	 mb = {0};
+	TCHAR		tszMsg[MAX_SECONDLINE];
+	va_list	vl;
+
+	va_start(vl, pszFormat);
+	mir_vsntprintf(tszMsg, SIZEOF(tszMsg), TranslateTS(pszFormat), vl);
+	va_end(vl);
+
+	mb.cbSize			= sizeof(MSGBOX);
+	mb.hParent			= hParent;
+	mb.hiLogo			= LoadIcon(ghInst, MAKEINTRESOURCE(IDI_MAIN));
+	mb.hiMsg			= NULL;
+	mb.ptszTitle		= TranslateTS(pszTitle);
+	mb.ptszInfoText		= TranslateTS(pszInfo);
+	mb.ptszMsg			= tszMsg;
+	mb.uType			= uType;
+	return MsgBoxService(NULL, (LPARAM)&mb);
+}
+
+/**
+ * name:	MsgErr
+ * desc:	calls a messagebox 
+ * param:	
+ **/
+INT_PTR CALLBACK MsgErr(HWND hParent, LPCTSTR pszFormat, ...)
+{
+	MSGBOX	mb = {0};
+	TCHAR	tszTitle[MAX_SECONDLINE];
+	TCHAR	tszMsg[MAX_SECONDLINE];
+	va_list	vl;
+
+	mir_sntprintf(tszTitle, SIZEOF(tszMsg),_T("%s - %s") ,_T(MODNAME), TranslateT("Error"));
+
+	va_start(vl, pszFormat);
+	mir_vsntprintf(tszMsg, SIZEOF(tszMsg), TranslateTS(pszFormat), vl);
+	va_end(vl);
+
+	mb.cbSize		= sizeof(MSGBOX);
+	mb.hParent		= hParent;
+	mb.hiLogo		= LoadIcon(ghInst, MAKEINTRESOURCE(IDI_MAIN));
+	mb.hiMsg		= NULL;
+	mb.ptszTitle	= tszTitle;
+	mb.ptszMsg		= tszMsg;
+	mb.uType		= MB_OK|MB_ICON_ERROR;
+	return MsgBoxService(NULL, (LPARAM)&mb);
+}
diff --git a/plugins/UserInfoEx/src/dlg_msgbox.h b/plugins/UserInfoEx/src/dlg_msgbox.h
new file mode 100644
index 0000000000..6b01cd55bf
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_msgbox.h
@@ -0,0 +1,103 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_msgbox.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_DLG_INCLUDED_
+#define _SVC_DLG_INCLUDED_ 1
+
+#define SET_POPUPMSGBOX			"PopupMsgBox"
+#define DEFVAL_POPUPMSGBOX		FALSE
+
+/* UserInfo/MsgBox	v0.1.0.3+
+Some little changed message box for nicer look of miranda's messages or questions :-)
+wParam=hContact				- can be null
+lParam=(_MSGBOX*)pMsg	- structure that holds information about the look of the message dialog
+uType member of _MSGBOX can be a combination of the following values, where most of them are defined in winuser.h:
+MB_OK
+MB_OKCANCEL
+MB_YESALLNO
+MB_YESNO
+For valid icon values use one of the following MB_ICON_...
+Funktion returns: IDOK, IDYES, IDALL, IDNO or IDCANCEL
+*/
+
+/*
+ Defined in winuser.h
+ ********************
+
+#define MB_OK					0x00000000L
+#define MB_OKCANCEL				0x00000001L
+#define MB_ABORTRETRYIGNORE		0x00000002L
+#define MB_YESNOCANCEL			0x00000003L
+#define MB_YESNO				0x00000004L
+#define MB_RETRYCANCEL			0x00000005L
+*/
+#define MB_YESALLNO				0x00000007L
+#define MB_TYPE(p)				((p)&MB_TYPEMASK)	
+
+/*
+valid predefined icon values
+*/
+#define MB_ICON_NONE			0x00000000L //	 0 - no icon
+#define MB_ICON_ERROR			0x00000010L //	16 - error icon
+#define MB_ICON_QUESTION		0x00000020L //	32 - question mark
+#define MB_ICON_WARNING			0x00000030L //	48 - warning
+#define MB_ICON_INFO			0x00000040L //	64 - info
+#define MB_ICON_OTHER			0x00000080L // 240 - use icon _MSGBOX->hiMsg
+#define MB_ICON_INDEX(p)		(((p)&MB_ICONMASK)>>4)
+
+/*
+flags
+*/
+#define MB_INFOBAR				0x00000100L
+#define MB_NOPOPUP				0x00000200L
+#define MB_CUSTOMCOLOR			0x00000300L
+
+typedef struct _MSGBOX 
+{
+	UINT		cbSize;			// size of this structure
+	UINT		uType;			// parameters
+	HICON		hiLogo;			// right upper icon of the info bar
+	HICON		hiMsg;			// icon left next to the message text
+	LPTSTR		ptszTitle;
+	LPTSTR		ptszInfoText;
+	LPTSTR		ptszMsg;
+	HWND		hParent;		// parent window for the messagebox
+	COLORREF	colorBack;		// valid if MB_CUSTOMCOLOR is set 
+	COLORREF	colorText;		// valid if MB_CUSTOMCOLOR is set
+} MSGBOX, *LPMSGBOX;
+
+
+INT_PTR CALLBACK	MsgBox(HWND hParent, UINT uType, LPTSTR pszTitle, LPTSTR pszInfo, LPTSTR pszFormat, ...);
+INT_PTR CALLBACK	MsgErr(HWND hParent, LPCTSTR pszFormat, ...);
+INT_PTR				MsgBoxService(WPARAM wParam, LPARAM lParam);
+
+INT_PTR CALLBACK	PopupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+#endif /* _SVC_DLG_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/dlg_propsheet.cpp b/plugins/UserInfoEx/src/dlg_propsheet.cpp
new file mode 100644
index 0000000000..eee049aea1
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_propsheet.cpp
@@ -0,0 +1,1867 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_propsheet.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System & local includes:
+ **/
+#include "commonheaders.h"
+#include "dlg_propsheet.h"
+#include "psp_base.h"
+#include "svc_ExImport.h"
+#include "svc_reminder.h"
+
+/**
+ * Miranda includes:
+ **/
+#include <m_protocols.h>
+#include <m_icq.h>
+
+#define OPTIONPAGE_OLD_SIZE  (offsetof(OPTIONSDIALOGPAGE, hLangpack))
+
+#define UPDATEANIMFRAMES		20
+
+// internal dialog message handler
+#define M_CHECKONLINE			(WM_USER+10)
+#define HM_PROTOACK				(WM_USER+11)
+#define HM_SETTING_CHANGED		(WM_USER+12)
+#define HM_RELOADICONS			(WM_USER+13)
+#define HM_SETWINDOWTITLE		(WM_USER+14)
+
+#define TIMERID_UPDATING		1
+#ifndef TIMERID_RENAME
+	#define TIMERID_RENAME		2
+#endif
+
+// flags for the PS structure
+#define PSF_CHANGED				0x00000100
+#define PSF_LOCKED				0x00000200
+#define PSF_INITIALIZED			0x00000400
+
+#define INIT_ICONS_NONE			0
+#define INIT_ICONS_OWNER		1
+#define INIT_ICONS_CONTACT		2
+#define INIT_ICONS_ALL			(INIT_ICONS_OWNER|INIT_ICONS_CONTACT)
+
+/***********************************************************************************************************
+ * internal variables
+ ***********************************************************************************************************/
+
+static BOOLEAN bInitIcons			= INIT_ICONS_NONE;
+static HANDLE ghWindowList			= NULL;
+static HANDLE ghDetailsInitEvent	= NULL;
+
+static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+CPsHdr::CPsHdr() :
+_ignore(10, (LIST<TCHAR>::FTSortFunc)_tcscmp)
+{
+	_dwSize = sizeof(*this);
+	_hContact = NULL;
+	_pszProto = NULL;
+	_pszPrefix = NULL;
+	_pPages = NULL;
+	_numPages = 0;
+	_dwFlags = 0;
+	_hImages = NULL;
+}
+
+CPsHdr::~CPsHdr() 
+{
+	INT i;
+
+	// delete data
+	for (i = 0 ; i < _ignore.getCount(); i++)
+	{
+		if (_ignore[i]) mir_free(_ignore[i]);
+	}
+	// delete the list
+	_ignore.destroy();
+}
+
+VOID CPsHdr::Free_pPages() 
+{
+	for ( int i = 0 ; i < _numPages; i++)
+		if (_pPages[i]) delete _pPages[i];
+	_numPages = 0;
+	MIR_FREE(_pPages);
+}
+
+/***********************************************************************************************************
+ * class CPsUpload
+ ***********************************************************************************************************/
+
+class CPsUpload {
+public:
+	enum EPsUpReturn {
+		UPLOAD_CONTINUE = 0,
+		UPLOAD_FINISH = 1,
+		UPLOAD_FINISH_CLOSE = 2
+	};
+
+private:
+	PROTOCOLDESCRIPTOR **_pPd;
+	INT			_numProto;
+	BOOLEAN	_bExitAfterUploading;
+	HANDLE	_hUploading;
+	LPPS		_pPs;
+	
+	/**
+	 * @class	Upload
+	 * @class	CPsUpload
+	 * @desc	start upload process for the current protocol
+	 * @param	none
+	 * @return	0 on success
+	 * @return	1 otherwise
+	 **/
+	INT Upload()
+	{
+		CPsTreeItem *pti;
+
+		// check if icq is online
+		if (!IsProtoOnline((*_pPd)->szName)) {
+			TCHAR		szMsg[MAX_PATH];
+			LPTSTR	ptszProto;
+
+			ptszProto = mir_a2t((*_pPd)->szName);
+			mir_sntprintf(szMsg, SIZEOF(szMsg), TranslateT("Protocol '%s' is offline"), ptszProto);
+			mir_free(ptszProto);
+
+			MsgBox(_pPs->hDlg, MB_ICON_WARNING, TranslateT("Upload Details"), szMsg,
+				TranslateT("You are not currently connected to the ICQ network.\nYou must be online in order to update your information on the server.\n\nYour changes will be saved to database only."));
+		}
+		// start uploading process
+		else {
+			_hUploading = (HANDLE)CallProtoService((*_pPd)->szName, PS_CHANGEINFOEX, CIXT_FULL, NULL);
+			if (_hUploading && _hUploading != (HANDLE)CALLSERVICE_NOTFOUND) {
+				EnableWindow(_pPs->pTree->Window(), FALSE);
+				if (pti = _pPs->pTree->CurrentItem()) {
+					EnableWindow(pti->Wnd(), FALSE);
+				}
+				EnableWindow(GetDlgItem(_pPs->hDlg, IDOK), FALSE);
+				EnableWindow(GetDlgItem(_pPs->hDlg, IDAPPLY), FALSE);
+				mir_snprintf(_pPs->szUpdating, SIZEOF(_pPs->szUpdating), "%s (%s)", Translate("Uploading"), (*_pPd)->szName);
+				ShowWindow(GetDlgItem(_pPs->hDlg, TXT_UPDATING), SW_SHOW);
+				SetTimer(_pPs->hDlg, TIMERID_UPDATING, 100, NULL);
+				return 0;
+			}
+		}
+		return 1;
+	}
+
+public:
+	/**
+	 * @name	CPsUpload
+	 * @class	CPsUpload
+	 * @desc	retrieves the list of installed protocols and initializes the class
+	 * @param	pPs			- the owning propertysheet
+	 * @param	bExitAfter	- whether the dialog is to close after upload or not
+	 * @return	nothing
+	 **/
+	CPsUpload(LPPS pPs, BOOLEAN bExitAfter)
+	{
+		_pPs = pPs;
+		_pPd = NULL;
+		_numProto = 0;
+		_hUploading = NULL;
+		_bExitAfterUploading = bExitAfter;
+	}
+
+	INT UploadFirst() {
+		// create a list of all protocols which support uploading contact information
+		if (CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&_numProto, (LPARAM)&_pPd)) {
+			return _bExitAfterUploading ? UPLOAD_FINISH_CLOSE : UPLOAD_FINISH;
+		}
+		return UploadNext();
+	}
+
+	/**
+	 * @name	~CPsUpload
+	 * @class	CPsUpload
+	 * @desc	clear pointer to the upload object
+	 * @param	none
+	 * @return	nothing
+	 **/
+	~CPsUpload()
+		{ _pPs->pUpload = NULL; }
+
+	/**
+	 * @name	Handle
+	 * @class	CPsUpload
+	 * @desc	returns the handle of the current upload process
+	 * @param	none
+	 * @return	handle of the current upload process
+	 **/
+	__inline HANDLE Handle() const
+		{ return _hUploading; };
+
+	/**
+	 * @name	UploadNext
+	 * @class	CPsUpload
+	 * @desc	Search the next protocol which supports uploading contact information
+	 *			and start uploading. Delete the object if ready
+	 * @param	none
+	 * @return	nothing
+	 **/
+	INT	UploadNext()
+	{
+		CHAR str[MAXMODULELABELLENGTH];
+		while (_pPd && *_pPd && _numProto-- > 0) {
+			if ((*_pPd)->type == PROTOTYPE_PROTOCOL) {
+				mir_strncpy(str, (*_pPd)->szName, MAXMODULELABELLENGTH);
+				mir_strncat(str, PS_CHANGEINFOEX, MAXMODULELABELLENGTH);
+				if (ServiceExists(str) && !Upload()) {
+					_pPd++;
+					return UPLOAD_CONTINUE;
+				}
+			}
+			_pPd++;
+		}
+		return _bExitAfterUploading ? UPLOAD_FINISH_CLOSE : UPLOAD_FINISH;
+	}
+};
+
+/***********************************************************************************************************
+ * propertysheet
+ ***********************************************************************************************************/
+
+/**
+ * @name	SortProc()
+ * @desc	used for sorting the tab pages
+ *
+ * @return	-1 or 0 or 1
+ **/
+static INT SortProc(CPsTreeItem** item1, CPsTreeItem** item2)
+{
+	if (*item1 && *item2) {
+		if ((*item2)->Pos() > (*item1)->Pos()) return -1;
+		if ((*item2)->Pos() < (*item1)->Pos()) return 1;
+	}
+	return 0;
+}
+
+/**
+ * This service routine creates the DetailsDialog
+ * @param	wParam		- handle to contact
+ * @param	lParam		- not used
+ *
+ * @retval	0 on success
+ * @retval	1 on failure
+ **/
+static INT_PTR ShowDialog(WPARAM wParam, LPARAM lParam)
+{
+	HWND hWnd;
+
+	// update some cached settings
+	myGlobals.ShowPropsheetColours = DB::Setting::GetByte(SET_PROPSHEET_SHOWCOLOURS, TRUE);
+	myGlobals.WantAeroAdaption = DB::Setting::GetByte(SET_PROPSHEET_AEROADAPTION, TRUE);
+
+	// allow only one dialog per user
+	if (hWnd = WindowList_Find(ghWindowList, (HANDLE)wParam)) {
+		SetForegroundWindow(hWnd);
+		SetFocus(hWnd);
+	}
+	else {
+		CPsHdr psh;
+		POINT metrics;
+		BOOLEAN bScanMetaSubContacts = FALSE;
+		HICON hDefIcon;
+
+		// init the treeview options
+		if (DB::Setting::GetByte(SET_PROPSHEET_SORTITEMS, FALSE))
+			psh._dwFlags |= PSTVF_SORTTREE;
+		if (DB::Setting::GetByte(SET_PROPSHEET_GROUPS, TRUE))
+			psh._dwFlags |= PSTVF_GROUPS;
+		// create imagelist
+		metrics.x = GetSystemMetrics(SM_CXSMICON);
+		metrics.y = GetSystemMetrics(SM_CYSMICON);
+		if ((psh._hImages = ImageList_Create(metrics.x, metrics.y, (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 0, 1)) == NULL) {
+			MsgErr(NULL, LPGENT("Creating the imagelist failed!"));
+			return 1;
+		}
+
+		hDefIcon = IcoLib_GetIcon(ICO_TREE_DEFAULT);
+		if (!hDefIcon)
+		{
+			hDefIcon = (HICON) LoadImage(ghInst, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, metrics.x, metrics.y, 0);
+		}
+		// add the default icon to imagelist
+		ImageList_AddIcon(psh._hImages, hDefIcon);
+
+		// init contact
+		psh._hContact = (HANDLE)wParam;
+		if (psh._hContact == NULL) {
+			// mark owner icons as initiated
+			bInitIcons |= INIT_ICONS_OWNER;
+			psh._pszProto = NULL;
+			psh._pszPrefix = NULL;
+		}
+		else {
+			// get contact's protocol
+			psh._pszPrefix = psh._pszProto = DB::Contact::Proto((HANDLE)wParam);
+			if (psh._pszProto == NULL) {
+				MsgErr(NULL, LPGENT("Could not find contact's protocol. Maybe it is not active!"));
+				return 1;
+			}
+			// prepare scanning for metacontact's subcontact's pages
+			if (bScanMetaSubContacts = DB::Module::IsMetaAndScan(psh._pszProto))
+				psh._dwFlags |= PSF_PROTOPAGESONLY_INIT;
+		}
+		// add the pages
+		NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, wParam);
+		if (!psh._pPages || !psh._numPages) {
+			MsgErr(NULL, LPGENT("No pages have been added. Canceling dialog creation!"));
+			return 1;
+		}
+		// metacontacts sub pages
+		if (bScanMetaSubContacts)
+		{
+			INT numSubs = DB::MetaContact::SubCount((HANDLE)wParam);
+
+			psh._dwFlags &= ~PSF_PROTOPAGESONLY_INIT;
+			psh._dwFlags |= PSF_PROTOPAGESONLY;
+			for (INT i = 0; i < numSubs; i++) 
+			{
+				psh._hContact = DB::MetaContact::Sub((HANDLE)wParam, i);
+				psh._nSubContact = i;
+				if (psh._hContact) 
+				{
+					psh._pszProto = DB::Contact::Proto(psh._hContact);
+					if ((INT_PTR)psh._pszProto != CALLSERVICE_NOTFOUND)
+						NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact);
+				}
+			}
+			psh._hContact = (HANDLE)wParam;
+		}
+
+		// sort the pages by the position read from database
+		if (!(psh._dwFlags & PSTVF_SORTTREE)) {
+			 qsort(psh._pPages, psh._numPages, sizeof(CPsTreeItem*), 
+				(INT (*)(const VOID*, const VOID*))SortProc);
+		}
+		// create the dialog itself
+		hWnd = CreateDialogParam(ghInst, MAKEINTRESOURCE(IDD_DETAILS), NULL, DlgProc, (LPARAM)&psh);
+		if (!hWnd) {
+			MsgErr(NULL, LPGENT("Details dialog failed to be created. Returning error is %d."), GetLastError());
+		}
+		else
+			MagneticWindows_AddWindow(hWnd);
+	}
+	return 0;
+}
+
+/**
+ * @name	AddPage()
+ * @desc	this adds a new pages
+ * @param	wParam		- The List of pages we want to add the new one to
+ * @param	lParam		- it's the page to add
+ *
+ * @return	0
+ **/
+static INT_PTR AddPage(WPARAM wParam, LPARAM lParam)
+{
+	CPsHdr				*pPsh	= (CPsHdr*)wParam;
+	OPTIONSDIALOGPAGE	*odp	= (OPTIONSDIALOGPAGE*)lParam;
+
+	// check size of the handled structures
+	if (pPsh == NULL || odp == NULL || pPsh->_dwSize != sizeof(CPsHdr))
+		return 1;
+
+	if (odp->cbSize != sizeof(OPTIONSDIALOGPAGE) && odp->cbSize != OPTIONPAGE_OLD_SIZE) {
+		MsgErr(NULL, LPGENT("The Page to add has invalid size %d bytes!"), odp->cbSize);
+		return 1;
+	}
+
+	// try to check whether the flag member is initialized or not
+	odp->flags = odp->flags > (ODPF_UNICODE|ODPF_BOLDGROUPS|ODPF_ICON|PSPF_PROTOPREPENDED) ? 0 : odp->flags;
+
+	if (pPsh->_dwFlags & (PSF_PROTOPAGESONLY|PSF_PROTOPAGESONLY_INIT)) {
+		BOOLEAN bIsUnicode = (odp->flags & ODPF_UNICODE) == ODPF_UNICODE;
+		TCHAR* ptszTitle = bIsUnicode ? mir_tstrdup(odp->ptszTitle) : mir_a2t(odp->pszTitle);
+
+		// avoid adding pages for a meta subcontact, which have been added for a metacontact.
+		if (pPsh->_dwFlags & PSF_PROTOPAGESONLY) {	
+			if (pPsh->_ignore.getIndex(ptszTitle) != -1) {
+				mir_free(ptszTitle);
+				return 0;
+			}
+		}
+		// init ignore list with pages added by metacontact
+		else if (pPsh->_dwFlags & PSF_PROTOPAGESONLY_INIT) 
+			pPsh->_ignore.insert(mir_tstrdup(ptszTitle));
+
+		mir_free(ptszTitle);
+	}
+
+	// create the new tree item
+	CPsTreeItem *pNew = new CPsTreeItem();
+	if (pNew) {
+		if (pNew->Create(pPsh, odp)) {
+			MIR_DELETE(pNew);
+			return 1;
+		}
+
+		// resize the array
+		pPsh->_pPages = (CPsTreeItem**)mir_realloc(pPsh->_pPages, (pPsh->_numPages + 1) * sizeof(CPsTreeItem*));
+		if (pPsh->_pPages != NULL) {
+			pPsh->_pPages[pPsh->_numPages++] = pNew;
+			return 0;
+		}
+		pPsh->_numPages = 0;
+	}
+	return 1;
+}
+
+/**
+ * @name	OnDeleteContact()
+ * @desc	a user was deleted, so need to close its details dialog, if one open
+ *
+ * @return	0
+ **/
+static INT OnDeleteContact(WPARAM wParam, LPARAM lParam)
+{
+	 HWND hWnd = WindowList_Find(ghWindowList, (HANDLE)wParam);
+	 if (hWnd != NULL)
+		 DestroyWindow(hWnd);
+	 return 0;
+}
+
+/**
+ * @name	OnShutdown()
+ * @desc	we need to emptify the windowlist
+ *
+ * @return	0
+ **/
+static INT OnShutdown(WPARAM wParam, LPARAM lParam)
+{
+	 WindowList_BroadcastAsync(ghWindowList, WM_DESTROY, 0, 0);
+	 return 0;
+}
+
+/**
+ * @name	AddProtocolPages()
+ * @desc	is called by Miranda if user selects to display the userinfo dialog
+ * @param	odp			- optiondialogpage structure to use
+ * @param	wParam		- the propertysheet init structure to pass
+ * @param	pszProto	- the protocol name to prepend as item name (can be NULL)
+ *
+ * @return	0
+ **/
+static INT AddProtocolPages(OPTIONSDIALOGPAGE& odp, WPARAM wParam, LPSTR pszProto = NULL)
+{
+	TCHAR szTitle[MAX_PATH];
+	const BYTE ofs = (pszProto) ? mir_sntprintf(szTitle, SIZEOF(szTitle), _T(TCHAR_STR_PARAM) _T("\\"), pszProto) : 0;
+
+	odp.ptszTitle = szTitle;
+	
+	odp.pszTemplate	= MAKEINTRESOURCEA(IDD_CONTACT_GENERAL);
+	odp.position	= 0x8000000;
+	odp.pfnDlgProc	= PSPProcGeneral;
+	odp.hIcon		= (HICON)ICONINDEX(IDI_TREE_GENERAL);
+	mir_tcsncpy(szTitle + ofs, LPGENT("General"), SIZEOF(szTitle) - ofs);
+	AddPage(wParam, (LPARAM)&odp);
+
+	odp.pszTemplate	= MAKEINTRESOURCEA(IDD_CONTACT_ADDRESS);
+	odp.position	= 0x8000001;
+	odp.pfnDlgProc	= PSPProcContactHome;
+	odp.hIcon		= (HICON)ICONINDEX(IDI_TREE_ADDRESS);
+	mir_tcsncpy(szTitle + ofs, LPGENT("General") _T("\\") LPGENT("Contact (private)"), SIZEOF(szTitle) - ofs);
+	AddPage(wParam, (LPARAM)&odp);
+
+	odp.pszTemplate	= MAKEINTRESOURCEA(IDD_CONTACT_ORIGIN);
+	odp.position	= 0x8000002;
+	odp.pfnDlgProc	= PSPProcOrigin;
+	odp.hIcon		= (HICON)ICONINDEX(IDI_TREE_ADVANCED);
+	mir_tcsncpy(szTitle + ofs, LPGENT("General") _T("\\") LPGENT("Origin"), SIZEOF(szTitle) - ofs);
+	AddPage(wParam, (LPARAM)&odp);
+		
+	odp.pszTemplate	= MAKEINTRESOURCEA(IDD_CONTACT_ANNIVERSARY);
+	odp.position	= 0x8000003;
+	odp.pfnDlgProc	= PSPProcAnniversary;
+	odp.hIcon		= (HICON)ICONINDEX(IDI_BIRTHDAY);
+	mir_tcsncpy(szTitle + ofs,  LPGENT("General") _T("\\") LPGENT("Anniversaries"), SIZEOF(szTitle) - ofs);
+	AddPage(wParam, (LPARAM)&odp);
+
+	odp.pszTemplate	= MAKEINTRESOURCEA(IDD_CONTACT_COMPANY);
+	odp.position	= 0x8000004;
+	odp.pfnDlgProc	= PSPProcCompany;
+	odp.hIcon		= (HICON)ICONINDEX(IDI_TREE_COMPANY);
+	mir_tcsncpy(szTitle + ofs, LPGENT("Work"), SIZEOF(szTitle) - ofs);
+	AddPage(wParam, (LPARAM)&odp);
+
+	odp.pszTemplate	= MAKEINTRESOURCEA(IDD_CONTACT_ADDRESS);
+	odp.position	= 0x8000005;
+	odp.pfnDlgProc	= PSPProcContactWork;
+	odp.hIcon		= (HICON)ICONINDEX(IDI_TREE_ADDRESS);
+	mir_tcsncpy(szTitle + ofs, LPGENT("Work") _T("\\") LPGENT("Contact (Work)"), SIZEOF(szTitle) - ofs);
+	AddPage(wParam, (LPARAM)&odp);
+		
+	odp.pszTemplate	= MAKEINTRESOURCEA(IDD_CONTACT_ABOUT);
+	odp.position	= 0x8000006;
+	odp.pfnDlgProc	= PSPProcAbout;
+	odp.hIcon		= (HICON)ICONINDEX(IDI_TREE_ABOUT);
+	mir_tcsncpy(szTitle + ofs, LPGENT("About"), SIZEOF(szTitle) - ofs);
+	AddPage(wParam, (LPARAM)&odp);
+
+	odp.pszTemplate	= MAKEINTRESOURCEA(IDD_CONTACT_PROFILE);
+	odp.position	= 0x8000007;
+	odp.pfnDlgProc	= PSPProcContactProfile;
+	odp.hIcon		= (HICON)ICONINDEX(IDI_TREE_PROFILE);
+	mir_tcsncpy(szTitle + ofs, LPGENT("About") _T("\\") LPGENT("Profile"), SIZEOF(szTitle) - ofs);
+	AddPage(wParam, (LPARAM)&odp);
+	return 0;
+}
+
+/**
+ * @name	InitDetails
+ * @desc	is called by Miranda if user selects to display the userinfo dialog
+ * @param	wParam		- the propertysheet init structure to pass
+ * @param	lParam		- handle to contact whose information are read
+ *
+ * @return	0
+ **/
+static INT InitDetails(WPARAM wParam, LPARAM lParam)
+{
+	CPsHdr* pPsh = (CPsHdr*)wParam;
+
+	if (!(pPsh->_dwFlags & PSF_PROTOPAGESONLY)) {
+
+		OPTIONSDIALOGPAGE odp;
+		BOOLEAN bChangeDetailsEnabled = myGlobals.CanChangeDetails && DB::Setting::GetByte(SET_PROPSHEET_CHANGEMYDETAILS, FALSE);
+		
+		// important to avoid craches!!
+		ZeroMemory(&odp, sizeof(odp));
+
+		if (lParam || bChangeDetailsEnabled) {
+			odp.cbSize = sizeof(odp);
+			odp.hInstance = ghInst;
+			odp.flags = ODPF_ICON | ODPF_TCHAR;
+			odp.ptszGroup = IcoLib_GetDefaultIconFileName();
+
+			if (lParam) {
+
+				// ignore common pages for weather contacts
+				if (!pPsh->_pszProto || stricmp(pPsh->_pszProto, "weather")) 
+				{
+					AddProtocolPages(odp, wParam);
+					odp.ptszTitle = LPGENT("About") _T("\\") LPGENT("Notes");
+				}
+				else {
+					odp.ptszTitle = LPGENT("Notes");
+				}
+				odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ABOUT);
+				odp.position = 0x8000008;
+				odp.pfnDlgProc = PSPProcMyNotes;
+				odp.hIcon = (HICON)ICONINDEX(IDI_TREE_NOTES);
+				AddPage(wParam, (LPARAM)&odp);
+			}
+			/* Editing owner details no longer supported due to leak of common interface for all protocols.
+			else 
+			if (!(pPsh->_dwFlags & PSTVF_INITICONS))
+			{
+				PROTOCOLDESCRIPTOR **pd;
+				INT ProtoCount, i;
+				CHAR str[MAXMODULELABELLENGTH];
+
+				odp.flags |= PSPF_PROTOPREPENDED;
+
+				// create a list of all protocols which support uploading contact information
+				if (!CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&ProtoCount, (LPARAM)&pd)) {
+					for (i = 0; i < ProtoCount; i++) {
+						if (pd[i]->type == PROTOTYPE_PROTOCOL) {
+							pPsh->_pszProto = pd[i]->szName;
+							mir_snprintf(str, MAXMODULELABELLENGTH, "%s"PS_CHANGEINFOEX, pd[i]->szName);
+							if (ServiceExists(str)) AddProtocolPages(odp, wParam, pd[i]->szName);
+						}
+					}
+				}
+			}
+			*/
+		}
+	}
+	return 0;
+}
+
+/**
+ * @name	InitTreeIcons()
+ * @desc	initalize all treeview icons
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID DlgContactInfoInitTreeIcons()
+{
+	// make sure this is run only once
+	if (!(bInitIcons & INIT_ICONS_ALL)) {
+		CPsHdr	psh;
+		POINT	metrics	= {0};
+		INT		i		= 0;
+
+		psh._dwFlags = PSTVF_INITICONS;
+
+		metrics.x = GetSystemMetrics(SM_CXSMICON);
+		metrics.y = GetSystemMetrics(SM_CYSMICON);
+		if (psh._hImages = ImageList_Create(metrics.x, metrics.y, (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 0, 1)) {
+			HICON hDefIcon = IcoLib_GetIcon(ICO_TREE_DEFAULT);
+			if (!hDefIcon)
+			{
+				hDefIcon = (HICON) LoadImage(ghInst, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, metrics.x, metrics.y, 0);
+			}
+			// add the default icon to imagelist
+			ImageList_AddIcon(psh._hImages, hDefIcon);
+		}
+
+		// avoid pages from loading doubled
+		if (!(bInitIcons & INIT_ICONS_CONTACT)) {
+			LPCSTR pszContactProto = NULL;
+			PROTOCOLDESCRIPTOR **pd;
+			INT ProtoCount = 0;
+
+			psh._dwFlags |= PSF_PROTOPAGESONLY_INIT;
+			
+			// enumerate all protocols
+			if (!CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&ProtoCount, (LPARAM)&pd)) {
+				for (i = 0; i < ProtoCount; i++) {
+					if (pd[i]->type == PROTOTYPE_PROTOCOL) {
+						// enumerate all contacts
+						for (psh._hContact = DB::Contact::FindFirst();
+							psh._hContact != NULL;
+							psh._hContact = DB::Contact::FindNext(psh._hContact))
+						{
+							// compare contact's protocol to the current one, to add
+							pszContactProto = DB::Contact::Proto(psh._hContact);
+							if ((INT_PTR)pszContactProto != CALLSERVICE_NOTFOUND && !mir_strcmp(pd[i]->szName, pszContactProto)) {
+								// call a notification for the contact to retrieve all protocol specific tree items
+								NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact);
+								if (psh._pPages) {
+									psh.Free_pPages();
+									psh._dwFlags = PSTVF_INITICONS|PSF_PROTOPAGESONLY;
+								}
+								break;
+							}
+						}
+					}
+				}
+			}
+			bInitIcons |= INIT_ICONS_CONTACT;
+		}
+		// load all treeitems for owner contact
+		if (!(bInitIcons & INIT_ICONS_OWNER)) {
+			psh._hContact = NULL;
+			psh._pszProto = NULL;
+			NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact);
+			if (psh._pPages) {
+				psh.Free_pPages();
+			}
+			bInitIcons |= INIT_ICONS_OWNER;
+		}
+		ImageList_Destroy(psh._hImages);
+	}
+}
+
+/**
+ * @name	UnLoadModule()
+ * @desc	unload the UserInfo Module
+ *
+ * @return	nothing
+ **/
+VOID DlgContactInfoUnLoadModule()
+{
+	DestroyHookableEvent(ghDetailsInitEvent);
+}
+
+/**
+ * @name	LoadModule()
+ * @desc	load the UserInfo Module
+ *
+ * @return	nothing
+ **/
+VOID DlgContactInfoLoadModule()
+{
+	ghDetailsInitEvent = CreateHookableEvent(ME_USERINFO_INITIALISE);
+
+	CreateServiceFunction(MS_USERINFO_SHOWDIALOG, ShowDialog);
+	CreateServiceFunction("UserInfo/AddPage", AddPage);
+
+	HookEvent(ME_DB_CONTACT_DELETED, OnDeleteContact);
+	HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown);
+	HookEvent(ME_USERINFO_INITIALISE, InitDetails);
+	ghWindowList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0);
+
+	// check whether changing my details via UserInfoEx is basically possible
+	{
+		PROTOACCOUNT **pAcc;
+		INT i, nAccCount;
+		
+		myGlobals.CanChangeDetails = FALSE;
+		if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc)))
+		{
+			for (i = 0; (i < nAccCount) && !myGlobals.CanChangeDetails; i++) 
+			{
+				if (IsProtoAccountEnabled(pAcc[i])) 
+				{
+					// update my contact information on icq server
+					myGlobals.CanChangeDetails = MIREXISTS(CallProtoService(pAcc[i]->szModuleName, PS_CHANGEINFOEX, NULL, NULL));
+				}
+			}
+		}
+	}
+}
+
+static void ResetUpdateInfo(LPPS pPs)
+{
+	INT i;
+
+	// free the array of accomblished acks
+	for (i = 0; i < (INT)pPs->nSubContacts; i++)
+		MIR_FREE(pPs->infosUpdated[i].acks);
+	MIR_FREE(pPs->infosUpdated);
+	pPs->nSubContacts = 0;
+}
+
+/*
+============================================================================================
+	PropertySheet's Dialog Procedures
+============================================================================================
+*/
+
+/**
+ * @name	DlgProc()
+ * @desc	dialog procedure for the main propertysheet dialog box
+ *
+ * @return	0 or 1
+ **/
+static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	LPPS pPs = (LPPS)GetUserData(hDlg);
+		 
+	// do not process any message if pPs is no longer existent
+	if (!PtrIsValid(pPs) && uMsg != WM_INITDIALOG)
+		return FALSE;
+
+	switch (uMsg)
+	{
+	/**
+	 * @class	WM_INITDIALOG
+	 * @desc	initiates all dialog controls
+	 * @param	wParam - not used
+	 *				lParam - pointer to a PSHDR structure, which contains all information to create the dialog
+	 *
+	 * @return	TRUE if everything is ok, FALSE if dialog creation should fail
+	 **/
+	case WM_INITDIALOG:
+		{
+			CPsHdr*	pPsh = (CPsHdr*)lParam;
+			WORD	needWidth = 0;
+			RECT	rc;
+			
+			if (!pPsh || pPsh->_dwSize != sizeof(CPsHdr))
+				return FALSE;
+
+			TranslateDialogDefault(hDlg);
+
+			//
+			// create data structures
+			//
+			if (!(pPs = (LPPS)mir_alloc(sizeof(PS))))
+				return FALSE;
+			ZeroMemory(pPs, sizeof(PS));
+
+			if (!(pPs->pTree = new CPsTree(pPs)))
+				return FALSE;
+			if (!(pPs->pTree->Create(GetDlgItem(hDlg, STATIC_TREE), pPsh))) {
+				return FALSE;
+			}
+			SetUserData(hDlg, pPs);
+			pPs->hDlg = hDlg;
+			pPs->dwFlags |= PSF_LOCKED;
+			pPs->hContact = pPsh->_hContact;
+			pPs->hProtoAckEvent = HookEventMessage(ME_PROTO_ACK, hDlg, HM_PROTOACK);
+			pPs->hSettingChanged = HookEventMessage(ME_DB_CONTACT_SETTINGCHANGED, hDlg, HM_SETTING_CHANGED);
+			pPs->hIconsChanged = HookEventMessage(ME_SKIN2_ICONSCHANGED, hDlg, HM_RELOADICONS);
+
+			ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG),IsAeroMode());
+			ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG2),!IsAeroMode());
+			
+			//
+			// set icons
+			//
+			SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(ghInst, MAKEINTRESOURCE(IDI_MAIN)));
+			DlgProc(hDlg, HM_RELOADICONS, NULL, NULL);
+
+			//
+			// load basic protocol for current contact (for faster load later on and better handling for owner protocol)
+			//
+			if (pPs->hContact) mir_strncpy(pPs->pszProto, pPsh->_pszPrefix, MAXMODULELABELLENGTH);
+
+			// set the windowtitle
+			DlgProc(hDlg, HM_SETWINDOWTITLE, NULL, NULL);
+			
+			// translate Userinfo buttons
+			{
+				SendDlgItemMessage(hDlg, BTN_UPDATE, BUTTONTRANSLATE, NULL, NULL);
+				SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
+				SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+				SendDlgItemMessage(hDlg, IDAPPLY, BUTTONTRANSLATE, NULL, NULL);
+				SendDlgItemMessage(hDlg, BTN_EXPORT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Export to file"), MBF_TCHAR);
+				SendDlgItemMessage(hDlg, BTN_IMPORT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Import from file"), MBF_TCHAR);
+			}
+
+			//
+			// set bold font for name in description area
+			//
+			{
+				LOGFONT lf;
+				HFONT hNormalFont = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0);
+
+				GetObject(hNormalFont, sizeof(lf), &lf);
+				lf.lfHeight = 22;
+				mir_tcscpy(lf.lfFaceName, _T("Segoe UI"));
+				pPs->hCaptionFont = CreateFontIndirect(&lf);
+				SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)pPs->hCaptionFont, 0);
+
+				GetObject(hNormalFont, sizeof(lf), &lf);
+				lf.lfWeight = FW_BOLD;
+				pPs->hBoldFont = CreateFontIndirect(&lf);
+			}
+
+			//
+			// initialize the optionpages and tree control
+			//
+			if (!pPs->pTree->InitTreeItems((LPWORD)&needWidth))
+				return FALSE;
+
+			//
+			// move and resize dialog and its controls
+			//
+			{
+				HWND hCtrl;
+				RECT rcTree;
+				POINT pt = { 0, 0 };
+				INT addWidth = 0;
+
+				// at least add width of scrollbar
+				needWidth += 8 + GetSystemMetrics(SM_CXVSCROLL);
+
+				// get tree rectangle
+				GetWindowRect(hDlg, &pPs->rcDisplay);
+				GetWindowRect(pPs->pTree->Window(), &rcTree);
+				ClientToScreen(hDlg, &pt);
+				OffsetRect(&rcTree, -pt.x, -pt.y);
+
+				// calculate the amout of pixels to resize the dialog by?
+				if (needWidth > rcTree.right - rcTree.left) {
+					RECT rcMax = { 0, 0, 0, 0 };
+
+					rcMax.right = 280;
+					MapDialogRect(hDlg, &rcMax);
+					addWidth = min(needWidth, rcMax.right) - rcTree.right + rcTree.left;
+					rcTree.right += addWidth;
+					// resize tree
+					MoveWindow(pPs->pTree->Window(), rcTree.left, rcTree.top, rcTree.right - rcTree.left, rcTree.bottom - rcTree.top, FALSE);
+
+					pPs->rcDisplay.right += addWidth;
+					MoveWindow(hDlg, pPs->rcDisplay.left, pPs->rcDisplay.top, 
+						pPs->rcDisplay.right - pPs->rcDisplay.left, 
+						pPs->rcDisplay.bottom - pPs->rcDisplay.top, FALSE);
+
+				}
+				GetClientRect(GetDlgItem(hDlg, IDC_PAGETITLEBG), &rc);
+				// calculate dislpay area for pages
+				OffsetRect(&pPs->rcDisplay, -pt.x, -pt.y);
+				pPs->rcDisplay.bottom = rcTree.bottom;
+				pPs->rcDisplay.left = rcTree.right + 2;
+				pPs->rcDisplay.top = rcTree.top+rc.bottom;
+				pPs->rcDisplay.right -= 2;
+
+				// move and resize the rest of the controls
+				if (addWidth > 0) {
+					const WORD	idResize[] = { IDC_HEADERBAR, STATIC_LINE2 };
+					const WORD	idMove[] = { IDC_PAGETITLE, IDC_PAGETITLEBG, IDC_PAGETITLEBG2, IDOK, IDCANCEL, IDAPPLY };
+					WORD i;
+
+					for (i = 0; i < SIZEOF(idResize); i++) {
+						if (hCtrl = GetDlgItem(hDlg, idResize[i])) {
+							GetWindowRect(hCtrl, &rc);
+							OffsetRect(&rc, -pt.x, -pt.y);
+							MoveWindow(hCtrl, rc.left, rc.top, rc.right - rc.left + addWidth, rc.bottom - rc.top, FALSE);
+						}
+					}						
+					for (i = 0; i < SIZEOF(idMove); i++) {
+						if (hCtrl = GetDlgItem(hDlg, idMove[i])) {
+							GetWindowRect(hCtrl, &rc);
+							OffsetRect(&rc, -pt.x, -pt.y);
+							MoveWindow(hCtrl, rc.left + addWidth, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
+						}
+					}
+				}
+				// restore window position and add required size
+				Utils_RestoreWindowPositionNoSize(hDlg, NULL, MODNAME, "DetailsDlg");
+			}
+
+			//
+			// show the first propsheetpage
+			//
+			// finally add the dialog to the window list
+			WindowList_Add(ghWindowList, hDlg, pPs->hContact);
+
+			// show the dialog
+			pPs->dwFlags &= ~PSF_LOCKED;
+			pPs->dwFlags |= PSF_INITIALIZED;
+
+
+			//
+			// initialize the "updating" button and statustext and check for online status
+			//
+			pPs->updateAnimFrame = 0;
+			if (pPs->hContact && *pPs->pszProto) 
+			{
+				GetDlgItemTextA(hDlg, TXT_UPDATING, pPs->szUpdating, SIZEOF(pPs->szUpdating));
+				ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+				
+				if (DlgProc(hDlg, M_CHECKONLINE, NULL, NULL)) 
+				{
+					DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(BTN_UPDATE, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, BTN_UPDATE));
+				}
+			}
+
+
+			{
+				INT nPage = pPs->pTree->CurrentItemIndex();
+				if (!pPs->pTree->IsIndexValid(nPage))
+					nPage = 0;
+				TreeView_Select(pPs->pTree->Window(), NULL, TVGN_CARET);
+				TreeView_Select(pPs->pTree->Window(), pPs->pTree->TreeItemHandle(nPage), TVGN_CARET);
+				ShowWindow(hDlg, SW_SHOW);
+			}
+		}
+		return TRUE;
+
+	/**
+	 * @class	WM_TIMER
+	 * @desc	is called to display the "updating" text in the status area
+	 * @param	wParam - not used
+	 *			lParam - not used
+	 *
+	 * @return	always FALSE
+	 **/
+	case WM_TIMER:
+		switch (wParam) 
+		{
+		case TIMERID_UPDATING:
+			{
+				CHAR str[84];
+
+				mir_snprintf(str, SIZEOF(str), "%.*s%s%.*s", pPs->updateAnimFrame%10, ".........", pPs->szUpdating, pPs->updateAnimFrame%10, ".........");
+				SetDlgItemTextA(hDlg, TXT_UPDATING, str);
+				if (++pPs->updateAnimFrame == UPDATEANIMFRAMES)
+					pPs->updateAnimFrame = 0;
+				return FALSE;
+			}
+		}
+		break;
+
+	case 0x031E: /*WM_DWMCOMPOSITIONCHANGED:*/
+		{
+			ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG),IsAeroMode());
+			InvalidateRect(hDlg, NULL, TRUE);
+		}
+		break;
+
+	/**
+	 * @class	WM_CTLCOLORSTATIC
+	 * @desc	sets the colour of some of the dialog's static controls
+	 * @param	wParam - HWND of the contrls
+	 *			lParam - HDC for drawing
+	 *
+	 * @return	StockObject
+	 **/
+	case WM_CTLCOLORSTATIC:
+		switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) 
+		{
+		case TXT_UPDATING:
+			{
+				COLORREF textCol, bgCol, newCol;
+				INT ratio;
+
+				textCol = GetSysColor(COLOR_BTNTEXT);
+				bgCol = GetSysColor(COLOR_3DFACE);
+				ratio = abs(UPDATEANIMFRAMES/2 - pPs->updateAnimFrame) * 510 / UPDATEANIMFRAMES;
+				newCol = RGB(GetRValue(bgCol) + (GetRValue(textCol) - GetRValue(bgCol)) * ratio / 256,
+						GetGValue(bgCol) + (GetGValue(textCol) - GetGValue(bgCol)) * ratio / 256,
+						GetBValue(bgCol) + (GetBValue(textCol) - GetBValue(bgCol)) * ratio / 256);
+				SetTextColor((HDC)wParam, newCol);
+				SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE));
+			}
+			return (INT_PTR)GetSysColorBrush(COLOR_3DFACE);
+		case IDC_PAGETITLE:
+		case IDC_PAGETITLEBG:
+			{
+				if (IsAeroMode())
+				{
+					SetTextColor((HDC)wParam, RGB(0,90,180));
+					SetBkColor((HDC)wParam, RGB(255, 255, 255));
+					return (INT_PTR)GetStockObject(WHITE_BRUSH);
+				}
+				else
+				{
+					SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE));
+					return (INT_PTR)GetSysColorBrush(COLOR_3DFACE);
+				}
+			}
+		}
+		SetBkMode((HDC)wParam, TRANSPARENT);
+		return (INT_PTR)GetStockObject(NULL_BRUSH);
+
+	/**
+	 * @class	PSM_CHANGED
+	 * @desc	indicates the propertysheet and the current selected page as changed
+	 * @param	wParam - not used
+	 *			lParam - not used
+	 *
+	 * @return	TRUE if successful FALSE if the dialog is locked at the moment
+	 **/
+	case PSM_CHANGED:
+		if (!(pPs->dwFlags & PSF_LOCKED)) 
+		{	 
+			pPs->dwFlags |= PSF_CHANGED;
+			pPs->pTree->CurrentItem()->AddFlags(PSPF_CHANGED);
+			EnableWindow(GetDlgItem(hDlg, IDAPPLY), TRUE);
+			return TRUE;
+		}
+		break;
+
+	/**
+	 * @class	PSM_GETBOLDFONT
+	 * @desc	returns the bold font
+	 * @param	wParam - not used
+	 *			lParam - pointer to a HFONT, which takes the boldfont
+	 *
+	 * @return	TRUE if successful, FALSE otherwise
+	 **/
+	case PSM_GETBOLDFONT:
+		if (pPs->hBoldFont && lParam) 
+		{
+			*(HFONT*)lParam = pPs->hBoldFont;
+			SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->hBoldFont);
+			return TRUE;
+		}
+		*(HFONT*)lParam = NULL;
+		SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0l);
+		break;
+
+	/**
+	 * @class	PSM_GETCONTACT
+	 * @desc	returns the handle to the contact, associated with this propertysheet
+	 * @param	wParam - index or -1 for current item
+	 *			lParam - pointer to a HANDLE, which takes the contact handle
+	 *
+	 * @return	TRUE if successful, FALSE otherwise
+	 **/
+	case PSM_GETCONTACT:
+		if (lParam) 
+		{
+			CPsTreeItem *pti = ((INT)wParam != -1) 
+				? pPs->pTree->TreeItem((INT)wParam)
+				: pPs->pTree->CurrentItem();
+
+			// prefer to return the contact accociated with the current page
+			if (pti && pti->hContact() != INVALID_HANDLE_VALUE) {
+				*(HANDLE*)lParam = pti->hContact();
+				SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->hContact());
+				return TRUE;
+			}
+			
+			// return contact who owns the details dialog
+			if (pPs->hContact != INVALID_HANDLE_VALUE) {
+				*(HANDLE*)lParam = pPs->hContact;
+				SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->hContact);
+				return TRUE;
+			}
+			*(HANDLE*)lParam = NULL;
+			SetWindowLongPtr(hDlg, DWLP_MSGRESULT, NULL);
+		}
+		break;
+
+	/**
+	 * @class	PSM_GETBASEPROTO
+	 * @desc	returns the basic protocol module for the associated contact
+	 * @param	wParam - index or -1 for current item
+	 *			lParam - pointer to a LPCSTR which takes the protocol string pointer
+	 *
+	 * @return	TRUE if successful, FALSE otherwise
+	 **/
+	case PSM_GETBASEPROTO:
+		if (lParam) {
+			CPsTreeItem *pti = ((INT)wParam != -1) 
+				? pPs->pTree->TreeItem((INT)wParam)
+				: pPs->pTree->CurrentItem();
+			
+			if (pti && pti->Proto()) {
+				// return custom protocol for the current page
+				*(LPCSTR*)lParam = pti->Proto();
+				SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->Proto());
+				return TRUE;
+			}
+
+			if (*pPs->pszProto) {
+				// return global protocol
+				*(LPSTR*)lParam = pPs->pszProto;
+				SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->pszProto);
+				return TRUE;
+			}
+		}
+		*(LPCSTR*)lParam = NULL;
+		SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0l);
+		break;
+
+	/**
+	 * @class	PSM_ISLOCKED
+	 * @desc	returns the lock state of the propertysheetpage
+	 * @param	wParam - not used
+	 *			lParam - not used
+	 *
+	 * @return	TRUE if propertysheet is locked, FALSE if not
+	 **/
+	case PSM_ISLOCKED:
+	{
+		BOOLEAN bLocked = (pPs->dwFlags & PSF_LOCKED) == PSF_LOCKED;
+
+		SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bLocked);
+		return bLocked;
+	}
+
+	/**
+	 * @class	PSM_FORCECHANGED
+	 * @desc	force all propertysheetpages to update their controls with new values from the database
+	 * @param	wParam - whether to replace changed settings too or not
+		*		lParam - not used
+	 *
+	 * @return	always FALSE
+	 **/
+	case PSM_FORCECHANGED:
+		if (!(pPs->dwFlags & PSF_LOCKED)) { 
+			BOOLEAN bChanged;
+
+			pPs->dwFlags |= PSF_LOCKED;
+			if (bChanged = pPs->pTree->OnInfoChanged())
+				pPs->dwFlags |= PSF_CHANGED;
+			else
+				pPs->dwFlags &= ~PSF_CHANGED;
+			pPs->dwFlags &= ~PSF_LOCKED;
+			EnableWindow(GetDlgItem(hDlg, IDAPPLY), bChanged);
+		}
+		break;
+
+	/**
+	 * @class	PSM_DLGMESSAGE
+	 * @desc	Sends a message to a specified propertysheetpage
+	 * @param	wParam - not used
+	 *			lParam - LPDLGCOMMAND structure, which contains information about the message to forward
+	 *
+	 * @return	E_FAIL if the page was not found
+	 **/
+	case PSM_DLGMESSAGE:
+	{
+		LPDLGCOMMAND pCmd = (LPDLGCOMMAND)lParam;
+		CPsTreeItem *pti;
+		
+		if (pCmd && (pti = pPs->pTree->FindItemByResource(pCmd->hInst, pCmd->idDlg)) &&	pti->Wnd()) {
+			if (!pCmd->idDlgItem)
+				return SendMessage(pti->Wnd(), pCmd->uMsg, pCmd->wParam, pCmd->lParam);
+			else
+				return SendDlgItemMessage(pti->Wnd(), pCmd->idDlgItem, pCmd->uMsg, pCmd->wParam, pCmd->lParam);
+		}
+		return E_FAIL;
+	}
+
+	/**
+	 * @class	PSM_GETPAGEHWND
+	 * @desc	get the window handle for a specified propertysheetpage
+	 * @param	wParam - recource id of the dialog recource
+	 *			lParam - hinstance of the plugin, which created the dialog box
+	 *
+	 * @return	TRUE if handle was found and dialog was created before, false otherwise
+	 **/
+	case PSM_GETPAGEHWND:
+		{
+			CPsTreeItem *pti;
+			if (pti = pPs->pTree->FindItemByResource((HINSTANCE)lParam, wParam)) {
+				SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->Wnd());
+				return (pti->Wnd() != NULL);
+			}
+		}
+		return FALSE;
+
+	case PSM_ISAEROMODE:
+		{
+			BOOLEAN bIsAeroMode = IsAeroMode();
+			if (lParam)
+			{
+				*(BOOLEAN*)lParam = bIsAeroMode;
+			}
+			return (INT_PTR)bIsAeroMode;
+		}
+
+	/**
+	 * @class	HM_SETWINDOWTITLE
+	 * @desc	set the window title and text of the infobar
+	 * @param	wParam - not used
+	 *			lParam - DBCONTACTWRITESETTING structure if called by HM_SETTING_CHANGED message handler
+	 *
+	 * @return	FALSE
+	 **/
+	case HM_SETWINDOWTITLE:
+	{
+		LPCTSTR pszName = NULL; 
+		TCHAR	newTitle[MAX_PATH];
+		RECT	rc;
+		POINT	pt = { 0, 0 };
+		HWND	hName = GetDlgItem(hDlg, TXT_NAME);
+		DBCONTACTWRITESETTING* pdbcws = (DBCONTACTWRITESETTING*)lParam;
+
+		if (!pPs->hContact)
+			pszName = TranslateT("Owner");
+		else
+		if (pdbcws && pdbcws->value.type == DBVT_TCHAR)
+			pszName = pdbcws->value.ptszVal;
+		else
+			pszName = DB::Contact::DisplayName(pPs->hContact);
+
+		SetWindowText(hName, pszName);
+		mir_sntprintf(newTitle, MAX_PATH, _T("%s - %s"), pszName, TranslateT("Edit Contact Information"));
+		SetWindowText(hDlg, newTitle);
+		mir_sntprintf(newTitle, MAX_PATH, _T("%s\n%s"), TranslateT("Edit Contact Information"), pszName);
+		SetDlgItemText(hDlg, IDC_HEADERBAR, newTitle);
+
+		// redraw the name control
+		ScreenToClient(hDlg, &pt);
+		GetWindowRect(hName, &rc);
+		OffsetRect(&rc, pt.x, pt.y);
+		InvalidateRect(hDlg, &rc, TRUE);
+		break;
+	}
+
+	/**
+	 * @class	HM_RELOADICONS
+	 * @desc	handles the changed icon event from the icolib plugin and reloads all icons
+	 * @param	wParam - not used
+	 *			lParam - not used
+	 *
+	 * @return	FALSE
+	 **/
+	case HM_RELOADICONS:
+	{
+		HWND	hCtrl;
+		HICON	hIcon;
+		const ICONCTRL idIcon[] = {
+			{ ICO_DLG_DETAILS,	STM_SETIMAGE,	ICO_DLGLOGO	},
+			{ ICO_BTN_UPDATE,	BM_SETIMAGE,	BTN_UPDATE	},
+			{ ICO_BTN_OK,		BM_SETIMAGE,	IDOK		},
+			{ ICO_BTN_CANCEL,	BM_SETIMAGE,	IDCANCEL	},
+			{ ICO_BTN_APPLY,	BM_SETIMAGE,	IDAPPLY		}
+		};
+		
+		const INT numIconsToSet = DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 1;
+		
+		IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);
+		
+		if (hCtrl = GetDlgItem(hDlg, BTN_IMPORT)) {
+			hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+			SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+			SetWindowText(hCtrl, hIcon ? _T("") : _T("I"));
+		}
+		if (hCtrl = GetDlgItem(hDlg, BTN_EXPORT)) {
+			hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+			SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+			SetWindowText(hCtrl, hIcon ? _T("") : _T("E"));
+		}
+		// update page icons
+		if (PtrIsValid(pPs) && (pPs->dwFlags & PSF_INITIALIZED))
+			pPs->pTree->OnIconsChanged();
+		break;
+	}
+
+	/**
+	 * @class	M_CHECKONLINE
+	 * @desc	determines whether miranda is online or not
+	 * @param	wParam - not used
+	 *			lParam - not used
+	 *
+	 * @return	TRUE if online, FALSE if offline
+	 **/
+	case M_CHECKONLINE:
+	{
+		if (IsProtoOnline(pPs->pszProto))
+		{
+			EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), !IsWindowVisible(GetDlgItem(hDlg, TXT_UPDATING)));
+			return TRUE;
+		}
+
+		EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE);
+		EnableWindow(GetDlgItem(hDlg, TXT_UPDATING), FALSE);
+		break;
+	}
+
+	/**
+	 * @class	HM_PROTOACK
+	 * @desc	handles all acks from the protocol plugin
+	 * @param	wParam - not used
+	 *			lParam - pointer to a ACKDATA structure
+	 *
+	 * @return	FALSE
+	 **/
+	case HM_PROTOACK:
+	{
+		ACKDATA *ack = (ACKDATA*)lParam;
+		INT i, iSubContact;
+		CPsTreeItem *pti;
+
+		if (!ack->hContact && ack->type == ACKTYPE_STATUS)
+			return DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+		
+		switch (ack->type) {
+		
+			case ACKTYPE_SETINFO:
+			{
+				if (ack->hContact != pPs->hContact || !pPs->pUpload || pPs->pUpload->Handle() != ack->hProcess)
+					break;
+				if (ack->result == ACKRESULT_SUCCESS) {
+					ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+					KillTimer(hDlg, TIMERID_UPDATING);
+					// upload next protocols contact information
+					switch (pPs->pUpload->UploadNext()) {
+						case CPsUpload::UPLOAD_FINISH_CLOSE:
+							MIR_DELETE(pPs->pUpload);
+							DestroyWindow(hDlg);
+						case CPsUpload::UPLOAD_CONTINUE:
+							return FALSE;
+						case CPsUpload::UPLOAD_FINISH:
+							MIR_DELETE(pPs->pUpload);
+							break;
+					}
+					DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+					EnableWindow(pPs->pTree->Window(), TRUE);
+					if (pti = pPs->pTree->CurrentItem()) {
+						EnableWindow(pti->Wnd(), TRUE);
+					}
+					EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
+					pPs->dwFlags &= ~PSF_LOCKED;
+				}
+				else 
+				if (ack->result == ACKRESULT_FAILED)	{
+					MsgBox(hDlg, MB_ICON_WARNING, 
+						LPGENT("Upload ICQ Details"),
+						LPGENT("Upload failed"),
+						LPGENT("Your details were not uploaded successfully.\nThey were written to database only."));
+					KillTimer(hDlg, TIMERID_UPDATING);
+					ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+					DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+
+					// upload next protocols contact information
+					switch (pPs->pUpload->UploadNext()) {
+						case CPsUpload::UPLOAD_FINISH_CLOSE:
+							MIR_DELETE(pPs->pUpload);
+							DestroyWindow(hDlg);
+						case CPsUpload::UPLOAD_CONTINUE:
+							return 0;
+						case CPsUpload::UPLOAD_FINISH:
+							MIR_DELETE(pPs->pUpload);
+							break;
+					}
+					if (pti = pPs->pTree->CurrentItem()) {
+						EnableWindow(pti->Wnd(), TRUE);
+					}
+					// activate all controls again
+					EnableWindow(pPs->pTree->Window(), TRUE);
+					EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
+					pPs->dwFlags &= ~PSF_LOCKED;
+				}
+				break;
+			}
+
+			case ACKTYPE_GETINFO:
+
+				// is contact the owner of the dialog or any metasubcontact of the owner? skip handling otherwise!
+				if (ack->hContact != pPs->hContact) {
+					
+					if (!myGlobals.szMetaProto)
+						break;
+					
+					if (!DB::Setting::GetByte(SET_META_SCAN, TRUE))
+						break;
+					
+					for (i = 0; i < pPs->nSubContacts; i++) {
+						if (pPs->infosUpdated[i].hContact == ack->hContact) {
+							iSubContact = i;
+							break;
+						}
+					}
+					if (i == pPs->nSubContacts)
+						break;
+				}
+				else {
+					iSubContact = 0;
+				}
+
+				// if they're not gonna send any more ACK's don't let that mean we should crash
+				if (!pPs->infosUpdated || (!ack->hProcess && !ack->lParam)) {
+					ResetUpdateInfo(pPs);
+
+					ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+					KillTimer(hDlg, TIMERID_UPDATING);
+					DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+					break;
+				}
+
+				if (iSubContact < pPs->nSubContacts) {
+
+					// init the acks structure for a sub contact
+					if (pPs->infosUpdated[iSubContact].acks == NULL) {
+						pPs->infosUpdated[iSubContact].acks	= (LPINT)mir_calloc(sizeof(INT) * (INT)(INT_PTR)ack->hProcess);
+						pPs->infosUpdated[iSubContact].count = (INT)(INT_PTR)ack->hProcess;
+					}
+
+					if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED)
+						pPs->infosUpdated[iSubContact].acks[ack->lParam] = 1;
+
+					// check for pending tasks
+					for (iSubContact = 0; iSubContact < pPs->nSubContacts; iSubContact++) {
+						for (i = 0; i < pPs->infosUpdated[iSubContact].count; i++) {
+							if (pPs->infosUpdated[iSubContact].acks[i] == 0)
+								break;
+						}
+						if (i < pPs->infosUpdated[iSubContact].count)
+							break;
+					}
+				}
+
+				// all acks are done, finish updating
+				if (iSubContact >= pPs->nSubContacts) {
+					ResetUpdateInfo(pPs);
+					ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE);
+					KillTimer(hDlg, TIMERID_UPDATING);
+					DlgProc(hDlg, M_CHECKONLINE, NULL, NULL);
+				}
+		}
+		break;
+	}
+
+	/**
+	 * @class	HM_SETTING_CHANGED
+	 * @desc	This message is called by the ME_DB_CONTACT_SETTINGCHANGED event and forces all
+	 *			unedited settings in the propertysheetpages to be updated
+	 * @param	wParam	- handle to the contact whose settings are to be changed
+	 *			lParam	- DBCONTACTWRITESETTING structure that identifies the changed setting
+	 * @return	FALSE
+	 **/
+	case HM_SETTING_CHANGED:
+		if (!(pPs->dwFlags & PSF_LOCKED)) {
+			HANDLE hContact = (HANDLE)wParam;
+			DBCONTACTWRITESETTING* pdbcws = (DBCONTACTWRITESETTING*)lParam;
+
+			if (hContact != pPs->hContact) {
+				if (!myGlobals.szMetaProto)
+					break;
+				if (pPs->hContact != (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, NULL))
+					break;
+				if (!DB::Setting::GetByte(SET_META_SCAN, TRUE))
+					break;
+			}
+
+			if (
+				!mir_stricmp(pdbcws->szSetting, SET_CONTACT_MYHANDLE) ||
+				!mir_stricmp(pdbcws->szSetting, SET_CONTACT_NICK)
+			 )
+			{
+				// force the update of all propertysheetpages
+				DlgProc(hDlg, PSM_FORCECHANGED, NULL, NULL);
+				// update the windowtitle
+				DlgProc(hDlg, HM_SETWINDOWTITLE, NULL, lParam);
+			}
+			else if (
+				!mir_stricmp(pdbcws->szModule, USERINFO) ||
+				!mir_stricmp(pdbcws->szModule, pPs->pszProto) ||
+				!mir_stricmp(pdbcws->szModule, MOD_MBIRTHDAY)
+			 )
+			{
+				// force the update of all propertysheetpages
+				DlgProc(hDlg, PSM_FORCECHANGED, NULL, NULL);
+			}
+		}
+		break;
+
+	case WM_NOTIFY:
+		switch (wParam) {
+
+			//
+			// Notification Messages sent by the TreeView
+			//
+			case STATIC_TREE:
+				switch (((LPNMHDR)lParam)->code) {
+					case TVN_SELCHANGING:
+					{	 
+						pPs->dwFlags |= PSF_LOCKED;
+						pPs->pTree->OnSelChanging();
+						pPs->dwFlags &= ~PSF_LOCKED;
+						break;
+					}
+
+					case TVN_SELCHANGED:
+						if (pPs->dwFlags & PSF_INITIALIZED) {
+							pPs->dwFlags |= PSF_LOCKED;
+							pPs->pTree->OnSelChanged((LPNMTREEVIEW)lParam);
+							if (pPs->pTree->CurrentItem())
+							{
+								RECT rc;
+								POINT pt = { 0, 0 };
+
+								GetWindowRect(GetDlgItem(hDlg, IDC_PAGETITLE), &rc);
+								ScreenToClient(hDlg, &pt);
+								OffsetRect(&rc, pt.x, pt.y);
+								SetDlgItemText(hDlg, IDC_PAGETITLE, pPs->pTree->CurrentItem()->Label());
+								InvalidateRect(GetDlgItem(hDlg, IDC_PAGETITLEBG), &rc, TRUE);
+								InvalidateRect(hDlg, &rc, TRUE);
+							}
+							pPs->dwFlags &= ~PSF_LOCKED;
+						}
+						break;
+
+					case TVN_BEGINDRAG:
+					{
+						LPNMTREEVIEW nmtv = (LPNMTREEVIEW)lParam;
+
+						if (nmtv->itemNew.hItem == TreeView_GetSelection(nmtv->hdr.hwndFrom)) {
+							SetCapture(hDlg);
+							pPs->pTree->BeginDrag(nmtv->itemNew.hItem);
+						}
+						TreeView_SelectItem(nmtv->hdr.hwndFrom, nmtv->itemNew.hItem);
+						break;
+					}
+
+					case TVN_ITEMEXPANDED:
+						pPs->pTree->AddFlags(PSTVF_STATE_CHANGED);
+						break;
+
+					case NM_KILLFOCUS:
+						KillTimer(hDlg, TIMERID_RENAME);
+						break;
+
+					case NM_CLICK:
+					{
+						TVHITTESTINFO hti;
+
+						GetCursorPos(&hti.pt);
+						ScreenToClient(pPs->pTree->Window(), &hti.pt);
+						TreeView_HitTest(pPs->pTree->Window(), &hti);
+						if ((hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) && hti.hItem == TreeView_GetSelection(pPs->pTree->Window())) 
+							SetTimer(hDlg, TIMERID_RENAME, 500, NULL);
+						break;
+					}
+
+					case NM_RCLICK:
+						pPs->pTree->PopupMenu();
+						return 0;
+
+				}
+				break;
+		}
+		break;
+	
+	case WM_MOUSEMOVE:
+		if (pPs->pTree->IsDragging()) {
+			TVHITTESTINFO hti;
+
+			hti.pt.x = (SHORT)LOWORD(lParam);
+			hti.pt.y = (SHORT)HIWORD(lParam);
+			MapWindowPoints(hDlg, pPs->pTree->Window(), &hti.pt, 1);
+			TreeView_HitTest(pPs->pTree->Window(), &hti);
+
+			if (hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+				RECT rc;
+				BYTE height;
+
+				// check where over the item, the pointer is
+				if (TreeView_GetItemRect(pPs->pTree->Window(), hti.hItem, &rc, FALSE)) {
+					height = (BYTE)(rc.bottom - rc.top);
+
+					if (hti.pt.y - (height / 3) < rc.top) {
+						SetCursor(LoadCursor(NULL, IDC_ARROW));
+						TreeView_SetInsertMark(pPs->pTree->Window(), hti.hItem, 0);
+					}
+					else
+					if (hti.pt.y + (height / 3) > rc.bottom) {
+						SetCursor(LoadCursor(NULL, IDC_ARROW));
+						TreeView_SetInsertMark(pPs->pTree->Window(), hti.hItem, 1);
+					}
+					else {
+						TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0);
+						SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(CURSOR_ADDGROUP)));
+					}
+				}
+			}
+			else {
+				if (hti.flags & TVHT_ABOVE) SendMessage(pPs->pTree->Window(), WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);
+				if (hti.flags & TVHT_BELOW) SendMessage(pPs->pTree->Window(), WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);
+				TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0);
+			}
+		}
+		break;
+	
+	case WM_LBUTTONUP:
+
+		// drop item
+		if (pPs->pTree->IsDragging()) {
+			TVHITTESTINFO hti;
+			RECT rc;
+			BYTE height;
+			BOOLEAN bAsChild = FALSE;
+
+			TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0);
+			ReleaseCapture();
+			SetCursor(LoadCursor(NULL, IDC_ARROW));
+			
+			hti.pt.x = (SHORT)LOWORD(lParam);
+			hti.pt.y = (SHORT)HIWORD(lParam);
+			MapWindowPoints(hDlg, pPs->pTree->Window(), &hti.pt, 1);
+			TreeView_HitTest(pPs->pTree->Window(), &hti);
+
+			if (hti.hItem == pPs->pTree->DragItem()) {
+				pPs->pTree->EndDrag();
+				break;
+			}
+
+			if (hti.flags & TVHT_ABOVE) {
+				hti.hItem = TVI_FIRST;
+			}
+			else
+			if (hti.flags & (TVHT_NOWHERE|TVHT_BELOW)) {
+				hti.hItem = TVI_LAST;
+			}
+			else
+			if (hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) {
+				// check where over the item, the pointer is
+				if (!TreeView_GetItemRect(pPs->pTree->Window(), hti.hItem, &rc, FALSE)) {
+					pPs->pTree->EndDrag();
+					break;
+				}
+				height = (BYTE)(rc.bottom - rc.top);
+
+				if (hti.pt.y - (height / 3) < rc.top) {
+					HTREEITEM hItem = hti.hItem;
+
+					if (!(hti.hItem = TreeView_GetPrevSibling(pPs->pTree->Window(), hItem))) {
+						if (!(hti.hItem = TreeView_GetParent(pPs->pTree->Window(), hItem)))
+							hti.hItem = TVI_FIRST;
+						else
+							bAsChild = TRUE;
+					}
+				}
+				else 
+				if (hti.pt.y + (height / 3) <= rc.bottom) {
+					bAsChild = TRUE;
+				}
+			}	
+			pPs->pTree->MoveItem(pPs->pTree->DragItem(), hti.hItem, bAsChild);
+			pPs->pTree->EndDrag();
+
+		}
+		break; 
+	
+	case WM_COMMAND:
+		switch (LOWORD(wParam)) {
+			case IDCANCEL:
+			{	 
+				pPs->pTree->OnCancel();
+				DestroyWindow(hDlg);
+				break;
+			}
+
+			/**
+			 *	name:	IDOK / IDAPPLY
+			 *	desc:	user clicked on apply or ok button in order to save changes
+			 **/
+			case IDOK:
+			case IDAPPLY:
+				if (pPs->dwFlags & PSF_CHANGED) {	 
+					// kill focus from children to make sure all data can be saved (ComboboxEx)
+					SetFocus(hDlg);
+
+					pPs->dwFlags |= PSF_LOCKED;
+					if (pPs->pTree->OnApply()) {
+						pPs->dwFlags &= ~(PSF_LOCKED|PSF_CHANGED);
+						break;
+					}
+
+					pPs->dwFlags &= ~PSF_CHANGED;
+					EnableWindow(GetDlgItem(hDlg, IDAPPLY), FALSE);
+					CallService(MS_CLIST_INVALIDATEDISPLAYNAME, (WPARAM)pPs->hContact, NULL);
+
+					// need to upload owners settings
+					if (!pPs->hContact && myGlobals.CanChangeDetails && DB::Setting::GetByte(SET_PROPSHEET_CHANGEMYDETAILS, FALSE)) {
+						if (pPs->pUpload = new CPsUpload(pPs, LOWORD(wParam) == IDOK)) {
+							if (pPs->pUpload->UploadFirst() == CPsUpload::UPLOAD_CONTINUE)
+								break;
+							MIR_DELETE(pPs->pUpload);
+						}
+					}
+					pPs->dwFlags &= ~PSF_LOCKED;
+				}
+				if (LOWORD(wParam) == IDOK) 
+					DestroyWindow(hDlg);
+				break;
+
+			case BTN_UPDATE:
+				{
+					if (pPs->hContact != NULL) 
+					{
+						ResetUpdateInfo(pPs);
+
+						mir_snprintf(pPs->szUpdating, SIZEOF(pPs->szUpdating), 
+							"%s (%s)", Translate("Updating"), pPs->pszProto);
+						
+						// need meta contact's subcontact information
+						if (DB::Module::IsMetaAndScan(pPs->pszProto)) 
+						{
+							HANDLE hSubContact;
+							CHAR szService[MAXSETTING];
+							INT	i, numSubs;
+							
+							numSubs = DB::MetaContact::SubCount(pPs->hContact);
+							
+							// count valid subcontacts whose protocol supports the PSS_GETINFO service to update the information
+							for (i = 0; i < numSubs; i++) 
+							{
+								hSubContact = DB::MetaContact::Sub(pPs->hContact, i);
+								if (hSubContact != NULL) 
+								{
+									mir_snprintf(szService, SIZEOF(szService), "%s%s", 
+										DB::Contact::Proto(hSubContact), PSS_GETINFO);
+									
+									if (ServiceExists(szService)) 
+									{
+										pPs->infosUpdated = (TAckInfo*)mir_realloc(pPs->infosUpdated, sizeof(TAckInfo) * (pPs->nSubContacts + 1));
+										pPs->infosUpdated[pPs->nSubContacts].hContact = hSubContact;
+										pPs->infosUpdated[pPs->nSubContacts].acks = NULL;
+										pPs->infosUpdated[pPs->nSubContacts].count = 0;
+										pPs->nSubContacts++;
+									}
+								}
+							}
+
+							if (pPs->nSubContacts != 0)
+							{
+								BOOLEAN bDo = FALSE;
+
+								// call the services
+								for (i = 0; i < pPs->nSubContacts; i++) 
+								{
+									if (!CallContactService(pPs->infosUpdated[pPs->nSubContacts].hContact, PSS_GETINFO, NULL, NULL))
+									{
+										bDo = TRUE;
+									}
+								}
+								if (bDo)
+								{
+									EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE);
+									ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_SHOW);
+									SetTimer(hDlg, TIMERID_UPDATING, 100, NULL);
+								}
+							}
+						}
+						else if (!CallContactService(pPs->hContact, PSS_GETINFO, NULL, NULL)) 
+						{
+							pPs->infosUpdated = (TAckInfo*)mir_calloc(sizeof(TAckInfo));
+							pPs->infosUpdated[0].hContact = pPs->hContact;
+							pPs->nSubContacts = 1;
+
+							EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE);
+							ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_SHOW);
+							SetTimer(hDlg, TIMERID_UPDATING, 100, NULL);
+						}
+					}
+				}
+				break;
+
+			case BTN_IMPORT:
+				svcExIm_ContactImport_Service((WPARAM) pPs->hContact, 0);
+				break;
+
+			case BTN_EXPORT:
+				// save changes before exporting data
+				DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(IDAPPLY, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, IDAPPLY));
+				// do the exporting stuff
+				svcExIm_ContactExport_Service((WPARAM) pPs->hContact, 0);
+				break;
+		}
+		break;
+
+	case WM_CLOSE:
+		DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, IDCANCEL));
+		break;
+
+	case WM_DESTROY:
+		{
+			INT i = 0;
+
+			// hide before destroy
+			ShowWindow(hDlg, SW_HIDE);
+
+			ResetUpdateInfo(pPs);
+
+			// avoid any further message processing for this dialog page
+			WindowList_Remove(ghWindowList, hDlg);
+			SetUserData(hDlg, NULL);
+
+			// unhook events and stop timers
+			KillTimer(hDlg, TIMERID_RENAME);
+			UnhookEvent(pPs->hProtoAckEvent);
+			UnhookEvent(pPs->hSettingChanged);
+			UnhookEvent(pPs->hIconsChanged);
+			
+			// save my window position
+			Utils_SaveWindowPosition(hDlg, NULL, MODNAME, "DetailsDlg");
+
+			// save current tree and destroy it
+			if (pPs->pTree != NULL) {
+				// save tree's current look
+				pPs->pTree->SaveState();
+				delete pPs->pTree;
+				pPs->pTree = NULL;
+			}
+
+			DeleteObject(pPs->hCaptionFont);
+			DeleteObject(pPs->hBoldFont);
+			mir_free(pPs); pPs = NULL;
+			MagneticWindows_RemoveWindow(hDlg);
+		}
+	}
+	return FALSE;
+}
+
diff --git a/plugins/UserInfoEx/src/dlg_propsheet.h b/plugins/UserInfoEx/src/dlg_propsheet.h
new file mode 100644
index 0000000000..622eb8fa33
--- /dev/null
+++ b/plugins/UserInfoEx/src/dlg_propsheet.h
@@ -0,0 +1,281 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/dlg_propsheet.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+typedef struct TPropSheet	PS, *LPPS;
+
+class CPsHdr;
+class CPsTree;
+
+/***********************************************************************************************************
+ * Tree item
+ ***********************************************************************************************************/
+
+class CPsTreeItem 
+{
+	INT				_idDlg;			// resource id of the property page
+	LPDLGTEMPLATE	_pTemplate;		// locked template for the property page
+	HINSTANCE		_hInst;			// instance handle of the owning plugin dll
+	DLGPROC			_pfnDlgProc;	// dialog procedure for the property page
+	HWND			_hWnd;			// window handle for the property page if shown jet
+	DWORD			_dwFlags;		// some flags
+	INT				_iPosition;		// initiating position if custom (used for sorting)
+	LPARAM			_initParam;
+	HANDLE			_hContact;		// contact the page is accociated with (may be a meta subcontact if details dialog is shown for a meta contact)
+	LPCSTR			_pszProto;		// protocol the page is accociated with (is the contact's protocol if _hContact is not NULL)
+	LPCSTR			_pszPrefix;		// pointer to the dialog owning contact's protocol
+
+	HTREEITEM		_hItem;			// handle to the treeview item if visible (NULL if this item is hidden)
+	INT				_iParent;		// index of the owning tree item
+	INT				_iImage;		// index of treeview item's image
+	BYTE			_bState;		// initial state of this treeitem
+	LPSTR			_pszName;		// original name, given by plugin (not customized)
+	LPTSTR			_ptszLabel;		// string to setting in db holding information about this treeitem
+
+	LPCSTR	GlobalName();
+
+	INT		Name(LPTSTR pszTitle, const BOOLEAN bIsUnicode);
+	INT		ItemLabel(const BOOLEAN bReadDBValue);
+
+	HICON	ProtoIcon();
+	INT		Icon(HIMAGELIST hIml, OPTIONSDIALOGPAGE *odp, BOOLEAN bInitIconsOnly);
+
+public:
+	CPsTreeItem();
+	~CPsTreeItem();
+	INT Create(CPsHdr* pPsh, OPTIONSDIALOGPAGE *odp);
+
+	__inline LPSTR Name() const { return _pszName; };
+	__inline LPCSTR Proto() const { return _pszProto; };
+	__inline LPTSTR Label() const { return _ptszLabel; };
+	VOID Rename( const LPTSTR pszLabel );
+	__inline HANDLE hContact() const { return _hContact; };
+
+	__inline HWND Wnd() const { return _hWnd; };
+	__inline INT DlgId() const { return _idDlg; };
+	__inline HINSTANCE Inst() const { return _hInst; };
+
+	__inline INT Image() const { return _iImage; };
+	__inline INT Pos() const { return _iPosition; };
+	__inline BYTE State() const { return _bState; };
+	__inline HTREEITEM Hti() const { return _hItem; };
+	__inline VOID Hti(HTREEITEM hti) { _hItem = hti; };
+	__inline INT Parent() const { return _iParent; };
+	__inline VOID Parent(const INT iParent) { _iParent = iParent; };
+
+	__inline DWORD Flags() const { return _dwFlags; };
+	__inline VOID Flags(DWORD dwFlags) { _dwFlags = dwFlags; };
+	__inline VOID AddFlags(DWORD dwFlags) { _dwFlags |= dwFlags; };
+	__inline VOID RemoveFlags(DWORD dwFlags) { _dwFlags &= ~dwFlags; };
+
+	BOOLEAN HasName(const LPCSTR pszName) const;
+
+	LPCSTR PropertyKey(LPCSTR pszProperty);
+	LPCSTR GlobalPropertyKey(LPCSTR pszProperty);
+	LPCSTR IconKey();
+
+	LPSTR ParentItemName();
+	HWND CreateWnd(LPPS pPs);
+	
+	WORD DBSaveItemState(LPCSTR pszGroup, INT iItemPosition, UINT iState, DWORD dwFlags);
+
+	// notification handlers
+	VOID OnInfoChanged();
+	VOID OnPageIconsChanged();
+	VOID OnIconsChanged(CPsTree *pTree);
+};
+
+
+/***********************************************************************************************************
+ * Tree control
+ ***********************************************************************************************************/
+
+#define MAX_TINAME			64
+
+// internal setting strings
+#define SET_LASTITEM		"LastItem"
+#define SET_ITEM_LABEL		"label"
+#define SET_ITEM_GROUP		"group"
+#define SET_ITEM_STATE		"state"
+#define SET_ITEM_POS		"pos"
+#define TREE_ROOTITEM		"<ROOT>"
+
+// treeview states
+#define DBTVIS_INVISIBLE	0
+#define DBTVIS_NORMAL		1
+#define DBTVIS_EXPANDED		2
+
+// flags for the treeview
+#define PSTVF_SORTTREE		0x00010000
+#define PSTVF_GROUPS		0x00020000
+#define PSTVF_LABEL_CHANGED	0x00040000
+#define PSTVF_POS_CHANGED	0x00080000
+#define PSTVF_STATE_CHANGED	0x00100000
+#define PSTVF_INITICONS		0x00200000		// flag that indicates the AddPage handler to load only basic stuff
+
+class CPsTree {
+	HWND					_hWndTree;
+	HIMAGELIST				_hImages;
+	CPsTreeItem**			_pItems;
+	INT						_curItem;
+	INT						_numItems;
+	DWORD					_dwFlags;
+	HWND					_hLabelEdit;
+	HTREEITEM				_hDragItem;
+	BOOLEAN					_isDragging;
+	LPPS					_pPs;
+
+	WORD	SaveItemsState(LPCSTR pszGroup, HTREEITEM hRootItem, INT& iItem);
+
+public:
+	CPsTree(LPPS pPs);
+	~CPsTree();
+	
+	__inline VOID BeginDrag(HTREEITEM hDragItem) { _isDragging = TRUE; _hDragItem = hDragItem; };
+	__inline VOID EndDrag() { _isDragging = FALSE; _hDragItem = NULL; };
+	__inline BOOLEAN IsDragging() const { return _isDragging; };
+	__inline HTREEITEM DragItem() const { return _hDragItem; };
+
+	__inline DWORD Flags() const { return _dwFlags; };
+	__inline VOID Flags(DWORD dwFlags) { _dwFlags = dwFlags; };
+	__inline VOID AddFlags(DWORD dwFlags) { _dwFlags |= dwFlags; };
+	__inline VOID RemoveFlags(DWORD dwFlags) { _dwFlags &= ~dwFlags; };
+
+	__inline INT NumItems() const { return _numItems; };
+	__inline HWND Window() const { return _hWndTree; };
+	__inline HIMAGELIST ImageList() const { return _hImages; };
+	__inline BOOLEAN IsIndexValid(const INT index) const { return (index >= 0 && index < _numItems); };
+	
+	__inline CPsTreeItem* TreeItem(INT index) const { return (IsIndexValid(index) ? _pItems[index] : NULL); };
+	__inline HTREEITEM TreeItemHandle(INT index) const { return (IsIndexValid(index) ? _pItems[index]->Hti() : NULL); };
+
+	__inline INT CurrentItemIndex() const { return _curItem; };
+	__inline CPsTreeItem* CurrentItem() const { return TreeItem(CurrentItemIndex()); };
+
+	INT				AddDummyItem(LPCSTR pszGroup);
+	BOOLEAN			Create(HWND hWndTree, CPsHdr* pPsh);
+	BOOLEAN			InitTreeItems(LPWORD needWidth);
+	
+	VOID			HideItem(const INT iPageIndex);
+	HTREEITEM ShowItem(const INT iPageIndex, LPWORD needWidth);
+
+	HTREEITEM MoveItem(HTREEITEM hItem, HTREEITEM hInsertAfter, BOOLEAN bAsChild = FALSE);
+	VOID			SaveState();
+	VOID			DBResetState();
+
+	INT				FindItemIndexByHandle(HTREEITEM hItem);
+	INT				FindItemIndexByName(LPCSTR pszName);
+
+	CPsTreeItem*	FindItemByName(LPCSTR pszName);
+	CPsTreeItem*	FindItemByHandle(HTREEITEM hItem);
+	HTREEITEM		FindItemHandleByName(LPCSTR pszName);
+	CPsTreeItem*	FindItemByResource(HINSTANCE hInst, INT idDlg);
+
+	INT				BeginLabelEdit( HTREEITEM hItem );
+	INT				EndLabelEdit( const BOOLEAN bSave );
+	VOID			PopupMenu();
+	
+	VOID			OnIconsChanged();
+	BOOLEAN			OnInfoChanged();
+	BOOLEAN			OnSelChanging();
+	VOID			OnSelChanged(LPNMTREEVIEW lpnmtv);
+	VOID			OnCancel();
+	INT				OnApply();
+};
+
+/***********************************************************************************************************
+ * common stuff
+ ***********************************************************************************************************/
+
+// internal flags for the PSP structure
+#define PSPF_CHANGED				4096
+#define PSPF_PROTOPREPENDED			8192	// the first token of the title is the protocol
+
+#define PSF_PROTOPAGESONLY			64		// load only contact's protocol pages
+#define PSF_PROTOPAGESONLY_INIT		128
+
+class CPsUpload;
+
+class CPsHdr 
+{
+public:
+	DWORD			_dwSize;		// size of this class in bytes
+	HANDLE			_hContact;		// handle to the owning contact
+	LPCSTR			_pszProto;		// owning contact's protocol 
+	LPCSTR			_pszPrefix;		// name prefix for treeitem settings
+	CPsTreeItem**	_pPages;		// the pages
+	WORD			_numPages;		// number of pages
+	DWORD			_dwFlags;		// some option flags
+	HIMAGELIST		_hImages;		// the imagelist with all tree item icons
+	LIST<TCHAR>		_ignore;		// list of to skipp items when adding metasubcontacts pages
+	INT				_nSubContact;	// index of a current subcontact
+
+	CPsHdr();
+	~CPsHdr();
+	
+	VOID Free_pPages();
+
+};
+
+struct TAckInfo 
+{
+	HANDLE	hContact;
+	LPINT/*PINT_PTR*/	acks;
+	INT		count;
+};
+
+struct TPropSheet 
+{
+	// dialogs owner
+	HANDLE		hContact;
+	CHAR		pszProto[MAXMODULELABELLENGTH];
+
+	HANDLE		hProtoAckEvent;		// eventhook for protocol acks
+	HANDLE		hSettingChanged;	// eventhook searching for changed contact information
+	HANDLE		hIconsChanged;		// eventhook for changed icons in icolib
+	HFONT		hCaptionFont;
+	HFONT		hBoldFont;
+	RECT		rcDisplay;
+	BYTE		updateAnimFrame;
+	CHAR		szUpdating[64];
+	DWORD		dwFlags;
+
+	TAckInfo	*infosUpdated;
+	INT			nSubContacts;
+	
+	// controls
+	HWND		hDlg;
+	CPsTree		*pTree;
+	CPsUpload	*pUpload;
+};
+
+VOID	DlgContactInfoInitTreeIcons();
+VOID	DlgContactInfoLoadModule();
+VOID	DlgContactInfoUnLoadModule();
diff --git a/plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp b/plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp
new file mode 100644
index 0000000000..0f75c0df43
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/classExImContactBase.cpp
@@ -0,0 +1,581 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/classExImContactBase.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "..\svc_contactinfo.h"
+#include "classExImContactBase.h"
+#include "mir_rfcCodecs.h"
+
+HANDLE CListFindGroup(LPCSTR pszGroup)
+{
+	CHAR buf[4];
+	BYTE i;
+	DBVARIANT dbv;
+
+	for (i = 0; i < 255; i++) {
+		_itoa(i, buf, 10);
+		if (DB::Setting::GetUString(NULL, "CListGroups", buf, &dbv))
+			break;
+		if (dbv.pszVal && pszGroup && !_stricmp(dbv.pszVal + 1, pszGroup)) {
+			DB::Variant::Free(&dbv);
+			return (HANDLE)(INT_PTR)(i+1);
+		}
+		DB::Variant::Free(&dbv);
+	}
+	return INVALID_HANDLE_VALUE;
+}
+
+/**
+ * name:	CExImContactBase
+ * class:	CExImContactBase
+ * desc:	default constructor
+ * param:	none
+ * return:	nothing
+ **/
+CExImContactBase::CExImContactBase()
+{
+	_pszNick		= NULL;
+	_pszDisp		= NULL;
+	_pszGroup		= NULL;
+	_pszProto		= NULL;
+	_pszProtoOld	= NULL;
+	_pszAMPro		= NULL;
+	_pszUIDKey		= NULL;
+	_dbvUIDHash		= NULL;
+	ZeroMemory(&_dbvUID, sizeof(DBVARIANT));
+	_hContact		= INVALID_HANDLE_VALUE;
+	_isNewContact	= FALSE;
+}
+
+/**
+ * name:	~CExImContactBase
+ * class:	CExImContactBase
+ * desc:	default denstructor
+ * param:	none
+ * return:	nothing
+ **/
+CExImContactBase::~CExImContactBase()
+{
+	MIR_FREE(_pszNick);
+	MIR_FREE(_pszDisp);
+	MIR_FREE(_pszGroup);
+	MIR_FREE(_pszProtoOld);
+	MIR_FREE(_pszProto);
+	MIR_FREE(_pszAMPro);
+	MIR_FREE(_pszUIDKey);
+	DB::Variant::Free(&_dbvUID);
+}
+
+/**
+ * name:	fromDB
+ * class:	CExImContactBase
+ * desc:	get contact information from database
+ * param:	hContact	- handle to contact whose information to read
+ * return:	TRUE if successful or FALSE otherwise
+ **/
+BOOLEAN CExImContactBase::fromDB(HANDLE hContact)
+{
+	BOOLEAN		ret			= FALSE;
+	BOOLEAN		isChatRoom	= FALSE;
+	LPSTR		pszProto;
+	LPCSTR		uidSetting;
+	DBVARIANT	dbv;
+	
+	_hContact				= hContact;
+	_dbvUIDHash				= 0;
+	MIR_FREE(_pszProtoOld);
+	MIR_FREE(_pszProto);
+	MIR_FREE(_pszAMPro);
+	MIR_FREE(_pszNick);
+	MIR_FREE(_pszDisp);
+	MIR_FREE(_pszGroup);
+	MIR_FREE(_pszUIDKey);
+	DB::Variant::Free(&_dbvUID);
+	ZeroMemory(&_dbvUID, sizeof(DBVARIANT));
+
+	// OWNER
+	if (!_hContact) return TRUE;
+	
+	// Proto
+	if (!(pszProto = DB::Contact::Proto(_hContact))) return FALSE;
+	_pszProto = mir_strdup(pszProto);
+
+	// AM_BaseProto
+	if (!DB::Setting::GetUString(NULL, pszProto, "AM_BaseProto", &dbv )) {
+		_pszAMPro = mir_strdup(dbv.pszVal);
+		DB::Variant::Free(&dbv);
+	}
+
+	// unique id (for ChatRoom)
+	if (isChatRoom = DB::Setting::GetByte(_hContact, pszProto, "ChatRoom", 0)) {
+		uidSetting = "ChatRoomID";
+		_pszUIDKey = mir_strdup(uidSetting);
+		if (!DB::Setting::GetAsIs(_hContact, pszProto, uidSetting, &_dbvUID)) {
+			ret = TRUE;
+		}
+	}
+	// unique id (normal)
+	else {
+		uidSetting = (LPCSTR)CallProtoService(pszProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+		// valid
+		if (uidSetting != NULL && (INT_PTR)uidSetting != CALLSERVICE_NOTFOUND) {
+			_pszUIDKey = mir_strdup(uidSetting);
+			if (!DB::Setting::GetAsIs(_hContact, pszProto, uidSetting, &_dbvUID)) {
+				ret = TRUE;
+			}
+		}
+		// fails because the protocol is no longer installed
+		else {
+			// assert(ret == TRUE);
+			ret = TRUE;
+		}
+	}
+	
+	// nickname
+	if (!DB::Setting::GetUString(_hContact, pszProto, SET_CONTACT_NICK, &dbv)) {
+		_pszNick = mir_strdup(dbv.pszVal);
+		DB::Variant::Free(&dbv);
+	}
+
+	if (_hContact && ret) {
+	// Clist Group
+		if (!DB::Setting::GetUString(_hContact, MOD_CLIST, "Group", &dbv)) {
+			_pszGroup = mir_strdup(dbv.pszVal);
+			DB::Variant::Free(&dbv);
+		}
+	// Clist DisplayName
+		if (!DB::Setting::GetUString(_hContact, MOD_CLIST, SET_CONTACT_MYHANDLE, &dbv)) {
+			_pszDisp = mir_strdup(dbv.pszVal);
+			DB::Variant::Free(&dbv);
+		}
+	}
+	return ret;
+}
+
+/**
+ * name:	fromIni
+ * class:	CExImContactBase
+ * desc:	get contact information from a row of a ini file
+ * param:	row	- the rows data
+ * return:	TRUE if successful or FALSE otherwise
+ **/
+BOOLEAN CExImContactBase::fromIni(LPSTR& row)
+{
+	LPSTR p1, p2 = NULL;
+	LPSTR pszUIDValue, pszUIDSetting, pszProto = NULL;
+	LPSTR pszBuf = &row[0];
+	size_t cchBuf = strlen(row);
+
+	MIR_FREE(_pszProtoOld);
+	MIR_FREE(_pszProto);
+	MIR_FREE(_pszAMPro);
+	MIR_FREE(_pszNick);
+	MIR_FREE(_pszDisp);
+	MIR_FREE(_pszGroup);
+	MIR_FREE(_pszUIDKey);
+	DB::Variant::Free(&_dbvUID);
+	ZeroMemory(&_dbvUID, sizeof(DBVARIANT));
+	_dbvUIDHash = 0;
+
+	// read uid value
+	if (cchBuf > 10 && (p1 = mir_strrchr(pszBuf, '*{')) && (p2 = mir_strchr(p1, '}*')) && p1 + 2 < p2) {
+		pszUIDValue = p1 + 1;
+		*p1 = *(p2 - 1) = 0;
+
+		// insulate the uid setting from buffer pointer
+		if (cchBuf > 0 && (p1 = mir_strrchr(pszBuf, '*<')) && (p2 = mir_strchr(p1, '>*')) && p1 + 2 < p2) {
+			pszUIDSetting = p1 + 1;
+			*p1 = *(p2 - 1) = 0;
+
+			// insulate the protocol name from buffer pointer
+			if (cchBuf > 0 && (p1 = mir_strrchr(pszBuf, '*(')) && (p2 = mir_strchr(p1, ')*')) && p1 + 2 < p2) {
+				pszProto = p1 + 1;
+				*(--p1) = *(p2 - 1) = 0;
+
+				// DBVT_DWORD
+				if (strspn(pszUIDValue, "0123456789") == mir_strlen(pszUIDValue)) {
+					_dbvUID.dVal = _atoi64(pszUIDValue);
+					_dbvUID.type = DBVT_DWORD;
+				}
+				else {
+				// DBVT_UTF8
+					_dbvUID.pszVal = mir_strdup(pszUIDValue);
+					_dbvUID.type = DBVT_UTF8;
+				}
+				_pszUIDKey = mir_strdup(pszUIDSetting);
+				_pszProto = mir_strdup(pszProto);
+			} //end insulate the protocol name from buffer pointer
+		} //end insulate the uid setting from buffer pointer
+	} //end read uid value
+
+	// create valid nickname
+	_pszNick = mir_strdup(pszBuf);
+	size_t i = strlen(_pszNick)-1;
+	while (i > 0 && (_pszNick[i] == ' ' || _pszNick[i] == '\t')) {
+		_pszNick[i] = 0;
+		i--;
+	}
+	// finally try to find contact in contact list
+	findHandle();
+	return FALSE;
+}
+
+/**
+ * name:	toDB
+ * class:	CExImContactBase
+ * desc:	searches the database for a contact representing the one
+ *			identified by this class or creates a new one if it was not found
+ * param:	hMetaContact - a meta contact to add this contact to
+ * return:	handle of the contact if successful
+ **/
+HANDLE CExImContactBase::toDB()
+{
+	DBCONTACTWRITESETTING cws;
+	
+	// create new contact if none exists
+	if (_hContact == INVALID_HANDLE_VALUE && _pszProto && _pszUIDKey && _dbvUID.type != DBVT_DELETED) {
+		PROTOACCOUNT* pszAccount = 0;
+		if(NULL == (pszAccount = ProtoGetAccount( _pszProto ))) {
+			//account does not exist
+			_hContact = INVALID_HANDLE_VALUE;
+			return INVALID_HANDLE_VALUE;
+		}
+		if (!IsAccountEnabled(pszAccount)) {
+			;
+		}
+		// create new contact
+		_hContact = DB::Contact::Add();
+		if (!_hContact) {
+			_hContact = INVALID_HANDLE_VALUE;
+			return INVALID_HANDLE_VALUE;
+		}
+		// Add the protocol to the new contact
+		if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)_hContact, (LPARAM)_pszProto)) {
+			DB::Contact::Delete(_hContact);
+			_hContact = INVALID_HANDLE_VALUE;
+			return INVALID_HANDLE_VALUE;
+		}
+		// write uid to protocol module
+		cws.szModule	= _pszProto;
+		cws.szSetting	= _pszUIDKey;
+		cws.value		= _dbvUID;
+		if (CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)_hContact, (LPARAM)&cws)) {
+			DB::Contact::Delete(_hContact);
+			_hContact = INVALID_HANDLE_VALUE;
+			return INVALID_HANDLE_VALUE;
+		}
+		// write nick and display name
+		if (_pszNick) DB::Setting::WriteUString(_hContact, _pszProto, SET_CONTACT_NICK, _pszNick);
+		if (_pszDisp) DB::Setting::WriteUString(_hContact, MOD_CLIST, SET_CONTACT_MYHANDLE, _pszDisp);
+
+		// add group
+		if (_pszGroup) {
+			DB::Setting::WriteUString(_hContact, MOD_CLIST, "Group", _pszGroup);
+			if (CListFindGroup(_pszGroup) == INVALID_HANDLE_VALUE) {
+				WPARAM hGroup = (WPARAM)CallService(MS_CLIST_GROUPCREATE, 0, 0);
+				if (hGroup) {
+					// renaming twice is stupid but the only way to avoid error dialog telling shit like
+					// a group with that name does exist
+					CallService(MS_CLIST_GROUPRENAME, (WPARAM)hGroup, (LPARAM)_pszGroup);
+				}
+			}
+		}
+	}
+	return _hContact;
+}
+
+/**
+ * name:	toIni
+ * class:	CExImContactBase
+ * desc:	writes the line to an opened ini file which identifies the contact
+ *			whose information are stored in this class
+ * param:	file	- pointer to the opened file
+ * return:	nothing
+ **/
+VOID CExImContactBase::toIni(FILE* file, int modCount)
+{
+	// getting dbeditor++ NickFromHContact(hContact)
+	static char name[512] = "";
+	char* ret = 0;
+
+	if (_hContact){
+		int loaded = _pszUIDKey ? 1 : 0;
+		if (_pszProto == NULL || !loaded) {
+			if (_pszProto){
+				if (_pszNick)
+					mir_snprintf(name, sizeof(name),"%s (%s)", _pszNick, _pszProto);
+				else
+					mir_snprintf(name, sizeof(name),"(UNKNOWN) (%s)", _pszProto);
+			}
+			else
+				mir_snprintf(name, sizeof(name),"(UNKNOWN)");
+		}
+		else {
+			// Proto loadet - GetContactName(hContact,pszProto,0)
+			LPSTR pszCI	= NULL;
+			CONTACTINFO ci;
+			ZeroMemory(&ci, sizeof(ci));
+
+			ci.cbSize		= sizeof(ci);
+			ci.hContact		= _hContact;
+			ci.szProto		= _pszProto;
+			ci.dwFlag		= CNF_DISPLAY;
+
+			if (!GetContactInfo(NULL, (LPARAM) &ci)) {
+				// CNF_DISPLAY always returns a string type
+				pszCI = (LPSTR)ci.pszVal;
+			}
+			LPSTR pszUID = uid2String(FALSE);
+			if (_pszUIDKey && pszUID)
+				mir_snprintf(name, sizeof(name), "%s *(%s)*<%s>*{%s}*", pszCI, _pszProto, _pszUIDKey, pszUID);
+			else 
+				mir_snprintf(name, sizeof(name), "%s (%s)", pszCI, _pszProto);
+
+			mir_free(pszCI);
+			mir_free(pszUID);
+		} // end else (Proto loadet)
+
+		// it is not the best solution (but still works if only basic modules export) - need rework
+		if (modCount > 3)
+			fprintf(file, "CONTACT: %s\n", name);
+		else
+			fprintf(file, "FROM CONTACT: %s\n", name);
+
+	} // end *if (_hContact)
+	else {
+		fprintf(file, "SETTINGS:\n");
+	}
+}
+
+BOOLEAN CExImContactBase::compareUID(DBVARIANT *dbv)
+{
+	DWORD hash = 0;
+	switch (dbv->type) {
+		case DBVT_BYTE:
+			if (dbv->bVal == _dbvUID.bVal) {
+				_dbvUID.type = dbv->type;
+				return TRUE;
+			}
+			break;
+		case DBVT_WORD:
+			if (dbv->wVal == _dbvUID.wVal) {
+				_dbvUID.type = dbv->type;
+				return TRUE;
+			}
+			break;
+		case DBVT_DWORD:
+			if (dbv->dVal == _dbvUID.dVal) {
+				_dbvUID.type = dbv->type;
+				return TRUE;
+			}
+			break;
+		case DBVT_ASCIIZ:
+			hash = hashSetting_M2(dbv->pszVal);
+		case DBVT_WCHAR:
+			if (!hash) hash = hashSettingW_M2((const char *)dbv->pwszVal);
+		case DBVT_UTF8:
+			if (!hash) {
+				LPWSTR tmp = mir_utf8decodeW(dbv->pszVal);
+				hash = hashSettingW_M2((const char *)tmp);
+				mir_free(tmp);
+			}
+			if(hash == _dbvUIDHash) return TRUE;
+			break;
+		case DBVT_BLOB:		//'n' cpbVal and pbVal are valid
+			if (_dbvUID.type == dbv->type && 
+				_dbvUID.cpbVal == dbv->cpbVal &&
+				memcmp(_dbvUID.pbVal, dbv->pbVal, dbv->cpbVal) == 0) {
+				return TRUE;
+			}
+			break;
+		default:
+			return FALSE;
+	}
+	return FALSE;
+}
+
+LPSTR CExImContactBase::uid2String(BOOLEAN bPrependType)
+{
+	CHAR szUID[MAX_PATH];
+	LPSTR ptr = szUID;
+
+	switch (_dbvUID.type) {
+		case DBVT_BYTE:		//'b' bVal and cVal are valid
+			if (bPrependType) *ptr++ = 'b';
+			_itoa(_dbvUID.bVal, ptr, 10);
+			break;
+		case DBVT_WORD:		//'w' wVal and sVal are valid
+			if (bPrependType) *ptr++ = 'w';
+			_itoa(_dbvUID.wVal, ptr, 10);
+			break;
+		case DBVT_DWORD:	//'d' dVal and lVal are valid
+			if (bPrependType) *ptr++ = 'd';
+			_itoa(_dbvUID.dVal, ptr, 10);
+			break;
+		case DBVT_WCHAR:	//'u' pwszVal is valid
+		{
+			LPSTR r = mir_utf8encodeW(_dbvUID.pwszVal);
+			if (bPrependType) {
+				LPSTR t = (LPSTR)mir_alloc(strlen(r)+2);
+				t[0] = 'u';
+				strcpy(t+1, r);
+				mir_free(r);
+				r = t;
+			}
+			return r;
+		}
+		case DBVT_UTF8:		//'u' pszVal is valid
+			{
+				if (bPrependType) *ptr++ = 'u';
+				mir_strncpy(ptr, _dbvUID.pszVal, SIZEOF(szUID)-1);
+			}
+			break;
+		case DBVT_ASCIIZ: {
+			LPSTR r = mir_utf8encode(_dbvUID.pszVal);
+			if (bPrependType) {
+				LPSTR t = (LPSTR)mir_alloc(strlen(r)+2);
+				t[0] = 's';
+				strcpy(t+1, r);
+				mir_free(r);
+				r = t;
+			}
+			return r;
+		}
+		case DBVT_BLOB:		//'n' cpbVal and pbVal are valid
+		{
+			if (bPrependType) {		//True = XML
+				INT_PTR baselen = Base64EncodeGetRequiredLength(_dbvUID.cpbVal, BASE64_FLAG_NOCRLF);
+				LPSTR t = (LPSTR)mir_alloc(baselen + 5 + bPrependType);
+				assert(t != NULL);
+				if (Base64Encode(_dbvUID.pbVal, _dbvUID.cpbVal, t + bPrependType, &baselen, BASE64_FLAG_NOCRLF)) {
+					if (baselen){
+						t[baselen + bPrependType] = 0;
+						if (bPrependType) t[0] = 'n';
+						return t;
+					}
+				}
+				mir_free(t);
+				return NULL;
+			}
+			else {		//FALSE = INI
+				WORD j;
+				INT_PTR baselen = (_dbvUID.cpbVal * 3);
+				LPSTR t = (LPSTR)mir_alloc(3 + baselen);
+				ZeroMemory(t, (3 + baselen));
+				for (j = 0; j < _dbvUID.cpbVal; j++) {
+					mir_snprintf((t + bPrependType + (j * 3)), 4,"%02X ", (BYTE)_dbvUID.pbVal[j]);
+				}
+				if (t){
+					if (bPrependType) t[0] = 'n';
+					return t;
+				}
+				else {
+					mir_free(t);
+					return NULL;
+				}
+			}
+			break;
+		}
+		default:
+			return NULL;
+	}
+	return mir_strdup(szUID);
+}
+
+BOOLEAN CExImContactBase::isMeta() const
+{
+	return DB::Module::IsMeta(_pszProto);
+}
+
+BOOLEAN CExImContactBase::isHandle(HANDLE hContact)
+{
+	LPCSTR pszProto;
+	DBVARIANT dbv;
+	BOOLEAN isEqual = FALSE;
+
+	// owner contact ?
+	if (!_pszProto) return hContact == NULL;
+
+	// compare protocols
+	pszProto = DB::Contact::Proto(hContact);
+	if (pszProto == NULL || (INT_PTR)pszProto == CALLSERVICE_NOTFOUND || mir_stricmp(pszProto, _pszProto))
+		return FALSE;
+
+	// compare uids
+	if (_pszUIDKey) {
+		// get uid
+		if (DB::Setting::GetAsIs(hContact, pszProto,_pszUIDKey, &dbv))
+			return FALSE;
+
+		isEqual = compareUID (&dbv);
+		DB::Variant::Free(&dbv);
+	}
+	// compare nicknames if no UID
+	else if (!DB::Setting::GetUString(hContact, _pszProto, SET_CONTACT_NICK, &dbv)) {
+		if (dbv.type == DBVT_UTF8 && dbv.pszVal && !mir_stricmp(dbv.pszVal,_pszNick)) {
+			LPTSTR ptszNick = mir_utf8decodeT(_pszNick);
+			LPTSTR ptszProto = mir_a2t(_pszProto);
+			INT ans = MsgBox(NULL, MB_ICONQUESTION|MB_YESNO, LPGENT("Question"), LPGENT("contact identificaion"),
+				LPGENT("The contact %s(%s) has no unique id in the vCard,\nbut there is a contact in your clist with the same nick and protocol.\nDo you wish to use this contact?"),
+				ptszNick, ptszProto);
+			MIR_FREE(ptszNick);
+			MIR_FREE(ptszProto);
+			isEqual = ans == IDYES;
+		}
+		DB::Variant::Free(&dbv);
+	}
+	return isEqual;
+}
+
+/**
+ * name:	handle
+ * class:	CExImContactBase
+ * desc:	return the handle of the contact
+ *			whose information are stored in this class
+ * param:	none
+ * return:	handle if successful, INVALID_HANDLE_VALUE otherwise
+ **/
+HANDLE CExImContactBase::findHandle()
+{
+	HANDLE hContact;
+
+	for (hContact = DB::Contact::FindFirst();
+		hContact != NULL;
+		hContact  = DB::Contact::FindNext(hContact))
+	{
+		if (isHandle(hContact)) {
+			_hContact = hContact;
+			_isNewContact = FALSE;
+			return hContact;
+		}
+	}
+	_isNewContact = TRUE;
+	_hContact = INVALID_HANDLE_VALUE;
+	return INVALID_HANDLE_VALUE;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/classExImContactBase.h b/plugins/UserInfoEx/src/ex_import/classExImContactBase.h
new file mode 100644
index 0000000000..55e5d94f67
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/classExImContactBase.h
@@ -0,0 +1,103 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/classExImContactBase.h $
+Revision       : $Revision: 196 $
+Last change on : $Date: 2010-09-21 03:24:30 +0400 (Вт, 21 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+#include "tinyxml.h"
+#include "..\svc_gender.h"
+
+HANDLE CListFindGroup(LPCSTR pszGroup);
+
+class CExImContactBase {
+	BOOLEAN		compareUID(DBVARIANT *dbv);
+
+protected:
+	LPSTR		_pszNick;		// utf8 encoded
+	LPSTR		_pszDisp;		// utf8 encoded
+	LPSTR		_pszGroup;		// utf8 encoded
+	LPSTR		_pszAMPro;
+	LPSTR		_pszProto;
+	LPSTR		_pszProtoOld;
+	LPSTR		_pszUIDKey;
+	DWORD		_dbvUIDHash;
+	DBVARIANT	_dbvUID;
+	HANDLE		_hContact;
+	BOOLEAN		_isNewContact;	// is this contact a new one?
+
+	HANDLE		findHandle();
+
+public:
+	CExImContactBase();
+	~CExImContactBase();
+
+//	__inline LPCSTR disp() const	{ return mir_strcmp(_pszDisp,"")? _pszDisp : NULL;		}
+//	__inline LPCSTR group() const	{ return mir_strcmp(_pszGroup,"")? _pszGroup : NULL;	}
+//	__inline LPCSTR nick() const	{ return mir_strcmp(_pszNick,"")? _pszNick : NULL;		}
+//	__inline LPCSTR proto() const	{ return mir_strcmp(_pszProto,"")? _pszProto : NULL;	}
+//	__inline LPCSTR ampro() const	{ return mir_strcmp(_pszAMPro,"")? _pszAMPro : NULL;	}
+//	__inline LPCSTR uidk() const	{ return mir_strcmp(_pszUIDKey,"")? _pszUIDKey : NULL;	}
+	__inline DBVARIANT& uid()		{ return _dbvUID;	}
+	__inline HANDLE handle() const	{ return _hContact;	}
+	
+	__inline VOID	disp(LPCSTR val)			{ _pszDisp		=	val ? mir_strdup(val): NULL; }
+	__inline VOID	group(LPCSTR val)			{ _pszGroup		=	val ? mir_strdup(val): NULL; }
+	__inline VOID	nick(LPCSTR val)			{ _pszNick		=	val ? mir_strdup(val): NULL; }
+	__inline VOID	proto(LPCSTR val)			{ _pszProto		=	val ? mir_strdup(val): NULL; }
+	__inline VOID	ampro(LPCSTR val)			{ _pszAMPro		=	val ? mir_strdup(val): NULL; }
+	__inline VOID	uidk(LPCSTR val)			{ _pszUIDKey	=	val ? mir_strdup(val): NULL; }
+	__inline VOID	uid(BYTE val)				{ _dbvUID.type	= DBVT_BYTE;  _dbvUID.bVal = val; }
+	__inline VOID	uid(WORD val)				{ _dbvUID.type	= DBVT_WORD;  _dbvUID.wVal = val; }
+	__inline VOID	uid(DWORD val)				{ _dbvUID.type	= DBVT_DWORD; _dbvUID.dVal = val; }
+	__inline VOID	uidn(PBYTE val, DWORD len)	{ _dbvUID.type	= DBVT_BLOB;  _dbvUID.pbVal= val; _dbvUID.cpbVal = (WORD)len; }
+	__inline VOID	uida(LPCSTR val)
+	{
+		_dbvUID.type = (_dbvUID.pszVal = mir_utf8decodeA(val))? DBVT_ASCIIZ : DBVT_DELETED;
+		_dbvUIDHash  = hashSetting_M2(_dbvUID.pszVal);
+	}
+	__inline VOID	uidu(LPCSTR val)
+	{
+		_dbvUID.type = (_dbvUID.pszVal = mir_strdup(val))? DBVT_UTF8 : DBVT_DELETED;
+		LPWSTR temp  = mir_utf8decodeW(val);
+		_dbvUIDHash  = hashSettingW_M2((const char *)temp);
+		mir_free(temp);
+	}
+
+	BOOLEAN			isHandle(HANDLE hContact);
+	BOOLEAN			isMeta() const;
+
+	LPSTR			uid2String(BOOLEAN bPrependType);
+
+	BOOLEAN			fromDB(HANDLE hContact);
+	BOOLEAN			fromIni(LPSTR& row);
+
+	HANDLE			toDB();
+	VOID			toIni(FILE* file, int modCount);
+
+	BOOLEAN operator = (HANDLE hContact)	{ return fromDB(hContact);	}
+};
diff --git a/plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp b/plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp
new file mode 100644
index 0000000000..2264f4d278
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/classExImContactXML.cpp
@@ -0,0 +1,1141 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/classExImContactXML.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "classExImContactXML.h"
+#include "svc_ExImXML.h"
+#include "mir_rfcCodecs.h"
+
+/***********************************************************************************************************
+ * common stuff
+ ***********************************************************************************************************/
+
+/**
+ * name:	SortProc
+ * desc:	used for bsearch in CExImContactXML::IsContactInfo
+ * param:	item1	- item to compare
+ *			item2	- item to compare
+ * return:	the difference
+ **/
+static INT SortProc(const LPDWORD item1, const LPDWORD item2)
+{
+	return *item1 - *item2;
+}
+
+/**
+ * name:	CExImContactXML
+ * class:	CExImContactXML
+ * desc:	the constructor for the contact class
+ * param:	pXmlFile	- the owning xml file
+ * return:	nothing
+ **/
+CExImContactXML::CExImContactXML(CFileXml * pXmlFile)
+	: CExImContactBase()
+{
+	_xmlNode	= NULL;
+	_pXmlFile	= pXmlFile;
+	_hEvent		= NULL;
+}
+
+/**
+ * name:	IsContactInfo
+ * class:	CExImContactXML
+ * desc:	this function compares the given setting key to the list of known contact
+ *			information keys
+ * param:	pszKey	- the settings key to check
+ * return:	TRUE if pszKey is a valid contact information
+ **/
+BOOLEAN CExImContactXML::IsContactInfo(LPCSTR pszKey)
+{
+	// This is a sorted list of all hashvalues of the contact information.
+	// This is the same as the szCiKey[] array below but sorted
+	const DWORD dwCiHash[] = {
+		0x6576F145,0x65780A70,0x6719120C,0x6776F145,0x67780A70,0x6EDB33D7,0x6F0466B5,
+		0x739B6915,0x73B11E48,0x760D8AD5,0x786A70D0,0x8813C350,0x88641AF8,0x8ED5652D,
+		0x96D64541,0x97768A14,0x9B786F9C,0x9B7889F9,0x9C26E6ED,0xA6675748,0xA813C350,
+		0xA8641AF8,0xAC408FCC,0xAC40AFCC,0xAC40CFCC,0xAEC6EA4C,0xB813C350,0xB8641AF8,
+		0xC5227954,0xCC68DE0E,0xCCD62E70,0xCCFBAAF4,0xCD715E13,0xD36182CF,0xD361C2CF,
+		0xD361E2CF,0xD42638DE,0xD4263956,0xD426395E,0xD453466E,0xD778D233,0xDB59D87A,
+		0xE406F60E,0xE406FA0E,0xE406FA4E,0xECF7E910,0xEF660441,0x00331041,0x0039AB3A,
+		0x003D88A6,0x07ABA803,0x113D8227,0x113DC227,0x113DE227,0x2288784F,0x238643D6,
+		0x2671C03E,0x275F720B,0x2EBDC0D6,0x3075C8C5,0x32674C9F,0x33EEAE73,0x40239C1C,
+		0x44DB75D0,0x44FA69D0,0x4C76989B,0x4FF38979,0x544B2F44,0x55AFAF8C,0x567A6BC5,
+		0x5A96C47F,0x6376F145,0x63780A70
+	};
+	if (pszKey && *pszKey) {
+		char buf[MAXSETTING];
+		// convert to hash and make bsearch as it is much faster then working with strings
+		const DWORD dwHash = hashSetting(_strlwr(mir_strncpy(buf, pszKey, SIZEOF(buf))));
+		return bsearch(&dwHash, dwCiHash, SIZEOF(dwCiHash), sizeof(dwCiHash[0]), (INT (*)(const VOID*, const VOID*))SortProc) != NULL;
+	}
+	return FALSE;
+/*
+	WORD i;
+	const LPCSTR szCiKey[] = {
+		// naming
+		SET_CONTACT_TITLE,SET_CONTACT_FIRSTNAME,SET_CONTACT_SECONDNAME,SET_CONTACT_LASTNAME,SET_CONTACT_PREFIX,
+		// private address
+		SET_CONTACT_STREET,SET_CONTACT_ZIP,SET_CONTACT_CITY,SET_CONTACT_STATE,SET_CONTACT_COUNTRY,
+		SET_CONTACT_PHONE,SET_CONTACT_FAX,SET_CONTACT_CELLULAR,
+		SET_CONTACT_EMAIL,SET_CONTACT_EMAIL0,SET_CONTACT_EMAIL1,SET_CONTACT_HOMEPAGE,
+		// origin
+		SET_CONTACT_ORIGIN_STREET,SET_CONTACT_ORIGIN_ZIP,SET_CONTACT_ORIGIN_CITY,SET_CONTACT_ORIGIN_STATE,SET_CONTACT_ORIGIN_COUNTRY,
+		// company
+		SET_CONTACT_COMPANY_POSITION,SET_CONTACT_COMPANY_OCCUPATION,SET_CONTACT_COMPANY_SUPERIOR,SET_CONTACT_COMPANY_ASSISTENT
+		SET_CONTACT_COMPANY,SET_CONTACT_COMPANY_DEPARTMENT,SET_CONTACT_COMPANY_OFFICE,
+		SET_CONTACT_COMPANY_STREET,SET_CONTACT_COMPANY_ZIP,SET_CONTACT_COMPANY_CITY,SET_CONTACT_COMPANY_STATE,SET_CONTACT_COMPANY_COUNTRY,
+		SET_CONTACT_COMPANY_PHONE,SET_CONTACT_COMPANY_FAX,SET_CONTACT_COMPANY_CELLULAR,
+		SET_CONTACT_COMPANY_EMAIL,SET_CONTACT_COMPANY_EMAIL0,SET_CONTACT_COMPANY_EMAIL1,SET_CONTACT_COMPANY_HOMEPAGE,
+		// personal information
+		SET_CONTACT_ABOUT,SET_CONTACT_MYNOTES,SET_CONTACT_MARITAL,SET_CONTACT_PARTNER,
+		SET_CONTACT_LANG1,SET_CONTACT_LANG2,SET_CONTACT_LANG3,SET_CONTACT_TIMEZONE,SET_CONTACT_TIMEZONENAME,SET_CONTACT_TIMEZONEINDEX,
+		SET_CONTACT_AGE,SET_CONTACT_GENDER,SET_CONTACT_BIRTHDAY,SET_CONTACT_BIRTHMONTH,SET_CONTACT_BIRTHYEAR,
+		"Past0", "Past0Text","Past1", "Past1Text","Past2", "Past2Text",
+		"Affiliation0", "Affiliation0Text","Affiliation1", "Affiliation1Text","Affiliation2", "Affiliation2Text",
+		"Interest0Cat", "Interest0Text","Interest1Cat", "Interest1Text","Interest2Cat", "Interest2Text"
+	};
+	DWORD *hash = new DWORD[SIZEOF(szCiKey)];
+	char buf[MAX_PATH];
+
+	for (i = 0; i < SIZEOF(szCiKey); i++) {
+		strcpy(buf, szCiKey[i]);
+		hash[i] = hashSetting(_strlwr((char*)buf));
+	}
+	qsort(hash, SIZEOF(szCiKey), sizeof(hash[0]), 
+	(INT (*)(const VOID*, const VOID*))SortProc);
+	
+	FILE* fil = fopen("D:\\temp\\id.txt", "wt");
+	for (i = 0; i < SIZEOF(szCiKey); i++) {
+		fprintf(fil, "0x%08X,", hash[i]);
+	}
+	fclose(fil);
+	return FALSE;
+	*/
+}
+
+/***********************************************************************************************************
+ * exporting stuff
+ ***********************************************************************************************************/
+
+/**
+ * name:	CreateXmlNode
+ * class:	CExImContactXML
+ * desc:	creates a new TiXmlElement representing the contact
+ *			whose information are stored in this class
+ * param:	none
+ * return:	pointer to the newly created TiXmlElement
+ **/
+TiXmlElement* CExImContactXML::CreateXmlElement()
+{
+	if (_hContact) {
+		if (_pszProto) {
+			_xmlNode = new TiXmlElement(XKEY_CONTACT);
+			
+			if (_xmlNode) {
+				LPSTR pszUID = uid2String(TRUE);
+				_xmlNode->SetAttribute("ampro", _pszAMPro);
+				_xmlNode->SetAttribute("proto", _pszProto);
+
+				if (_pszDisp)  _xmlNode->SetAttribute("disp", _pszDisp);
+				if (_pszNick)  _xmlNode->SetAttribute("nick", _pszNick);
+				if (_pszGroup) _xmlNode->SetAttribute("group",_pszGroup);
+				
+				if (pszUID) {
+
+					if (_pszUIDKey) {
+						_xmlNode->SetAttribute("uidk", _pszUIDKey);
+						_xmlNode->SetAttribute("uidv", pszUID);
+					}
+					else {
+						_xmlNode->SetAttribute("uidk", "#NV");
+						_xmlNode->SetAttribute("uidv", "UNLOADED");
+					}
+					mir_free(pszUID);
+				}
+			}
+		}
+		else
+			_xmlNode = NULL;
+	}
+	else {
+		_xmlNode = new TiXmlElement(XKEY_OWNER);
+	}
+	return _xmlNode;
+}
+
+/**
+ * name:	ExportContact
+ * class:	CExImContactXML
+ * desc:	exports a contact
+ * param:	none
+ * return:	ERROR_OK on success or any other on failure
+ **/
+INT CExImContactXML::ExportContact(DB::CEnumList* pModules)
+{
+	if (_pXmlFile->_wExport & EXPORT_DATA) 
+	{
+		if (pModules) 
+		{
+			INT i;
+			LPSTR p;
+
+			for (i = 0; i < pModules->getCount(); i++)
+			{
+				p = (*pModules)[i];
+
+				/*Filter/
+				if (mir_stricmp(p, "Protocol") && !DB::Module::IsMeta(p))*/
+				{
+					ExportModule(p);
+				}
+			}
+		}
+		else 
+		{
+			ExportModule(USERINFO);
+			ExportModule(MOD_MBIRTHDAY);
+		}
+	}
+	
+	// export contact's events
+	if (_pXmlFile->_wExport & EXPORT_HISTORY)
+	{
+		ExportEvents();
+	}
+
+	return ERROR_OK;
+}
+
+/**
+ * name:	ExportSubContact
+ * class:	CExImContactXML
+ * desc:	exports a meta sub contact
+ * param:	none
+ * return:	ERROR_OK on success or any other on failure
+ **/
+INT CExImContactXML::ExportSubContact(CExImContactXML *vMetaContact, DB::CEnumList* pModules)
+{
+	// create xmlNode
+	if (!CreateXmlElement()) 
+	{
+		return ERROR_INVALID_CONTACT;
+	}
+	if (ExportContact(pModules) == ERROR_OK) 
+	{
+		if (!_xmlNode->NoChildren() && vMetaContact->_xmlNode->LinkEndChild(_xmlNode)) 
+		{
+			return ERROR_OK;
+		}
+	}
+	if (_xmlNode) delete _xmlNode;
+	return ERROR_NOT_ADDED;
+}
+
+/**
+ * name:	Export
+ * class:	CExImContactXML
+ * desc:	exports a contact
+ * param:	xmlfile		- handle to the open file to write the contact to
+ *			pModules	- list of modules to export for each contact
+ * return:	ERROR_OK on success or any other on failure
+ **/
+INT CExImContactXML::Export(FILE *xmlfile, DB::CEnumList* pModules)
+{
+	if (!xmlfile) 
+	{
+		return ERROR_INVALID_PARAMS;
+	}
+
+	if (_hContact == INVALID_HANDLE_VALUE)
+	{
+		return ERROR_INVALID_CONTACT;
+	}
+
+	if (!CreateXmlElement()) 
+	{
+		return ERROR_INVALID_CONTACT;
+	}
+
+	// export meta
+	if (isMeta())
+	{
+		CExImContactXML vContact(_pXmlFile);
+
+		const INT cnt = DB::MetaContact::SubCount(_hContact);
+		const INT def = DB::MetaContact::SubDefNum(_hContact);
+		HANDLE hSubContact = DB::MetaContact::Sub(_hContact, def);
+		INT i;
+
+		// export default subcontact
+		if (hSubContact && vContact.fromDB(hSubContact))
+		{
+			vContact.ExportSubContact(this, pModules);
+		}
+
+		for (i = 0; i < cnt; i++)
+		{
+			if (i != def)
+			{
+				hSubContact = DB::MetaContact::Sub(_hContact, i);
+				if (hSubContact && vContact.fromDB(hSubContact))
+				{
+					vContact.ExportSubContact(this, pModules);
+				}
+			}
+		}
+	}
+	ExportContact(pModules);
+
+	// add xContact to document
+	if (_xmlNode->NoChildren())
+	{
+		delete _xmlNode;
+		_xmlNode = NULL;
+		return ERROR_NOT_ADDED;
+	}
+	_xmlNode->Print(xmlfile, 1);
+	fputc('\n', xmlfile);
+
+	delete _xmlNode;
+	_xmlNode = NULL;
+
+	return ERROR_OK;
+}
+
+/**
+ * name:	ExportModule
+ * class:	CExImContactXML
+ * desc:	enumerates all settings of a database module and adds them to the xml tree
+ * params:	pszModule	- the module which is to export
+ * return:	ERROR_OK on success or any other on failure
+ **/
+INT CExImContactXML::ExportModule(LPCSTR pszModule)
+{
+	DB::CEnumList	Settings;
+	if (!pszModule || !*pszModule) {
+		return ERROR_INVALID_PARAMS;
+	}
+	if (!Settings.EnumSettings(_hContact, pszModule)) {
+		INT i;
+		TiXmlElement *xmod;
+		xmod = new TiXmlElement(XKEY_MOD);
+		if (!xmod) {
+			return ERROR_MEMORY_ALLOC;
+		}
+		xmod->SetAttribute("key", pszModule);
+		for (i = 0; i < Settings.getCount(); i++) {
+			ExportSetting(xmod, pszModule, Settings[i]);
+		}
+
+		if (!xmod->NoChildren() && _xmlNode->LinkEndChild(xmod)) {
+			return ERROR_OK;
+		}
+		delete xmod;
+	}
+	return ERROR_EMPTY_MODULE;
+}
+
+/**
+ * name:	ExportSetting
+ * desc:	read a setting from database and add an xmlelement to contact node
+ * params:	xmlModule	- xml node to add the setting to
+ *			hContact	- handle of the contact whose event chain is to export
+ *			pszModule	- the module which is to export
+ *			pszSetting	- the setting which is to export
+ * return:	pointer to the added element
+ **/
+INT CExImContactXML::ExportSetting(TiXmlElement *xmlModule, LPCSTR pszModule, LPCSTR pszSetting)
+{
+	DBVARIANT		dbv;
+	TiXmlElement	*xmlEntry	= NULL;
+	TiXmlText		*xmlValue	= NULL;
+	CHAR			buf[32];
+	LPSTR			str			= NULL;
+
+	if (DB::Setting::GetAsIs(_hContact, pszModule, pszSetting, &dbv))
+		return ERROR_INVALID_VALUE;
+	switch (dbv.type) {
+		case DBVT_BYTE:		//'b' bVal and cVal are valid
+			buf[0] = 'b';
+			_ultoa(dbv.bVal, buf + 1, 10);
+			xmlValue = new TiXmlText(buf);
+			break;
+		case DBVT_WORD:		//'w' wVal and sVal are valid
+			buf[0] = 'w';
+			_ultoa(dbv.wVal, buf + 1, 10);
+			xmlValue = new TiXmlText(buf);
+			break;
+		case DBVT_DWORD:	//'d' dVal and lVal are valid
+			buf[0] = 'd';
+			_ultoa(dbv.dVal, buf + 1, 10);
+			xmlValue = new TiXmlText(buf);
+			break;
+		case DBVT_ASCIIZ:	//'s' pszVal is valid
+		{
+			if(mir_IsEmptyA(dbv.pszVal)) break;
+			DB::Variant::ConvertString(&dbv, DBVT_UTF8);
+			if (str = (LPSTR)mir_alloc(mir_strlen(dbv.pszVal) + 2)) {
+				str[0] = 's';
+				mir_strcpy(&str[1], dbv.pszVal);
+				xmlValue = new TiXmlText(str);
+				mir_free(str);
+			}
+			break;
+		}
+		case DBVT_UTF8:		//'u' pszVal is valid
+		{
+			if(mir_IsEmptyA(dbv.pszVal)) break;
+			if (str = (LPSTR)mir_alloc(mir_strlen(dbv.pszVal) + 2)) {
+				str[0] = 'u';
+				mir_strcpy(&str[1], dbv.pszVal);
+				xmlValue = new TiXmlText(str);
+				mir_free(str);
+			}
+			break;
+		}
+		case DBVT_WCHAR:	//'u' pwszVal is valid
+		{
+			if(mir_IsEmptyW(dbv.pwszVal)) break;
+			DB::Variant::ConvertString(&dbv, DBVT_UTF8);
+			if (str = (LPSTR)mir_alloc(mir_strlen(dbv.pszVal) + 2)) {
+				str[0] = 'u';
+				mir_strcpy(&str[1], dbv.pszVal);
+				xmlValue = new TiXmlText(str);
+				mir_free(str);
+			}
+			break;
+		}
+		case DBVT_BLOB:		//'n' cpbVal and pbVal are valid
+		{
+			// new buffer for base64 encoded data
+			INT_PTR baselen = Base64EncodeGetRequiredLength(dbv.cpbVal, BASE64_FLAG_NOCRLF);
+			str = (LPSTR)mir_alloc(baselen + 6);
+			assert(str != NULL);
+			// encode data
+			if (Base64Encode(dbv.pbVal, dbv.cpbVal, str+1, &baselen, BASE64_FLAG_NOCRLF)) {
+				if (baselen){
+					str[baselen+1] = 0;
+					str[0] = 'n';
+					xmlValue = new TiXmlText(str);
+				}
+			}
+			mir_free(str);
+			break;
+		}
+		case DBVT_DELETED:	//this setting just got deleted, no other values are valid
+			#if defined(_DEBUG)
+				OutputDebugStringA("DBVT_DELETED\n");
+			#endif
+			break;
+		default:
+			#if defined(_DEBUG)
+				OutputDebugStringA("DBVT_TYPE unknown\n");
+			#endif
+			; // nothing
+	}
+	DB::Variant::Free(&dbv);
+	if (xmlValue) {
+		xmlEntry = new TiXmlElement(XKEY_SET);
+		if (xmlEntry) {
+			xmlEntry->SetAttribute("key", pszSetting);
+			if (xmlEntry->LinkEndChild(xmlValue) && xmlModule->LinkEndChild(xmlEntry))
+				return ERROR_OK;
+			delete xmlEntry;
+		}
+		delete xmlValue;
+	}
+	return ERROR_MEMORY_ALLOC;
+}
+
+/**
+ * name:	ExportEvents
+ * desc:	adds the event chain for a given contact to the xml tree
+ * params:	xContact	- the xml node to add the events as childs to
+ *			hContact	- handle of the contact whose event chain is to export
+ * return:	TRUE on success, FALSE otherwise
+ **/
+BOOLEAN CExImContactXML::ExportEvents()
+{
+	DBEVENTINFO	dbei; 
+	HANDLE		hDbEvent;
+	PBYTE		pbEventBuf			= NULL;
+	DWORD		cbEventBuf			= 0,
+				dwNumEvents			= 0,
+				dwNumEventsAdded	= 0;
+	LPSTR		pBase64Data			= NULL;
+	INT_PTR		cbBase64Data		= 0,
+				cbNewBase64Data		= 0;
+
+	TiXmlNode	*xmlModule			= NULL;
+	TiXmlElement *xmlEvent			= NULL;
+	TiXmlText	*xmlText			= NULL;
+
+	dwNumEvents = CallService(MS_DB_EVENT_GETCOUNT, (WPARAM)_hContact, NULL);
+	if(dwNumEvents == 0) return FALSE;
+
+	try {
+		ZeroMemory(&dbei, sizeof(DBEVENTINFO));
+		dbei.cbSize = sizeof(DBEVENTINFO);
+
+		// read out all events for the current contact
+		for (hDbEvent = DB::Event::FindFirst(_hContact); hDbEvent != NULL; hDbEvent = DB::Event::FindNext(hDbEvent)) {
+			if (!DB::Event::GetInfoWithData(hDbEvent, &dbei)) {
+				// new buffer for base64 encoded data
+				cbNewBase64Data = Base64EncodeGetRequiredLength(dbei.cbBlob, BASE64_FLAG_NOCRLF);
+				if (cbNewBase64Data > cbBase64Data) {
+					pBase64Data = (LPSTR)mir_realloc(pBase64Data, cbNewBase64Data + 5);
+					if (pBase64Data == NULL) {
+						MessageBoxA(NULL, "mir_realloc(cbNewBase64Data + 5) == NULL", "Error", 0);
+						break;
+					}
+					cbBase64Data = cbNewBase64Data;
+				}
+
+				// encode data
+				if (Base64Encode(dbei.pBlob, dbei.cbBlob, pBase64Data, &cbNewBase64Data, BASE64_FLAG_NOCRLF)) {
+					pBase64Data[cbNewBase64Data] = 0;
+					xmlEvent = new TiXmlElement("evt");
+					if (xmlEvent) {
+						xmlEvent->SetAttribute("type", dbei.eventType);
+						xmlEvent->SetAttribute("time", dbei.timestamp);
+						xmlEvent->SetAttribute("flag", dbei.flags);
+
+						xmlText = new TiXmlText(pBase64Data);
+						xmlEvent->LinkEndChild(xmlText);
+
+						// find module
+						for (xmlModule = _xmlNode->FirstChild(); xmlModule != NULL; xmlModule = xmlModule->NextSibling()) {
+							if (!mir_stricmp(((TiXmlElement*)xmlModule)->Attribute("key"), dbei.szModule)) break;
+						}
+						// create new module
+						if (!xmlModule) {
+							xmlModule = _xmlNode->InsertEndChild(TiXmlElement(XKEY_MOD));
+							if (!xmlModule) break;
+							((TiXmlElement*)xmlModule)->SetAttribute("key", dbei.szModule);
+						}
+
+						xmlModule->LinkEndChild(xmlEvent);
+						dwNumEventsAdded++;
+						xmlEvent = NULL; // avoid final deleting
+					}
+				}
+				MIR_FREE(dbei.pBlob);
+			}
+		}
+	}
+	catch(...) {
+		// fuck, do nothing
+		MIR_FREE(dbei.pBlob);
+		dwNumEventsAdded = 0;
+	}
+
+	mir_free(pbEventBuf);
+	mir_free(pBase64Data);
+	if (xmlEvent) delete xmlEvent;
+
+	return dwNumEventsAdded == dwNumEvents;
+}
+
+/***********************************************************************************************************
+ * importing stuff
+ ***********************************************************************************************************/
+
+/**
+ * name:	CountKeys
+ * desc:	Counts the number of events and settings stored for a contact
+ * params:	xmlContact	- the contact, who is the owner of the keys to count
+ * return:	nothing
+ **/
+VOID CExImContactXML::CountKeys(DWORD &numSettings, DWORD &numEvents)
+{
+	TiXmlNode *xmod, *xkey;
+
+	numSettings = numEvents = 0;
+	for (xmod = _xmlNode->FirstChild(); 
+		xmod != NULL; 
+		xmod = xmod->NextSibling(XKEY_MOD)) {
+		for (xkey = xmod->FirstChild();
+			xkey != NULL;
+			xkey = xkey->NextSibling()) {
+			if (!mir_stricmp(xkey->Value(), XKEY_SET)) numSettings++;
+			else numEvents++;
+		}
+	}
+}
+
+/**
+ * name:	LoadXmlElemnt
+ * class:	CExImContactXML
+ * desc:	get contact information from XML-file
+ * param:	xContact	- TiXmlElement representing a contact
+ * return:	ERROR_OK if successful or any other error number otherwise
+ **/
+INT CExImContactXML::LoadXmlElemnt(TiXmlElement *xContact)
+{
+	if (xContact == NULL) return ERROR_INVALID_PARAMS;
+
+	LPSTR pszMetaProto = myGlobals.szMetaProto ? myGlobals.szMetaProto : "MetaContacts";
+
+	// delete last contact
+	DB::Variant::Free(&_dbvUID);
+	_hContact = INVALID_HANDLE_VALUE;
+
+	_xmlNode = xContact;
+	MIR_FREE(_pszAMPro);		ampro(xContact->Attribute("ampro"));
+	MIR_FREE(_pszNick);			nick (xContact->Attribute("nick"));
+	MIR_FREE(_pszDisp);			disp (xContact->Attribute("disp"));
+	MIR_FREE(_pszGroup);		group(xContact->Attribute("group"));
+	MIR_FREE(_pszProto);
+	MIR_FREE(_pszProtoOld);
+	MIR_FREE(_pszUIDKey);
+
+	// is contact a metacontact
+	if (_pszAMPro && !strcmp(_pszAMPro, pszMetaProto) /*_xmlNode->FirstChildElement(XKEY_CONTACT)*/) {
+		TiXmlElement *xSub;
+		proto(pszMetaProto);
+
+		// meta contact must be uniquelly identified by its subcontacts
+		// the metaID may change during an export or import call
+		for(xSub  = xContact->FirstChildElement(XKEY_CONTACT); 
+			xSub != NULL;
+			xSub  = xSub->NextSiblingElement(XKEY_CONTACT)) {
+			CExImContactXML vSub(_pXmlFile);
+			if (vSub = xSub) {
+				// identify metacontact by the first valid subcontact in xmlfile
+				if (_hContact == INVALID_HANDLE_VALUE && vSub.handle() != INVALID_HANDLE_VALUE) {
+					HANDLE hMeta = (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)vSub.handle(), NULL);
+					if (hMeta != NULL) {
+						_hContact = hMeta;
+						break;
+					}
+				}
+			}
+		}
+		// if no handle was found, this is a new meta contact
+		_isNewContact = _hContact == INVALID_HANDLE_VALUE;
+	}
+	// entry is a default contact
+	else {
+		proto(xContact->Attribute("proto"));
+		uidk (xContact->Attribute("uidk"));
+		if (!_pszProto) {
+			// check if this is the owner contact
+			if (mir_stricmp(xContact->Value(), XKEY_OWNER))
+				return ERROR_INVALID_PARAMS;
+			_hContact = NULL;
+			_xmlNode  = xContact;
+			return ERROR_OK;
+		}
+
+		if (_pszUIDKey && mir_strcmp("#NV", _pszUIDKey) !=0) {
+			LPCSTR pUID = xContact->Attribute("uidv");
+
+			if (pUID != NULL) {
+				size_t	len		= 0;
+				INT_PTR	baselen	= NULL;
+				PBYTE	pbVal	= NULL;
+
+				switch (*(pUID++)) {
+					case 'b':
+						uid((BYTE)atoi(pUID));
+						break;
+					case 'w':
+						uid((WORD)atoi(pUID));
+						break;
+					case 'd':
+						uid((DWORD)_atoi64(pUID));
+						break;
+					case 's':
+						// utf8 -> asci
+						uida(pUID);
+						break;
+					case 'u':
+						uidu(pUID);
+						break;
+					case 'n':
+						len		= strlen(pUID);
+						baselen	= Base64DecodeGetRequiredLength(len);
+						pbVal	= (PBYTE)mir_alloc(baselen /*+1*/);
+						if (pbVal != NULL){
+							if (Base64Decode(pUID, len, pbVal, &baselen)) {
+								uidn(pbVal, baselen);
+							}
+							else {
+								assert(pUID != NULL);
+							}
+						}
+						break;
+					default:
+						uidu((LPCSTR)NULL);
+						break;
+				}
+			}
+		}
+		// finally try to find contact in contact list
+		findHandle();
+	}
+	return ERROR_OK;
+}
+
+/**
+ * name:	ImportContact
+ * class:	CExImContactXML
+ * desc:	create the contact if neccessary and copy
+ *			all information from the xmlNode to database
+ * param:	none
+ * return:	ERROR_OK on success or any other error number otherwise
+ **/
+INT CExImContactXML::ImportContact()
+{
+	TiXmlNode *xmod;
+
+	// create the contact if not yet exists
+	if (toDB() != INVALID_HANDLE_VALUE) {
+		DWORD numSettings, numEvents;
+
+		_hEvent = NULL;
+
+		// count settings and events and init progress dialog
+		CountKeys(numSettings, numEvents);
+		_pXmlFile->_progress.SetSettingsCount(numSettings + numEvents);
+		_pXmlFile->_numSettingsTodo += numSettings;
+		_pXmlFile->_numEventsTodo += numEvents;
+
+		// import all modules
+		for(xmod  = _xmlNode->FirstChild();
+			xmod != NULL;
+			xmod = xmod->NextSibling(XKEY_MOD)) {
+
+			// import module
+			if (ImportModule(xmod) == ERROR_ABORTED) {
+				// ask to delete new incomplete contact
+				if (_isNewContact && _hContact != NULL) {
+					INT result = MsgBox(NULL, MB_YESNO|MB_ICONWARNING, 
+						LPGENT("Question"), 
+						LPGENT("Importing a new contact was aborted!"), 
+						LPGENT("You aborted import of a new contact.\nSome information may be missing for this contact.\n\nDo you want to delete the incomplete contact?"));
+					if (result == IDYES) {
+						DB::Contact::Delete(_hContact);
+						_hContact = INVALID_HANDLE_VALUE;
+					}
+				}
+				return ERROR_ABORTED;
+			}
+		}
+		return ERROR_OK;
+	}
+	return ERROR_NOT_ADDED;
+}
+
+/**
+ * name:	ImportNormalContact
+ * class:	CExImContactXML
+ * desc:	create the contact if neccessary and copy
+ *			all information from the xmlNode to database.
+ *			Remove contact from a metacontact if it is a subcontact
+ * param:	none
+ * return:	ERROR_OK on success or any other error number otherwise
+ **/
+INT CExImContactXML::ImportNormalContact()
+{
+	INT err = ImportContact();
+
+	// remove contact from a metacontact
+	if (err == ERROR_OK && CallService(MS_MC_GETMETACONTACT, (WPARAM)_hContact, NULL)) {
+		CallService(MS_MC_REMOVEFROMMETA, NULL, (LPARAM)_hContact);
+	}	
+	return err;
+}
+
+/**
+ * name:	Import
+ * class:	CExImContactXML
+ * desc:	create the contact if neccessary and copy
+ *			all information from the xmlNode to database.
+ *			Remove contact from a metacontact if it is a subcontact
+ * param:	TRUE = keepMetaSubContact
+ * return:	ERROR_OK on success or any other error number otherwise
+ **/
+INT CExImContactXML::Import(BOOLEAN keepMetaSubContact)
+{
+	INT result;
+	TiXmlElement *xContact = _xmlNode->FirstChildElement("CONTACT");
+
+	// xml contact contains subcontacts?
+	if (xContact) {
+
+		// contact is a metacontact and metacontacts plugin is installed?
+		if (isMeta()) {
+			// create object for first sub contact
+			CExImContactXML	vContact(_pXmlFile);
+			LPTSTR pszNick;
+
+			// the contact does not yet exist
+			if (_isNewContact) {
+				// import default contact as normal contact and convert to meta contact
+				if (!(vContact = xContact)) {
+					return ERROR_CONVERT_METACONTACT;
+				}
+				// import as normal contact
+				result = vContact.ImportContact();
+				if (result != ERROR_OK) return result;
+				// convert default subcontact to metacontact
+				_hContact = (HANDLE)CallService(MS_MC_CONVERTTOMETA, (WPARAM)vContact.handle(), NULL);
+				if (_hContact == NULL) {
+					_hContact = INVALID_HANDLE_VALUE;
+					return ERROR_CONVERT_METACONTACT;
+				}
+
+				_pXmlFile->_numContactsDone++;
+				// do not load first meta contact twice
+				xContact = xContact->NextSiblingElement("CONTACT");
+			}
+			// xml contact contains more than one subcontacts?
+			if (xContact) {
+				// load all subcontacts
+				do {
+					// update progressbar and abort if user clicked cancel
+					pszNick = mir_utf8decodeT(xContact->Attribute("nick"));
+					result = _pXmlFile->_progress.UpdateContact(_T("Sub Contact: %s (") _T(TCHAR_STR_PARAM) _T(")"), pszNick, xContact->Attribute("proto"));
+					if (pszNick) mir_free(pszNick);
+					// user clicked abort button
+					if (!result) break;
+					if (vContact = xContact) {
+						if (vContact.ImportMetaSubContact(this) == ERROR_ABORTED)
+							return ERROR_ABORTED;
+						_pXmlFile->_numContactsDone++;
+					}
+				}
+				while (xContact = xContact->NextSiblingElement("CONTACT"));
+			}
+			// load metacontact information (after subcontact for faster import)
+			ImportContact();
+			return ERROR_OK;
+		}
+		// import sub contacts as normal contacts
+		return _pXmlFile->ImportContacts(_xmlNode);
+	}
+
+	// load contact information
+	result = ImportContact();
+	if (result == ERROR_OK && !keepMetaSubContact)
+	{
+		CallService(MS_MC_REMOVEFROMMETA, NULL, (LPARAM)_hContact);
+	}
+
+	return result;
+}
+
+/**
+ * name:	ImportMetaSubContact
+ * class:	CExImContactXML
+ * desc:	create the contact if neccessary and copy
+ *			all information from the xmlNode to database.
+ *			Add this contact to an meta contact
+ * param:	pMetaContact	- the meta contact to add this one to
+ * return:	
+ **/
+INT CExImContactXML::ImportMetaSubContact(CExImContactXML * pMetaContact)
+{
+	INT err = ImportContact();
+
+	// abort here if contact was not imported correctly
+	if (err != ERROR_OK) return err;
+
+	// check if contact is subcontact of the desired meta contact
+	if ((HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)_hContact, NULL) != pMetaContact->handle()) {
+		// add contact to the metacontact (this service returns TRUE if successful)	
+		err = CallService(MS_MC_ADDTOMETA, (WPARAM)_hContact, (LPARAM)pMetaContact->handle());
+		if (err == FALSE) {
+			// ask to delete new contact
+			if (_isNewContact && _hContact != NULL) {
+				LPTSTR ptszNick = mir_utf8decodeT(_pszNick);
+				LPTSTR ptszMetaNick = mir_utf8decodeT(pMetaContact->_pszNick);
+				INT result = MsgBox(NULL, MB_YESNO|MB_ICONWARNING, 
+					LPGENT("Question"), 
+					LPGENT("Importing a new meta subcontact failed!"), 
+					LPGENT("The newly created MetaSubContact '%s'\ncould not be added to MetaContact '%s'!\n\nDo you want to delete this contact?"),
+					ptszNick, ptszMetaNick);
+				MIR_FREE(ptszNick);
+				MIR_FREE(ptszMetaNick);
+				if (result == IDYES) {
+					DB::Contact::Delete(_hContact);
+					_hContact = INVALID_HANDLE_VALUE;
+				}
+			}
+			return ERROR_ADDTO_METACONTACT;
+		}
+	}
+	return ERROR_OK;
+}
+
+/**
+ * name:	ImportModule
+ * class:	CExImContactXML
+ * desc:	interprete an xmlnode as module and add the children to database.
+ * params:	hContact	- handle to the contact, who is the owner of the setting to import
+ *			xmlModule	- xmlnode representing the module
+ *			stat		- structure used to collect some statistics
+ * return:	ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CExImContactXML::ImportModule(TiXmlNode* xmlModule)
+{
+	TiXmlElement *xMod;
+	TiXmlElement *xKey;
+	LPCSTR pszModule;
+	BOOLEAN isProtoModule;
+	BOOLEAN isMetaModule;
+	
+	// check if parent is really a module
+	if (!xmlModule || mir_stricmp(xmlModule->Value(), XKEY_MOD))
+		return ERROR_INVALID_SIGNATURE;
+	// convert to element
+	if (!(xMod = xmlModule->ToElement()))
+		return ERROR_INVALID_PARAMS;
+	// get module name
+	pszModule = xMod->Attribute("key");
+	if (!pszModule || !*pszModule)
+		return ERROR_INVALID_PARAMS;
+	// ignore Modul 'Protocol' as it would cause trouble
+	if (!mir_stricmp(pszModule, "Protocol"))
+		return ERROR_OK;
+	
+	for (xKey = xmlModule->FirstChildElement(); xKey != NULL; xKey = xKey->NextSiblingElement()) {
+		// import setting
+		if (!mir_stricmp(xKey->Value(), XKEY_SET)) {
+			// check if the module to import is the contact's protocol module
+			isProtoModule	= !mir_stricmp(pszModule, _pszProto)/* || DB::Module::IsMeta(pszModule)*/;
+			isMetaModule	= DB::Module::IsMeta(pszModule);
+
+			// just ignore MetaModule on normal contact to avoid errors (only keys)
+			if (!isProtoModule && isMetaModule) {
+				continue;
+			}
+			// just ignore MetaModule on Meta to avoid errors (only import spetial keys)
+			else if(isProtoModule && isMetaModule) {
+				if (!mir_stricmp(xKey->Attribute("key"),"Nick") ||
+					!mir_stricmp(xKey->Attribute("key"),"TzName") ||
+					!mir_stricmp(xKey->Attribute("key"),"Timezone")) {
+					if (ImportSetting(pszModule, xKey->ToElement()) == ERROR_OK) {
+						_pXmlFile->_numSettingsDone++;
+					}
+				}
+			}
+			// just ignore some settings of protocol module to avoid errors (only keys)
+			else if (isProtoModule && !isMetaModule) {
+				if (!IsContactInfo(xKey->Attribute("key"))) {
+					if (ImportSetting(pszModule, xKey->ToElement()) == ERROR_OK) {
+						_pXmlFile->_numSettingsDone++;
+					}
+				}
+			}
+			// other module
+			else if (ImportSetting(pszModule, xKey->ToElement()) == ERROR_OK) {
+					_pXmlFile->_numSettingsDone++;
+			}
+			if (!_pXmlFile->_progress.UpdateSetting(LPGENT("Settings: %S"), pszModule))
+				return ERROR_ABORTED;
+		}
+		// import event
+		else if (!mir_stricmp(xKey->Value(), XKEY_EVT)) {
+			INT error = ImportEvent(pszModule, xKey->ToElement());
+			switch (error) {
+				case ERROR_OK:
+					_pXmlFile->_numEventsDone++;
+					break;
+				case ERROR_DUPLICATED:
+					_pXmlFile->_numEventsDuplicated++;
+					break;
+			}
+			if (!_pXmlFile->_progress.UpdateSetting(LPGENT("Events: %S"), pszModule))
+				return ERROR_ABORTED;
+		}
+	} //*end for
+	return ERROR_OK;
+}
+
+/**
+ * name:	ImportSetting
+ * class:	CExImContactXML
+ * desc:	interprete an setting representing xmlnode and write the corresponding setting to database.
+ * params:	xmlModule	- xmlnode representing the module to write the setting to in the database
+ *			xmlEntry	- xmlnode representing the setting to import
+ * return:	ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CExImContactXML::ImportSetting(LPCSTR pszModule, TiXmlElement *xmlEntry)
+{
+	DBCONTACTWRITESETTING cws = {0};
+	TiXmlText* xval;
+	LPCSTR value;
+	
+	// validate parameter
+	if (!xmlEntry || !pszModule || !*pszModule)
+		return ERROR_INVALID_PARAMS;
+
+	// validate value
+	xval = (TiXmlText*)xmlEntry->FirstChild();
+	if (!xval || xval->Type() != TiXmlText::TEXT)
+		return ERROR_INVALID_VALUE;
+	value = xval->Value();
+
+	// init write structure
+	cws.szModule = (LPSTR)pszModule;
+	cws.szSetting = xmlEntry->Attribute("key");
+	
+	// convert data
+	size_t	len		= 0;
+	INT_PTR	baselen	= NULL;
+
+	switch (value[0]) {
+		case 'b':			//'b' bVal and cVal are valid
+			cws.value.type = DBVT_BYTE;
+			cws.value.bVal = (BYTE)atoi(value + 1);
+			break;
+		case 'w':			//'w' wVal and sVal are valid
+			cws.value.type = DBVT_WORD;
+			cws.value.wVal = (WORD)atoi(value + 1);
+			break;
+		case 'd':			//'d' dVal and lVal are valid
+			cws.value.type = DBVT_DWORD;
+			cws.value.dVal = (DWORD)_atoi64(value + 1);
+//			cws.value.dVal = (DWORD)atoi(value + 1);
+			break;
+		case 's':			//'s' pszVal is valid
+			cws.value.type = DBVT_ASCIIZ;
+			cws.value.pszVal = (LPSTR)mir_utf8decodeA((LPSTR)(value + 1));
+			break;
+		case 'u':
+			cws.value.type = DBVT_UTF8;
+			cws.value.pszVal = (LPSTR)mir_strdup((LPSTR)(value + 1));
+			break;
+		case 'n':
+			len = strlen(value + 1);
+			baselen = Base64DecodeGetRequiredLength(len);
+			cws.value.type = DBVT_BLOB;
+			cws.value.pbVal = (PBYTE)mir_alloc(baselen +1);
+			if (cws.value.pbVal != NULL){
+				if (Base64Decode((value + 1), len, cws.value.pbVal, &baselen)) {
+					cws.value.cpbVal = baselen;
+				}
+				else {
+					mir_free(cws.value.pbVal);
+					return ERROR_NOT_ADDED;
+				}
+			}
+			break;
+		default:
+			return ERROR_INVALID_TYPE;
+	}
+	// write value to db
+	if (CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)_hContact, (LPARAM)&cws)) {
+		//if (cws.value.pbVal>0)
+		mir_free(cws.value.pbVal);
+		if(cws.value.type == DBVT_ASCIIZ || cws.value.type == DBVT_UTF8) mir_free(cws.value.pszVal);
+		return ERROR_NOT_ADDED;
+	}
+	//if (cws.value.pbVal>0)
+	mir_free(cws.value.pbVal);
+	if(cws.value.type == DBVT_ASCIIZ || cws.value.type == DBVT_UTF8) mir_free(cws.value.pszVal);
+	return ERROR_OK;
+}
+
+/**
+ * name:	ImportEvent
+ * class:	CExImContactXML
+ * desc:	interprete an xmlnode and add the corresponding event to database.
+ * params:	hContact	- handle to the contact, who is the owner of the setting to import
+ *			xmlModule	- xmlnode representing the module to write the setting to in the database
+ *			xmlEvent	- xmlnode representing the event to import
+ * return:	ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CExImContactXML::ImportEvent(LPCSTR pszModule, TiXmlElement *xmlEvent)
+{
+	DBEVENTINFO	dbei;
+	TiXmlText	*xmlValue;
+	LPCSTR		tmp;
+	size_t		cbSrc;
+	INT_PTR		baselen;
+
+	// dont import events from metacontact
+	if (isMeta()) {
+		return ERROR_DUPLICATED;
+	}
+
+	if (!xmlEvent || !pszModule || !*pszModule)
+		return ERROR_INVALID_PARAMS;
+
+	if (stricmp(xmlEvent->Value(), "evt"))
+		return ERROR_NOT_ADDED;
+
+	// timestamp must be valid
+	xmlEvent->Attribute("time", (LPINT)&dbei.timestamp);
+	if (dbei.timestamp == 0) return ERROR_INVALID_TIMESTAMP;
+
+	xmlValue = (TiXmlText*)xmlEvent->FirstChild();
+	if (!xmlValue || xmlValue->Type() != TiXmlText::TEXT)
+		return ERROR_INVALID_VALUE;
+	tmp = xmlValue->Value();
+	if (!tmp || tmp[0] == 0)
+		return ERROR_INVALID_VALUE;
+
+	cbSrc		= strlen(tmp);
+	baselen		= Base64DecodeGetRequiredLength(cbSrc);
+	dbei.cbBlob	= NULL;
+	dbei.pBlob	= NULL;
+	dbei.pBlob	= (PBYTE)mir_alloc(baselen + 1);
+	if (dbei.pBlob != NULL) {
+		if (Base64Decode(tmp, cbSrc, dbei.pBlob, &baselen)) {
+			INT_PTR hEvent;
+
+			// event owning module
+			dbei.cbSize		= sizeof(dbei);
+			dbei.szModule	= (LPSTR)pszModule;
+			dbei.cbBlob		= baselen;
+
+			xmlEvent->Attribute("type", (LPINT)&dbei.eventType);
+			xmlEvent->Attribute("flag", (LPINT)&dbei.flags);
+			if (dbei.flags == 0) dbei.flags = DBEF_READ;
+
+			// search in new and existing contact for existing event to avoid duplicates
+			if (/*!_isNewContact && */DB::Event::Exists(_hContact, _hEvent, &dbei)) {
+				mir_free(dbei.pBlob);
+				return ERROR_DUPLICATED;
+			}
+
+			hEvent = CallService(MS_DB_EVENT_ADD, (WPARAM)_hContact, (LPARAM)&dbei);
+			mir_free(dbei.pBlob);
+			if (hEvent) {
+				_hEvent = (HANDLE)hEvent;
+				return ERROR_OK;
+			}
+		}
+		mir_free(dbei.pBlob);
+	}
+	return ERROR_NOT_ADDED;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/classExImContactXML.h b/plugins/UserInfoEx/src/ex_import/classExImContactXML.h
new file mode 100644
index 0000000000..52ad2ef087
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/classExImContactXML.h
@@ -0,0 +1,103 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/classExImContactXML.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _CLASS_EXIM_CONTACT_XML_INCLUDED_
+#define _CLASS_EXIM_CONTACT_XML_INCLUDED_ 1
+
+#include "svc_ExImport.h"
+#include "classExImContactBase.h"
+#include "svc_ExImXML.h"
+#include "dlg_ExImProgress.h"
+
+#define XKEY_MOD		"MOD"
+#define XKEY_SET		"SET"
+#define XKEY_EVT		"evt"
+#define XKEY_CONTACT	"CONTACT"
+#define XKEY_OWNER		"OWNER"
+
+enum EError {
+	ERROR_OK					= 0,
+	ERROR_NOT_ADDED				= 1,
+	ERROR_INVALID_PARAMS		= 2,
+	ERROR_INVALID_VALUE			= 3,
+	ERROR_INVALID_TIMESTAMP		= 4,
+	ERROR_INVALID_TYPE			= 5,
+	ERROR_DUPLICATED			= 6,
+	ERROR_MEMORY_ALLOC			= 7,
+	ERROR_INVALID_CONTACT		= 8,
+	ERROR_INVALID_SIGNATURE		= 9,
+	ERROR_ABORTED				= 10,
+	ERROR_CONVERT_METACONTACT	= 11,
+	ERROR_ADDTO_METACONTACT		= 12,
+	ERROR_EMPTY_MODULE			= 13
+};
+
+class CExImContactXML : public CExImContactBase {
+
+	CFileXml*		_pXmlFile;		// the xmlfile
+	TiXmlElement*	_xmlNode;		// xmlnode with contact information
+	HANDLE			_hEvent;
+
+	BOOLEAN IsContactInfo(LPCSTR pszKey);
+
+	// private importing methods
+	INT		ImportModule(TiXmlNode* xmlModule);
+	INT		ImportSetting(LPCSTR pszModule, TiXmlElement *xmlEntry);
+	INT		ImportEvent(LPCSTR pszModule, TiXmlElement *xmlEvent);
+	INT		ImportContact();
+	INT		ImportNormalContact();
+	INT		ImportMetaSubContact(CExImContactXML * pMetaContact);
+	VOID	CountKeys(DWORD &numSettings, DWORD &numEvents);
+
+	// private exporting methods
+	INT		ExportModule(LPCSTR pszModule);
+	INT		ExportSetting(TiXmlElement *xmlModule, LPCSTR pszModule, LPCSTR pszSetting);
+	BOOLEAN	ExportEvents();
+
+	INT		ExportContact(DB::CEnumList* pModules);
+	INT		ExportSubContact(CExImContactXML *vMetaContact, DB::CEnumList* pModules);
+
+public:
+	CExImContactXML(CFileXml * pXmlFile);
+
+	// exporting stuff
+	TiXmlElement*	CreateXmlElement();
+	INT		Export(FILE *xmlfile, DB::CEnumList* pModules);
+
+	// importing stuff
+	INT		LoadXmlElemnt(TiXmlElement *xContact);
+	INT		Import(BOOLEAN keepMetaSubContact = FALSE);
+
+	BOOLEAN operator = (TiXmlElement* xmlContact)	{
+		return LoadXmlElemnt(xmlContact) == ERROR_OK;	
+	}
+};
+
+#endif /* _CLASS_EXIM_CONTACT_XML_INCLUDED_ */
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp b/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp
new file mode 100644
index 0000000000..9ca27d7289
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.cpp
@@ -0,0 +1,464 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImModules.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+//#include "svc_ExImport.h"
+#include "dlg_ExImModules.h"
+
+/***********************************************************************************************************
+ * typedefs
+ ***********************************************************************************************************/
+
+typedef struct {
+	lpExImParam		ExImContact;
+	DB::CEnumList*	pModules;
+} EXPORTDATA, *LPEXPORTDATA;
+
+/***********************************************************************************************************
+ * modules stuff
+ ***********************************************************************************************************/
+
+/**
+ * name:	ExportTree_AppendModuleList
+ * desc:	according to the checked list items create the module list for exporting
+ * param:	hTree		- handle to the window of the treeview
+ *			hParent		- parent tree item for the item to add
+ *			pModules	- module list to fill
+ * return:	nothing
+ **/
+void ExportTree_AppendModuleList(HWND hTree, HTREEITEM hParent, DB::CEnumList* pModules)
+{
+	TVITEMA tvi;
+
+	// add all checked modules
+	if (tvi.hItem = TreeView_GetChild(hTree, hParent)) {
+		CHAR szModule[MAXSETTING];
+
+		// add optional items
+		tvi.mask = TVIF_STATE|TVIF_TEXT;
+		tvi.stateMask = TVIS_STATEIMAGEMASK;
+		tvi.pszText = szModule;
+		tvi.cchTextMax = MAXSETTING;
+
+		do {
+			if (
+				SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi) &&
+				(
+					tvi.state == INDEXTOSTATEIMAGEMASK(0) ||
+					tvi.state == INDEXTOSTATEIMAGEMASK(2)
+				) 
+			 )
+			{
+				pModules->Insert(tvi.pszText);
+			}
+		}
+		while (tvi.hItem = TreeView_GetNextSibling(hTree, tvi.hItem));
+	}
+}
+
+/**
+ * name:	ExportTree_FindItem
+ * desc:	find a item by its label
+ * param:	hTree		- handle to the window of the treeview
+ *			hParent		- parent tree item for the item to add
+ *			pszText		- text to match the label against
+ * return:	a handle to the found treeitem or NULL
+ **/
+HTREEITEM ExportTree_FindItem(HWND hTree, HTREEITEM hParent, LPSTR pszText)
+{
+	TVITEMA tvi;
+	CHAR szBuf[128];
+
+	if (!pszText || !*pszText) return NULL;
+
+	tvi.mask = TVIF_TEXT;
+	tvi.pszText = szBuf;
+	tvi.cchTextMax = SIZEOF(szBuf);
+
+	for (tvi.hItem = TreeView_GetChild(hTree, hParent);
+		tvi.hItem != NULL;
+		tvi.hItem = TreeView_GetNextSibling(hTree, tvi.hItem))
+	{
+		if (SendMessageA(hTree, TVM_GETITEMA, NULL, (LPARAM)&tvi) && !mir_stricmp(tvi.pszText, pszText))
+			return tvi.hItem;
+	}
+	return NULL;
+}
+
+/**
+ * name:	ExportTree_AddItem
+ * desc:	add an item to the tree view with given options
+ * param:	hTree		- handle to the window of the treeview
+ *			hParent		- parent tree item for the item to add
+ *			pszDesc		- item label
+ *			bUseImages	- icons are loaded
+ *			bState		- 0-hide checkbox/1-unchecked/2-checked
+ * return:	return handle to added treeitem 
+ **/
+HTREEITEM ExportTree_AddItem(HWND hTree, HTREEITEM hParent, LPSTR pszDesc, BOOLEAN bUseImages, BYTE bState)
+{
+	TVINSERTSTRUCTA	tvii;
+	HTREEITEM hItem = NULL;
+
+	tvii.hParent = hParent;
+	tvii.hInsertAfter = TVI_SORT;
+	tvii.itemex.mask = TVIF_TEXT;
+	if (bUseImages) {
+		tvii.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+		tvii.itemex.iImage = tvii.itemex.iSelectedImage = 1;
+	}
+	tvii.itemex.pszText = pszDesc;
+	if (hItem = (HTREEITEM)SendMessageA(hTree, TVM_INSERTITEMA, NULL, (LPARAM)&tvii))
+		TreeView_SetItemState(hTree, hItem, INDEXTOSTATEIMAGEMASK(bState), TVIS_STATEIMAGEMASK);
+	return hItem;
+}
+
+/**
+ * name:	SelectModulesToExport_DlgProc
+ * desc:	dialog procedure for a dialogbox, which lists modules for a specific contact
+ * param:	hDlg		- handle to the window of the dialogbox
+ *			uMsg		- message to handle
+ *			wParam		- message specific parameter
+ *			lParam		- message specific parameter
+ * return:	message specific
+ **/
+INT_PTR CALLBACK SelectModulesToExport_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	LPEXPORTDATA pDat = (LPEXPORTDATA)GetUserData(hDlg);
+
+	switch (uMsg) {
+	
+		case WM_INITDIALOG:
+		{
+			HWND hTree;
+			BOOLEAN bImagesLoaded = 0;
+
+			// get tree handle and set treeview style
+			if (!(hTree = GetDlgItem(hDlg, IDC_TREE))) break;
+			SetWindowLongPtr(hTree, GWL_STYLE, GetWindowLongPtr(hTree, GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+
+			// init the datastructure
+			if (!(pDat = (LPEXPORTDATA)mir_alloc(sizeof(EXPORTDATA))))
+				return FALSE;
+			pDat->ExImContact	= ((LPEXPORTDATA)lParam)->ExImContact;
+			pDat->pModules		= ((LPEXPORTDATA)lParam)->pModules;
+			SetUserData(hDlg, pDat);
+
+			// set icons
+			{
+				HICON hIcon;
+				HIMAGELIST hImages;
+				OSVERSIONINFO osvi;
+				const ICONCTRL idIcon[] = {
+					{ ICO_DLG_EXPORT,	WM_SETICON,		NULL		},
+					{ ICO_DLG_EXPORT,	STM_SETIMAGE,	ICO_DLGLOGO	},
+					{ ICO_BTN_EXPORT,	BM_SETIMAGE,	IDOK		},
+					{ ICO_BTN_CANCEL,	BM_SETIMAGE,	IDCANCEL	}
+				};
+				const INT numIconsToSet = DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 2;
+				IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);
+
+				// create imagelist for treeview
+				osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+				GetVersionEx(&osvi);
+				if ((hImages = ImageList_Create(
+						GetSystemMetrics(SM_CXSMICON),
+						GetSystemMetrics(SM_CYSMICON),
+						((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion >= 5 && osvi.dwMinorVersion >= 1) ? ILC_COLOR32 : ILC_COLOR16)|ILC_MASK,
+						0, 1)
+					) != NULL)
+				{
+					SendMessage(hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImages);
+
+					bImagesLoaded 
+						= ((((hIcon = IcoLib_GetIcon(ICO_LST_MODULES)) != NULL) && 0 == ImageList_AddIcon(hImages, hIcon))
+						&& (((hIcon = IcoLib_GetIcon(ICO_LST_FOLDER)) != NULL) && 1 == ImageList_AddIcon(hImages, hIcon)));
+				}
+			}
+			// do the translation stuff
+			{
+				SendDlgItemMessage(hDlg, BTN_CHECK, BUTTONTRANSLATE, NULL, NULL);
+				SendDlgItemMessage(hDlg, BTN_UNCHECK, BUTTONTRANSLATE, NULL, NULL);
+				SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
+				SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+			}
+			// Set the Window Title and description
+			{
+				LPCTSTR name = NULL; 
+				TCHAR	oldTitle[MAXDATASIZE], 
+						newTitle[MAXDATASIZE];
+				switch (pDat->ExImContact->Typ) {
+					case EXIM_ALL:
+					case EXIM_GROUP:
+						name = TranslateT("All Contacts");
+						break;
+					case EXIM_CONTACT:
+						if (pDat->ExImContact->hContact == NULL) {
+							name = TranslateT("Owner");
+						}
+						else {
+							name = DB::Contact::DisplayName(pDat->ExImContact->hContact);
+						}
+						break;
+					case EXIM_SUBGROUP:
+						name = (LPCTSTR) pDat->ExImContact->ptszName;
+						break;
+					case EXIM_ACCOUNT:
+						PROTOACCOUNT* acc = ProtoGetAccount(pDat->ExImContact->pszName);
+						name = (LPCTSTR) acc->tszAccountName;
+						break;
+				}
+				TranslateDialogDefault(hDlg);			//to translate oldTitle
+				GetWindowText(hDlg, oldTitle, MAXSETTING);
+				mir_sntprintf(newTitle, MAXDATASIZE - 1, _T("%s - %s"), name, oldTitle);
+				SetWindowText(hDlg, newTitle);
+			}
+
+			{
+				LPSTR pszProto;
+				TVINSERTSTRUCT	tviiT;
+				DB::CEnumList Modules;
+				HTREEITEM hItemEssential, hItemOptional;
+
+				TreeView_SetIndent(hTree, 6);
+				TreeView_SetItemHeight(hTree, 18);
+
+				pszProto = (pDat->ExImContact->Typ == EXIM_CONTACT && pDat->ExImContact->hContact != NULL)
+					? (LPSTR)DB::Contact::Proto(pDat->ExImContact->hContact)
+					: NULL;
+
+				// add items that are always exported
+				tviiT.hParent = TVI_ROOT;
+				tviiT.hInsertAfter = TVI_FIRST;
+				tviiT.itemex.mask = TVIF_TEXT|TVIF_STATE;
+				tviiT.itemex.pszText = TranslateT("Required modules");
+				tviiT.itemex.state = tviiT.itemex.stateMask = TVIS_EXPANDED;
+				if (bImagesLoaded) {
+					tviiT.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+					tviiT.itemex.iImage = tviiT.itemex.iSelectedImage = 0;
+				}
+				if (hItemEssential = TreeView_InsertItem(hTree, &tviiT)) {
+					// disable state images
+					TreeView_SetItemState(hTree, hItemEssential, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
+
+					// insert essential items (modul from UIEX)
+					ExportTree_AddItem(hTree, hItemEssential, USERINFO, bImagesLoaded, 0);
+					ExportTree_AddItem(hTree, hItemEssential, MOD_MBIRTHDAY, bImagesLoaded, 0);
+
+					/*Filter/ protocol module is ignored for owner contact
+					if (pDat->hContact != NULL) {
+						ExportTree_AddItem(hTree, hItemEssential, "Protocol", bImagesLoaded, 0);
+					}*/
+
+					// base protocol is only valid for single exported contact at this position
+					if (pszProto) {
+						ExportTree_AddItem(hTree, hItemEssential, pszProto, bImagesLoaded, 0);
+					}
+				}
+
+				// add items that are optional (and more essential)
+				tviiT.hInsertAfter = TVI_LAST;
+				tviiT.itemex.pszText = TranslateT("Optional modules");
+				if (hItemOptional = TreeView_InsertItem(hTree, &tviiT)) {
+					TreeView_SetItemState(hTree, hItemOptional, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
+
+					if (!Modules.EnumModules())	// init Modul list
+					{
+						INT i;
+						LPSTR p;
+
+						for (i = 0; i < Modules.getCount(); i++)
+						{
+							p = Modules[i];
+							/*Filter/
+							if (!DB::Module::IsMeta(p))/end Filter*/
+							{
+								// module must exist in at least one contact
+								if (pDat->ExImContact->Typ != EXIM_CONTACT) // TRUE = All Contacts
+								{
+									HANDLE hContact;
+									
+									for (hContact = DB::Contact::FindFirst();
+											 hContact != NULL;
+											 hContact = DB::Contact::FindNext(hContact))
+									{
+										// ignore empty modules
+										if (!DB::Module::IsEmpty(hContact, p)) {
+											pszProto = DB::Contact::Proto(hContact);
+											// Filter by mode
+											switch (pDat->ExImContact->Typ)
+											{
+												case EXIM_ALL:
+												case EXIM_GROUP:
+													break;
+												case EXIM_SUBGROUP:
+													if (mir_tcsncmp(pDat->ExImContact->ptszName, DB::Setting::GetTString(hContact, "CList", "Group"), mir_tcslen(pDat->ExImContact->ptszName))) {
+														continue;
+													}
+													break;
+												case EXIM_ACCOUNT:
+													if (mir_strcmp(pDat->ExImContact->pszName, pszProto)) {
+														continue;
+													}
+													break;
+											}
+
+											// contact's base protocol is to be added to the treeview uniquely
+											if (!mir_stricmp(p, pszProto))
+											{
+												if (!ExportTree_FindItem(hTree, hItemEssential, p))
+												{
+													ExportTree_AddItem(hTree, hItemEssential, p, bImagesLoaded, 0);
+												}
+												break;
+											}
+
+											// add optional module, which is valid for at least one contact
+											/*/Filter/*/
+											if ( mir_stricmp(p, USERINFO) &&
+												mir_stricmp(p, MOD_MBIRTHDAY) &&
+												// Meta is only valid as base Proto at this point
+												mir_stricmp(p, myGlobals.szMetaProto) /*&&
+												mir_stricmp(p, "Protocol")*/
+											 ) 
+											{
+												ExportTree_AddItem(hTree, hItemOptional, p, bImagesLoaded, 1);
+												break;
+											}
+										}
+									} // end for
+								} // end TRUE = All Contacts
+
+								// module must exist in the selected contact
+								else if (
+									/*Filter/*/
+									!DB::Module::IsEmpty(pDat->ExImContact->hContact, p) &&
+									(!pDat->ExImContact->hContact || mir_stricmp(p, pszProto)) &&
+									//mir_stricmp(p, "Protocol") &&
+									mir_stricmp(p, USERINFO) &&
+									mir_stricmp(p, MOD_MBIRTHDAY))
+								{
+									ExportTree_AddItem(hTree, hItemOptional, (LPSTR)p, bImagesLoaded, 1);
+								}
+							} // end 
+						}
+					}
+				}
+			}
+			TranslateDialogDefault(hDlg);
+			return TRUE;
+		}
+		case WM_CTLCOLORSTATIC:
+			if (GetDlgItem(hDlg, STATIC_WHITERECT) == (HWND)lParam || GetDlgItem(hDlg, ICO_DLGLOGO) == (HWND)lParam) {
+				SetBkColor((HDC)wParam, RGB(255, 255, 255));
+				return (INT_PTR)GetStockObject(WHITE_BRUSH);
+			}
+			SetBkMode((HDC)wParam, TRANSPARENT);
+			return (INT_PTR)GetStockObject(NULL_BRUSH);
+
+		case WM_COMMAND:
+			switch (LOWORD(wParam)) {
+				case IDOK:
+				{
+					HWND hTree = GetDlgItem(hDlg, IDC_TREE);
+					HTREEITEM hParent;
+
+					// search the tree item of optional items
+					for (hParent = TreeView_GetRoot(hTree);
+						hParent != NULL;
+						hParent = TreeView_GetNextSibling(hTree, hParent))
+					{
+						ExportTree_AppendModuleList(hTree, hParent, pDat->pModules);
+					}
+					return EndDialog(hDlg, IDOK);
+				}
+				case IDCANCEL:
+					return EndDialog(hDlg, IDCANCEL);
+
+				case BTN_CHECK:
+				case BTN_UNCHECK:
+				{
+					HWND hTree = GetDlgItem(hDlg, IDC_TREE);
+					LPCSTR pszRoot = Translate("Optional modules");
+					TVITEMA tvi;
+					CHAR szText[128];
+
+					tvi.mask = TVIF_TEXT;
+					tvi.pszText = szText;
+					tvi.cchTextMax = sizeof(szText);
+
+					// search the tree item of optional items
+					for (tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, NULL);
+						tvi.hItem != NULL && SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi);
+						tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)tvi.hItem))
+					{
+						if (!mir_stricmp(tvi.pszText, pszRoot)) {
+							tvi.mask = TVIF_STATE;
+							tvi.state = INDEXTOSTATEIMAGEMASK(LOWORD(wParam) == BTN_UNCHECK ? 1 : 2);
+							tvi.stateMask = TVIS_STATEIMAGEMASK;
+
+							for (tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)tvi.hItem);
+								tvi.hItem != NULL;
+								tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)tvi.hItem))
+							{
+								SendMessageA(hTree, TVM_SETITEMA, NULL, (LPARAM)&tvi);
+							}
+							break;
+						}
+					}
+					break;
+				}
+			}
+			break;
+
+		case WM_DESTROY:
+			mir_free(pDat);
+			break;
+	}
+	return 0;
+}
+
+/**
+ * name:	DlgExImModules_SelectModulesToExport
+ * desc:	calls a dialog box that lists all modules for a specific contact
+ * param:	ExImContact	- lpExImParam
+ *			pModules	- pointer to an ENUMLIST structure that retrieves the resulting list of modules
+ *			hParent		- handle to a window which should act as the parent of the created dialog
+ * return:	0 if user pressed ok, 1 on cancel
+ **/
+INT DlgExImModules_SelectModulesToExport(lpExImParam ExImContact, DB::CEnumList* pModules, HWND hParent)
+{
+	EXPORTDATA dat;
+
+	dat.ExImContact = ExImContact;
+	dat.pModules = pModules;
+	return (IDOK != DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_EXPORT), hParent, SelectModulesToExport_DlgProc, (LPARAM)&dat));
+}
+
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h b/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h
new file mode 100644
index 0000000000..69ca65ddd3
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImModules.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImModules.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _DLG_EXIMMODULES_H_
+#define _DLG_EXIMMODULES_H_
+
+#pragma once
+#include "svc_ExImport.h"
+
+INT DlgExImModules_SelectModulesToExport(lpExImParam ExImContact, DB::CEnumList* pModules, HWND hParent);
+
+#endif /* _DLG_EXIMMODULES_H_ */
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp b/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp
new file mode 100644
index 0000000000..d9d0efbd7d
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.cpp
@@ -0,0 +1,371 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImOpenSaveFile.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+
+
+ #include <dlgs.h>
+
+
+#include "m_db3xSA.h"
+#include "dlg_ExImOpenSaveFile.h"
+
+
+
+#define HKEY_MIRANDA_PLACESBAR	_T("Software\\Miranda IM\\PlacesBar")
+#define HKEY_WINPOL_PLACESBAR	_T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\ComDlg32\\PlacesBar")
+
+static WNDPROC	DefPlacesBarProc;
+
+/**
+ * This function maps the current users registry to a dummy key and
+ * changes the policy hive which is responsible for the places to be displayed,
+ * so the desired places are visible.
+ *
+ * @param		nothing
+ * @return		nothing
+ **/
+static VOID InitAlteredPlacesBar()
+{
+	// do not try it on a win9x Box
+	if (IsWinVer2000Plus())
+	{
+		HKEY hkMiranda;
+		LONG result;
+
+		// create or open temporary hive for miranda specific places
+		result = RegCreateKey(HKEY_CURRENT_USER, HKEY_MIRANDA_PLACESBAR, &hkMiranda);
+		if (SUCCEEDED(result)) 
+		{
+			HKEY hkPlacesBar;
+
+			// map the current users registry
+			RegOverridePredefKey(HKEY_CURRENT_USER, hkMiranda);
+			// open the policy key
+			result = RegCreateKey(HKEY_CURRENT_USER, HKEY_WINPOL_PLACESBAR, &hkPlacesBar);
+			// install the places bar
+			if (SUCCEEDED(result)) 
+			{
+				DWORD dwFolderID;
+				LPSTR p;
+				CHAR szMirandaPath[MAX_PATH];
+				CHAR szProfilePath[MAX_PATH];
+
+				// default places: Desktop, My Documents, My Computer
+				dwFolderID = 0;	 RegSetValueEx(hkPlacesBar, _T("Place0"), 0, REG_DWORD, (PBYTE)&dwFolderID, sizeof(DWORD));
+				dwFolderID = 5;	RegSetValueEx(hkPlacesBar, _T("Place1"), 0, REG_DWORD, (PBYTE)&dwFolderID, sizeof(DWORD));
+				dwFolderID = 17; RegSetValueEx(hkPlacesBar, _T("Place2"), 0, REG_DWORD, (PBYTE)&dwFolderID, sizeof(DWORD));
+
+				// Miranda's installation path
+				GetModuleFileNameA(GetModuleHandle(NULL), szMirandaPath, SIZEOF(szMirandaPath)); 
+				p = mir_strrchr(szMirandaPath, '\\');
+				if (p) 
+				{
+					RegSetValueExA(hkPlacesBar, "Place3", 0, REG_SZ, (PBYTE)szMirandaPath, (p - szMirandaPath) + 1);
+				}
+
+				// Miranda's profile path
+				if (!CallService(MS_DB_GETPROFILEPATH, SIZEOF(szProfilePath), (LPARAM)szProfilePath))
+				{
+					// only add if different from profile path
+					RegSetValueExA(hkPlacesBar, "Place4", 0, REG_SZ, (PBYTE)szProfilePath, (DWORD)strlen(szProfilePath) + 1);
+				}
+
+				RegCloseKey(hkPlacesBar);
+			}
+			RegCloseKey(hkMiranda);
+		}
+	}
+}
+
+/**
+ * name:		ResetAlteredPlaceBars
+ * desc:		Remove the mapping of current users registry
+ *				and delete the temporary registry hive
+ * params:		nothing
+ * return:		nothing
+ **/
+static VOID ResetAlteredPlaceBars()
+{
+	// make sure not to call the following on a Win9x Box
+	if (IsWinVer2000Plus()) 
+	{
+		RegOverridePredefKey(HKEY_CURRENT_USER, NULL);
+		SHDeleteKey(HKEY_CURRENT_USER, HKEY_MIRANDA_PLACESBAR);
+	}
+}
+
+/**
+ * name:		PlacesBarSubclassProc
+ * params:		hWnd	- handle, to control's window
+ *				uMsg	- the message to handle
+ *				wParam	- message dependend parameter
+ *				lParam	- message dependend parameter
+ * return:		depends on message
+ **/
+static LRESULT PlacesBarSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg) 
+	{
+		case TB_ADDBUTTONS: 
+		{
+			TBBUTTON *tbb = (TBBUTTON *)lParam;
+			TCHAR szBtnText[MAX_PATH];
+			INT iString;
+			HWND hWndToolTip;
+
+			if (tbb)
+			{
+				switch (tbb->idCommand)
+				{
+					// miranda button
+					case 41063:
+						ZeroMemory(szBtnText, sizeof(szBtnText));
+						
+						mir_tcsncpy(szBtnText, TranslateT("Miranda IM"), SIZEOF(szBtnText));
+						iString = SendMessage(hWnd, TB_ADDSTRING, NULL, (LPARAM)szBtnText);
+						if (iString != -1) tbb->iString = iString;
+						// set tooltip
+						hWndToolTip = (HWND)SendMessage(hWnd, TB_GETTOOLTIPS, NULL, NULL);
+						if (hWndToolTip) {
+							TOOLINFO ti;
+
+							ZeroMemory(&ti, sizeof(ti));
+							ti.cbSize = sizeof(ti);
+							ti.hwnd = hWnd;
+							ti.lpszText = TranslateT("Shows Miranda's installation directory.");
+							ti.uId = tbb->idCommand;
+							SendMessage(hWndToolTip, TTM_ADDTOOL, NULL, (LPARAM)&ti);
+						}
+						break;
+					// profile button
+					case 41064:
+						// set button text
+						iString = SendMessage(hWnd, TB_ADDSTRING, NULL, (LPARAM) TranslateT("Profile"));
+						if (iString != -1) tbb->iString = iString;
+
+						// set tooltip
+						hWndToolTip = (HWND)SendMessage(hWnd, TB_GETTOOLTIPS, NULL, NULL);
+						if (hWndToolTip) {
+							TOOLINFO ti;
+
+							ZeroMemory(&ti, sizeof(ti));
+							ti.cbSize = sizeof(ti);
+							ti.hwnd = hWnd;
+							ti.lpszText = TranslateT("Shows the directory with all your Miranda's profiles.");
+							ti.uId = tbb->idCommand;
+							SendMessage(hWndToolTip, TTM_ADDTOOL, NULL, (LPARAM)&ti);
+						}
+						// unmap registry and delete keys
+						ResetAlteredPlaceBars();
+						break;
+				}
+			}
+			break;
+		}
+	}
+	return CallWindowProc(DefPlacesBarProc, hWnd, uMsg, wParam,lParam);
+}
+
+/**
+ * name:		OpenSaveFileDialogHook
+ * desc:		it subclasses the places bar to provide the own interface for adding places
+ * params:		hDlg	- handle, to control's window
+ *				uMsg	- the message to handle
+ *				wParam	- message dependend parameter
+ *				lParam	- message dependend parameter
+ * return:		depends on message
+ **/
+static UINT_PTR CALLBACK OpenSaveFileDialogHook(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg) {
+		case WM_NOTIFY:
+			if (((LPNMHDR)lParam)->code == CDN_INITDONE) {
+				HWND hPlacesBar = GetDlgItem(GetParent(hDlg), ctl1);
+
+				// we have a places bar?
+				if (hPlacesBar != NULL) {
+					InitAlteredPlacesBar();
+					// finally subclass the places bar
+					DefPlacesBarProc = SubclassWindow(hPlacesBar, PlacesBarSubclassProc);
+				}
+			}
+			break;
+		case WM_DESTROY:
+			// unmap registry and delete keys 
+			// (is to make it sure, if somehow the last places button was not added which also calls this function)
+			ResetAlteredPlaceBars();
+			break;
+	}
+	return FALSE;
+}
+
+
+
+/**
+ * name:		GetInitialDir
+ * desc:		read the last vCard directory from database
+ * pszInitialDir	- buffer to store the initial dir to (size must be MAX_PATH)
+ * return:		nothing
+ **/
+static VOID GetInitialDir(LPSTR pszInitialDir)
+{
+	CHAR szRelative[MAX_PATH];
+
+	ZeroMemory(szRelative, SIZEOF(szRelative));
+
+	// is some standard path defined
+	if (!DB::Setting::GetStatic(0, MODNAME, "vCardPath", szRelative, SIZEOF(szRelative))) {
+		if (!CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)szRelative, (WPARAM)pszInitialDir))
+			strcpy(pszInitialDir, szRelative);
+	}
+	else if (//try to use environment variables supported by pathpatch of db3xSA
+		!ServiceExists(MS_DB_GETPROFILEPATH_BASIC) ||		
+		!CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)PROFILEPATH "\\" PROFILENAME, (WPARAM)pszInitialDir)
+	 ) {
+		// use standard path to absolute
+		if (!CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)"", (WPARAM)pszInitialDir))
+			*pszInitialDir = 0;
+	}
+	else
+		*pszInitialDir = 0;
+}
+
+/**
+ * name:		SaveInitialDir
+ * desc:		save the last vCard directory from database
+ *				pszInitialDir	- buffer to store the initial dir to (size must be MAX_PATH)
+ * return:		nothing
+ **/
+static VOID SaveInitialDir(LPSTR pszInitialDir)
+{
+	CHAR szRelative[MAX_PATH];
+	LPSTR p;
+
+	if (p = mir_strrchr(pszInitialDir, '\\')) {
+		*p = 0;
+		if (CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)pszInitialDir, (LPARAM)szRelative))
+			DB::Setting::WriteAString(0, MODNAME, "vCardPath", szRelative);
+		else
+			DB::Setting::WriteAString(0, MODNAME, "vCardPath", pszInitialDir);
+		*p = '\\';
+	}	
+}
+
+/**
+ * name:		InitOpenFileNameStruct
+ * desc:		initialize the openfilename structure
+ * params:		pofn			- OPENFILENAME structure to initialize
+ *				hWndParent		- parent window
+ *				pszTitle		- title for the dialog
+ *				pszFilter		- the filters to offer
+ *				pszInitialDir	- buffer to store the initial dir to (size must be MAX_PATH)
+ *				pszFile			- this is the buffer to store the file to (size must be MAX_PATH)
+ * return:		nothing
+ **/
+static VOID InitOpenFileNameStruct(OPENFILENAMEA *pofn, HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszInitialDir, LPSTR pszFile)
+{
+	ZeroMemory(pofn, sizeof(OPENFILENAME));
+
+	pofn->Flags				= OFN_NONETWORKBUTTON|OFN_ENABLESIZING;
+	pofn->hwndOwner			= hWndParent;
+	pofn->lpstrTitle		= pszTitle;
+	pofn->lpstrFilter		= pszFilter;
+	pofn->lpstrFile			= pszFile;
+	pofn->nMaxFile			= MAX_PATH;
+	pofn->lpstrDefExt		= "xml";
+
+	GetInitialDir(pszInitialDir);
+	pofn->lpstrInitialDir = pszInitialDir;
+
+
+	if (IsWinVer2000Plus()) {
+		pofn->lStructSize = sizeof (OPENFILENAME);
+		pofn->Flags		 |= OFN_ENABLEHOOK|OFN_EXPLORER;
+		pofn->lpfnHook		= (LPOFNHOOKPROC)OpenSaveFileDialogHook;
+	}
+	else {
+		pofn->lStructSize = OPENFILENAME_SIZE_VERSION_400;
+	}
+
+}
+
+
+/**
+ * name:		DlgExIm_OpenFileName
+ * desc:		displayes a slightly modified OpenFileName DialogBox
+ * params:		hWndParent		- parent window
+ *				pszTitle		- title for the dialog
+ *				pszFilter		- the filters to offer
+ *				pszFile			- this is the buffer to store the file to (size must be MAX_PATH)
+ * return:		-1 on error/abort or filter index otherwise
+ **/
+INT DlgExIm_OpenFileName(HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszFile)
+{
+	OPENFILENAMEA ofn;
+	CHAR szInitialDir[MAX_PATH];
+
+	InitOpenFileNameStruct(&ofn, hWndParent, pszTitle, pszFilter, szInitialDir, pszFile);
+	ofn.Flags |= OFN_PATHMUSTEXIST;
+	if (!GetOpenFileNameA(&ofn)) {
+		DWORD dwError = CommDlgExtendedError();
+		if (dwError) MsgErr(ofn.hwndOwner, LPGENT("The OpenFileDialog returned an error: %d!"), dwError);
+		return -1;
+	}
+	SaveInitialDir(pszFile);
+	return ofn.nFilterIndex;
+}
+
+/**
+ * name:		DlgExIm_SaveFileName
+ * desc:		displayes a slightly modified SaveFileName DialogBox
+ * params:		hWndParent		- parent window
+ *				pszTitle		- title for the dialog
+ *				pszFilter		- the filters to offer
+ *				pszFile			- this is the buffer to store the file to (size must be MAX_PATH)
+ * return:		-1 on error/abort or filter index otherwise
+ **/
+INT DlgExIm_SaveFileName(HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszFile)
+{
+	OPENFILENAMEA ofn;
+	CHAR szInitialDir[MAX_PATH];
+
+	InitOpenFileNameStruct(&ofn, hWndParent, pszTitle, pszFilter, szInitialDir, pszFile);
+	ofn.Flags |= OFN_OVERWRITEPROMPT;
+
+	if (!GetSaveFileNameA(&ofn)) {
+		DWORD dwError = CommDlgExtendedError();
+
+		if (dwError) MsgErr(ofn.hwndOwner, LPGENT("The SaveFileDialog returned an error: %d!"), dwError);
+		return -1;
+	}
+	SaveInitialDir(pszFile);
+	return ofn.nFilterIndex;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h b/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h
new file mode 100644
index 0000000000..eb47e9ab30
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImOpenSaveFile.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImOpenSaveFile.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _DLG_EXIMOPENSAVEFFILE_H_
+#define _DLG_EXIMOPENSAVEFFILE_H_
+
+#pragma once
+
+INT DlgExIm_OpenFileName(HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszFile);
+INT DlgExIm_SaveFileName(HWND hWndParent, LPCSTR pszTitle, LPCSTR pszFilter, LPSTR pszFile);
+
+#endif /* _DLG_EXIMOPENSAVEFFILE_H_ */
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp b/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp
new file mode 100644
index 0000000000..9da4ee4373
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.cpp
@@ -0,0 +1,244 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImProgress.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "dlg_ExImProgress.h"
+
+/***********************************************************************************************************
+ * windows procedure
+ ***********************************************************************************************************/
+
+/**
+ * name:	DlgProcProgress
+ * desc:	dialog procedure for the progress dialog
+ * params:	none
+ * return:	nothing
+ **/
+LRESULT CALLBACK DlgProcProgress(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	switch (msg) 
+	{
+		case WM_INITDIALOG:
+		{
+			const ICONCTRL idIcon[] = {
+				{ ICO_DLG_IMPORT,	WM_SETICON,		NULL		},
+				{ ICO_DLG_IMPORT,	STM_SETIMAGE,	ICO_DLGLOGO	},
+				{ ICO_BTN_CANCEL,	BM_SETIMAGE,	IDCANCEL	}
+			};
+			const INT numIconsToSet = DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 2;
+			IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);
+
+			TranslateDialogDefault(hDlg);
+			SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+			SendDlgItemMessage(hDlg, IDC_PROGRESS, PBM_SETPOS, 0, 0);
+			SendDlgItemMessage(hDlg, IDC_PROGRESS2, PBM_SETPOS, 0, 0);
+			SetWindowLongPtr(hDlg, GWLP_USERDATA, 0);
+			UpdateWindow(hDlg);
+			break;
+		}
+		case WM_CTLCOLORSTATIC:
+			switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) {
+				//case IDC_HEADERBAR
+				case STATIC_WHITERECT:
+				case TXT_SETTING:
+				case IDC_PROGRESS:
+				case TXT_CONTACT:
+				case IDC_PROGRESS2:
+				//case ICO_DLGLOGO:
+				//case IDC_INFO:
+					SetBkColor((HDC)wParam, RGB(255, 255, 255));
+					return (INT_PTR)GetStockObject(WHITE_BRUSH);
+			}
+			return FALSE;
+		case WM_COMMAND:
+			if (HIWORD(wParam) == BN_CLICKED) {
+				switch (LOWORD(wParam)) {
+					case IDCANCEL:
+					// in the progress dialog, use the user data to indicate that the user has pressed cancel
+					ShowWindow(hDlg, SW_HIDE);
+					SetWindowLongPtr(hDlg, GWLP_USERDATA, 1);
+					return TRUE;
+				}
+			}
+			break;
+	}
+	return FALSE;
+}
+
+/**
+ * name:	CProgress
+ * class:	CProgress
+ * desc:	create the progress dialog and return a handle as pointer to the datastructure
+ * params:	none
+ * return:	nothing
+ **/
+CProgress::CProgress()
+{
+	_dwStartTime = GetTickCount();
+	_hDlg = CreateDialog(ghInst, MAKEINTRESOURCE(IDD_COPYPROGRESS), 0, (DLGPROC)DlgProcProgress);
+}
+
+/**
+ * name:	~CProgress
+ * class:	CProgress
+ * desc:	destroy the progress dialog and its data structure
+ * params:	none
+ * return:	nothing
+ **/
+CProgress::~CProgress()
+{
+	if(IsWindow(_hDlg)) DestroyWindow(_hDlg);
+}
+
+/**
+ * name:	SetContactCount
+ * class:	CProgress
+ * desc:	number of contacts to show 100% for
+ * params:	numContacts	- the number of contacts
+ * return:	nothing
+ **/
+VOID CProgress::SetContactCount(DWORD numContacts)
+{
+	if (_hDlg) {
+		HWND hProgress = GetDlgItem(_hDlg, IDC_PROGRESS2);
+		SendMessage(hProgress, PBM_SETRANGE32, 0, numContacts);	 
+		SendMessage(hProgress, PBM_SETPOS, 0,	0);	 
+	}
+}
+
+/**
+ * name:	SetSettingsCount
+ * class:	CProgress
+ * desc:	number of settings & events to show 100% for
+ * params:	numSettings	- the number of settings & events
+ * return:	nothing
+ **/
+VOID CProgress::SetSettingsCount(DWORD numSettings)
+{
+	if (_hDlg) {
+		HWND hProgress = GetDlgItem(_hDlg, IDC_PROGRESS);
+		SendMessage(hProgress, PBM_SETRANGE32, 0, numSettings);	 
+		SendMessage(hProgress, PBM_SETPOS, 0,	0);	 
+	}
+}
+
+/**
+ * name:	Hide
+ * class:	CProgress
+ * desc:	hides the dialog
+ * params:	none
+ * return:	nothing
+ **/
+VOID CProgress::Hide()
+{
+	ShowWindow(_hDlg, SW_HIDE);
+}
+
+/**
+ * name:	Update
+ * class:	CProgress
+ * desc:	update the progress dialog
+ * params:	nothing
+ * return:	FALSE if user pressed cancel, TRUE otherwise
+ **/
+BOOLEAN CProgress::Update()
+{
+	MSG msg;
+	
+	// show dialog after one second
+	if (GetTickCount() > _dwStartTime + 1000) {
+		ShowWindow(_hDlg, SW_SHOW);
+	}
+
+	UpdateWindow(_hDlg);
+
+	while (PeekMessage(&msg, _hDlg, 0, 0, PM_REMOVE) != 0) {
+		 if (!IsDialogMessage(_hDlg, &msg)) {
+			TranslateMessage(&msg);
+			DispatchMessage(&msg);
+		 }
+	}
+	return GetWindowLongPtr(_hDlg, GWLP_USERDATA) == 0;
+}
+
+/**
+ * name:	UpdateContact
+ * class:	CProgress
+ * desc:	increase contact's progressbar by one and set new text
+ * params:	pszFormat	- the text to display for the contact
+ * return:	FALSE if user pressed cancel, TRUE otherwise
+ **/
+BOOLEAN CProgress::UpdateContact(LPCTSTR pszFormat, ...)
+{
+	if (_hDlg != NULL) {
+		HWND hProg = GetDlgItem(_hDlg, IDC_PROGRESS2);
+		if (pszFormat) {
+			TCHAR buf[MAX_PATH];
+			va_list vl;
+
+			va_start(vl, pszFormat);
+			mir_vsntprintf(buf, SIZEOF(buf), TranslateTS(pszFormat), vl);
+			va_end(vl);
+			SetDlgItemText(_hDlg, TXT_CONTACT, buf);	 
+		}
+		SendMessage(hProg, PBM_SETPOS, (INT)SendMessage(hProg, PBM_GETPOS, 0, 0) + 1,	0);
+		return Update();
+	}
+	return TRUE;
+}
+
+/**
+ * name:	UpdateContact
+ * class:	CProgress
+ * desc:	increase setting's progressbar by one and set new text
+ * params:	pszFormat	- the text to display for the setting
+ * return:	FALSE if user pressed cancel, TRUE otherwise
+ **/
+BOOLEAN CProgress::UpdateSetting(LPCTSTR pszFormat, ...)
+{
+	if (_hDlg != NULL) {
+		HWND hProg = GetDlgItem(_hDlg, IDC_PROGRESS);
+		if (pszFormat) {
+			TCHAR buf[MAX_PATH];
+			TCHAR tmp[MAX_PATH];
+			va_list vl;
+
+			va_start(vl, pszFormat);
+			mir_vsntprintf(buf, SIZEOF(buf), TranslateTS(pszFormat), vl);
+			va_end(vl);
+			GetDlgItemText(_hDlg, TXT_SETTING, tmp, SIZEOF(tmp));
+			if(mir_tcsicmp(tmp,buf))
+				SetDlgItemText(_hDlg, TXT_SETTING, buf);
+		}
+		SendMessage(hProg, PBM_SETPOS, (INT)SendMessage(hProg, PBM_GETPOS, 0, 0) + 1, 0);
+		return Update();
+	}
+	return TRUE;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h b/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h
new file mode 100644
index 0000000000..55ab614cfe
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/dlg_ExImProgress.h
@@ -0,0 +1,56 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/dlg_ExImProgress.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _DLG_EXIMPROGRESS_H_
+#define _DLG_EXIMPROGRESS_H_
+
+#pragma once
+
+class CProgress
+{
+	HWND	_hDlg;
+	DWORD	_dwStartTime;
+
+	BOOLEAN	Update();
+
+public:
+	CProgress();
+	~CProgress();
+
+	VOID Hide();
+
+	VOID SetContactCount(DWORD numContacts);
+	VOID SetSettingsCount(DWORD numSettings);
+	
+	BOOLEAN UpdateContact(LPCTSTR pszFormat, ...);
+	BOOLEAN UpdateSetting(LPCTSTR pszFormat, ...);
+};
+
+#endif /* _DLG_EXIMPROGRESS_H_ */
diff --git a/plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h b/plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h
new file mode 100644
index 0000000000..0fe287ec93
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/mir_rfcCodecs.h
@@ -0,0 +1,371 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/mir_rfcCodecs.h $
+Revision       : $Revision: 190 $
+Last change on : $Date: 2010-09-14 14:32:57 +0400 (Вт, 14 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include <stdlib.h>
+
+//Not including CRLFs
+//NOTE: For BASE64 and UUENCODE, this actually
+//represents the amount of unencoded characters
+//per line
+#define TSSMTPMAX_QP_LINE_LENGTH			 76
+#define TSSMTPMAX_BASE64_LINE_LENGTH	 57
+#define TSSMTPMAX_UUENCODE_LINE_LENGTH 45
+
+//=======================================================================
+// Base64Encode/Base64Decode
+// compliant with RFC 2045
+//=======================================================================
+//
+#define BASE64_FLAG_NONE	0
+#define BASE64_FLAG_NOPAD	1
+#define BASE64_FLAG_NOCRLF	2
+
+inline INT_PTR Base64EncodeGetRequiredLength(INT_PTR nSrcLen, DWORD dwFlags = BASE64_FLAG_NONE)
+{
+	INT_PTR nRet = nSrcLen*4/3;
+
+	if ((dwFlags & BASE64_FLAG_NOPAD) == 0)
+		nRet += nSrcLen % 3;
+
+	INT_PTR nCRLFs = nRet / 76 + 3;
+	INT_PTR nOnLastLine = nRet % 76;
+
+	if (nOnLastLine) {
+		if (nOnLastLine % 4)
+			nRet += 4 - (nOnLastLine % 4);
+	}
+
+	nCRLFs *= 2;
+
+	if ((dwFlags & BASE64_FLAG_NOCRLF) == 0)
+		nRet += nCRLFs;
+
+	return nRet;
+}
+
+inline INT_PTR Base64DecodeGetRequiredLength(INT_PTR nSrcLen)
+{
+	return nSrcLen;
+}
+
+inline BOOL Base64Encode(
+	const BYTE *pbSrcData,
+	INT_PTR nSrcLen,
+	LPSTR szDest,
+	INT_PTR *pnDestLen,
+	DWORD dwFlags = BASE64_FLAG_NONE)
+{
+	static const char s_chBase64EncodingTable[64] = {
+		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
+		'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',	'h',
+		'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
+		'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
+
+	if (!pbSrcData || !szDest || !pnDestLen)
+		return FALSE;
+
+	INT_PTR nWritten(0);
+	INT_PTR nLen1((nSrcLen / 3) * 4);
+	INT_PTR nLen2(nLen1 / 76);
+	INT_PTR nLen3(19);
+	INT_PTR i, j, k, n;
+
+	for (i = 0; i <= nLen2; i++) {
+		if (i == nLen2)
+			nLen3 = (nLen1 % 76) / 4;
+
+		for (j = 0; j < nLen3; j++) {
+			DWORD dwCurr(0);
+			for (INT_PTR n = 0; n < 3; n++)	{
+				dwCurr |= *pbSrcData++;
+				dwCurr <<= 8;
+			}
+			for (k = 0; k < 4; k++) {
+				BYTE b = (BYTE)(dwCurr >> 26);
+				*szDest++ = s_chBase64EncodingTable[b];
+				dwCurr <<= 6;
+			}
+		}
+		nWritten += nLen3 * 4;
+
+		if ((dwFlags & BASE64_FLAG_NOCRLF) == 0) {
+			*szDest++ = '\r';
+			*szDest++ = '\n';
+			*szDest++ = '\t';		// as vcards have tabs in second line of binary data
+			nWritten += 3;
+		}
+	}
+
+	if (nWritten && (dwFlags & BASE64_FLAG_NOCRLF) == 0) {
+		szDest -= 2;
+		nWritten -= 2;
+	}
+
+	nLen2 = nSrcLen % 3 ? nSrcLen % 3 + 1 : 0;
+	if (nLen2) {
+		DWORD dwCurr(0);
+		for (n = 0; n < 3; n++)
+		{
+			if (n < (nSrcLen % 3))
+				dwCurr |= *pbSrcData++;
+			dwCurr <<= 8;
+		}
+		for (k = 0; k < nLen2; k++) {
+			BYTE b = (BYTE)(dwCurr >> 26);
+			*szDest++ = s_chBase64EncodingTable[b];
+			dwCurr <<= 6;
+		}
+		nWritten+= nLen2;
+		if ((dwFlags & BASE64_FLAG_NOPAD) == 0) {
+			nLen3 = nLen2 ? 4 - nLen2 : 0;
+			for (j = 0; j < nLen3; j++)	{
+				*szDest++ = '=';
+			}
+			nWritten+= nLen3;
+		}
+	}
+
+	*pnDestLen = nWritten;
+	return TRUE;
+}
+
+inline INT_PTR DecodeBase64Char(UINT ch) throw()
+{
+	// returns -1 if the character is invalid
+	// or should be skipped
+	// otherwise, returns the 6-bit code for the character
+	// from the encoding table
+	if (ch >= 'A' && ch <= 'Z')
+		return ch - 'A' + 0;	// 0 range starts at 'A'
+	if (ch >= 'a' && ch <= 'z')
+		return ch - 'a' + 26;	// 26 range starts at 'a'
+	if (ch >= '0' && ch <= '9')
+		return ch - '0' + 52;	// 52 range starts at '0'
+	if (ch == '+')
+		return 62;
+	if (ch == '/')
+		return 63;
+	return -1;
+}
+
+inline BOOL Base64Decode(LPCSTR szSrc, INT_PTR nSrcLen, BYTE *pbDest, INT_PTR *pnDestLen) throw()
+{
+	// walk the source buffer
+	// each four character sequence is converted to 3 bytes
+	// CRLFs and =, and any characters not in the encoding table
+	// are skiped
+
+	if (szSrc == NULL || pnDestLen == NULL) {
+		return FALSE;
+	}
+
+	LPCSTR szSrcEnd = szSrc + nSrcLen;
+	INT_PTR nWritten = 0;
+
+	BOOL bOverflow = (pbDest == NULL) ? TRUE : FALSE;
+
+	while (szSrc < szSrcEnd) {
+		DWORD dwCurr = 0;
+		INT_PTR i;
+		INT_PTR nBits = 0;
+		for (i=0; i<4; i++) {
+			if (szSrc >= szSrcEnd)
+				break;
+			INT_PTR nCh = DecodeBase64Char(*szSrc);
+			szSrc++;
+			if (nCh == -1) {
+				// skip this char
+				i--;
+				continue;
+			}
+			dwCurr <<= 6;
+			dwCurr |= nCh;
+			nBits += 6;
+		}
+
+		if (!bOverflow && nWritten + (nBits/8) > (*pnDestLen))
+			bOverflow = TRUE;
+
+		// dwCurr has the 3 bytes to write to the output buffer
+		// left to right
+		dwCurr <<= 24-nBits;
+		for (i=0; i<nBits/8; i++) {
+			if (!bOverflow) {
+				*pbDest = (BYTE) ((dwCurr & 0x00ff0000) >> 16);
+				pbDest++;
+			}
+			dwCurr <<= 8;
+			nWritten++;
+		}
+	}
+	*pnDestLen = nWritten;
+	return bOverflow ? FALSE:TRUE;
+}
+
+//=======================================================================
+// Quoted Printable encode/decode
+// compliant with RFC 2045
+//=======================================================================
+//
+#define TSSMTPQPENCODE_DOT 1
+#define TSSMTPQPENCODE_TRAILING_SOFT 2
+
+inline INT_PTR QPEncodeGetRequiredLength(INT_PTR nSrcLen)
+{
+	INT_PTR nRet = 3*((3*nSrcLen)/(TSSMTPMAX_QP_LINE_LENGTH-8));
+	nRet += 3*nSrcLen;
+	nRet += 3;
+	return nRet;
+}
+
+inline INT_PTR QPDecodeGetRequiredLength(INT_PTR nSrcLen)
+{
+	return nSrcLen;
+}
+
+inline BOOL QPEncode(BYTE* pbSrcData, INT_PTR nSrcLen, LPSTR szDest, INT_PTR* pnDestLen, BOOLEAN *bEncoded, DWORD dwFlags = 0)
+{
+	//The hexadecimal character set
+	static const CHAR s_chHexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
+											'A', 'B', 'C', 'D', 'E', 'F'};
+	INT_PTR nRead = 0, nWritten = 0, nLineLen = 0;
+	CHAR ch;
+	BOOLEAN bChanged = FALSE;
+
+
+	if (!pbSrcData || !szDest || !pnDestLen) {
+		return FALSE;
+	}
+
+	while (nRead < nSrcLen) {
+		ch = *pbSrcData++;
+		nRead++;
+		if (nLineLen == 0 && ch == '.' && (dwFlags & TSSMTPQPENCODE_DOT)) {
+			*szDest++ = '.';
+			nWritten++;
+			nLineLen++;
+			bChanged = TRUE;
+		}
+		if ((ch > 32 && ch < 61) || (ch > 61 && ch < 127)) {
+			*szDest++ = ch;
+			nWritten++;
+			nLineLen++;
+		}
+		else
+		if ((ch == ' ' || ch == '\t') && (nLineLen < (TSSMTPMAX_QP_LINE_LENGTH - 12))) {
+			*szDest++ = ch;
+			nWritten++;
+			nLineLen++;
+		}	
+		else {
+			*szDest++ = '=';
+			*szDest++ = s_chHexChars[(ch >> 4) & 0x0F];
+			*szDest++ = s_chHexChars[ch & 0x0F];
+			nWritten += 3;
+			nLineLen += 3;
+			bChanged = TRUE;
+		}
+		if (nLineLen >= (TSSMTPMAX_QP_LINE_LENGTH - 11)) {
+			*szDest++ = '=';
+			*szDest++ = '\r';
+			*szDest++ = '\n';
+			nLineLen = 0;
+			nWritten += 3;
+			bChanged = TRUE;
+		}
+	}
+	if (dwFlags & TSSMTPQPENCODE_TRAILING_SOFT) {
+		*szDest++ = '=';
+		*szDest++ = '\r';
+		*szDest++ = '\n';
+		nWritten += 3;
+		bChanged = TRUE;
+	}
+	*pnDestLen = nWritten;
+	if (bEncoded) *bEncoded = bChanged;
+	return TRUE;
+}
+
+
+inline BOOL QPDecode(BYTE* pbSrcData, INT_PTR nSrcLen, LPSTR szDest, INT_PTR* pnDestLen, DWORD dwFlags = 0)
+{
+	if (!pbSrcData || !szDest || !pnDestLen)
+	{
+		return FALSE;
+	}
+
+	INT_PTR nRead = 0, nWritten = 0, nLineLen = -1;
+	char ch;
+	while (nRead <= nSrcLen)
+	{
+		ch = *pbSrcData++;
+		nRead++;
+		nLineLen++;
+		if (ch == '=')
+		{
+			//if the next character is a digit or a character, convert
+			if (nRead < nSrcLen && (isdigit(*pbSrcData) || isalpha(*pbSrcData)))
+			{
+				char szBuf[5];
+				szBuf[0] = *pbSrcData++;
+				szBuf[1] = *pbSrcData++;
+				szBuf[2] = '\0';
+				char* tmp = '\0';
+				*szDest++ = (BYTE)strtoul(szBuf, &tmp, 16);
+				nWritten++;
+				nRead += 2;
+				continue;
+			}
+			//if the next character is a carriage return or line break, eat it
+			if (nRead < nSrcLen && *pbSrcData == '\r' && (nRead+1 < nSrcLen) && *(pbSrcData+1)=='\n')
+			{
+				pbSrcData++;
+				nRead++;
+				nLineLen = -1;
+				continue;
+			}
+			return FALSE;
+		}
+		if (ch == '\r' || ch == '\n')
+		{
+			nLineLen = -1;
+			continue;
+		}
+		if ((dwFlags & TSSMTPQPENCODE_DOT) && ch == '.' && nLineLen == 0)
+		{
+			continue;
+		}
+		*szDest++ = ch;
+		nWritten++;
+	}
+
+	*pnDestLen = nWritten-1;
+	return TRUE;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp b/plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp
new file mode 100644
index 0000000000..f3c4ce6362
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImINI.cpp
@@ -0,0 +1,547 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImINI.cpp $
+Revision       : $Revision: 196 $
+Last change on : $Date: 2010-09-21 03:24:30 +0400 (Вт, 21 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * system & local includes:
+ **/
+#include "commonheaders.h"
+#include "classExImContactBase.h"
+#include "dlg_ExImModules.h"
+#include "svc_ExImport.h"
+#include "svc_ExImINI.h"
+
+/**
+ * Miranda includes
+ **/
+#include <m_protocols.h>
+#include <m_protosvc.h>
+
+/***********************************************************************************************************
+ * exporting stuff
+ ***********************************************************************************************************/
+
+/**
+ * name:	ExportModule
+ * desc:	write all settings from a database module to file
+ * param:	hContact	- handle of contact the module is owned from
+ *			pszModule	- name of the module to save
+ *			file		- file to write the settings to
+ * return	nothing
+ **/
+static VOID ExportModule(HANDLE hContact, LPCSTR pszModule, FILE* file)
+{
+	DB::CEnumList	Settings;
+
+	if (!Settings.EnumSettings(hContact, pszModule))
+	{
+		DBVARIANT dbv;
+		LPSTR here;
+		WORD j;
+		INT i;
+		LPSTR pszSetting;
+		//char tmp[32];
+
+		// print the module header..
+		fprintf(file, "\n[%s]\n", pszModule);
+
+		for (i = 0; i < Settings.getCount(); i++)
+		{
+			pszSetting = Settings[i];
+
+			if (!DB::Setting::GetAsIs(hContact, pszModule, pszSetting, &dbv))
+			{
+				switch (dbv.type) 
+				{
+					case DBVT_BYTE:
+						{
+							fprintf(file, "%s=b%u\n", pszSetting, dbv.bVal);
+						}
+						break;
+
+					case DBVT_WORD:
+						{
+							fprintf(file, "%s=w%u\n", pszSetting, dbv.wVal);
+						}
+						break;
+
+					case DBVT_DWORD:
+						{
+							fprintf(file, "%s=d%u\n", pszSetting, dbv.dVal);
+						}
+						break;
+
+					case DBVT_ASCIIZ:
+					case DBVT_UTF8:
+						{
+							for (here = dbv.pszVal; here && *here; here++) 
+							{
+								switch (*here) {
+									// convert \r to STX
+									case '\r':
+										*here = 2;
+										break;
+
+									// convert \n to ETX
+									case '\n':
+										*here = 3;
+								}
+							}
+							if (dbv.type == DBVT_UTF8) 
+								fprintf(file, "%s=u%s\n", pszSetting, dbv.pszVal);
+							else
+								fprintf(file, "%s=s%s\n", pszSetting, dbv.pszVal);
+						}
+						break;
+
+					case DBVT_BLOB:
+						{
+							fprintf(file, "%s=n", pszSetting);
+							for (j = 0; j < dbv.cpbVal; j++)
+							{
+								fprintf(file, "%02X ", (BYTE)dbv.pbVal[j]);
+							}
+							fputc('\n', file);
+						}
+						break;
+				}
+				DB::Variant::Free(&dbv);
+			}
+		}
+	}
+}
+
+/**
+ * name:	ExportContact
+ * desc:	Exports a certain contact to an ini file.
+ * param:	hContact	- contact to export or -1 to export all contacts
+ *			pModules	- module to export, NULL to export all modules of a contact
+ *			file		- ini file to write the contact to
+ **/
+static BOOLEAN ExportContact(HANDLE hContact, DB::CEnumList* pModules, FILE* file)
+{
+	CExImContactBase vcc;
+
+	if (pModules) 
+	{
+		if ((vcc = hContact) >= NULL)
+		{
+			INT i;
+			LPSTR p;
+
+			vcc.toIni(file, pModules->getCount()-1);
+
+			for (i = 0; i < pModules->getCount(); i++)
+			{
+				p = (*pModules)[i];
+
+				/*Filter/
+				if (mir_stricmp(p, "Protocol"))*/
+				{
+					ExportModule(hContact, p, file);
+				}
+			}
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * name:	SvcExImINI_Export
+ * desc:	Exports a certain contact or all contacts to an ini file.
+ * param:	hContact	- contact to export or -1 to export all contacts
+ *			pszFileName	- ini-filename to write the contact to
+ **/
+INT SvcExImINI_Export(lpExImParam ExImContact, LPCSTR pszFileName)
+{
+	FILE* file;
+	errno_t err;
+	DB::CEnumList Modules;
+	SYSTEMTIME now;
+	HANDLE hContact;
+
+	if (!DlgExImModules_SelectModulesToExport(ExImContact, &Modules, NULL))
+	{
+		if ((err = fopen_s(&file, pszFileName, "wt")) != NULL)
+		{
+			MsgErr(NULL, 
+				LPGENT("The ini-file \"%s\"\nfor saving contact information could not be opened."),
+				pszFileName);
+			return 1;
+		}
+		
+		SetCursor(LoadCursor(NULL, IDC_WAIT));
+
+		// write header
+		GetLocalTime(&now);
+		fprintf(file, 
+			";DATE = %04d-%02d-%02d %02d:%02d:%02d\n\n",
+			now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond
+		);
+
+		if (Modules.getCount() == 0)
+		{
+			Modules.EnumModules();
+		}
+		
+		//	hContact == -1 export entire db.
+		if (ExImContact->Typ != EXIM_CONTACT)
+		{
+			// Owner
+			ExportContact(NULL, &Modules, file);
+			fprintf(file, "\n\n");
+			// Contacts
+			for (hContact = DB::Contact::FindFirst();
+					 hContact != NULL;
+					 hContact = DB::Contact::FindNext(hContact))
+			{
+				ExportContact(hContact, &Modules, file);
+				fprintf(file, "\n\n");
+			}
+		}
+		// export only one contact
+		else 
+		{
+			ExportContact(ExImContact->hContact, &Modules, file);
+		}
+
+		if (file)
+			fclose(file);
+		SetCursor(LoadCursor(NULL, IDC_ARROW));
+	}
+	return 0;
+}
+
+/***********************************************************************************************************
+ * importing stuff
+ ***********************************************************************************************************/
+
+LPSTR strnrchr(LPSTR string, INT ch, DWORD len)
+{
+	LPSTR start = (LPSTR)string;
+
+	string += len;		/* find end of string */
+						/* search towards front */
+	while (--string != start && *string != (CHAR)ch);
+		if (*string == (CHAR)ch)		/* char found ? */
+			return ((LPSTR)string);
+	return(NULL);
+}
+
+/**
+ * name:	ImportreadLine
+ * desc:	read exactly one line into a buffer and return its pointer. Size of buffer is managed.
+ * param:	file	- pointer to a file
+ *			string	- the string to write the read line to
+ * return:	pointer to the buffer on success or NULL on error
+ **/
+static DWORD ImportreadLine(FILE* file, LPSTR &str)
+{
+	CHAR c;
+	DWORD l = 0;
+	BOOLEAN bComment = 0;
+
+	str[0] = 0;
+	while (!feof(file)) {
+		switch (c = fgetc(file)) {
+			case EOF:
+				// reading error
+				if (ferror(file)) {
+					MIR_FREE(str);
+					return 0;
+				}
+				// end of line & file
+				return l;
+
+			case '\r':
+			case '\n':
+				// ignore empty lines
+				if (l == 0) { 
+					bComment = 0;
+					continue;
+				}
+				return l;
+			
+			case ';':
+				// found a comment line
+				bComment |= l == 0;
+			case '\t':
+			case ' ':
+				// ignore space and tab at the beginning of the line
+				if (l == 0) break;
+			
+			default:
+				if (!bComment) {
+					str = mir_strncat_c(str, c);
+					l++;
+				}
+				break;
+		}
+	}
+	return 0;
+}
+
+/**
+ * name:	ImportFindContact
+ * desc:	This function decodes the given line, which is already identified to be a contact line.
+ *			The resulting information is matcht to the given hContact if it isn't NULL.
+ *			Otherwise all existing contacts are matched.
+ * param:	hContact	- handle to contact to match or NULL to match all existing
+ *			pszBuf		- pointer to the buffer holding the string of the current line in the ini.-file
+ *			cchBuf		- character count of the buffer
+ * return:	handle to the contact that matches the information or NULL if no match
+ **/
+static HANDLE ImportFindContact(HANDLE hContact, LPSTR &strBuf, BOOLEAN bCanCreate)
+{
+	CExImContactBase vcc;
+
+	vcc.fromIni(strBuf);
+	if (vcc.handle() != INVALID_HANDLE_VALUE) {
+		//if (vcc.isHandle(hContact))
+		//	return hContact;
+		return vcc.handle();
+	}
+	else if (bCanCreate)
+		return vcc.toDB();
+
+	return vcc.handle();
+}
+
+/**
+ * name:	ImportSetting
+ * desc:	This function writes a line identified as a setting to the database
+ * param:	hContact	- handle to contact to match or NULL to match all existing
+ *			pszModule	- module to write the setting to
+ *			strLine		- string with the setting and its value to write to db
+ * return:	0 if writing was ok, 1 otherwise
+ **/
+INT ImportSetting(HANDLE hContact, LPCSTR pszModule, LPSTR &strLine)
+{
+	DBCONTACTWRITESETTING cws;
+	LPSTR end, value;
+	size_t numLines = 0;
+	size_t brk;
+	LPSTR pszLine = strLine;
+
+	// check Module and filter "Protocol"
+	if (!pszModule || !*pszModule || mir_strncmp(pszModule,"Protocol",8) == 0)
+		return 1;
+	if ((end = value = mir_strchr(pszLine, '=')) == NULL)
+		return 1;
+
+	// truncate setting string if it has spaces at the end
+	do {
+		if (end == pszLine)
+			return 1;
+		*(end--) = 0;
+	} while (*end == '\t' || *end == ' ' || *end < 27);
+
+	cws.szModule = pszModule;
+	cws.szSetting = pszLine;
+
+	// skip spaces from the beginning of the value
+	do {
+		value++;
+		// if the value is empty, delete it from db
+		if (*value == '\0')
+			return DB::Setting::Delete(hContact, pszModule, pszLine);
+	} while (*value == '\t' || *value == ' ');
+
+	// decode database type and value
+	switch (*(value++)) {
+		case 'b':
+		case 'B':
+			if (brk = strspn(value, "0123456789-"))
+				*(value + brk) = 0;
+			cws.value.type = DBVT_BYTE;
+			cws.value.bVal = (BYTE)atoi(value);
+			break;
+		case 'w':
+		case 'W':
+			if (brk = strspn(value, "0123456789-"))
+				*(value + brk) = 0;
+			cws.value.type = DBVT_WORD;
+			cws.value.wVal = (WORD)atoi(value);
+			break;
+		case 'd':
+		case 'D':
+			if (brk = strspn(value, "0123456789-"))
+				*(value + brk) = 0;
+			cws.value.type = DBVT_DWORD;
+			cws.value.dVal = (DWORD)_atoi64(value);
+			break;
+		case 's':
+		case 'S':
+		case 'u':
+		case 'U':
+			for (end = value; end && *end; end++) {
+				switch (*end) {
+					// convert STX back to \r
+					case 2:
+						*end = '\r';
+						break;
+					// convert ETX back to \n
+					case 3:
+						*end = '\n';
+						break;
+				}
+			}
+			switch (*(value - 1)) {
+				case 's':
+				case 'S':
+					cws.value.type = DBVT_ASCIIZ;
+					cws.value.pszVal = value;
+					break;
+				case 'u':
+				case 'U':
+					cws.value.type = DBVT_UTF8;
+					cws.value.pszVal = value;
+					break;
+			}
+			break;
+		case 'n':
+		case 'N':
+		{
+			PBYTE dest;
+			cws.value.type = DBVT_BLOB;
+			cws.value.cpbVal = (WORD)mir_strlen(value) / 3;
+			cws.value.pbVal = (PBYTE)value;
+			for ( dest = cws.value.pbVal, value = strtok(value, " ");
+					value && *value;
+					value = strtok(NULL, " "))
+				*(dest++) = (BYTE)strtol(value, NULL, 16);
+			*dest = 0;
+			break;
+		}
+		default:
+			cws.value.type = DBVT_DELETED;
+			//return 1;
+	}
+	return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&cws);
+}
+
+/**
+ * name:	Import
+ * desc:	This function imports an ini file
+ * param:	hContact	- handle to contact to match or NULL to match all existing
+ *			file		- module to write the setting to
+ *			strLine		- string with the setting and its value to write to db
+ * return:	0 if writing was ok, 1 otherwise
+ **/
+INT SvcExImINI_Import(HANDLE hContact, LPCSTR pszFileName)
+{
+	FILE	*file;
+	HANDLE	hNewContact				= INVALID_HANDLE_VALUE;
+	DWORD	end,
+			numLines				= 0;
+	CHAR	szModule[MAXSETTING]	= {0};
+	WORD	numContactsInFile		= 0,		// number of contacts in the inifile
+			numContactsAdded		= 0;		// number of contacts, that were added to the database
+	CHAR	*strBuf					= (CHAR *) mir_alloc(1);
+			*strBuf					= 0;
+	
+	if (file = fopen(pszFileName, "rt")) {
+		SetCursor(LoadCursor(NULL, IDC_WAIT));
+
+		while (ImportreadLine(file, strBuf)) {
+			numLines++;
+
+			// contact was found and imported
+			if (hContact != INVALID_HANDLE_VALUE && hNewContact != INVALID_HANDLE_VALUE)
+				break;
+
+			// importing settings is only valid vor the main menu item
+			if (hContact == INVALID_HANDLE_VALUE) {
+				if (!strncmp(strBuf, "SETTINGS:", 9)) {
+					*szModule = 0;
+					hNewContact = NULL;
+					continue;
+				}
+			}
+
+			// there are some modules of a contact (import only if contact exist)
+			if (!strncmp(strBuf, "FROM CONTACT:", 13)) {
+				strBuf = mir_strnerase(strBuf, 0, 13);
+				while (strBuf[0] == ' ' || strBuf[0] == '\t')
+					strBuf = mir_strnerase(strBuf, 0, 1);
+
+				numContactsInFile++;
+				if ((hNewContact = ImportFindContact(hContact, strBuf, FALSE)) != INVALID_HANDLE_VALUE)
+					numContactsAdded++;
+				continue;
+			}
+
+			// there is a contact to import / add
+			if (!strncmp(strBuf, "CONTACT:", 8)) {
+				strBuf = mir_strnerase(strBuf, 0, 8);
+				while (strBuf[0] == ' ' || strBuf[0] == '\t')
+					strBuf = mir_strnerase(strBuf, 0, 1);
+
+				*szModule = 0;
+				numContactsInFile++;
+				if ((hNewContact = ImportFindContact(hContact, strBuf, TRUE)) != INVALID_HANDLE_VALUE)
+					numContactsAdded++;
+				continue;
+			}
+
+			// read modules and settings only for valid contacts
+			if (hNewContact != INVALID_HANDLE_VALUE) {
+				// found a module line
+				if (strBuf[0] == '[' && (end = (strchr(strBuf, ']') - strBuf)) > 0) {
+					mir_strncpy(szModule, &strBuf[1], end);
+					continue;
+				}
+				// try to import a setting
+				ImportSetting(hNewContact, szModule, strBuf);
+			}
+		} //end while
+		fclose(file);
+		mir_free(strBuf);
+		SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+		// the contact was not found in the file
+		if (numContactsInFile > 0 && !numContactsAdded) {
+			MsgErr(NULL,
+				LPGENT("None of the %d contacts, stored in the ini-file, match the selected contact!\nNothing will be imported"),
+				numContactsInFile);
+		}
+		// Import complete
+		else{
+			MsgBox(NULL, MB_ICON_INFO, LPGENT("Import complete"), LPGENT("Some basic statistics"),
+				LPGENT("Added %d of %d contacts stored in the ini-file."),
+				numContactsAdded, numContactsInFile);
+		}
+		return 0;
+	}
+	MsgErr(NULL, 
+		LPGENT("The ini-file \"%s\"\nfor reading contact information could not be opened."),
+		pszFileName);
+	return 1;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImINI.h b/plugins/UserInfoEx/src/ex_import/svc_ExImINI.h
new file mode 100644
index 0000000000..63ecce7cbd
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImINI.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImINI.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_EXIMINI_H_
+#define _SVC_EXIMINI_H_
+
+#pragma once
+
+	INT SvcExImINI_Export(lpExImParam ExImContact, LPCSTR pszFileName);
+	INT SvcExImINI_Import(HANDLE hContact, LPCSTR pszFileName);
+
+#endif /* _SVC_EXIMINI_H_ */
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp b/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp
new file mode 100644
index 0000000000..53faf60c02
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.cpp
@@ -0,0 +1,1364 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImVCF.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * system & local includes:
+ **/
+#include "commonheaders.h"
+#include "../svc_reminder.h"
+#include "svc_ExImport.h"
+#include "svc_ExImVCF.h"
+
+#include <m_protosvc.h>
+
+#define BLOCKSIZE 260
+
+#define DELIM	";"
+
+/**
+ * name:	IsUSASCII
+ * desc:	determine whether the pBuffer string is ascii or not
+ * param:	pBuffer	- string to check
+ *
+ * return	TRUE or FALSE
+ **/
+BOOLEAN IsUSASCII(LPCSTR pBuffer, LPDWORD pcbBuffer) 
+{
+	 BYTE c;
+	 PBYTE s = (PBYTE)pBuffer;
+	 BOOLEAN bIsUTF = 0;
+
+	 if (s == NULL) return 1;
+	 while ((c = *s++) != 0) {
+			if (c < 0x80) continue;
+			if (!pcbBuffer) return 0;
+			bIsUTF = 1;
+	 }
+	 if (pcbBuffer) *pcbBuffer = s - (PBYTE)pBuffer;
+	 return !bIsUTF;
+}
+
+/*
+=========================================================================================================================
+ class CLineBuffer
+=========================================================================================================================
+*/
+
+
+/**
+ * name:	CLineBuffer::CLineBuffer
+ * desc:	initializes all members on construction of the class
+ * param:	none
+ *
+ * return:	nothing
+ **/
+CLineBuffer::CLineBuffer()
+{
+	_pVal	= NULL;
+	_pTok	= NULL;
+	_cbVal	= 0;
+	_cbUsed	= 0;
+}
+
+/**
+ * name:	CLineBuffer::~CLineBuffer
+ * desc:	frees up all memory on class destruction
+ * param:	none
+ *
+ * return:	nothing
+ **/
+CLineBuffer::~CLineBuffer()
+{
+	if (_pVal) mir_free(_pVal);
+}
+
+/**
+ * name:	CLineBuffer::_resizeBuf
+ * desc:	ensure, the right size for the buffer
+ * param:	cbReq	-	number of bytes required for the next operation
+ *
+ * return:	TRUE if reallocation successful or memoryblock is large enough, FALSE otherwise
+ **/
+BOOLEAN CLineBuffer::_resizeBuf(const size_t cbReq)
+{
+	if (cbReq > _cbVal - _cbUsed) {
+		if (!(_pVal = (PBYTE)mir_realloc(_pVal, BLOCKSIZE + _cbVal + 1))) {
+			_cbVal = 0;
+			_cbUsed = 0;
+			return FALSE;
+		}
+		_cbVal += BLOCKSIZE;
+	}
+	return TRUE;
+}
+
+/**
+ * name:	CLineBuffer::operator =
+ * desc:	applys the specified string to the class's _pVal member
+ * param:	szVal	-	string to apply
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator = (const CHAR *szVal)
+{
+	if (szVal) {
+		size_t cbLength = mir_strlen(szVal);
+
+		_cbUsed = 0;
+		if (_resizeBuf(cbLength)) {
+			memcpy(_pVal, szVal, cbLength);
+			_cbUsed += cbLength;
+			return	cbLength;
+		}
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::operator +
+ * desc:	appends the specified string to the class's _pVal member
+ * param:	szVal	-	string to add
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator + (const CHAR *szVal)
+{
+	if (szVal) {
+		size_t cbLength = mir_strlen(szVal);
+
+		if (_resizeBuf(cbLength)) {
+			memcpy(_pVal + _cbUsed, szVal, cbLength);
+			_cbUsed += cbLength;
+			return	cbLength;
+		}
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::operator +
+ * desc:	appends the specified unicode string to the class's _pVal member
+ * param:	wszVal	-	string to add
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator + (const WCHAR *wszVal)
+{
+	if (wszVal) {
+		size_t cbLength = mir_wcslen(wszVal);
+		CHAR* szVal = mir_u2a(wszVal);
+
+		if (szVal) {
+			size_t cbLength = mir_strlen(szVal);
+
+			if (_resizeBuf(cbLength)) {
+				memcpy(_pVal + _cbUsed, szVal, cbLength);
+				_cbUsed += cbLength;
+				return	cbLength;
+			}
+		}
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::operator +
+ * desc:	appends the specified character's value (-127 ... 128) as a string to the class's _pVal member
+ * param:	cVal	-	character whose value to add
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator + (const CHAR cVal)
+{
+	if (_resizeBuf(1)) {
+		*(_pVal + _cbUsed++) = cVal;
+		return	1;
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::operator +
+ * desc:	appends the specified bytes's value (0 ... 255) as a string to the class's _pVal member
+ * param:	bVal	-	character whose value to add
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator + (const BYTE bVal)
+{
+	size_t cbLength = 3;
+	
+	if (_resizeBuf(cbLength)) {
+		cbLength = mir_strlen(_itoa(bVal, (LPSTR)(_pVal + _cbUsed), 10));
+		_cbUsed += cbLength;
+		return	cbLength;
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::operator +
+ * desc:	appends the specified short integer as a string to the class's _pVal member
+ * param:	sVal	-	short integer whose value to add
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator + (const SHORT sVal)
+{
+	size_t cbLength = 6;
+	
+	if (_resizeBuf(cbLength)) {
+		cbLength = mir_strlen(_itoa(sVal, (LPSTR)(_pVal + _cbUsed), 10));
+		_cbUsed += cbLength;
+		return	cbLength;
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::operator +
+ * desc:	appends the specified word as a string to the class's _pVal member
+ * param:	wVal	-	word whose value to add
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator + (const WORD wVal)
+{
+	size_t cbLength = 5;
+	
+	if (_resizeBuf(cbLength)) {
+		cbLength = mir_strlen(_itoa(wVal, (LPSTR)(_pVal + _cbUsed), 10));
+		_cbUsed += cbLength;
+		return cbLength;
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::operator +
+ * desc:	appends the specified long integer as a string to the class's _pVal member
+ * param:	lVal	-	long integer whose value to add
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator + (const LONG lVal)
+{
+	size_t cbLength = 11;
+	
+	if (_resizeBuf(cbLength)) {
+		cbLength = mir_strlen(_ltoa(lVal, (LPSTR)(_pVal + _cbUsed), 10));
+		_cbUsed += cbLength;
+		return	cbLength;
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::operator +
+ * desc:	appends the specified double word integer as a string to the class's _pVal member
+ * param:	dVal	-	double word integer whose value to add
+ *
+ * return:	length of the string, added
+ **/
+size_t CLineBuffer::operator + (const DWORD dVal)
+{
+	size_t cbLength = 10;
+	
+	if (_resizeBuf(cbLength)) {
+		cbLength = mir_strlen(_ltoa(dVal, (LPSTR)(_pVal + _cbUsed), 10));
+		_cbUsed += cbLength;
+		return	cbLength;
+	}
+	return 0;
+}
+
+/**
+ * name:	CLineBuffer::GetLength
+ * desc:	returns the length of the _pVal string
+ * param:	nothing
+ *
+ * return:	length of the string
+ **/
+size_t CLineBuffer::GetLength()
+{
+	return _cbUsed;
+}
+
+/**
+ * name:	CLineBuffer::GetBuffer
+ * desc:	returns the pointer of the _pVal string
+ *			!!Use carefully
+ * param:	nothing
+ *
+ * return:	pointer to _pVal
+ **/
+LPCSTR CLineBuffer::GetBuffer() 
+{
+	return (LPCSTR)_pVal;
+}
+
+/**
+ * name:	CLineBuffer::TruncToLength
+ * desc:	resulting string has cbLength characters
+ * param:	cbLength	- desired length of the string member
+ *
+ * return:	nothing
+ **/
+VOID CLineBuffer::TruncToLength(size_t cbLength)
+{
+	if (cbLength < _cbUsed) {
+		_cbUsed = cbLength;
+		_pVal[cbLength] = 0;
+	}
+}
+
+/**
+ * name:	CLineBuffer::TruncToLength
+ * desc:	resulting string is Truncated by the specified number of bytes
+ * param:	count	- desired count of bytes to Truncate
+ *
+ * return:	nothing
+ **/
+VOID CLineBuffer::Truncate(size_t count)
+{
+	if (_cbUsed <= count) {
+		_cbUsed = 0;
+		*_pVal = 0;
+	}
+	else {
+		_cbUsed -= count;
+		_pVal[_cbUsed] = 0;
+	}
+}
+
+/**
+ * name:	CLineBuffer::TruncateSMS
+ * desc:	resulting string is Truncated by the " SMS" if found
+ * param:	nothing
+ *
+ * return:	nothing
+ **/
+VOID CLineBuffer::TruncateSMS()
+{
+	if (!strncmp((LPSTR)(_pVal + _cbUsed - 4), " SMS", 4)) {
+		_cbUsed -= 4;
+		_pVal[_cbUsed] = 0;
+	}
+}
+
+/**
+ * name:	CLineBuffer::fput
+ * desc:	string member is written to the specified stream and Truncated to zero afterwards
+ * param:	outfile		-	the stream to write to
+ *
+ * return:	nothing
+ **/
+VOID CLineBuffer::fput(FILE *outfile)
+{
+	if (_pVal) {
+		_pVal[_cbUsed] = 0;
+		fputs((LPCSTR)_pVal, outfile);
+		_cbUsed = 0;
+		*_pVal = 0;
+	}
+}
+
+/**
+ * name:	CLineBuffer::fputEncoded
+ * desc:	string member is encoded and written to the specified stream and Truncated to zero afterwards
+ * param:	outfile		-	the stream to write to
+ *
+ * return:	nothing
+ **/
+VOID CLineBuffer::fputEncoded(FILE *outFile)
+{
+	PBYTE pVal = _pVal;
+
+	if (pVal && _cbUsed > 0) {
+		_pVal[_cbUsed] = 0;
+		while (_cbUsed > 0 && *pVal) {
+			switch (*pVal) {
+				// translate special characters
+				case ':':
+				case ';':
+				case '\r':
+				case '\n':
+				case '\t':
+					fprintf(outFile, "=%02X", *pVal);
+					break;
+				// Some database texts may contain encoded escapes, that have to be translated too.
+				case '\\':
+					if (*(pVal+1) == 'r') {
+						fprintf(outFile, "=%02X", '\r');
+						pVal++;
+						break;
+					}
+					if (*(pVal+1) == 't') {
+						fprintf(outFile, "=%02X", '\t');
+						pVal++;
+						break;
+					}
+					if (*(pVal+1) == 'n') {
+						fprintf(outFile, "=%02X", '\n');
+						pVal++;
+						break;
+					}
+				// translate all characters which are not contained in the USASCII code
+				default:
+					if (*pVal > 127)	fprintf(outFile, "=%02X", *pVal);
+					else fputc(*pVal, outFile);
+					break;
+			}
+			pVal++;
+			(_cbUsed)--;
+		}
+		*_pVal = 0;
+	}
+}
+
+/**
+ * name:	CLineBuffer::fgetEncoded
+ * desc:	string member is read from the specified stream decoded
+ * param:	outfile		-	the stream to write to
+ *
+ * return:	nothing
+ **/
+INT CLineBuffer::fgetEncoded(FILE *inFile)
+{
+	CHAR c;
+	CHAR hex[3];
+	WORD wAdd = 0;
+
+	hex[2] = 0;
+
+	_cbUsed = 0;
+
+	while (EOF != (c = fgetc(inFile))) {
+		switch (c) {
+			case '\n':
+				if (_cbUsed > 0 && _pVal[_cbUsed - 1] == '\r') {
+					_pVal[--_cbUsed] = 0;
+					wAdd--;
+				}
+				else
+					_pVal[_cbUsed] = 0;
+				return wAdd;
+			case '=':
+				if (_resizeBuf(1)) {
+					fread(hex, 2, 1, inFile);
+					*(_pVal + _cbUsed++) = (BYTE)strtol(hex, NULL, 16);
+					wAdd++;
+				}
+				break;
+			default:
+				if (_resizeBuf(1)) {
+					*(_pVal + _cbUsed++) = c;
+					wAdd++;
+				}
+				break;
+		}
+	}
+	_pVal[_cbUsed] = 0;
+	return _cbUsed > 0 ? wAdd : EOF;
+}
+
+/**
+ * name:	CLineBuffer::GetTokenFirst
+ * desc:	scans for the first <delim> in the _pVal member and returns all characters
+ *			that come before the first <delim> in a new CLineBuffer class
+ * param:	delim		-	the deliminer which delimins the string
+ *			pBuf		-	pointer to a new CLineBuffer class holding the result
+ *
+ * return:	length of the found string value
+ **/
+size_t CLineBuffer::GetTokenFirst(const CHAR delim, CLineBuffer * pBuf)
+{
+	PBYTE here;
+	size_t wLength;
+
+	_pTok = _pVal;
+
+	if (!_pTok || !*_pTok)
+		return 0;
+
+	for (here = _pTok;; here++) {
+		if (*here == 0 || *here == '\n' || *here == delim) {
+			wLength = here - _pTok;
+			if (pBuf) {
+				CHAR c = *here;
+				*here = 0;
+				*pBuf = (LPCSTR)_pTok;
+				*here = c;
+			}
+			_pTok = (*here == 0	|| *here == '\n') ? NULL : ++here;
+			break;
+		}
+	}
+	return wLength;
+}
+
+/**
+ * name:	CLineBuffer::GetTokenNext
+ * desc:	scans for the next <delim> in the _pVal member and returns all characters
+ *			that come before the first <delim> in a new CLineBuffer class
+ * param:	delim		-	the deliminer which delimins the string
+ *			pBuf		-	pointer to a new CLineBuffer class holding the result
+ *
+ * return:	length of the found string value
+ **/
+size_t CLineBuffer::GetTokenNext(const CHAR delim, CLineBuffer * pBuf)
+{
+	PBYTE here;
+	size_t wLength;
+
+	if (!_pTok || !*_pTok)
+		return 0;
+
+	for (here = _pTok;; here++) {
+		if (*here == 0 || *here == '\n' || *here == delim) {
+			wLength = here - _pTok;
+
+			if (pBuf) {
+				CHAR c = *here;
+				
+				*here = 0;
+				*pBuf = (LPCSTR)_pTok;
+				*here = c;
+			}
+			_pTok = (*here == 0	|| *here == '\n') ? NULL : ++here;
+			break;
+		}
+	}
+	return wLength;
+}
+
+/**
+ * name:	CLineBuffer::DBWriteTokenFirst
+ * desc:	scans for the first <delim> in the _pVal member and writes all characters
+ *			that come before the first <delim> to database
+ * param:	hContact	-	handle to the contact to write the setting to
+ *			pszModule	-	destination module
+ *			pszSetting	-	destination setting for the value
+ *			delim		-	the deliminer which delimins the string
+ *
+ * return:	0 if successful, 1 otherwise
+ **/
+INT CLineBuffer::DBWriteTokenFirst(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting, const CHAR delim)
+{
+	PBYTE here;
+	INT iRet = 1;
+	_pTok = _pVal;
+
+	if (_pTok && *_pTok) {
+		for (here = _pTok;; here++) {
+			if (*here == 0 || *here == '\n' || *here == delim) {
+				
+				if (here - _pTok > 0) {
+					CHAR c = *here;
+				
+					*here = 0;
+					iRet = DB::Setting::WriteAString(hContact, pszModule, pszSetting, (LPSTR)_pTok);
+					*here = c;
+				}
+				_pTok = (*here == 0	|| *here == '\n') ? NULL : ++here;
+				break;
+			}
+		}
+	}
+	if (iRet) iRet = DB::Setting::Delete(hContact, pszModule, pszSetting);
+	return iRet;
+}
+
+/**
+ * name:	CLineBuffer::GetTokenNext
+ * desc:	scans for the next <delim> in the _pVal member and writes all characters
+ *			that come before the first <delim> to database
+ * param:	hContact	-	handle to the contact to write the setting to
+ *			pszModule	-	destination module
+ *			pszSetting	-	destination setting for the value
+ *			delim		-	the deliminer which delimins the string
+ *
+ * return:	0 if successful, 1 otherwise
+ **/
+INT CLineBuffer::DBWriteTokenNext(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting, const CHAR delim)
+{
+	PBYTE here;
+	INT iRet = 1;
+
+	if (_pTok && *_pTok) {
+		for (here = _pTok;; here++) {
+			if (*here == 0 || *here == '\n' || *here == delim) {
+				
+				if (here - _pTok > 0) {
+					CHAR c = *here;
+				
+					*here = 0;
+					iRet = DB::Setting::WriteAString(hContact, pszModule, pszSetting, (LPSTR)_pTok);
+					*here = c;
+				}
+				_pTok = (*here == 0	|| *here == '\n') ? NULL : ++here;
+				break;
+			}
+		}
+	}
+	if (iRet) iRet = DB::Setting::Delete(hContact, pszModule, pszSetting);
+	return iRet;
+}
+
+/**
+ * name:	CLineBuffer::GetTokenNext
+ * desc:	writes _pVal member to database
+ * param:	hContact	-	handle to the contact to write the setting to
+ *			pszModule	-	destination module
+ *			pszSetting	-	destination setting for the value
+ *
+ * return:	0 if successful, 1 otherwise
+ **/
+INT CLineBuffer::DBWriteSettingString(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting)
+{
+	if (_pVal && _cbUsed > 0)
+		return DB::Setting::WriteAString(hContact, pszModule, pszSetting, (LPSTR)_pVal);
+	return 1;
+}
+
+/*
+=========================================================================================================================
+ class CVCardFileVCF
+=========================================================================================================================
+*/
+
+/**
+ * name:	CVCardFileVCF
+ * desc:	default constructor
+ * param:	none
+ * return	none
+ **/
+CVCardFileVCF::CVCardFileVCF()
+{
+	_pFile			= NULL;
+	_hContact		= INVALID_HANDLE_VALUE;
+	_pszBaseProto	= NULL;
+	_hasUtf8		= 0;
+	_useUtf8		= FALSE;
+}
+
+/**
+ * name:	CVCardFileVCF::CVCardFileVCF
+ * desc:	searches a static stringlist for the occureance of iID and adds 
+ *			the translated string value to the line buffer
+ * param:	pList	-	pointer to the list to search in
+ *			nList	-	number of items in the list
+ *			iID		-	the id to search for
+ *			cbRew	-	number of characters to truncate the _clVal by before writing to file
+ *
+ * return	number of the added bytes
+ **/
+size_t CVCardFileVCF::packList(LPIDSTRLIST pList, UINT nList, INT iID, size_t *cbRew)
+{
+	UINT i;
+	WORD wAdd = 0;
+
+	for (i = 0; i < nList; i++) {
+		if (pList[i].nID == iID) {
+			return (_clVal + pList[i].ptszTranslated);
+		}
+	}
+	if (cbRew) (*cbRew)++;
+	return 0;
+}
+
+/**
+ * name:	CVCardFileVCF::GetSetting
+ * desc:	trys to read a value from the pszModule and than from the basic protocol module of the contact
+ *			where strings are converted to ansi
+ * param:	pszModule	-	module to read the value from
+ *			pszSetting	-	setting to read the value from
+ *			dbv			-	pointer to the structure holding the result
+ *
+ * return	value type
+ **/
+BOOLEAN CVCardFileVCF::GetSetting(const CHAR *pszModule, const CHAR *pszSetting, DBVARIANT *dbv)
+{
+	DBCONTACTGETSETTING cgs;
+
+	cgs.szModule = pszModule;
+	cgs.szSetting = pszSetting;
+	cgs.pValue = dbv;
+	dbv->type = _useUtf8 ? DBVT_UTF8 : DBVT_ASCIIZ;
+	dbv->pszVal = NULL;
+	if (!pszModule || CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM)_hContact, (LPARAM)&cgs) || (dbv->type == DBVT_ASCIIZ && !dbv->pszVal && !*dbv->pszVal)) {
+		cgs.szModule = _pszBaseProto;
+		cgs.szSetting = pszSetting;
+		cgs.pValue = dbv;
+		dbv->type = DBVT_ASCIIZ;
+		if (!_pszBaseProto || CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM)_hContact, (LPARAM)&cgs) || (dbv->type == DBVT_ASCIIZ && !dbv->pszVal && !*dbv->pszVal)) {
+			return DBVT_DELETED;
+		}
+	}
+	_hasUtf8 += _useUtf8 && !IsUSASCII(dbv->pszVal, NULL);
+	return dbv->type;
+}
+
+/**
+ * name:	CVCardFileVCF::packDB
+ * desc:	read a value from the database and add it to the line buffer
+ * param:	pszModule	-	module to read the value from
+ *			pszSetting	-	setting to read the value from
+ *			cbRew		-	number of characters to truncate the _clVal by before writing to file
+ *
+ * return	number of bytes, added to the linebuffer
+ **/
+size_t CVCardFileVCF::packDB(const CHAR *pszModule, const CHAR *pszSetting, size_t *cbRew)
+{
+	DBVARIANT dbv;
+
+	switch (GetSetting(pszModule, pszSetting, &dbv)) {
+		case DBVT_DELETED:
+			if (cbRew) (*cbRew)++;
+			break;
+		case DBVT_BYTE:
+			return _clVal + dbv.bVal;
+		case DBVT_WORD:
+			return _clVal + dbv.wVal;
+		case DBVT_DWORD:
+			return _clVal + dbv.dVal;
+		case DBVT_UTF8:
+		case DBVT_ASCIIZ:
+		{
+			size_t wAdd = _clVal + dbv.pszVal;
+			DB::Variant::Free(&dbv);
+			return wAdd;
+		}
+		default:
+			if (cbRew) (*cbRew)++;
+			DB::Variant::Free(&dbv);
+			break;
+	}
+	return 0;
+}
+
+/**
+ * name:	CVCardFileVCF::packDBList
+ * desc:	read a value from the database and add a found list item to the line buffer
+ * param:	pszModule	-	module to read the value from
+ *			pszSetting	-	setting to read the value from
+ *			GetList		-	pointer to a function retrieving the list pointer
+ *			bSigned		-	is the read database value signed?
+ *			cbRew		-	number of characters to truncate the _clVal by before writing to file
+ *
+ * return	number of bytes, added to the linebuffer
+ **/
+size_t CVCardFileVCF::packDBList(const CHAR *pszModule, const CHAR *pszSetting, MIRANDASERVICE GetList, BOOLEAN bSigned, size_t *cbRew)
+{
+	DBVARIANT dbv;
+	UINT nList;
+	LPIDSTRLIST pList;
+	size_t wAdd = 0;
+
+	GetList((WPARAM)&nList, (LPARAM)&pList);
+	switch (GetSetting(pszModule, pszSetting, &dbv)) {
+		case DBVT_BYTE:
+			wAdd = packList(pList, nList, (INT)(bSigned ? dbv.cVal : dbv.bVal), cbRew);
+			break;
+		case DBVT_WORD:
+			wAdd = packList(pList, nList, (INT)(bSigned ? dbv.sVal : dbv.wVal), cbRew);
+			break;
+		case DBVT_DWORD:
+			wAdd = packList(pList, nList, (INT)(bSigned ? dbv.lVal : dbv.dVal), cbRew);
+			break;
+		case DBVT_UTF8:
+		case DBVT_ASCIIZ:
+			wAdd = _clVal + Translate(dbv.pszVal);
+			DB::Variant::Free(&dbv);
+			break;
+		case DBVT_DELETED:
+			wAdd = 0;
+			break;
+		default:
+			wAdd = 0;
+			DB::Variant::Free(&dbv);
+			break;
+	}
+	if (cbRew) *cbRew = wAdd ? 0 : *cbRew + 1;
+	return wAdd;
+}
+
+/**
+ * name:	CVCardFileVCF::writeLine
+ * desc:	write a line as clear text to the vcard file
+ * param:	szSet	-	the string, which identifies the line
+ *			cbRew	-	number of characters to truncate the _clVal by before writing to file
+ *
+ * return	number of bytes, added to the linebuffer
+ **/
+VOID CVCardFileVCF::writeLine(const CHAR *szSet, size_t *cbRew)
+{
+	if (cbRew) {
+		_clVal.Truncate(*cbRew);
+		*cbRew = 0;
+	}
+	if (_clVal.GetLength() > 0) {
+		fputs(szSet, _pFile);
+		if (_hasUtf8) {
+			fputs(";CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:", _pFile);
+			_clVal.fputEncoded(_pFile);
+			_hasUtf8 = FALSE;
+		}
+		else {
+			fputc(':', _pFile);
+			_clVal.fput(_pFile);
+		}
+		fputc('\n', _pFile);
+	}
+}
+
+/**
+ * name:	CVCardFileVCF::writeLineEncoded
+ * desc:	write a line as encoded text to the vcard file
+ * param:	szSet	-	the string, which identifies the line
+ *			cbRew	-	number of characters to truncate the _clVal by before writing to file
+ *
+ * return	number of bytes, added to the linebuffer
+ **/
+VOID CVCardFileVCF::writeLineEncoded(const CHAR *szSet, size_t *cbRew)
+{
+	if (cbRew) {
+		_clVal.Truncate(*cbRew);
+		*cbRew = 0;
+	}
+	if (_clVal.GetLength() > 0) {
+		fputs(szSet, _pFile);
+		if (_hasUtf8) {
+			fputs(";CHARSET=UTF-8", _pFile);
+			_hasUtf8 = FALSE;
+		}
+		fputs(";ENCODING=QUOTED-PRINTABLE:", _pFile);
+		_clVal.fputEncoded(_pFile);
+		fputc('\n', _pFile);
+	}
+}
+
+/**
+ * name:	Open
+ * desc:	open a specified filename and link to the contact
+ * param:	hContact	- handle to the contact to link with the vCard file
+ *			pszFileName	- path to the file to open
+ *			pszMode		- the mode the file should be opened with
+ * return	TRUE or FALSE
+ **/
+BOOLEAN CVCardFileVCF::Open(HANDLE hContact,	LPCSTR pszFileName, LPCSTR pszMode)
+{
+	if (!(_pFile = fopen(pszFileName, pszMode)))
+		return FALSE;
+	if ((_hContact = hContact) == INVALID_HANDLE_VALUE)
+		return FALSE;
+	if (!(_pszBaseProto = DB::Contact::Proto(_hContact)))
+		return FALSE;
+	return TRUE;
+}
+
+/**
+ * name:	Close
+ * desc:	close up the file
+ * param:	hContact	- handle to the contact to link with the vCard file
+ *			pszFileName	- path to the file to open
+ *			pszMode		- the mode the file should be opened with
+ * return	TRUE or FALSE
+ **/
+VOID CVCardFileVCF::Close(VOID)
+{
+	if (_pFile) 
+		fclose(_pFile);
+	_pFile			= NULL;
+	_hContact		= INVALID_HANDLE_VALUE;
+	_pszBaseProto	= NULL;
+}
+
+/**
+ * name:	Export
+ * desc:	export the contacts information
+ * param:	none
+ * return	TRUE or FALSE
+ **/
+BOOLEAN CVCardFileVCF::Export(BOOLEAN bExportUtf)
+{
+	size_t cbRew = 0;
+
+	_useUtf8 = bExportUtf;
+
+	fputs("BEGIN:VCARD\nVERSION:2.1\n", _pFile);
+
+	//
+	// naming
+	//
+	packDB(USERINFO, SET_CONTACT_LASTNAME, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_FIRSTNAME, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_SECONDNAME, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_TITLE, &cbRew);
+	_clVal + DELIM;
+	packDBList(USERINFO, SET_CONTACT_PREFIX, (MIRANDASERVICE)GetNamePrefixList, FALSE, &cbRew);
+	writeLine("N", &cbRew);
+
+	if (packDB(USERINFO, SET_CONTACT_TITLE))
+		_clVal + " ";
+
+	if (packDB(USERINFO, SET_CONTACT_FIRSTNAME))
+		_clVal + " ";
+	else
+		cbRew = 1;
+
+	if (packDB(USERINFO, SET_CONTACT_SECONDNAME))
+		_clVal + " ";
+	else
+		cbRew = 1;
+
+	if (packDB(USERINFO, SET_CONTACT_LASTNAME))
+		_clVal + " ";
+	else
+		cbRew = 1;
+
+	packDBList(USERINFO, SET_CONTACT_PREFIX, (MIRANDASERVICE)GetNamePrefixList, FALSE, &cbRew);
+	writeLine("FN");
+
+	packDB(USERINFO, SET_CONTACT_NICK);
+	writeLine("NICKNAME");
+
+	//
+	// organisation
+	//
+	packDB(USERINFO, SET_CONTACT_COMPANY, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_COMPANY_DEPARTMENT, &cbRew);
+	writeLine("ORG", &cbRew);
+	packDB(USERINFO, SET_CONTACT_COMPANY_POSITION);
+	writeLine("TITLE");
+	packDBList(USERINFO, SET_CONTACT_COMPANY_OCCUPATION, (MIRANDASERVICE)GetOccupationList, FALSE);
+	writeLine("ROLE");
+
+	//
+	// phone numbers
+	//
+	if (packDB(USERINFO, SET_CONTACT_PHONE)) {
+		_clVal.TruncateSMS();
+		writeLine("TEL;HOME;VOICE");
+	}
+	if (packDB(USERINFO, SET_CONTACT_FAX)) {
+		_clVal.TruncateSMS();
+		writeLine("TEL;HOME;FAX");
+	}
+	if (packDB(USERINFO, SET_CONTACT_CELLULAR)) {
+		_clVal.TruncateSMS();
+		writeLine("TEL;CELL;VOICE");
+	}
+	if (packDB(USERINFO, SET_CONTACT_COMPANY_PHONE)) {
+		_clVal.TruncateSMS();
+		writeLine("TEL;WORK;VOICE");
+	}
+	if (packDB(USERINFO, SET_CONTACT_COMPANY_FAX)) {
+		_clVal.TruncateSMS();
+		writeLine("TEL;WORK;FAX");
+	}
+	if (packDB(USERINFO, SET_CONTACT_COMPANY_CELLULAR)) {
+		_clVal.TruncateSMS();
+		writeLine("TEL;PAGER;VOICE");
+	}
+
+	//
+	// private address
+	//
+	_clVal + ";;"; 
+	cbRew = 1;
+	packDB(USERINFO, SET_CONTACT_STREET, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_CITY, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_STATE, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_ZIP, &cbRew);
+	_clVal + DELIM;
+	packDBList(USERINFO, SET_CONTACT_COUNTRY, (MIRANDASERVICE)GetCountryList, FALSE, &cbRew);
+	writeLine("ADR;HOME", &cbRew);
+
+	//
+	// company address
+	//
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_COMPANY_OFFICE, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_COMPANY_STREET, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_COMPANY_CITY, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_COMPANY_STATE, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_COMPANY_ZIP, &cbRew);
+	_clVal + DELIM;
+	packDBList(USERINFO, SET_CONTACT_COMPANY_COUNTRY, (MIRANDASERVICE)GetCountryList, FALSE, &cbRew);
+	writeLine("ADR;WORK", &cbRew);
+
+	//
+	// origin address
+	//
+	_clVal + ";;";
+	cbRew = 1;
+	packDB(USERINFO, SET_CONTACT_ORIGIN_STREET, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_ORIGIN_CITY, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_ORIGIN_STATE, &cbRew);
+	_clVal + DELIM;
+	packDB(USERINFO, SET_CONTACT_ORIGIN_ZIP, &cbRew);
+	_clVal + DELIM;
+	packDBList(USERINFO, SET_CONTACT_ORIGIN_COUNTRY, (MIRANDASERVICE)GetCountryList, FALSE, &cbRew);
+	writeLine("ADR;POSTAL", &cbRew);
+
+	//
+	// homepages
+	//
+	if (packDB(USERINFO, SET_CONTACT_HOMEPAGE))
+		writeLine("URL;HOME");
+	if (packDB(USERINFO, SET_CONTACT_COMPANY_HOMEPAGE))
+		writeLine("URL;WORK");
+
+	//
+	// e-mails
+	//
+	if (packDB(USERINFO, SET_CONTACT_EMAIL))
+		writeLine("EMAIL;PREF;intERNET");
+	if (packDB(USERINFO, SET_CONTACT_EMAIL0))
+		writeLine("EMAIL;intERNET");
+	if (packDB(USERINFO, SET_CONTACT_EMAIL1))
+		writeLine("EMAIL;intERNET");
+
+	//
+	// gender
+	//
+	{
+		BYTE gender = DB::Setting::GetByte(_hContact, USERINFO, SET_CONTACT_GENDER, 0);
+		if (!gender) gender = DB::Setting::GetByte(_hContact, _pszBaseProto, SET_CONTACT_GENDER, 0);
+		switch (gender) {
+			case 'F':
+				fputs("X-WAB-GENDER:1\n", _pFile);
+				break;
+			case 'M':
+				fputs("X-WAB-GENDER:2\n", _pFile);
+				break;
+		}
+	}
+
+	//
+	// birthday
+	//
+	{
+		MAnnivDate mdb;
+
+		if (!mdb.DBGetBirthDate(_hContact, NULL))
+			fprintf(_pFile, "BDAY:%d%02d%02d\n\0", mdb.Year(), mdb.Month(), mdb.Day());
+	}
+	
+	//
+	// notes
+	//
+	if (packDB(USERINFO, SET_CONTACT_MYNOTES))
+		writeLineEncoded("NOTE");
+
+	//
+	// about
+	//
+	if (packDB(USERINFO, SET_CONTACT_ABOUT))
+		writeLineEncoded("ABOUT");
+
+	//
+	// contacts protocol, uin setting, uin value
+	//
+	{
+		CHAR szUID[MAXUID];
+		LPCSTR uid;
+
+		uid = (LPCSTR)CallProtoService(_pszBaseProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+		if ((INT_PTR)uid != CALLSERVICE_NOTFOUND && uid) {
+			if (!DB::Setting::GetStatic(_hContact, _pszBaseProto, uid, szUID, sizeof(szUID)))
+				fprintf(_pFile, "IM;%s;%s:%s\n", _pszBaseProto, uid, szUID);
+		}
+	}
+	
+	//
+	// time of creation
+	//
+	{
+		SYSTEMTIME	st;
+	
+		GetLocalTime(&st);
+		fprintf(_pFile, "REV:%04d%02d%02dD%02d%02d%02dT\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
+	}
+
+	fputs("END:VCARD", _pFile);
+	return 0;
+}
+
+/**
+ * name:	CVCardFileVCF::readLine
+ * desc:	read one line from the VCF file and return the setting string
+ *			to the szVCFSetting and the value to _clVal
+ * param:	szVCFSetting	- string holding the setting information
+ *			cchSetting		- number of bytes the szVCFSetting can hold
+ *
+ * return:	number of characters read from the file or EOF
+ **/
+INT CVCardFileVCF::readLine(LPSTR szVCFSetting, WORD cchSetting)
+{
+	LPSTR here;
+	
+	// read setting (size is never larger than MAX_SETTING, error otherwise!)
+	for (here = szVCFSetting; here - szVCFSetting < cchSetting && EOF != (*here = fgetc(_pFile)); here++) {
+		// end of the setting string
+		if (*here == ':') {
+			*here = 0;
+			break;
+		}
+		// end of line before value?
+		if (*here == '\n')
+			return 0;
+	}
+	// ignore line if setting was not read correctly
+	if (here - szVCFSetting == cchSetting)
+		return 0;
+	
+	// read the value to the linebuffer, because its length may be very large
+	return _clVal.fgetEncoded(_pFile);
+}
+
+/**
+ * name:	CVCardFileVCF::Import
+ * desc:	imports all lines from the file and writes them to database
+ * param:	nothing
+ *
+ * return:	number of characters read from the file or EOF
+ **/
+BOOLEAN CVCardFileVCF::Import()
+{
+	CHAR szEnt[MAX_PATH];
+	LPSTR pszParam;
+	INT cbLine;
+	BYTE numEmails = 0;
+
+	while (EOF != (cbLine = readLine(szEnt, MAX_PATH))) {
+
+		// ignore empty lines
+		if (!cbLine) continue;
+
+		// isolate the param string
+		if (pszParam = mir_strchr(szEnt, ';')) {
+			*(pszParam++) = 0;
+		}
+		switch (*szEnt) {
+			case 'A':
+				if (!strcmp(szEnt, "ABOUT")) {
+					_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_ABOUT);
+					continue;
+				}
+				if (!strcmp(szEnt, "ADR")) {
+					if (!pszParam) continue;
+					if (!strcmp(pszParam, "HOME")) {
+						_clVal.GetTokenFirst(';', NULL);
+						_clVal.GetTokenNext(';', NULL);
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_STREET, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_CITY, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_STATE, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ZIP, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COUNTRY, ';');
+						continue;
+					}
+					if (!strcmp(pszParam, "WORK")) {
+						_clVal.GetTokenFirst(';', NULL);
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_OFFICE, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_STREET, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_CITY, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_STATE, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_ZIP, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_COUNTRY, ';');
+						continue;
+					}
+					if (!strcmp(pszParam, "POSTAL")) {
+						_clVal.GetTokenFirst(';', NULL);
+						_clVal.GetTokenNext(';', NULL);
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_STREET, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_CITY, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_STATE, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_ZIP, ';');
+						_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_ORIGIN_COUNTRY, ';');
+					}
+				}
+				continue;
+
+			case 'B':
+				if (!strcmp(szEnt, "BDAY")) {
+					if (_clVal.GetLength() == 8) {
+						CHAR buf[5];
+
+						memcpy(buf, _clVal.GetBuffer(), 4);
+						buf[4] = 0;
+						DB::Setting::WriteWord(_hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHYEAR, (WORD)strtol(buf, NULL, 10));
+						memcpy(buf, _clVal.GetBuffer() + 4, 2);
+						buf[2] = 0;
+						DB::Setting::WriteByte(_hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHMONTH, (BYTE)strtol(buf, NULL, 10));
+						memcpy(buf, _clVal.GetBuffer() + 6, 2);
+						buf[2] = 0;
+						DB::Setting::WriteByte(_hContact, MOD_MBIRTHDAY, SET_CONTACT_BIRTHDAY, (BYTE)strtol(buf, NULL, 10));
+					}
+				}
+				continue;
+
+			case 'E':
+				if (!strcmp(szEnt, "EMAIL")) {
+					if (!pszParam || !strstr(pszParam, "intERNET"))
+						continue;
+					if (strstr(pszParam, "PREF")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_EMAIL);
+						continue;
+					}
+					switch (numEmails++) {
+						case 0:
+							_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_EMAIL0);
+							break;
+						case 1:
+							_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_EMAIL1);
+							break;
+					}
+				}
+				continue;
+			/*
+			case 'I':
+				if (!strcmp(szEnt, "IM")) {
+					LPSTR	pszModule, pszSetting;
+
+					if (pszParam && (pszModule = strtok(pszParam, DELIM)) && (pszSetting = strtok(NULL, DELIM)))
+						_clVal.DBWriteSettingString(_hContact, pszModule, pszSetting);
+				}
+				continue;
+			*/
+			case 'N':
+				if (!strcmp(szEnt, "N")) {
+					_clVal.DBWriteTokenFirst(_hContact, USERINFO, SET_CONTACT_LASTNAME, ';');
+					_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_FIRSTNAME, ';');
+					_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_SECONDNAME, ';');
+					_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_TITLE, ';');
+					_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_PREFIX, ';');
+					continue;
+				}
+				if (!strcmp(szEnt, "NICKNAME")) {
+					_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_NICK);
+					continue;
+				}
+				if (!strcmp(szEnt, "NOTE")) {
+					_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_MYNOTES);
+				}
+				continue;
+
+			case 'O':
+				if (!strcmp(szEnt, "ORG")) {
+					_clVal.DBWriteTokenFirst(_hContact, USERINFO, SET_CONTACT_COMPANY, ';');
+					_clVal.DBWriteTokenNext(_hContact, USERINFO, SET_CONTACT_COMPANY_DEPARTMENT, ';');
+				}
+				continue;
+
+			case 'R':
+				if (!strcmp(szEnt, "ROLE")) {
+					_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_OCCUPATION);
+				}
+				continue;
+
+			case 'T':
+				if (!strcmp(szEnt, "TITLE")) {
+					_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_POSITION);
+					continue;
+				}
+				if (!strcmp(szEnt, "TEL")) {
+
+					if (!pszParam) continue;
+					
+					if (!strcmp(pszParam, "HOME;VOICE")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_PHONE);
+						continue;
+					}
+					if (!strcmp(pszParam, "HOME;FAX")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_FAX);
+						continue;
+					}
+					if (!strcmp(pszParam, "CELL;VOICE")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_CELLULAR);
+						continue;
+					}
+					if (!strcmp(pszParam, "WORK;VOICE")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_PHONE);
+						continue;
+					}
+					if (!strcmp(pszParam, "WORK;FAX")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_FAX);
+						continue;
+					}
+					if (!strcmp(pszParam, "PAGER;VOICE")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_CELLULAR);
+						continue;
+					}
+				}
+				continue;
+		
+			case 'U':
+				if (!strcmp(szEnt, "URL")) {
+
+					if (!pszParam) continue;
+
+					if (!strcmp(pszParam, "HOME")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_HOMEPAGE);
+						continue;
+					}
+					if (!strcmp(pszParam, "WORK")) {
+						_clVal.DBWriteSettingString(_hContact, USERINFO, SET_CONTACT_COMPANY_HOMEPAGE);
+					}
+				}
+				continue;
+
+			case 'X':
+				if (!strcmp(szEnt, "X-WAB-GENDER")) {
+					if (!strcmp(_clVal.GetBuffer(), "1"))
+						DB::Setting::WriteByte(_hContact, USERINFO, SET_CONTACT_GENDER, 'F');
+					else
+					if (!strcmp(_clVal.GetBuffer(), "2"))
+						DB::Setting::WriteByte(_hContact, USERINFO, SET_CONTACT_GENDER, 'M');
+				}
+				continue;
+		}
+	}
+	return TRUE;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h b/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h
new file mode 100644
index 0000000000..4465614b80
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImVCF.h
@@ -0,0 +1,103 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImVCF.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#pragma once
+
+class CLineBuffer
+{
+private:
+	PBYTE	_pVal;
+	PBYTE	_pTok;
+	size_t	_cbVal;
+	size_t	_cbUsed;
+
+	BOOLEAN _resizeBuf(const size_t cbReq);
+
+public:
+	CLineBuffer();
+	~CLineBuffer();
+	
+	size_t operator = (const CHAR *szVal);
+
+	size_t operator + (const CHAR *szVal);
+	size_t operator + (const WCHAR *wszVal);
+	size_t operator + (const CHAR cVal);
+	size_t operator + (const BYTE bVal);
+	size_t operator + (const SHORT sVal);
+	size_t operator + (const WORD wVal);
+	size_t operator + (const LONG lVal);
+	size_t operator + (const DWORD dVal);
+
+	size_t GetLength();
+	LPCSTR GetBuffer();
+
+	VOID TruncToLength(size_t cbLength);
+	VOID Truncate(size_t count);
+	VOID TruncateSMS();
+	
+	VOID fput(FILE *outfile);
+	VOID fputEncoded(FILE *outFile);
+	INT	fgetEncoded(FILE *inFile);
+
+	size_t GetTokenFirst(const CHAR delim, CLineBuffer * pBuf);
+	size_t GetTokenNext(const CHAR delim, CLineBuffer * pBuf);
+	INT	 DBWriteTokenFirst(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting, const CHAR delim);
+	INT	 DBWriteTokenNext(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting, const CHAR delim);
+	INT	 DBWriteSettingString(HANDLE hContact, const CHAR* pszModule, const CHAR* pszSetting);
+};
+
+class CVCardFileVCF
+{
+private:
+	CLineBuffer		 _clVal;
+	FILE			*_pFile;
+	HANDLE			 _hContact;
+	const CHAR		*_pszBaseProto;
+	WORD			 _cbRew;
+	BOOLEAN			 _useUtf8;
+	WORD			 _hasUtf8;
+
+	size_t	packList(LPIDSTRLIST pList, UINT nList, INT iID, size_t *cbRew = NULL);
+	BOOLEAN GetSetting(const CHAR *pszModule, const CHAR *pszSetting, DBVARIANT *dbv);
+	size_t	packDB(const CHAR *pszModule, const CHAR *pszSetting, size_t *cbRew = NULL);
+	size_t	packDBList(const CHAR *pszModule, const CHAR *pszSetting, MIRANDASERVICE GetList, BOOLEAN bSigned = FALSE, size_t *cbRew = NULL);
+	
+	VOID	writeLine(const CHAR *szSet, size_t *cbRew = NULL);
+	VOID	writeLineEncoded(const CHAR *szSet, size_t *cbRew = NULL);
+	INT		readLine(LPSTR szVCFSetting, WORD cchSetting);
+
+public:
+	CVCardFileVCF();
+
+	BOOLEAN Open(HANDLE hContact, LPCSTR pszFileName, LPCSTR pszMode);
+	VOID	Close(VOID);
+	BOOLEAN Export(BOOLEAN bExportUtf);
+	BOOLEAN Import();
+};
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp b/plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp
new file mode 100644
index 0000000000..dca52f1685
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImXML.cpp
@@ -0,0 +1,453 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImXML.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+#define XMLCARD_VERSION	"1.1"
+
+/**
+ * system & local includes:
+ **/
+#include "dlg_ExImModules.h"
+#include "classExImContactXML.h"
+#include "svc_ExImport.h"
+
+LRESULT CALLBACK DlgProc_DataHistory(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	switch (msg) 
+	{
+		case WM_INITDIALOG:
+		{
+			const ICONCTRL idIcon[] = {
+				{ ICO_DLG_EXPORT,	WM_SETICON,		NULL		},
+				{ ICO_DLG_EXPORT,	STM_SETIMAGE,	ICO_DLGLOGO	},
+				{ ICO_BTN_EXPORT,	BM_SETIMAGE,	IDOK		},
+				{ ICO_BTN_CANCEL,	BM_SETIMAGE,	IDCANCEL	}
+			};
+			const INT numIconsToSet = DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 2;
+			IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);
+
+			TranslateDialogDefault(hDlg);
+			SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
+			SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+			break;
+		}
+		case WM_CTLCOLORSTATIC:
+			switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) {
+				case STATIC_WHITERECT:
+				case ICO_DLGLOGO:
+				case IDC_INFO:
+					SetBkColor((HDC)wParam, RGB(255, 255, 255));
+					return (INT_PTR)GetStockObject(WHITE_BRUSH);
+			}
+			return FALSE;
+		case WM_COMMAND:
+			if (HIWORD(wParam) == BN_CLICKED) {
+				switch (LOWORD(wParam)) {
+					case IDCANCEL:
+						EndDialog(hDlg, 0);
+						break;
+					case IDOK: {
+						WORD hiWord = 0;
+
+						if (IsDlgButtonChecked(hDlg, IDC_CHECK1))
+							hiWord |= EXPORT_DATA;
+						if (IsDlgButtonChecked(hDlg, IDC_CHECK2))
+							hiWord |= EXPORT_HISTORY;
+						EndDialog(hDlg, (INT_PTR)MAKELONG(IDOK, hiWord));
+						break;
+					}
+				}
+			}
+			break;
+	}
+	return FALSE;
+}
+
+/***********************************************************************************************************
+ * exporting stuff
+ ***********************************************************************************************************/
+
+/**
+ * name:	Export
+ * desc:	globally accessible function which does the whole export stuff.
+ * params:	hContact	- handle to the contact who is to export
+ *			pszFileName	- full qualified path to the xml file which is destination for the export process
+ * return:	0 on success, 1 otherwise
+ **/
+INT CFileXml::Export(lpExImParam ExImContact, LPCSTR pszFileName)
+{
+	FILE *xmlfile;
+	DB::CEnumList Modules;
+	LONG cbHeader;
+	SYSTEMTIME now;
+	DWORD result;
+	HANDLE hContact;
+
+	result = (DWORD)DialogBox(ghInst, 
+							MAKEINTRESOURCE(IDD_EXPORT_DATAHISTORY),
+							NULL, (DLGPROC)DlgProc_DataHistory);
+	if (LOWORD(result) != IDOK)
+	{
+		return 0;
+	}
+	_wExport = HIWORD(result);
+
+	// show dialog to enable user to select modules for export
+	if (!(_wExport & EXPORT_DATA) ||
+		!DlgExImModules_SelectModulesToExport(ExImContact, &Modules, NULL)) 
+	{
+
+		xmlfile = fopen(pszFileName, "wt");
+		if (!xmlfile)
+		{
+			MsgErr(NULL, LPGENT("Can't create xml file!\n%S"), pszFileName);
+			return 1;
+		}
+		
+		GetLocalTime(&now);
+
+		// write xml header raw as it is without using the tinyxml api
+		fprintf(xmlfile, 
+			"%c%c%c<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+			"<XMLCard ver=\""XMLCARD_VERSION"\" ref=\"%04d-%02d-%02d %02d:%02d:%02d\">\n",
+			0xefU, 0xbbU, 0xbfU, now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond
+		);
+		// remember the header's size
+		cbHeader = ftell(xmlfile);
+
+		CExImContactXML vContact(this);
+
+		// write data
+		if ( ExImContact->Typ == EXIM_CONTACT) {
+			// export single contact
+			_hContactToWorkOn = ExImContact->hContact;
+			if (vContact.fromDB(ExImContact->hContact)) {
+				vContact.Export(xmlfile, &Modules);
+			}
+		}
+		else {
+			// other export mode
+			_hContactToWorkOn = INVALID_HANDLE_VALUE;
+#ifdef _DEBUG
+			LARGE_INTEGER freq, t1, t2;
+
+			QueryPerformanceFrequency(&freq);
+			QueryPerformanceCounter(&t1);
+#endif
+			// export owner contact
+			if (ExImContact->Typ == EXIM_ALL && vContact.fromDB(NULL)) {
+				vContact.Export(xmlfile, &Modules);
+			}
+			// loop for all other contact
+			for (hContact = DB::Contact::FindFirst();
+				hContact != NULL;
+				hContact = DB::Contact::FindNext(hContact))
+			{
+				switch (ExImContact->Typ)
+				{
+					case EXIM_ALL:
+					case EXIM_GROUP:
+						// dont export meta subcontacts by default
+						if (!DB::MetaContact::IsSub(hContact)) {
+							if (vContact.fromDB(hContact)) {
+								vContact.Export(xmlfile, &Modules);
+							}
+						}
+						break;
+					case EXIM_SUBGROUP:
+						// dont export meta subcontacts by default and
+						// export only contact with selectet group name
+						if (!DB::MetaContact::IsSub(hContact) && 
+							mir_tcsncmp(ExImContact->ptszName, DB::Setting::GetTString(hContact, "CList", "Group"), mir_tcslen(ExImContact->ptszName))== 0)
+							{
+							if (vContact.fromDB(hContact)) {
+								vContact.Export(xmlfile, &Modules);
+							}
+						}
+						break;
+					case EXIM_ACCOUNT:
+						// export only contact with selectet account name
+						if (!mir_strncmp(ExImContact->pszName, DB::Contact::Proto(hContact), mir_strlen(ExImContact->pszName))) {
+							if (vContact.fromDB(hContact)) {
+								vContact.Export(xmlfile, &Modules);
+							}
+						}
+						break;
+				}
+			} // *end for
+#ifdef _DEBUG
+			QueryPerformanceCounter(&t2);
+			MsgErr(NULL, LPGENT("Export took %f msec"),
+				(long double)(t2.QuadPart - t1.QuadPart) / freq.QuadPart * 1000.);
+#endif
+		}// *end other export mode
+
+		// nothing exported?
+		if (cbHeader == ftell(xmlfile)) {
+			fclose(xmlfile);
+			DeleteFileA(pszFileName);
+			return 1;
+		}
+		fputs("</XMLCard>\n", xmlfile);
+		fclose(xmlfile);
+	}
+	return 0;
+}
+
+
+/***********************************************************************************************************
+ * importing stuff
+ ***********************************************************************************************************/
+
+CFileXml::CFileXml()
+{
+	_numContactsTodo		= 0;
+	_numContactsDone		= 0;
+	_numSettingsTodo		= 0;
+	_numSettingsDone		= 0;
+	_numEventsTodo			= 0;
+	_numEventsDone			= 0;
+	_numEventsDuplicated	= 0;
+}
+
+/**
+ * name:	ImportOwner
+ * desc:	Interpretes an xmlnode as owner contact, finds a corresponding contact in database
+ *			or adds a new one including all xml childnodes.
+ * params:	xContact	- xmlnode representing the contact
+ *			stat		- structure used to collect some statistics
+ * return:	ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CFileXml::ImportOwner(TiXmlElement* xContact)
+{
+	CExImContactXML vContact(this);
+
+	if (vContact = xContact) {
+		vContact.Import();
+		return ERROR_OK;
+	}
+	return ERROR_NOT_ADDED;
+}
+
+/**
+ * name:	ImportContacts
+ * desc:	Parse all child nodes of an given parent node and try to import all found contacts
+ * params:	xmlParent	 - xmlnode representing the parent of the list of contacts
+ *			hContact	 - handle to the contact, who is the owner of the setting to import
+ *			stat		 - structure used to collect some statistics
+ * return:	ERROR_OK if at least one contact was successfully imported
+ **/
+INT CFileXml::ImportContacts(TiXmlElement* xmlParent)
+{
+	TiXmlElement *xContact;
+	CExImContactXML vContact(this);
+	INT result;
+	LPTSTR pszNick;
+
+	// import contacts
+	for (xContact = xmlParent->FirstChildElement(); xContact != NULL; xContact = xContact->NextSiblingElement()) {
+		if (!mir_stricmp(xContact->Value(), XKEY_CONTACT)) {
+			// update progressbar and abort if user clicked cancel
+			pszNick = mir_utf8decodeT(xContact->Attribute("nick"));
+			// user clicked abort button
+			if (_progress.UpdateContact(LPGENT("Contact: %s (%S)"), pszNick, xContact->Attribute("proto"))) {
+				result = vContact.LoadXmlElemnt(xContact);
+				switch (result) {
+					case ERROR_OK:
+						// init contact class and import if matches the user desires
+						if (_hContactToWorkOn == INVALID_HANDLE_VALUE || vContact.handle() == _hContactToWorkOn) {
+							result = vContact.Import(_hContactToWorkOn != INVALID_HANDLE_VALUE);
+							switch (result) {
+								case ERROR_OK:
+									_numContactsDone++;
+									break;
+								case ERROR_ABORTED:
+									if (pszNick) mir_free(pszNick);
+									return ERROR_ABORTED;
+#ifdef _DEBUG
+								default:
+									MsgErr(NULL, LPGENT("Importing %s caused error %d"), pszNick, result);
+									break;
+#endif
+							}
+						}
+						break;
+					case ERROR_ABORTED:
+						if (pszNick) mir_free(pszNick);
+						return ERROR_ABORTED;
+#ifdef _DEBUG
+					default:
+						MsgErr(NULL, LPGENT("Loading contact %s from xml failed with error %d"), pszNick, result);
+						break;
+#endif
+				}
+			}
+			if (pszNick) mir_free(pszNick);
+		}
+		// import owner contact
+		else if (_hContactToWorkOn == INVALID_HANDLE_VALUE && !mir_stricmp(xContact->Value(), XKEY_OWNER) && (vContact = xContact)) {
+			result = vContact.Import();
+			switch (result) {
+				case ERROR_OK:
+					_numContactsDone++;
+					break;
+				case ERROR_ABORTED:
+					return ERROR_ABORTED;
+#ifdef _DEBUG
+				default:
+					MsgErr(NULL, LPGENT("Importing Owner caused error %d"), result);
+#endif
+			}
+		}
+	}
+	return ERROR_OK;
+}
+
+/**
+ * name:	CountContacts
+ * desc:	Counts the number of contacts stored in the file
+ * params:	xContact	- the contact, who is the owner of the keys to count
+ * return:	nothing
+ **/
+DWORD CFileXml::CountContacts(TiXmlElement* xmlParent)
+{
+	DWORD dwCount = 0;
+	TiXmlNode *xContact;
+
+	try {
+		// count contacts in file for progress bar
+		for (xContact = xmlParent->FirstChild(); xContact != NULL; xContact = xContact->NextSibling()) {
+			if (!mir_stricmp(xContact->Value(), XKEY_CONTACT) || !mir_stricmp(xContact->Value(), XKEY_OWNER)) {
+				dwCount += CountContacts(xContact->ToElement()) + 1;
+			}
+		}
+	}
+	catch(...) {
+		return 0;
+	}
+	return dwCount;
+}
+
+/**
+ * name:	Import
+ * desc:	Interpretes an xmlnode as owner contact, finds a corresponding contact in database
+ *			or adds a new one including all xml childnodes.
+ * params:	hContact	- handle to the contact, who is the owner of the setting to import
+ *			pszFileName	- full qualified path to the xml file which is to import
+ * return:	ERROR_OK on success or one other element of ImportError to tell the type of failure
+ **/
+INT CFileXml::Import(HANDLE hContact, LPCSTR pszFileName)
+{
+	TiXmlDocument doc;
+	TiXmlElement *xmlCard = NULL;
+
+	try {
+		_hContactToWorkOn = hContact;
+		// load xml file
+		if (!doc.LoadFile(pszFileName)) {
+			MsgErr(NULL, LPGENT("Parser is unable to load XMLCard \"%s\"\nError: %d\nDescription: %s"),
+				pszFileName, doc.ErrorId(), doc.ErrorDesc());
+			return 1;
+		}
+		// is xmlfile a XMLCard ?
+		if ((xmlCard = doc.FirstChildElement("XMLCard")) == NULL) {
+			MsgErr(NULL, LPGENT("The selected file is no valid XMLCard"));
+			return 1;
+		}
+		// check version
+		if (mir_strcmp(xmlCard->Attribute("ver"), XMLCARD_VERSION)) {
+			MsgErr(NULL, LPGENT("The version of the XMLCard is not supported by UserInfoEx"));
+			return 1;
+		}
+
+		// is owner contact to import ?
+		if (_hContactToWorkOn == NULL) {
+			INT ret;
+			
+			// disable database safty mode to speed up the operation
+			CallService(MS_DB_SETSAFETYMODE, 0, 0);
+			// import owner contact
+			ret = ImportOwner(xmlCard->FirstChildElement(XKEY_OWNER));
+			// as soon as possible enable safty mode again!
+			CallService(MS_DB_SETSAFETYMODE, 1, 0);
+
+			if (!ret) {
+				MsgBox(NULL, MB_ICONINFORMATION, 
+					LPGENT("Complete"),
+					LPGENT("Import complete"),
+					LPGENT("Owner contact successfully imported."));
+				return 0;
+			} else {
+				MsgErr(NULL, LPGENT("Selected XMLCard does not contain an owner contact!"));
+				return 1;
+			}
+		}
+		else {
+#ifdef _DEBUG
+			LARGE_INTEGER freq, t1, t2;
+
+			QueryPerformanceFrequency(&freq);
+			QueryPerformanceCounter(&t1);
+#endif
+			// count contacts in file for progress bar
+			_numContactsTodo = CountContacts(xmlCard);
+			if (_numContactsTodo > 0) {
+				_progress.SetContactCount(_numContactsTodo);
+				// disable database safty mode to speed up the operation
+				CallService(MS_DB_SETSAFETYMODE, 0, 0);
+				// import the contacts
+				ImportContacts(xmlCard);
+				// as soon as possible enable safty mode again!
+				CallService(MS_DB_SETSAFETYMODE, 1, 0);
+			}
+			// finally hide the progress dialog
+			_progress.Hide();
+
+#ifdef _DEBUG
+			QueryPerformanceCounter(&t2);
+			MsgErr(NULL, LPGENT("Import took %f msec"),
+				(long double)(t2.QuadPart - t1.QuadPart) / freq.QuadPart * 1000.);
+#endif
+			// show results
+			MsgBox(NULL, MB_ICONINFORMATION, LPGENT("Import complete"), LPGENT("Some basic statistics"), 
+				LPGENT("added contacts: %u / %u\nadded settings: %u / %u\nadded events %u / %u\nduplicated events: %u"),
+				_numContactsDone, _numContactsTodo,
+				_numSettingsDone, _numSettingsTodo,
+				_numEventsDone, _numEventsTodo,
+				_numEventsDuplicated);
+			
+		}
+	}	
+	catch(...) {
+		MsgErr(NULL, LPGENT("FATAL: An exception was thrown while importing contacts from xmlCard!"));
+		return 1;
+	}
+	return 0;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImXML.h b/plugins/UserInfoEx/src/ex_import/svc_ExImXML.h
new file mode 100644
index 0000000000..a2524a7dd6
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImXML.h
@@ -0,0 +1,75 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImXML.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _SVC_FILEXML_INCLUDED_
+#define _SVC_FILEXML_INCLUDED_ 1
+
+#include "svc_ExImport.h"
+#include "classExImContactBase.h"
+#include "dlg_ExImProgress.h"
+
+#define EXPORT_DATA		1
+#define EXPORT_HISTORY	2
+#define EXPORT_ALL		(EXPORT_DATA|EXPORT_HISTORY)
+
+class CFileXml {
+	friend class CExImContactXML;
+
+	DWORD		_numContactsTodo;
+	DWORD		_numContactsDone;
+	DWORD		_numSettingsTodo;
+	DWORD		_numSettingsDone;
+	DWORD		_numEventsTodo;
+	DWORD		_numEventsDone;
+	DWORD		_numEventsDuplicated;
+
+	HANDLE		_hContactToWorkOn;	// contact to ex/import (NULL=owner|INVALID_HANDLE_VALUE=all|HADNLE=one user)
+
+	WORD		_wExport;
+
+	CProgress	_progress;
+
+	INT		ImportOwner(TiXmlElement* xmlContact);
+	INT		ImportContacts(TiXmlElement* xmlParent);
+	
+	DWORD	CountContacts(TiXmlElement* xmlParent);
+
+	/*
+	INT ExportOwner(FILE *xmlfile, BOOLEAN bExportEvents);
+	INT ExportContact(FILE *xmlfile, HANDLE hContact, BOOLEAN bExportEvents, LPENUMLIST pModules);
+	INT ExportSubContact(TiXmlElement *xContact, HANDLE hContact, BOOLEAN bExportEvents);
+	*/
+
+public:
+	CFileXml();
+	INT		Import(HANDLE hContact, LPCSTR pszFileName);
+	INT		Export(lpExImParam ExImContact, LPCSTR pszFileName);
+};
+
+#endif /* _SVC_FILEXML_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp b/plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp
new file mode 100644
index 0000000000..35bb59c3b9
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImport.cpp
@@ -0,0 +1,382 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImport.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "classExImContactBase.h"
+#include "dlg_ExImModules.h"
+#include "dlg_ExImOpenSaveFile.h"
+#include "svc_ExImport.h"
+#include "svc_ExImINI.h"
+#include "svc_ExImVCF.h"
+#include "svc_ExImXML.h"
+
+#include <m_clui.h>
+#include <m_clc.h>
+
+/***********************************************************************************************************
+ * internal functions
+ ***********************************************************************************************************/
+
+/**
+ * name:	DisplayNameToFileName
+ * desc:	convert contact's display name to valid filename
+ * param:	hContact	- handle of contact to create the filename for
+ *			pszFileName	- buffer, retrieving the converted filename
+ *			cchFileName	- number of maximum characters the filename can be
+ * return:	nothing
+ **/
+static VOID DisplayNameToFileName(lpExImParam ExImContact, LPSTR pszFileName, WORD cchFileName)
+{
+	LPCSTR	disp = 0;
+	LPSTR	temp = 0;
+
+	cchFileName--;
+
+	ZeroMemory(pszFileName, cchFileName);
+
+	switch (ExImContact->Typ) {
+		case EXIM_ALL:
+		case EXIM_GROUP:
+			mir_strncpy(pszFileName, Translate("all Contacts"), cchFileName);
+			return;
+		case EXIM_CONTACT:
+			if (ExImContact->hContact == NULL) {
+				mir_strncpy(pszFileName, Translate("Owner"), cchFileName);
+				return;
+			}
+			else {
+				disp = (LPCSTR)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)ExImContact->hContact, NULL);
+			}
+			break;
+		case EXIM_SUBGROUP:
+			temp = mir_t2a(ExImContact->ptszName);
+			disp = temp;
+			break;
+		case EXIM_ACCOUNT:
+			PROTOACCOUNT* acc = ProtoGetAccount(ExImContact->pszName);
+			temp = mir_t2a(acc->tszAccountName);
+			disp = temp;
+			break;
+	}
+
+	// replace unwanted characters
+	while (*disp != 0 && cchFileName > 1) {
+		switch (*disp) {
+			case '?':	case '*':	case ':':
+			case '\\':	case '|':	case '/':
+			case '<':	case '>':	case '"':
+				*(pszFileName++) = '_';
+				break;
+			default:
+				*(pszFileName++) = *disp;
+				break;
+		}
+		disp++;
+		cchFileName--;
+	}
+	mir_free(temp);
+}
+
+LPCSTR FilterString(lpExImParam ExImContact)
+{
+	LPCSTR pszFilter = 0;
+	switch (ExImContact->Typ) {
+		case EXIM_SUBGROUP:
+		case EXIM_ACCOUNT:
+			pszFilter = ("XMLCard 1.0 (*.xml)\0*.xml\0");
+			break;
+		case EXIM_ALL:
+		case EXIM_GROUP:
+			pszFilter = ("XMLCard 1.0 (*.xml)\0*.xml\0DBEditor++ File (*.ini)\0*.ini\0");
+			break;
+		case EXIM_CONTACT:
+			pszFilter = ("XMLCard 1.0 (*.xml)\0*.xml\0DBEditor++ File (*.ini)\0*.ini\0Standard vCard 2.1 (*.vcf)\0*.vcf\0");
+			break;
+	}
+	return pszFilter;
+}
+
+/**
+ * name:	SvcExImport_Export
+ * desc:	service function to export contact information
+ * param:	wParam	- handle to contact or NULL
+ *			lParam	- parent window
+ * return:	0 always
+ **/
+INT_PTR SvcExImport_Export(lpExImParam ExImContact, HWND hwndParent)
+{
+	CHAR szFileName[MAX_PATH] = { 0 };
+	// create the filename to suggest the user for the to export contact
+	DisplayNameToFileName(ExImContact, szFileName, SIZEOF(szFileName));
+	INT nIndex = DlgExIm_SaveFileName(hwndParent, 
+		Translate("Select a destination file..."),
+		FilterString(ExImContact),
+		szFileName);
+
+	switch (nIndex) {
+		case 1:		// .xml
+		{
+			CFileXml xmlFile;
+			return xmlFile.Export(ExImContact, szFileName);
+		}
+		case 2:		// .ini
+		{
+			return SvcExImINI_Export(ExImContact, szFileName);
+		}
+		case 3:		// .vcf
+		{
+			CVCardFileVCF vcfFile;
+			SetCursor(LoadCursor(NULL, IDC_WAIT));
+			if (vcfFile.Open(ExImContact->hContact, szFileName, "wt")) {
+				vcfFile.Export(FALSE);
+				vcfFile.Close();
+			}
+			SetCursor(LoadCursor(NULL, IDC_ARROW));
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ * name:	SvcExImport_Import
+ * desc:	service function to export contact information
+ * param:	wParam	- handle to contact or NULL
+ *			lParam	- parent window
+ * return:	0 always
+ **/
+INT_PTR SvcExImport_Import(lpExImParam ExImContact, HWND hwndParent)
+{
+	CHAR szFileName[MAX_PATH] = { 0 };
+
+	// create the filename to suggest the user for the to export contact
+	DisplayNameToFileName(ExImContact, szFileName, SIZEOF(szFileName));
+
+	INT nIndex = DlgExIm_OpenFileName(hwndParent, 
+		Translate("Import User Details from VCard"),
+		FilterString(ExImContact),
+		szFileName);
+
+// Stop during develop
+if (ExImContact->Typ == EXIM_ACCOUNT || 
+	ExImContact->Typ == EXIM_GROUP) return 1;
+
+	switch (nIndex) {
+		case 1:
+		{
+			CFileXml xmlFile;
+			CallService(MS_CLIST_SETHIDEOFFLINE, -1, 0);	//workarround to refresh the clist....
+			xmlFile.Import(ExImContact->hContact, szFileName);
+			CallService(MS_CLIST_SETHIDEOFFLINE, -1, 0);	//...after import.
+			//pcli->pfnClcBroadcast(CLM_AUTOREBUILD, 0, 0); //does not work
+			return 0;
+		}
+		// .ini
+		case 2:
+			return SvcExImINI_Import(ExImContact->hContact, szFileName);
+
+		// .vcf
+		case 3:
+		{
+			CVCardFileVCF vcfFile;
+
+			if (vcfFile.Open(ExImContact->hContact, szFileName, "rt")) {
+				SetCursor(LoadCursor(NULL, IDC_WAIT));
+				vcfFile.Import();
+				vcfFile.Close();
+				SetCursor(LoadCursor(NULL, IDC_ARROW));
+			}
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/***********************************************************************************************************
+ * service functions
+ ***********************************************************************************************************/
+
+/*********************************
+ * Ex/import All (MainMenu)
+ *********************************/
+INT_PTR svcExIm_MainExport_Service(WPARAM wParam, LPARAM lParam)
+{
+	ExImParam ExIm;
+	ZeroMemory(&ExIm, sizeof(ExIm));
+	ExIm.hContact = INVALID_HANDLE_VALUE;
+	ExIm.Typ = EXIM_ALL;
+	return SvcExImport_Export(&ExIm, (HWND)lParam);
+}
+
+INT_PTR svcExIm_MainImport_Service(WPARAM wParam, LPARAM lParam)
+{
+	ExImParam ExIm;
+	ZeroMemory(&ExIm, sizeof(ExIm));
+	ExIm.hContact = INVALID_HANDLE_VALUE;
+	ExIm.Typ = EXIM_ALL;
+	return SvcExImport_Import(&ExIm, (HWND)lParam);
+}
+
+
+/*********************************
+ * Ex/import Contact (ContactMenu)
+ *********************************/
+INT_PTR svcExIm_ContactExport_Service(WPARAM wParam, LPARAM lParam)
+{
+	ExImParam ExIm;
+	ZeroMemory(&ExIm, sizeof(ExIm));
+	ExIm.hContact = (HANDLE)wParam;
+	ExIm.Typ = EXIM_CONTACT;
+	return SvcExImport_Export(&ExIm, (HWND)lParam);
+}
+
+INT_PTR svcExIm_ContactImport_Service(WPARAM wParam, LPARAM lParam)
+{
+	ExImParam ExIm;
+	ZeroMemory(&ExIm, sizeof(ExIm));
+	ExIm.hContact = (HANDLE)wParam;
+	ExIm.Typ = EXIM_CONTACT;
+	return SvcExImport_Import(&ExIm, (HWND)lParam);
+}
+
+
+/*********************************
+ *Ex/import (Sub)Group (GroupMenu)
+ *********************************/
+
+/**
+ * This service is call by (Sub)Group MenuItem Export and MenuItem Import
+ *
+ * @param	wParam				- gmp.wParam  = 0 ->Import
+ * @param	wParam				- gmp.wParam != 0 ->Export
+ * @param	lParam				- gmp.lParam not used
+  *
+ * @return	always 0
+ **/
+INT_PTR svcExIm_Group_Service(WPARAM wParam, LPARAM lParam)
+{
+	ExImParam ExIm;
+	INT_PTR hItem = 0, hRoot = 0, hParent = 0;
+	TCHAR tszGroup[120], tszItem[120];
+	ZeroMemory(&tszGroup, sizeof(tszGroup));
+	ZeroMemory(&tszItem, sizeof(tszItem));
+	ZeroMemory(&ExIm, sizeof(ExIm));
+	LPTSTR ptszGroup = tszGroup;
+	LPTSTR ptszItem = tszItem;
+
+	HWND hClist = (HWND)CallService(MS_CLUI_GETHWNDTREE,0,0);
+	// get clist selection
+	hItem = SendMessage(hClist,CLM_GETSELECTION,0,0);
+	hRoot = SendMessage(hClist,CLM_GETNEXTITEM, (WPARAM)CLGN_ROOT, (LPARAM)hItem);
+	while (hItem) {
+		if (SendMessage(hClist,CLM_GETITEMTYPE, (WPARAM)hItem, 0) == CLCIT_GROUP) {
+			SendMessage(hClist,CLM_GETITEMTEXT, (WPARAM)hItem, (LPARAM)ptszItem);
+			LPTSTR temp = mir_tstrdup(ptszGroup);
+			mir_sntprintf(tszGroup, SIZEOF(tszGroup),_T("%s%s%s"), ptszItem, _tcslen(temp)? _T("\\"):_T(""), temp);
+			mir_free (temp);
+		}
+		hParent = SendMessage(hClist,CLM_GETNEXTITEM, (WPARAM)CLGN_PARENT, (LPARAM)hItem);
+		hItem = (hParent != hRoot) ? hParent : 0;
+	}
+	ExIm.ptszName = ptszGroup;
+	ExIm.Typ = EXIM_SUBGROUP;
+
+	if (wParam) {
+		//Export	"/ExportGroup"
+		SvcExImport_Export(&ExIm, hClist);
+	}
+	else {
+		//Import	"/ImportGroup"
+		SvcExImport_Import(&ExIm, hClist);
+	}
+
+	return 0;
+};
+
+
+/*********************************
+ *Ex/Import Account (AccountMenu)
+ *********************************/
+typedef struct
+// neeed for MO_MENUITEMGETOWNERDATA
+// taken from core clistmenus.ccp
+{
+	char *proto;			//This is unique protoname
+	int protoindex;
+	int status;
+
+	BOOL custom;
+	char *svc;
+	HANDLE hMenuItem;
+}	StatusMenuExecParam,*lpStatusMenuExecParam;
+
+/**
+ * This service is call by Account MenuItem Export and MenuItem Import
+ *
+ * @param	wParam				- not used
+ * @param	lParam				- MenuItem from MS_CLIST_ADDSTATUSMENUITEM
+ *
+ * @return	always 0
+ **/
+INT_PTR svcExIm_Account_Service(WPARAM wParam, LPARAM lParam)
+{
+	ExImParam ExIm;
+	ZeroMemory(&ExIm, sizeof(ExIm));
+	HWND hClist = (HWND)CallService(MS_CLUI_GETHWNDTREE,0,0);
+	lpStatusMenuExecParam smep = (lpStatusMenuExecParam) CallService(MO_MENUITEMGETOWNERDATA, (WPARAM) lParam, NULL);
+	ExIm.pszName = mir_strdup(smep->proto);
+	ExIm.Typ = EXIM_ACCOUNT;
+
+	if (strstr( smep->svc, "/ExportAccount" )) {
+		//Export	"/ExportAccount"
+		SvcExImport_Export(&ExIm, hClist);
+	}
+	else {
+		//Import	"/ImportAccount"
+		SvcExImport_Import(&ExIm, hClist);
+	}
+	mir_free(ExIm.pszName);
+	return 0;
+};
+
+/**
+ * name:	SvcExImport_LoadModule()
+ * desc:	initializes the Ex/Import Services
+ *
+ * return:	0 or 1
+ **/
+VOID SvcExImport_LoadModule()
+{
+	CreateServiceFunction(MS_USERINFO_VCARD_EXPORTALL,	svcExIm_MainExport_Service);
+	CreateServiceFunction(MS_USERINFO_VCARD_IMPORTALL,	svcExIm_MainImport_Service);
+	CreateServiceFunction(MS_USERINFO_VCARD_EXPORT,		svcExIm_ContactExport_Service);
+	CreateServiceFunction(MS_USERINFO_VCARD_IMPORT,		svcExIm_ContactImport_Service);
+	return;
+}
diff --git a/plugins/UserInfoEx/src/ex_import/svc_ExImport.h b/plugins/UserInfoEx/src/ex_import/svc_ExImport.h
new file mode 100644
index 0000000000..3539ad72cd
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/svc_ExImport.h
@@ -0,0 +1,62 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/svc_ExImport.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_EXIMPORT_INCLUDED_
+#define _SVC_EXIMPORT_INCLUDED_ 1
+
+typedef struct
+{
+	BYTE Typ;
+	union {
+		HANDLE hContact;	
+		LPSTR pszName;	
+		LPTSTR ptszName;
+	};
+}
+	ExImParam,*lpExImParam;
+
+enum ExImType {
+	EXIM_ALL		= 1,
+	EXIM_CONTACT	= 2,
+	EXIM_GROUP		= 4,
+	EXIM_SUBGROUP	= 8,
+	EXIM_ACCOUNT	= 16
+};
+
+INT_PTR svcExIm_MainExport_Service(WPARAM wParam, LPARAM lParam);
+INT_PTR svcExIm_MainImport_Service(WPARAM wParam, LPARAM lParam);
+INT_PTR svcExIm_ContactExport_Service(WPARAM wParam, LPARAM lParam);
+INT_PTR svcExIm_ContactImport_Service(WPARAM wParam, LPARAM lParam);
+INT_PTR svcExIm_Group_Service(WPARAM wParam,LPARAM lParam);
+INT_PTR svcExIm_Account_Service(WPARAM wParam,LPARAM lParam);
+
+VOID SvcExImport_LoadModule();
+
+#endif /* _SVC_EXIMPORT_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/ex_import/tinystr.cpp b/plugins/UserInfoEx/src/ex_import/tinystr.cpp
new file mode 100644
index 0000000000..3ec0fe4676
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinystr.cpp
@@ -0,0 +1,143 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original file by Yves Berquin.
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. 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.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+/*
+ * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.
+ *
+ * - completely rewritten. compact, clean, and fast implementation.
+ * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)
+ * - fixed reserve() to work as per specification.
+ * - fixed buggy compares operator==(), operator<(), and operator>()
+ * - fixed operator+=() to take a const ref argument, following spec.
+ * - added "copy" constructor with length, and most compare operators.
+ * - added swap(), clear(), size(), capacity(), operator+().
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinystr.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+ */
+
+#ifndef TIXML_USE_STL
+
+#ifdef USE_MMGR
+#include <assert.h>
+#include <string.h>
+
+#include "mmgr.h"
+#endif
+
+#include "tinystr.h"
+
+// Error value for find primitive
+const TiXmlString::size_type TiXmlString::npos = static_cast< size_type >(-1);
+
+// Null rep.
+TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, '\0' };
+
+
+void TiXmlString::reserve (size_type cap)
+{
+	if (cap > capacity())
+	{
+		TiXmlString tmp;
+		tmp.init(length(), cap);
+		memcpy(tmp.start(), data(), length());
+		swap(tmp);
+	}
+}
+
+
+TiXmlString& TiXmlString::assign(const char* str, size_type len)
+{
+	size_type cap = capacity();
+	if (len > cap || cap > 3*(len + 8))
+	{
+		TiXmlString tmp;
+		tmp.init(len);
+		memcpy(tmp.start(), str, len);
+		swap(tmp);
+	}
+	else
+	{
+		memmove(start(), str, len);
+		set_size(len);
+	}
+	return *this;
+}
+
+
+TiXmlString& TiXmlString::append(const char* str, size_type len)
+{
+	size_type newsize = length() + len;
+	if (newsize > capacity())
+	{
+		reserve (newsize + capacity());
+	}
+	memmove(finish(), str, len);
+	set_size(newsize);
+	return *this;
+}
+
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
+{
+	TiXmlString tmp;
+	tmp.reserve(a.length() + b.length());
+	tmp += a;
+	tmp += b;
+	return tmp;
+}
+
+TiXmlString operator + (const TiXmlString & a, const char* b)
+{
+	TiXmlString tmp;
+	TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>(strlen(b));
+	tmp.reserve(a.length() + b_len);
+	tmp += a;
+	tmp.append(b, b_len);
+	return tmp;
+}
+
+TiXmlString operator + (const char* a, const TiXmlString & b)
+{
+	TiXmlString tmp;
+	TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>(strlen(a));
+	tmp.reserve(a_len + b.length());
+	tmp.append(a, a_len);
+	tmp += b;
+	return tmp;
+}
+
+
+#endif	// TIXML_USE_STL
diff --git a/plugins/UserInfoEx/src/ex_import/tinystr.h b/plugins/UserInfoEx/src/ex_import/tinystr.h
new file mode 100644
index 0000000000..1061e795d8
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinystr.h
@@ -0,0 +1,335 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original file by Yves Berquin.
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. 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.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+/*
+ * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.
+ *
+ * - completely rewritten. compact, clean, and fast implementation.
+ * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)
+ * - fixed reserve() to work as per specification.
+ * - fixed buggy compares operator==(), operator<(), and operator>()
+ * - fixed operator+=() to take a const ref argument, following spec.
+ * - added "copy" constructor with length, and most compare operators.
+ * - added swap(), clear(), size(), capacity(), operator+().
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinystr.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+ */
+
+#ifndef TIXML_USE_STL
+
+#ifndef TIXML_STRING_INCLUDED
+#define TIXML_STRING_INCLUDED
+
+#ifndef USE_MMGR
+#include <assert.h>
+#include <string.h>
+#endif
+
+/*	The support for explicit isn't that universal, and it isn't really
+	required - it is used to check that the TiXmlString class isn't incorrectly
+	used. Be nice to old compilers and macro it here:
+*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+	// Microsoft visual studio, version 6 and higher.
+	#define TIXML_EXPLICIT explicit
+#elif defined(__GNUC__) && (__GNUC__ >= 3)
+	// GCC version 3 and higher.s
+	#define TIXML_EXPLICIT explicit
+#else
+	#define TIXML_EXPLICIT
+#endif
+
+
+/*
+	 TiXmlString is an emulation of a subset of the std::string template.
+	 Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
+	 Only the member functions relevant to the TinyXML project have been implemented.
+	 The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
+	 a string and there's no more room, we allocate a buffer twice as big as we need.
+*/
+class TiXmlString
+{
+	public :
+	// The size type used
+		typedef size_t size_type;
+
+	// Error value for find primitive
+	static const size_type npos; // = -1;
+
+
+	// TiXmlString empty constructor
+	TiXmlString () : rep_(&nullrep_)
+	{
+	}
+
+	// TiXmlString copy constructor
+	TiXmlString (const TiXmlString & copy)
+	{
+		init(copy.length());
+		memcpy(start(), copy.data(), length());
+	}
+
+	// TiXmlString constructor, based on a string
+	TIXML_EXPLICIT TiXmlString (const char * copy)
+	{
+		init(static_cast<size_type>(strlen(copy)));
+		memcpy(start(), copy, length());
+	}
+
+	// TiXmlString constructor, based on a string
+	TIXML_EXPLICIT TiXmlString (const char * str, size_type len)
+	{
+		init(len);
+		memcpy(start(), str, len);
+	}
+
+	// TiXmlString destructor
+	~TiXmlString ()
+	{
+		quit();
+	}
+
+	// = operator
+	TiXmlString& operator = (const char * copy)
+	{
+		return assign(copy, (size_type)strlen(copy));
+	}
+
+	// = operator
+	TiXmlString& operator = (const TiXmlString & copy)
+	{
+		return assign(copy.start(), copy.length());
+	}
+
+
+	// += operator. Maps to append
+	TiXmlString& operator += (const char * suffix)
+	{
+		return append(suffix, static_cast<size_type>(strlen(suffix)));
+	}
+
+	// += operator. Maps to append
+	TiXmlString& operator += (char single)
+	{
+		return append(&single, 1);
+	}
+
+	// += operator. Maps to append
+	TiXmlString& operator += (const TiXmlString & suffix)
+	{
+		return append(suffix.data(), suffix.length());
+	}
+
+
+	// Convert a TiXmlString into a null-terminated char *
+	const char * c_str () const { return rep_->str; }
+
+	// Convert a TiXmlString into a char * (need not be null terminated).
+	const char * data () const { return rep_->str; }
+
+	// Return the length of a TiXmlString
+	size_type length () const { return rep_->size; }
+
+	// Alias for length()
+	size_type size () const { return rep_->size; }
+
+	// Checks if a TiXmlString is empty
+	bool empty () const { return rep_->size == 0; }
+
+	// Return capacity of string
+	size_type capacity () const { return rep_->capacity; }
+
+
+	// single char extraction
+	const char& at (size_type index) const
+	{
+		assert(index < length());
+		return rep_->str[ index ];
+	}
+
+	// [] operator
+	char& operator [] (size_type index) const
+	{
+		assert(index < length());
+		return rep_->str[ index ];
+	}
+
+	// find a char in a string. Return TiXmlString::npos if not found
+	size_type find (char lookup) const
+	{
+		return find(lookup, 0);
+	}
+
+	// find a char in a string from an offset. Return TiXmlString::npos if not found
+	size_type find (char tofind, size_type offset) const
+	{
+		if (offset >= length()) return npos;
+
+		for (const char* p = c_str() + offset; *p != '\0'; ++p)
+		{
+			 if (*p == tofind) return static_cast< size_type >(p - c_str());
+		}
+		return npos;
+	}
+
+	void clear ()
+	{
+		//Lee:
+		//The original was just too strange, though correct:
+		//	TiXmlString().swap(*this);
+		//Instead use the quit & re-init:
+		quit();
+		init(0,0);
+	}
+
+	/*	Function to reserve a big amount of data when we know we'll need it. Be aware that this
+		function DOES NOT clear the content of the TiXmlString if any exists.
+	*/
+	void reserve (size_type cap);
+
+	TiXmlString& assign (const char* str, size_type len);
+
+	TiXmlString& append (const char* str, size_type len);
+
+	void swap (TiXmlString& other)
+	{
+		Rep* r = rep_;
+		rep_ = other.rep_;
+		other.rep_ = r;
+	}
+
+	private:
+
+	void init(size_type sz) { init(sz, sz); }
+	void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
+	char* start() const { return rep_->str; }
+	char* finish() const { return rep_->str + rep_->size; }
+
+	struct Rep
+	{
+		size_type size, capacity;
+		char str[1];
+	};
+
+	void init(size_type sz, size_type cap)
+	{
+		if (cap)
+		{
+			// Lee: the original form:
+			//	rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
+			// doesn't work in some cases of new being overloaded. Switching
+			// to the normal allocation, although use an 'int' for systems
+			// that are overly picky about structure alignment.
+			const size_type bytesNeeded = sizeof(Rep) + cap;
+			const size_type intsNeeded = (bytesNeeded + sizeof(int) - 1) / sizeof(int); 
+			rep_ = reinterpret_cast<Rep*>(new int[ intsNeeded ]);
+
+			rep_->str[ rep_->size = sz ] = '\0';
+			rep_->capacity = cap;
+		}
+		else
+		{
+			rep_ = &nullrep_;
+		}
+	}
+
+	void quit()
+	{
+		if (rep_ != &nullrep_)
+		{
+			// The rep_ is really an array of ints. (see the allocator, above).
+			// Cast it back before delete, so the compiler won't incorrectly call destructors.
+			delete [] (reinterpret_cast<int*>(rep_));
+		}
+	}
+
+	Rep * rep_;
+	static Rep nullrep_;
+
+} ;
+
+
+inline bool operator == (const TiXmlString & a, const TiXmlString & b)
+{
+	return		(a.length() == b.length())				// optimization on some platforms
+				 && (strcmp(a.c_str(), b.c_str()) == 0);	// actual compare
+}
+inline bool operator < (const TiXmlString & a, const TiXmlString & b)
+{
+	return strcmp(a.c_str(), b.c_str()) < 0;
+}
+
+inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
+inline bool operator >	(const TiXmlString & a, const TiXmlString & b) { return b < a; }
+inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
+inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
+
+inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
+inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
+inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
+inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
+TiXmlString operator + (const TiXmlString & a, const char* b);
+TiXmlString operator + (const char* a, const TiXmlString & b);
+
+
+/*
+	 TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
+	 Only the operators that we need for TinyXML have been developped.
+*/
+class TiXmlOutStream : public TiXmlString
+{
+public :
+
+	// TiXmlOutStream << operator.
+	TiXmlOutStream & operator << (const TiXmlString & in)
+	{
+		*this += in;
+		return *this;
+	}
+
+	// TiXmlOutStream << operator.
+	TiXmlOutStream & operator << (const char * in)
+	{
+		*this += in;
+		return *this;
+	}
+
+} ;
+
+#endif	// TIXML_STRING_INCLUDED
+#endif	// TIXML_USE_STL
diff --git a/plugins/UserInfoEx/src/ex_import/tinyxml.cpp b/plugins/UserInfoEx/src/ex_import/tinyxml.cpp
new file mode 100644
index 0000000000..2eba13a7ea
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinyxml.cpp
@@ -0,0 +1,1978 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier)copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. 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.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinyxml.cpp $
+Revision       : $Revision: 196 $
+Last change on : $Date: 2010-09-21 03:24:30 +0400 (Вт, 21 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+ */
+#include "tinyxml.h"
+
+#include <ctype.h>
+
+#ifdef TIXML_USE_STL
+#include <sstream>
+#include <iostream>
+#endif
+
+#ifdef USE_MMGR
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "mmgr.h"
+#endif
+
+
+bool TiXmlBase::condenseWhiteSpace = true;
+
+void TiXmlBase::StreamDepth(TIXML_OSTREAM* stream, int depth) const
+{
+	for (int i = 0; i < depth; ++i)
+	{
+		(*stream) << " ";
+	}
+}
+
+void TiXmlBase::PutString(const TIXML_STRING& str, TIXML_OSTREAM* stream)
+{
+	TIXML_STRING buffer;
+	PutString(str, &buffer);
+	(*stream) << buffer;
+}
+
+void TiXmlBase::PutString(const TIXML_STRING& str, TIXML_STRING* outString)
+{
+	int i=0;
+
+	while (i<(int)str.length())
+	{
+		unsigned char c = (unsigned char) str[i];
+
+		if (	 c == '&'
+				 && i < ((int)str.length() - 2)
+			 && str[i+1] == '#'
+			 && str[i+2] == 'x')
+		{
+			// Hexadecimal character reference.
+			// Pass through unchanged.
+			// &#xA9;	-- copyright symbol, for example.
+			//
+			// The -1 is a bug fix from Rob Laveaux. It keeps
+			// an overflow from happening if there is no ';'.
+			// There are actually 2 ways to exit this loop -
+			// while fails (error case) and break (semicolon found).
+			// However, there is no mechanism (currently) for
+			// this function to return an error.
+			while (i<(int)str.length()-1)
+			{
+				outString->append(str.c_str() + i, 1);
+				++i;
+				if (str[i] == ';')
+					break;
+			}
+		}
+		else if (c == '&')
+		{
+			outString->append(entity[0].str, entity[0].strLength);
+			++i;
+		}
+		else if (c == '<')
+		{
+			outString->append(entity[1].str, entity[1].strLength);
+			++i;
+		}
+		else if (c == '>')
+		{
+			outString->append(entity[2].str, entity[2].strLength);
+			++i;
+		}
+		else if (c == '\"')
+		{
+			outString->append(entity[3].str, entity[3].strLength);
+			++i;
+		}
+		else if (c == '\'')
+		{
+			outString->append(entity[4].str, entity[4].strLength);
+			++i;
+		}
+		else if (c < 32)
+		{
+			// Easy pass at non-alpha/numeric/symbol
+			// Below 32 is symbolic.
+			char buf[ 32 ];
+
+			#if defined(TIXML_SNPRINTF)
+				TIXML_SNPRINTF(buf, sizeof(buf), "&#x%02X;", (unsigned) (c & 0xff));
+			#else
+				sprintf(buf, "&#x%02X;", (unsigned) (c & 0xff));
+			#endif
+
+			//*ME:	warning C4267: convert 'size_t' to 'int'
+			//*ME:	Int-Cast to make compiler happy ...
+			outString->append(buf, (int)strlen(buf));
+			++i;
+		}
+		else
+		{
+			//char realc = (char) c;
+			//outString->append(&realc, 1);
+			*outString += (char) c;	// somewhat more efficient function call.
+			++i;
+		}
+	}
+}
+
+
+// <-- Strange class for a bug fix. Search for STL_STRING_BUG
+TiXmlBase::StringToBuffer::StringToBuffer(const TIXML_STRING& str)
+{
+	buffer = new char[ str.length()+1 ];
+	if (buffer)
+	{
+		strcpy(buffer, str.c_str());
+	}
+}
+
+
+TiXmlBase::StringToBuffer::~StringToBuffer()
+{
+	delete [] buffer;
+}
+// End strange bug fix. -->
+
+
+TiXmlNode::TiXmlNode(NodeType _type) : TiXmlBase()
+{
+	parent = 0;
+	type = _type;
+	firstChild = 0;
+	lastChild = 0;
+	prev = 0;
+	next = 0;
+}
+
+
+TiXmlNode::~TiXmlNode()
+{
+	TiXmlNode* node = firstChild;
+	TiXmlNode* temp = 0;
+
+	while (node)
+	{
+		temp = node;
+		node = node->next;
+
+		delete temp;
+	}
+}
+
+
+void TiXmlNode::CopyTo(TiXmlNode* target) const
+{
+	target->SetValue (value.c_str());
+	target->userData = userData;
+}
+
+
+void TiXmlNode::Clear()
+{
+	TiXmlNode* node = firstChild;
+	TiXmlNode* temp = 0;
+
+	while (node)
+	{
+		temp = node;
+		node = node->next;
+		delete temp;
+	}
+
+	firstChild = 0;
+	lastChild = 0;
+}
+
+
+TiXmlNode* TiXmlNode::LinkEndChild(TiXmlNode* node)
+{
+	assert(node->parent == 0 || node->parent == this);
+	assert(node->GetDocument() == 0 || node->GetDocument() == this->GetDocument());
+
+	if (node->Type() == TiXmlNode::DOCUMENT)
+	{
+		delete node;
+		if (GetDocument()) GetDocument()->SetError(TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return 0;
+	}
+
+	node->parent = this;
+
+	node->prev = lastChild;
+	node->next = 0;
+
+	if (lastChild)
+		lastChild->next = node;
+	else
+		firstChild = node;			// it was an empty list.
+
+	lastChild = node;
+	return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertEndChild(const TiXmlNode& addThis)
+{
+	if (addThis.Type() == TiXmlNode::DOCUMENT)
+	{
+		if (GetDocument()) GetDocument()->SetError(TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return 0;
+	}
+	TiXmlNode* node = addThis.Clone();
+	if (!node)
+		return 0;
+
+	return LinkEndChild(node);
+}
+
+
+TiXmlNode* TiXmlNode::InsertBeforeChild(TiXmlNode* beforeThis, const TiXmlNode& addThis)
+{
+	if (!beforeThis || beforeThis->parent != this) {
+		return 0;
+	}
+	if (addThis.Type() == TiXmlNode::DOCUMENT)
+	{
+		if (GetDocument()) GetDocument()->SetError(TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return 0;
+	}
+
+	TiXmlNode* node = addThis.Clone();
+	if (!node)
+		return 0;
+	node->parent = this;
+
+	node->next = beforeThis;
+	node->prev = beforeThis->prev;
+	if (beforeThis->prev)
+	{
+		beforeThis->prev->next = node;
+	}
+	else
+	{
+		assert(firstChild == beforeThis);
+		firstChild = node;
+	}
+	beforeThis->prev = node;
+	return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertAfterChild(TiXmlNode* afterThis, const TiXmlNode& addThis)
+{
+	if (!afterThis || afterThis->parent != this) {
+		return 0;
+	}
+	if (addThis.Type() == TiXmlNode::DOCUMENT)
+	{
+		if (GetDocument()) GetDocument()->SetError(TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return 0;
+	}
+
+	TiXmlNode* node = addThis.Clone();
+	if (!node)
+		return 0;
+	node->parent = this;
+
+	node->prev = afterThis;
+	node->next = afterThis->next;
+	if (afterThis->next)
+	{
+		afterThis->next->prev = node;
+	}
+	else
+	{
+		assert(lastChild == afterThis);
+		lastChild = node;
+	}
+	afterThis->next = node;
+	return node;
+}
+
+
+TiXmlNode* TiXmlNode::ReplaceChild(TiXmlNode* replaceThis, const TiXmlNode& withThis)
+{
+	if (replaceThis->parent != this)
+		return 0;
+
+	TiXmlNode* node = withThis.Clone();
+	if (!node)
+		return 0;
+
+	node->next = replaceThis->next;
+	node->prev = replaceThis->prev;
+
+	if (replaceThis->next)
+		replaceThis->next->prev = node;
+	else
+		lastChild = node;
+
+	if (replaceThis->prev)
+		replaceThis->prev->next = node;
+	else
+		firstChild = node;
+
+	delete replaceThis;
+	node->parent = this;
+	return node;
+}
+
+
+bool TiXmlNode::RemoveChild(TiXmlNode* removeThis)
+{
+	if (removeThis->parent != this)
+	{
+		assert(0);
+		return false;
+	}
+
+	if (removeThis->next)
+		removeThis->next->prev = removeThis->prev;
+	else
+		lastChild = removeThis->prev;
+
+	if (removeThis->prev)
+		removeThis->prev->next = removeThis->next;
+	else
+		firstChild = removeThis->next;
+
+	delete removeThis;
+	return true;
+}
+
+const TiXmlNode* TiXmlNode::FirstChild(const char * _value) const
+{
+	const TiXmlNode* node;
+	for (node = firstChild; node; node = node->next)
+	{
+		if (strcmp(node->Value(), _value) == 0)
+			return node;
+	}
+	return 0;
+}
+
+
+TiXmlNode* TiXmlNode::FirstChild(const char * _value)
+{
+	TiXmlNode* node;
+	for (node = firstChild; node; node = node->next)
+	{
+		if (strcmp(node->Value(), _value) == 0)
+			return node;
+	}
+	return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::LastChild(const char * _value) const
+{
+	const TiXmlNode* node;
+	for (node = lastChild; node; node = node->prev)
+	{
+		if (strcmp(node->Value(), _value) == 0)
+			return node;
+	}
+	return 0;
+}
+
+TiXmlNode* TiXmlNode::LastChild(const char * _value)
+{
+	TiXmlNode* node;
+	for (node = lastChild; node; node = node->prev)
+	{
+		if (strcmp(node->Value(), _value) == 0)
+			return node;
+	}
+	return 0;
+}
+
+const TiXmlNode* TiXmlNode::IterateChildren(const TiXmlNode* previous) const
+{
+	if (!previous)
+	{
+		return FirstChild();
+	}
+	else
+	{
+		assert(previous->parent == this);
+		return previous->NextSibling();
+	}
+}
+
+TiXmlNode* TiXmlNode::IterateChildren(TiXmlNode* previous)
+{
+	if (!previous)
+	{
+		return FirstChild();
+	}
+	else
+	{
+		assert(previous->parent == this);
+		return previous->NextSibling();
+	}
+}
+
+const TiXmlNode* TiXmlNode::IterateChildren(const char * val, const TiXmlNode* previous) const
+{
+	if (!previous)
+	{
+		return FirstChild(val);
+	}
+	else
+	{
+		assert(previous->parent == this);
+		return previous->NextSibling(val);
+	}
+}
+
+TiXmlNode* TiXmlNode::IterateChildren(const char * val, TiXmlNode* previous)
+{
+	if (!previous)
+	{
+		return FirstChild(val);
+	}
+	else
+	{
+		assert(previous->parent == this);
+		return previous->NextSibling(val);
+	}
+}
+
+const TiXmlNode* TiXmlNode::NextSibling(const char * _value) const
+{
+	const TiXmlNode* node;
+	for (node = next; node; node = node->next)
+	{
+		if (strcmp(node->Value(), _value) == 0)
+			return node;
+	}
+	return 0;
+}
+
+TiXmlNode* TiXmlNode::NextSibling(const char * _value)
+{
+	TiXmlNode* node;
+	for (node = next; node; node = node->next)
+	{
+		if (strcmp(node->Value(), _value) == 0)
+			return node;
+	}
+	return 0;
+}
+
+const TiXmlNode* TiXmlNode::PreviousSibling(const char * _value) const
+{
+	const TiXmlNode* node;
+	for (node = prev; node; node = node->prev)
+	{
+		if (strcmp(node->Value(), _value) == 0)
+			return node;
+	}
+	return 0;
+}
+
+TiXmlNode* TiXmlNode::PreviousSibling(const char * _value)
+{
+	TiXmlNode* node;
+	for (node = prev; node; node = node->prev)
+	{
+		if (strcmp(node->Value(), _value) == 0)
+			return node;
+	}
+	return 0;
+}
+
+void TiXmlElement::RemoveAttribute(const char * name)
+{
+	TIXML_STRING str(name);
+	TiXmlAttribute* node = attributeSet.Find(str);
+	if (node)
+	{
+		attributeSet.Remove(node);
+		delete node;
+	}
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement() const
+{
+	const TiXmlNode* node;
+
+	for (	node = FirstChild();
+			node;
+			node = node->NextSibling())
+	{
+		if (node->ToElement())
+			return node->ToElement();
+	}
+	return 0;
+}
+
+TiXmlElement* TiXmlNode::FirstChildElement()
+{
+	TiXmlNode* node;
+
+	for (	node = FirstChild();
+			node;
+			node = node->NextSibling())
+	{
+		if (node->ToElement())
+			return node->ToElement();
+	}
+	return 0;
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement(const char * _value) const
+{
+	const TiXmlNode* node;
+
+	for (	node = FirstChild(_value);
+			node;
+			node = node->NextSibling(_value))
+	{
+		if (node->ToElement())
+			return node->ToElement();
+	}
+	return 0;
+}
+
+TiXmlElement* TiXmlNode::FirstChildElement(const char * _value)
+{
+	TiXmlNode* node;
+
+	for (	node = FirstChild(_value);
+			node;
+			node = node->NextSibling(_value))
+	{
+		if (node->ToElement())
+			return node->ToElement();
+	}
+	return 0;
+}
+
+const TiXmlElement* TiXmlNode::NextSiblingElement() const
+{
+	const TiXmlNode* node;
+
+	for (	node = NextSibling();
+	node;
+	node = node->NextSibling())
+	{
+		if (node->ToElement())
+			return node->ToElement();
+	}
+	return 0;
+}
+
+TiXmlElement* TiXmlNode::NextSiblingElement()
+{
+	TiXmlNode* node;
+
+	for (	node = NextSibling();
+	node;
+	node = node->NextSibling())
+	{
+		if (node->ToElement())
+			return node->ToElement();
+	}
+	return 0;
+}
+
+const TiXmlElement* TiXmlNode::NextSiblingElement(const char * _value) const
+{
+	const TiXmlNode* node;
+
+	for (	node = NextSibling(_value);
+	node;
+	node = node->NextSibling(_value))
+	{
+		if (node->ToElement())
+			return node->ToElement();
+	}
+	return 0;
+}
+
+TiXmlElement* TiXmlNode::NextSiblingElement(const char * _value)
+{
+	TiXmlNode* node;
+
+	for (	node = NextSibling(_value);
+	node;
+	node = node->NextSibling(_value))
+	{
+		if (node->ToElement())
+			return node->ToElement();
+	}
+	return 0;
+}
+
+
+const TiXmlDocument* TiXmlNode::GetDocument() const
+{
+	const TiXmlNode* node;
+
+	for (node = this; node; node = node->parent)
+	{
+		if (node->ToDocument())
+			return node->ToDocument();
+	}
+	return 0;
+}
+
+TiXmlDocument* TiXmlNode::GetDocument()
+{
+	TiXmlNode* node;
+
+	for (node = this; node; node = node->parent)
+	{
+		if (node->ToDocument())
+			return node->ToDocument();
+	}
+	return 0;
+}
+
+TiXmlElement::TiXmlElement (const char * _value)
+	: TiXmlNode(TiXmlNode::ELEMENT)
+{
+	firstChild = lastChild = 0;
+	value = _value;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlElement::TiXmlElement(const std::string& _value)
+	: TiXmlNode(TiXmlNode::ELEMENT)
+{
+	firstChild = lastChild = 0;
+	value = _value;
+}
+#endif
+
+
+TiXmlElement::TiXmlElement(const TiXmlElement& copy)
+	: TiXmlNode(TiXmlNode::ELEMENT)
+{
+	firstChild = lastChild = 0;
+	copy.CopyTo(this);
+}
+
+
+void TiXmlElement::operator=(const TiXmlElement& base)
+{
+	ClearThis();
+	base.CopyTo(this);
+}
+
+
+TiXmlElement::~TiXmlElement()
+{
+	ClearThis();
+}
+
+
+void TiXmlElement::ClearThis()
+{
+	Clear();
+	while (attributeSet.First())
+	{
+		TiXmlAttribute* node = attributeSet.First();
+		attributeSet.Remove(node);
+		delete node;
+	}
+}
+
+
+const char * TiXmlElement::Attribute(const char * name) const
+{
+	TIXML_STRING str(name);
+	const TiXmlAttribute* node = attributeSet.Find(str);
+
+	if (node)
+		return node->Value();
+
+	return 0;
+}
+
+
+const char * TiXmlElement::Attribute(const char * name, int* i) const
+{
+	const char * s = Attribute(name);
+	if (i)
+	{
+		if (s)
+			*i = (int)_atoi64(s);
+		else
+			*i = 0;
+	}
+	return s;
+}
+
+
+const char * TiXmlElement::Attribute(const char * name, double* d) const
+{
+	const char * s = Attribute(name);
+	if (d)
+	{
+		if (s)
+			*d = atof(s);
+		else
+			*d = 0;
+	}
+	return s;
+}
+
+
+int TiXmlElement::QueryIntAttribute(const char* name, int* ival) const
+{
+	TIXML_STRING str(name);
+	const TiXmlAttribute* node = attributeSet.Find(str);
+	if (!node)
+		return TIXML_NO_ATTRIBUTE;
+
+	return node->QueryIntValue(ival);
+}
+
+
+int TiXmlElement::QueryDoubleAttribute(const char* name, double* dval) const
+{
+	TIXML_STRING str(name);
+	const TiXmlAttribute* node = attributeSet.Find(str);
+	if (!node)
+		return TIXML_NO_ATTRIBUTE;
+
+	return node->QueryDoubleValue(dval);
+}
+
+
+void TiXmlElement::SetAttribute(const char * name, int val)
+{
+	char buf[64];
+	#if defined(TIXML_SNPRINTF)
+		TIXML_SNPRINTF(buf, sizeof(buf), "%d", val);
+	#else
+		sprintf(buf, "%d", val);
+	#endif
+	SetAttribute(name, buf);
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute(const std::string& name, int val)
+{
+	 std::ostringstream oss;
+	 oss << val;
+	 SetAttribute(name, oss.str());
+}
+#endif
+
+
+void TiXmlElement::SetDoubleAttribute(const char * name, double val)
+{
+	char buf[256];
+	#if defined(TIXML_SNPRINTF)
+		TIXML_SNPRINTF(buf, sizeof(buf), "%f", val);
+	#else
+		sprintf(buf, "%f", val);
+	#endif
+	SetAttribute(name, buf);
+}
+
+
+void TiXmlElement::SetAttribute(const char * cname, const char * cvalue)
+{
+	TIXML_STRING _name(cname);
+	TIXML_STRING _value(cvalue);
+
+	TiXmlAttribute* node = attributeSet.Find(_name);
+	if (node)
+	{
+		node->SetValue(cvalue);
+		return;
+	}
+
+	TiXmlAttribute* attrib = new TiXmlAttribute(cname, cvalue);
+	if (attrib)
+	{
+		attributeSet.Add(attrib);
+	}
+	else
+	{
+		TiXmlDocument* document = GetDocument();
+		if (document) document->SetError(TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN);
+	}
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute(const std::string& name, const std::string& _value)
+{
+	TiXmlAttribute* node = attributeSet.Find(name);
+	if (node)
+	{
+		node->SetValue(_value);
+		return;
+	}
+
+	TiXmlAttribute* attrib = new TiXmlAttribute(name, _value);
+	if (attrib)
+	{
+		attributeSet.Add(attrib);
+	}
+	else
+	{
+		TiXmlDocument* document = GetDocument();
+		if (document) document->SetError(TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN);
+	}
+}
+#endif
+
+
+void TiXmlElement::Print(FILE* cfile, int depth) const
+{
+	int i;
+	for (i=0; i<depth; i++)
+	{
+		fprintf(cfile, "		");
+	}
+
+	fprintf(cfile, "<%s", value.c_str());
+
+	const TiXmlAttribute* attrib;
+	for (attrib = attributeSet.First(); attrib; attrib = attrib->Next())
+	{
+		fprintf(cfile, " ");
+		attrib->Print(cfile, depth);
+	}
+
+	// There are 3 different formatting approaches:
+	// 1) An element without children is printed as a <foo /> node
+	// 2) An element with only a text child is printed as <foo> text </foo>
+	// 3) An element with children is printed on multiple lines.
+	TiXmlNode* node;
+	if (!firstChild)
+	{
+		fprintf(cfile, " />");
+	}
+	else if (firstChild == lastChild && firstChild->ToText())
+	{
+		fprintf(cfile, ">");
+		firstChild->Print(cfile, depth + 1);
+		fprintf(cfile, "</%s>", value.c_str());
+	}
+	else
+	{
+		fprintf(cfile, ">");
+
+		for (node = firstChild; node; node=node->NextSibling())
+		{
+			if (!node->ToText())
+			{
+				fprintf(cfile, "\n");
+			}
+			node->Print(cfile, depth+1);
+		}
+		fprintf(cfile, "\n");
+		for (i=0; i<depth; ++i)
+		fprintf(cfile, "		");
+		fprintf(cfile, "</%s>", value.c_str());
+	}
+}
+
+void TiXmlElement::StreamOut(TIXML_OSTREAM * stream) const
+{
+	(*stream) << "<" << value;
+
+	const TiXmlAttribute* attrib;
+	for (attrib = attributeSet.First(); attrib; attrib = attrib->Next())
+	{
+		(*stream) << " ";
+		attrib->StreamOut(stream);
+	}
+
+	// If this node has children, give it a closing tag. Else
+	// make it an empty tag.
+	TiXmlNode* node;
+	if (firstChild)
+	{
+		(*stream) << ">";
+
+		for (node = firstChild; node; node=node->NextSibling())
+		{
+			node->StreamOut(stream);
+		}
+		(*stream) << "</" << value << ">";
+	}
+	else
+	{
+		(*stream) << " />";
+	}
+}
+
+void TiXmlElement::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+	// Adding tabs to get the proper tree format
+	int oldDepth = depth;
+	StreamDepth(stream, depth);
+
+	// Element name
+	(*stream) << "<" << value;
+
+	// Attributes
+	const TiXmlAttribute* attrib;
+	for (attrib = attributeSet.First(); attrib; attrib = attrib->Next())
+	{
+		(*stream) << " ";
+		attrib->StreamOut(stream);
+	}
+
+	// There are 3 different formatting approaches:
+	// 1) An element without children is printed as a <foo /> node
+	// 2) An element with only a text child is printed as <foo> text </foo>
+	// 3) An element with children is printed on multiple lines.
+	TiXmlNode* node;
+	if (!firstChild)
+	{
+		(*stream) << " />" << TIXML_ENDL;
+	}
+	else if (firstChild == lastChild && firstChild->ToText())
+	{
+		(*stream) << ">";
+		firstChild->FormattedStreamOut(stream, depth + 1);
+		(*stream) << "</" << value << ">" << TIXML_ENDL;
+	}
+	else
+	{
+		(*stream) << ">" << TIXML_ENDL;
+
+		// Children
+		depth++;
+		for (node = firstChild; node; node=node->NextSibling())
+		{
+			node->FormattedStreamOut(stream, depth);
+		}
+		StreamDepth(stream, oldDepth);
+		(*stream) << "</" << value << ">" << TIXML_ENDL;
+	}
+}
+
+void TiXmlElement::CopyTo(TiXmlElement* target) const
+{
+	// superclass:
+	TiXmlNode::CopyTo(target);
+
+	// Element class:
+	// Clone the attributes, then clone the children.
+	const TiXmlAttribute* attribute = 0;
+	for (	attribute = attributeSet.First();
+	attribute;
+	attribute = attribute->Next())
+	{
+		target->SetAttribute(attribute->Name(), attribute->Value());
+	}
+
+	TiXmlNode* node = 0;
+	for (node = firstChild; node; node = node->NextSibling())
+	{
+		target->LinkEndChild(node->Clone());
+	}
+}
+
+
+TiXmlNode* TiXmlElement::Clone() const
+{
+	TiXmlElement* clone = new TiXmlElement(Value());
+	if (!clone)
+		return 0;
+
+	CopyTo(clone);
+	return clone;
+}
+
+
+const char* TiXmlElement::GetText() const
+{
+	const TiXmlNode* child = this->FirstChild();
+	if (child) {
+		const TiXmlText* childText = child->ToText();
+		if (childText) {
+			return childText->Value();
+		}
+	}
+	return 0;
+}
+
+
+TiXmlDocument::TiXmlDocument() : TiXmlNode(TiXmlNode::DOCUMENT)
+{
+	tabsize = 4;
+	useMicrosoftBOM = false;
+	ClearError();
+}
+
+TiXmlDocument::TiXmlDocument(const char * documentName) : TiXmlNode(TiXmlNode::DOCUMENT)
+{
+	tabsize = 4;
+	useMicrosoftBOM = false;
+	value = documentName;
+	ClearError();
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDocument::TiXmlDocument(const std::string& documentName) : TiXmlNode(TiXmlNode::DOCUMENT)
+{
+	tabsize = 4;
+	useMicrosoftBOM = false;
+		value = documentName;
+	ClearError();
+}
+#endif
+
+
+TiXmlDocument::TiXmlDocument(const TiXmlDocument& copy) : TiXmlNode(TiXmlNode::DOCUMENT)
+{
+	copy.CopyTo(this);
+}
+
+
+void TiXmlDocument::operator=(const TiXmlDocument& copy)
+{
+	Clear();
+	copy.CopyTo(this);
+}
+
+
+bool TiXmlDocument::LoadFile(TiXmlEncoding encoding)
+{
+	// See STL_STRING_BUG below.
+	StringToBuffer buf(value);
+
+	if (buf.buffer && LoadFile(buf.buffer, encoding))
+		return true;
+
+	return false;
+}
+
+
+bool TiXmlDocument::SaveFile() const
+{
+	// See STL_STRING_BUG below.
+	StringToBuffer buf(value);
+
+	if (buf.buffer && SaveFile(buf.buffer))
+		return true;
+
+	return false;
+}
+
+#ifdef TIXML_USE_STL
+std::string TiXmlDocument::GetAsString()
+{
+	std::stringstream out;
+	FormattedStreamOut(&out, 0);
+	return out.str();
+}
+#endif
+
+bool TiXmlDocument::GetAsCharBuffer(char* buffer, size_t bufferSize)
+{
+	#ifdef TIXML_USE_STL
+	std::string data = GetAsString();
+	#else
+	TIXML_OSTREAM data;
+	FormattedStreamOut(&data, 0);
+	#endif
+
+	if (bufferSize < data.length())
+	{
+		return false;
+	}
+	else
+	{
+		strcpy(buffer, data.c_str());
+		return true;
+	}
+}
+
+bool TiXmlDocument::LoadFile(const char* filename, TiXmlEncoding encoding)
+{
+	// There was a really terrifying little bug here. The code:
+	//		value = filename
+	// in the STL case, cause the assignment method of the std::string to
+	// be called. What is strange, is that the std::string had the same
+	// address as it's c_str() method, and so bad things happen. Looks
+	// like a bug in the Microsoft STL implementation.
+	// See STL_STRING_BUG above.
+	// Fixed with the StringToBuffer class.
+	value = filename;
+
+	// reading in binary mode so that tinyxml can normalize the EOL
+	FILE* file = fopen(value.c_str (), "rb");
+
+	if (file)
+	{
+		bool result = LoadFile(file, encoding);
+		fclose(file);
+		return result;
+	}
+	else
+	{
+		SetError(TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return false;
+	}
+}
+
+bool TiXmlDocument::LoadFile(FILE* file, TiXmlEncoding encoding)
+{
+	if (!file)
+	{
+		SetError(TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return false;
+	}
+
+	// Delete the existing data:
+	Clear();
+	location.Clear();
+
+	// Get the file size, so we can pre-allocate the string. HUGE speed impact.
+	long length = 0;
+	fseek(file, 0, SEEK_END);
+	length = ftell(file);
+	fseek(file, 0, SEEK_SET);
+
+	// Strange case, but good to handle up front.
+	if (length == 0)
+	{
+		SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return false;
+	}
+
+	// If we have a file, assume it is all one big XML file, and read it in.
+	// The document parser may decide the document ends sooner than the entire file, however.
+	TIXML_STRING data;
+	data.reserve(length);
+
+	// Subtle bug here. TinyXml did use fgets. But from the XML spec:
+	// 2.11 End-of-Line Handling
+	// <snip>
+	// <quote>
+	// ...the XML processor MUST behave as if it normalized all line breaks in external
+	// parsed entities (including the document entity) on input, before parsing, by translating
+	// both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
+	// a single #xA character.
+	// </quote>
+	//
+	// It is not clear fgets does that, and certainly isn't clear it works cross platform.
+	// Generally, you expect fgets to translate from the convention of the OS to the c/unix
+	// convention, and not work generally.
+
+	/*
+	while (fgets(buf, sizeof(buf), file))
+	{
+		data += buf;
+	}
+	*/
+
+	char* buf = new char[ length+1 ];
+	buf[0] = 0;
+
+	if (fread(buf, length, 1, file) != 1) {
+		delete [] buf;
+		SetError(TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return false;
+	}
+
+	const char* lastPos = buf;
+	const char* p = buf;
+
+	buf[length] = 0;
+	while (*p) {
+		assert(p < (buf+length));
+		if (*p == 0xa) {
+			// Newline character. No special rules for this. Append all the characters
+			// since the last string, and include the newline.
+			data.append(lastPos, (p-lastPos+1));	// append, include the newline
+			++p;									// move past the newline
+			lastPos = p;							// and point to the new buffer (may be 0)
+			assert(p <= (buf+length));
+		}
+		else if (*p == 0xd) {
+			// Carriage return. Append what we have so far, then
+			// handle moving forward in the buffer.
+			if ((p-lastPos) > 0) {
+				data.append(lastPos, p-lastPos);	// do not add the CR
+			}
+			data += (char)0xa;						// a proper newline
+
+			if (*(p+1) == 0xa) {
+				// Carriage return - new line sequence
+				p += 2;
+				lastPos = p;
+				assert(p <= (buf+length));
+			}
+			else {
+				// it was followed by something else...that is presumably characters again.
+				++p;
+				lastPos = p;
+				assert(p <= (buf+length));
+			}
+		}
+		else {
+			++p;
+		}
+	}
+	// Handle any left over characters.
+	if (p-lastPos) {
+		data.append(lastPos, p-lastPos);
+	}
+	delete [] buf;
+	buf = 0;
+
+	Parse(data.c_str(), 0, encoding);
+
+	if ( Error())
+				return false;
+		else
+		return true;
+}
+
+
+bool TiXmlDocument::SaveFile(const char * filename) const
+{
+	// The old c stuff lives on...
+	FILE* fp = fopen(filename, "w");
+	if (fp)
+	{
+		bool result = SaveFile(fp);
+		fclose(fp);
+		return result;
+	}
+	return false;
+}
+
+
+bool TiXmlDocument::SaveFile(FILE* fp) const
+{
+	if (useMicrosoftBOM)
+	{
+		const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+		const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+		const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+		fputc(TIXML_UTF_LEAD_0, fp);
+		fputc(TIXML_UTF_LEAD_1, fp);
+		fputc(TIXML_UTF_LEAD_2, fp);
+	}
+	Print(fp, 0);
+	return true;
+}
+
+
+void TiXmlDocument::CopyTo(TiXmlDocument* target) const
+{
+	TiXmlNode::CopyTo(target);
+
+	target->error = error;
+	target->errorDesc = errorDesc.c_str ();
+
+	TiXmlNode* node = 0;
+	for (node = firstChild; node; node = node->NextSibling())
+	{
+		target->LinkEndChild(node->Clone());
+	}
+}
+
+
+TiXmlNode* TiXmlDocument::Clone() const
+{
+	TiXmlDocument* clone = new TiXmlDocument();
+	if (!clone)
+		return 0;
+
+	CopyTo(clone);
+	return clone;
+}
+
+
+void TiXmlDocument::Print(FILE* cfile, int depth) const
+{
+	const TiXmlNode* node;
+	for (node=FirstChild(); node; node=node->NextSibling())
+	{
+		node->Print(cfile, depth);
+		fprintf(cfile, "\n");
+	}
+}
+
+void TiXmlDocument::StreamOut(TIXML_OSTREAM * out) const
+{
+	const TiXmlNode* node;
+	for (node=FirstChild(); node; node=node->NextSibling())
+	{
+		node->StreamOut(out);
+
+		// Special rule for streams: stop after the root element.
+		// The stream in code will only read one element, so don't
+		// write more than one.
+		if (node->ToElement())
+			break;
+	}
+}
+
+void TiXmlDocument::FormattedStreamOut(TIXML_OSTREAM * out, int depth) const
+{
+	const TiXmlNode* node;
+	for (node=FirstChild(); node; node=node->NextSibling())
+	{
+		node->FormattedStreamOut(out, depth);
+
+		// Special rule for streams: stop after the root element.
+		// The stream in code will only read one element, so don't
+		// write more than one.
+		if (node->ToElement())
+			break;
+	}
+}
+
+const TiXmlAttribute* TiXmlAttribute::Next() const
+{
+	// We are using knowledge of the sentinel. The sentinel
+	// have a value or name.
+	if (next->value.empty() && next->name.empty())
+		return 0;
+	return next;
+}
+
+TiXmlAttribute* TiXmlAttribute::Next()
+{
+	// We are using knowledge of the sentinel. The sentinel
+	// have a value or name.
+	if (next->value.empty() && next->name.empty())
+		return 0;
+	return next;
+}
+
+const TiXmlAttribute* TiXmlAttribute::Previous() const
+{
+	// We are using knowledge of the sentinel. The sentinel
+	// have a value or name.
+	if (prev->value.empty() && prev->name.empty())
+		return 0;
+	return prev;
+}
+
+TiXmlAttribute* TiXmlAttribute::Previous()
+{
+	// We are using knowledge of the sentinel. The sentinel
+	// have a value or name.
+	if (prev->value.empty() && prev->name.empty())
+		return 0;
+	return prev;
+}
+
+void TiXmlAttribute::Print(FILE* cfile, int /*depth*/) const
+{
+	TIXML_STRING n, v;
+
+	PutString(name, &n);
+	PutString(value, &v);
+
+	if (value.find ('\"') == TIXML_STRING::npos)
+		fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str());
+	else
+		fprintf (cfile, "%s='%s'", n.c_str(), v.c_str());
+}
+
+
+void TiXmlAttribute::StreamOut(TIXML_OSTREAM * stream) const
+{
+	if (value.find('\"') != TIXML_STRING::npos)
+	{
+		PutString(name, stream);
+		(*stream) << "=" << "'";
+		PutString(value, stream);
+		(*stream) << "'";
+	}
+	else
+	{
+		PutString(name, stream);
+		(*stream) << "=" << "\"";
+		PutString(value, stream);
+		(*stream) << "\"";
+	}
+}
+
+int TiXmlAttribute::QueryIntValue(int* ival) const
+{
+	if (sscanf(value.c_str(), "%d", ival) == 1)
+		return TIXML_SUCCESS;
+	return TIXML_WRONG_TYPE;
+}
+
+int TiXmlAttribute::QueryDoubleValue(double* dval) const
+{
+	if (sscanf(value.c_str(), "%lf", dval) == 1)
+		return TIXML_SUCCESS;
+	return TIXML_WRONG_TYPE;
+}
+
+void TiXmlAttribute::SetIntValue(int _value)
+{
+	char buf [64];
+	#if defined(TIXML_SNPRINTF)
+		TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value);
+	#else
+		sprintf (buf, "%d", _value);
+	#endif
+	SetValue (buf);
+}
+
+void TiXmlAttribute::SetDoubleValue(double _value)
+{
+	char buf [256];
+	#if defined(TIXML_SNPRINTF)
+		TIXML_SNPRINTF(buf, sizeof(buf), "%lf", _value);
+	#else
+		sprintf (buf, "%lf", _value);
+	#endif
+	SetValue (buf);
+}
+
+int TiXmlAttribute::IntValue() const
+{
+	return (int)_atoi64(value.c_str ());
+}
+
+double	TiXmlAttribute::DoubleValue() const
+{
+	return atof (value.c_str ());
+}
+
+
+TiXmlComment::TiXmlComment(const TiXmlComment& copy) : TiXmlNode(TiXmlNode::COMMENT)
+{
+	copy.CopyTo(this);
+}
+
+
+void TiXmlComment::operator=(const TiXmlComment& base)
+{
+	Clear();
+	base.CopyTo(this);
+}
+
+
+void TiXmlComment::Print(FILE* cfile, int depth) const
+{
+	for (int i=0; i<depth; i++)
+	{
+		fputs("		", cfile);
+	}
+	fprintf(cfile, "<!--%s-->", value.c_str());
+}
+
+void TiXmlComment::StreamOut(TIXML_OSTREAM * stream) const
+{
+	(*stream) << "<!--";
+	//PutString(value, stream);
+	(*stream) << value;
+	(*stream) << "-->";
+}
+
+void TiXmlComment::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+	StreamDepth(stream, depth);
+
+	StreamOut(stream);
+
+	(*stream) << TIXML_ENDL;
+}
+
+void TiXmlComment::CopyTo(TiXmlComment* target) const
+{
+	TiXmlNode::CopyTo(target);
+}
+
+
+TiXmlNode* TiXmlComment::Clone() const
+{
+	TiXmlComment* clone = new TiXmlComment();
+
+	if (!clone)
+		return 0;
+
+	CopyTo(clone);
+	return clone;
+}
+
+
+void TiXmlText::Print(FILE* cfile, int depth) const
+{
+	if (cdata)
+	{
+		int i;
+		fprintf(cfile, "\n");
+		for (i=0; i<depth; i++) {
+			fprintf(cfile, "		");
+		}
+		fprintf(cfile, "<![CDATA[");
+		fprintf(cfile, "%s", value.c_str());	// unformatted output
+		fprintf(cfile, "]]>\n");
+	}
+	else
+	{
+		TIXML_STRING buffer;
+		PutString(value, &buffer);
+		fprintf(cfile, "%s", buffer.c_str());
+	}
+}
+
+
+void TiXmlText::StreamOut(TIXML_OSTREAM * stream) const
+{
+	if (cdata)
+	{
+		(*stream) << "<![CDATA[" << value << "]]>";
+	}
+	else
+	{
+		PutString(value, stream);
+	}
+}
+
+void TiXmlText::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+	if (cdata)
+	{
+		(*stream) << TIXML_ENDL;
+		StreamDepth(stream, depth);
+		(*stream) << "<![CDATA[" << value << "]]>" << TIXML_ENDL;
+	}
+	else
+	{
+		PutString(value, stream);
+	}
+}
+
+void TiXmlText::CopyTo(TiXmlText* target) const
+{
+	TiXmlNode::CopyTo(target);
+	target->cdata = cdata;
+}
+
+
+TiXmlNode* TiXmlText::Clone() const
+{
+	TiXmlText* clone = 0;
+	clone = new TiXmlText("");
+
+	if (!clone)
+		return 0;
+
+	CopyTo(clone);
+	return clone;
+}
+
+
+TiXmlDeclaration::TiXmlDeclaration(const char * _version,
+									const char * _encoding,
+									const char * _standalone)
+	: TiXmlNode(TiXmlNode::DECLARATION)
+{
+	version = _version;
+	encoding = _encoding;
+	standalone = _standalone;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDeclaration::TiXmlDeclaration(	const std::string& _version,
+									const std::string& _encoding,
+									const std::string& _standalone)
+	: TiXmlNode(TiXmlNode::DECLARATION)
+{
+	version = _version;
+	encoding = _encoding;
+	standalone = _standalone;
+}
+#endif
+
+
+TiXmlDeclaration::TiXmlDeclaration(const TiXmlDeclaration& copy)
+	: TiXmlNode(TiXmlNode::DECLARATION)
+{
+	copy.CopyTo(this);
+}
+
+
+void TiXmlDeclaration::operator=(const TiXmlDeclaration& copy)
+{
+	Clear();
+	copy.CopyTo(this);
+}
+
+
+void TiXmlDeclaration::Print(FILE* cfile, int /*depth*/) const
+{
+	fprintf (cfile, "<?xml ");
+
+	if (!version.empty())
+		fprintf (cfile, "version=\"%s\" ", version.c_str ());
+	if (!encoding.empty())
+		fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
+	if (!standalone.empty())
+		fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
+	fprintf (cfile, "?>");
+}
+
+void TiXmlDeclaration::StreamOut(TIXML_OSTREAM * stream) const
+{
+	(*stream) << "<?xml ";
+
+	if (!version.empty())
+	{
+		(*stream) << "version=\"";
+		PutString(version, stream);
+		(*stream) << "\" ";
+	}
+	if (!encoding.empty())
+	{
+		(*stream) << "encoding=\"";
+		PutString(encoding, stream);
+		(*stream) << "\" ";
+	}
+	if (!standalone.empty())
+	{
+		(*stream) << "standalone=\"";
+		PutString(standalone, stream);
+		(*stream) << "\" ";
+	}
+	(*stream) << "?>";
+}
+
+void TiXmlDeclaration::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+	StreamDepth(stream, depth);
+	StreamOut(stream);
+	(*stream) << TIXML_ENDL;
+}
+
+void TiXmlDeclaration::CopyTo(TiXmlDeclaration* target) const
+{
+	TiXmlNode::CopyTo(target);
+
+	target->version = version;
+	target->encoding = encoding;
+	target->standalone = standalone;
+}
+
+
+TiXmlNode* TiXmlDeclaration::Clone() const
+{
+	TiXmlDeclaration* clone = new TiXmlDeclaration();
+
+	if (!clone)
+		return 0;
+
+	CopyTo(clone);
+	return clone;
+}
+
+
+void TiXmlUnknown::Print(FILE* cfile, int depth) const
+{
+	for (int i=0; i<depth; i++)
+		fprintf(cfile, "		");
+	fprintf(cfile, "<%s>", value.c_str());
+}
+
+
+void TiXmlUnknown::StreamOut(TIXML_OSTREAM * stream) const
+{
+	(*stream) << "<" << value << ">";		// Don't use entities here! It is unknown.
+}
+
+void TiXmlUnknown::FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const
+{
+	StreamDepth(stream, depth);
+	(*stream) << "<" << value << ">" << TIXML_ENDL;		// Don't use entities here! It is unknown.
+}
+
+void TiXmlUnknown::CopyTo(TiXmlUnknown* target) const
+{
+	TiXmlNode::CopyTo(target);
+}
+
+
+TiXmlNode* TiXmlUnknown::Clone() const
+{
+	TiXmlUnknown* clone = new TiXmlUnknown();
+
+	if (!clone)
+		return 0;
+
+	CopyTo(clone);
+	return clone;
+}
+
+
+TiXmlAttributeSet::TiXmlAttributeSet()
+{
+	sentinel.next = &sentinel;
+	sentinel.prev = &sentinel;
+}
+
+
+TiXmlAttributeSet::~TiXmlAttributeSet()
+{
+	assert(sentinel.next == &sentinel);
+	assert(sentinel.prev == &sentinel);
+}
+
+
+void TiXmlAttributeSet::Add(TiXmlAttribute* addMe)
+{
+	assert(!Find(TIXML_STRING(addMe->Name())));	// Shouldn't be multiply adding to the set.
+
+	addMe->next = &sentinel;
+	addMe->prev = sentinel.prev;
+
+	sentinel.prev->next = addMe;
+	sentinel.prev			= addMe;
+}
+
+void TiXmlAttributeSet::Remove(TiXmlAttribute* removeMe)
+{
+	TiXmlAttribute* node;
+
+	for (node = sentinel.next; node != &sentinel; node = node->next)
+	{
+		if (node == removeMe)
+		{
+			node->prev->next = node->next;
+			node->next->prev = node->prev;
+			node->next = 0;
+			node->prev = 0;
+			return;
+		}
+	}
+	assert(0);		// we tried to remove a non-linked attribute.
+}
+
+const TiXmlAttribute* TiXmlAttributeSet::Find(const TIXML_STRING& name) const
+{
+	const TiXmlAttribute* node;
+
+	for (node = sentinel.next; node != &sentinel; node = node->next)
+	{
+		if (node->name == name)
+			return node;
+	}
+	return 0;
+}
+
+TiXmlAttribute*	TiXmlAttributeSet::Find(const TIXML_STRING& name)
+{
+	TiXmlAttribute* node;
+
+	for (node = sentinel.next; node != &sentinel; node = node->next)
+	{
+		if (node->name == name)
+			return node;
+	}
+	return 0;
+}
+
+#ifdef TIXML_USE_STL
+TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base)
+{
+	TIXML_STRING tag;
+	tag.reserve(8 * 1000);
+	base.StreamIn(&in, &tag);
+
+	base.Parse(tag.c_str(), 0, TIXML_DEFAULT_ENCODING);
+	return in;
+}
+#endif
+
+
+TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base)
+{
+	base.StreamOut (& out);
+	return out;
+}
+
+
+#ifdef TIXML_USE_STL
+std::string & operator<< (std::string& out, const TiXmlNode& base)
+{
+	 std::ostringstream os_stream(std::ostringstream::out);
+	 base.StreamOut(&os_stream);
+
+	 out.append(os_stream.str());
+	 return out;
+}
+#endif
+
+
+TiXmlHandle TiXmlHandle::FirstChild() const
+{
+	if (node)
+	{
+		TiXmlNode* child = node->FirstChild();
+		if (child)
+			return TiXmlHandle(child);
+	}
+	return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChild(const char * value) const
+{
+	if (node)
+	{
+		TiXmlNode* child = node->FirstChild(value);
+		if (child)
+			return TiXmlHandle(child);
+	}
+	return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement() const
+{
+	if (node)
+	{
+		TiXmlElement* child = node->FirstChildElement();
+		if (child)
+			return TiXmlHandle(child);
+	}
+	return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement(const char * value) const
+{
+	if (node)
+	{
+		TiXmlElement* child = node->FirstChildElement(value);
+		if (child)
+			return TiXmlHandle(child);
+	}
+	return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::Child(int count) const
+{
+	if (node)
+	{
+		int i;
+		TiXmlNode* child = node->FirstChild();
+		for (	i=0;
+				child && i<count;
+				child = child->NextSibling(), ++i)
+		{
+			// nothing
+		}
+		if (child)
+			return TiXmlHandle(child);
+	}
+	return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::Child(const char* value, int count) const
+{
+	if (node)
+	{
+		int i;
+		TiXmlNode* child = node->FirstChild(value);
+		for (	i=0;
+				child && i<count;
+				child = child->NextSibling(value), ++i)
+		{
+			// nothing
+		}
+		if (child)
+			return TiXmlHandle(child);
+	}
+	return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement(int count) const
+{
+	if (node)
+	{
+		int i;
+		TiXmlElement* child = node->FirstChildElement();
+		for (	i=0;
+				child && i<count;
+				child = child->NextSiblingElement(), ++i)
+		{
+			// nothing
+		}
+		if (child)
+			return TiXmlHandle(child);
+	}
+	return TiXmlHandle(0);
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement(const char* value, int count) const
+{
+	if (node)
+	{
+		int i;
+		TiXmlElement* child = node->FirstChildElement(value);
+		for (	i=0;
+				child && i<count;
+				child = child->NextSiblingElement(value), ++i)
+		{
+			// nothing
+		}
+		if (child)
+			return TiXmlHandle(child);
+	}
+	return TiXmlHandle(0);
+}
diff --git a/plugins/UserInfoEx/src/ex_import/tinyxml.h b/plugins/UserInfoEx/src/ex_import/tinyxml.h
new file mode 100644
index 0000000000..0bc947422f
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinyxml.h
@@ -0,0 +1,1599 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier)copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. 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.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinyxml.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+
+#ifndef TINYXML_INCLUDED
+#define TINYXML_INCLUDED
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4530)
+#pragma warning(disable : 4786)
+#endif
+
+#ifndef USE_MMGR
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#endif
+
+// Help out windows:
+#if defined(_DEBUG) && !defined(DEBUG)
+#define DEBUG
+#endif
+
+#ifdef TIXML_USE_TICPP
+	#ifndef TIXML_USE_STL
+		#define TIXML_USE_STL
+	#endif
+#endif
+
+#ifdef TIXML_USE_STL
+	#include <string>
+	 #include <iostream>
+	 #include <sstream>
+	#define TIXML_STRING	std::string
+	#define TIXML_ISTREAM	std::istream
+	#define TIXML_OSTREAM	std::ostream
+	#define TIXML_ENDL		std::endl
+#else
+	#include "tinystr.h"
+	#define TIXML_STRING	TiXmlString
+	#define TIXML_OSTREAM	TiXmlOutStream
+	#define TIXML_ENDL		"\n"
+#endif
+
+// Deprecated library function hell. Compilers want to use the
+// new safe versions. This probably doesn't fully address the problem,
+// but it gets closer. There are too many compilers for me to fully
+// test. If you get compilation troubles, undefine TIXML_SAFE
+
+#define TIXML_SAFE		// TinyXml isn't fully buffer overrun protected, safe code. This is work in progress.
+#ifdef TIXML_SAFE
+	#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+		// Microsoft visual studio, version 2005 and higher.
+		#define TIXML_SNPRINTF _snprintf_s
+		#define TIXML_SNSCANF	_snscanf_s
+	#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+		// Microsoft visual studio, version 6 and higher.
+		//#pragma message("Using _sn* functions.")
+		#define TIXML_SNPRINTF _snprintf
+		#define TIXML_SNSCANF	_snscanf
+	#elif defined(__GNUC__) && (__GNUC__ >= 3)
+		// GCC version 3 and higher.s
+		//#warning("Using sn* functions.")
+		#define TIXML_SNPRINTF snprintf
+		#define TIXML_SNSCANF	snscanf
+	#endif
+#endif
+
+class TiXmlDocument;
+class TiXmlElement;
+class TiXmlComment;
+class TiXmlUnknown;
+class TiXmlAttribute;
+class TiXmlText;
+class TiXmlDeclaration;
+class TiXmlParsingData;
+
+const int TIXML_MAJOR_VERSION = 2;
+const int TIXML_MINOR_VERSION = 4;
+const int TIXML_PATCH_VERSION = 3;
+
+/*	Internal structure for tracking location of items
+	in the XML file.
+*/
+struct TiXmlCursor
+{
+	TiXmlCursor()		{ Clear(); }
+	void Clear()		{ row = col = -1; }
+
+	int row;	// 0 based.
+	int col;	// 0 based.
+};
+
+
+// Only used by Attribute::Query functions
+enum
+{
+	TIXML_SUCCESS,
+	TIXML_NO_ATTRIBUTE,
+	TIXML_WRONG_TYPE
+};
+
+
+// Used by the parsing routines.
+enum TiXmlEncoding
+{
+	TIXML_ENCODING_UNKNOWN,
+	TIXML_ENCODING_UTF8,
+	TIXML_ENCODING_LEGACY
+};
+
+const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
+
+/** TiXmlBase is a base class for every class in TinyXml.
+	It does little except to establish that TinyXml classes
+	can be printed and provide some utility functions.
+
+	In XML, the document and elements can contain
+	other elements and other types of nodes.
+
+	@verbatim
+	A Document can contain:	Element	(container or leaf)
+							Comment (leaf)
+							Unknown (leaf)
+							Declaration(leaf)
+
+	An Element can contain:	Element (container or leaf)
+							Text	(leaf)
+							Attributes (not on tree)
+							Comment (leaf)
+							Unknown (leaf)
+
+	A Decleration contains: Attributes (not on tree)
+	@endverbatim
+*/
+#ifdef TIXML_USE_TICPP
+#include "ticpprc.h"
+class TiXmlBase : public TiCppRC
+#else
+class TiXmlBase
+#endif
+{
+	friend class TiXmlNode;
+	friend class TiXmlElement;
+	friend class TiXmlDocument;
+
+public:
+	TiXmlBase()	:	userData(0) {}
+	virtual ~TiXmlBase()					{}
+
+	/**	All TinyXml classes can print themselves to a filestream.
+		This is a formatted print, and will insert tabs and newlines.
+
+		(For an unformatted stream, use the << operator.)
+	*/
+	virtual void Print(FILE* cfile, int depth) const = 0;
+
+	/**	The world does not agree on whether white space should be kept or
+		not. In order to make everyone happy, these global, static functions
+		are provided to set whether or not TinyXml will condense all white space
+		into a single space or not. The default is to condense. Note changing this
+		values is not thread safe.
+	*/
+	static void SetCondenseWhiteSpace(bool condense)		{ condenseWhiteSpace = condense; }
+
+	/// Return the current white space setting.
+	static bool IsWhiteSpaceCondensed()						{ return condenseWhiteSpace; }
+
+	/** Return the position, in the original source file, of this node or attribute.
+		The row and column are 1-based. (That is the first row and first column is
+		1,1). If the returns values are 0 or less, then the parser does not have
+		a row and column value.
+
+		Generally, the row and column value will be set when the TiXmlDocument::Load(void),
+		TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
+		when the DOM was created from operator>>.
+
+		The values reflect the initial load. Once the DOM is modified programmatically
+		(by adding or changing nodes and attributes) the new values will NOT update to
+		reflect changes in the document.
+
+		There is a minor performance cost to computing the row and column. Computation
+		can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
+
+		@sa TiXmlDocument::SetTabSize()
+	*/
+	int Row() const			{ return location.row + 1; }
+	int Column() const		{ return location.col + 1; }	///< See Row()
+
+	void	_SetUserData(void* user)			{ userData = user; }
+	void* _GetUserData()						{ return userData; }
+
+	// Table that returs, for a given lead byte, the total number of bytes
+	// in the UTF-8 sequence.
+	static const int utf8ByteTable[256];
+
+	virtual const char* Parse(	const char* p,
+								TiXmlParsingData* data,
+								TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */) = 0;
+
+	enum
+	{
+		TIXML_NO_ERROR = 0,
+		TIXML_ERROR,
+		TIXML_ERROR_OPENING_FILE,
+		TIXML_ERROR_OUT_OF_MEMORY,
+		TIXML_ERROR_PARSING_ELEMENT,
+		TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
+		TIXML_ERROR_READING_ELEMENT_VALUE,
+		TIXML_ERROR_READING_ATTRIBUTES,
+		TIXML_ERROR_PARSING_EMPTY,
+		TIXML_ERROR_READING_END_TAG,
+		TIXML_ERROR_PARSING_UNKNOWN,
+		TIXML_ERROR_PARSING_COMMENT,
+		TIXML_ERROR_PARSING_DECLARATION,
+		TIXML_ERROR_DOCUMENT_EMPTY,
+		TIXML_ERROR_EMBEDDED_NULL,
+		TIXML_ERROR_PARSING_CDATA,
+		TIXML_ERROR_DOCUMENT_TOP_ONLY,
+
+		TIXML_ERROR_STRING_COUNT
+	};
+
+protected:
+
+	void StreamDepth(TIXML_OSTREAM* stream, int depth) const;
+
+	// See STL_STRING_BUG
+	// Utility class to overcome a bug.
+	class StringToBuffer
+	{
+		public:
+		StringToBuffer(const TIXML_STRING& str);
+		~StringToBuffer();
+		char* buffer;
+	};
+
+	static const char*	SkipWhiteSpace(const char*, TiXmlEncoding encoding);
+	inline static bool	IsWhiteSpace(char c)
+	{
+		return (isspace((unsigned char) c) || c == '\n' || c == '\r');
+	}
+	inline static bool	IsWhiteSpace(int c)
+	{
+		if (c < 256)
+			return IsWhiteSpace((char) c);
+		return false;	// Again, only truly correct for English/Latin...but usually works.
+	}
+
+	virtual void StreamOut (TIXML_OSTREAM *) const = 0;
+	virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const = 0;
+
+	#ifdef TIXML_USE_STL
+			static bool	StreamWhiteSpace(TIXML_ISTREAM * in, TIXML_STRING * tag);
+			static bool StreamTo(TIXML_ISTREAM * in, int character, TIXML_STRING * tag);
+	#endif
+
+	/*	Reads an XML name into the string provided. Returns
+		a pointer just past the last character of the name,
+		or 0 if the function has an error.
+	*/
+	static const char* ReadName(const char* p, TIXML_STRING* name, TiXmlEncoding encoding);
+
+	/*	Reads text. Returns a pointer past the given end tag.
+		Wickedly complex options, but it keeps the (sensitive) code in one place.
+	*/
+	static const char* ReadText(	const char* in,				// where to start
+									TIXML_STRING* text,			// the string read
+									bool ignoreWhiteSpace,		// whether to keep the white space
+									const char* endTag,			// what ends this text
+									bool ignoreCase,			// whether to ignore case in the end tag
+									TiXmlEncoding encoding);	// the current encoding
+
+	// If an entity has been found, transform it into a character.
+	static const char* GetEntity(const char* in, char* value, int* length, TiXmlEncoding encoding);
+
+	// Get a character, while interpreting entities.
+	// The length can be from 0 to 4 bytes.
+	inline static const char* GetChar(const char* p, char* _value, int* length, TiXmlEncoding encoding)
+	{
+		assert(p);
+		if (encoding == TIXML_ENCODING_UTF8)
+		{
+			*length = utf8ByteTable[ *((unsigned char*)p) ];
+			assert(*length >= 0 && *length < 5);
+		}
+		else
+		{
+			*length = 1;
+		}
+
+		if (*length == 1)
+		{
+			if (*p == '&')
+				return GetEntity(p, _value, length, encoding);
+			*_value = *p;
+			return p+1;
+		}
+		else if (*length)
+		{
+			//strncpy(_value, p, *length);	// lots of compilers don't like this function (unsafe),
+												// and the null terminator isn't needed
+			for (int i=0; p[i] && i<*length; ++i) {
+				_value[i] = p[i];
+			}
+			return p + (*length);
+		}
+		else
+		{
+			// Not valid text.
+			return 0;
+		}
+	}
+
+	// Puts a string to a stream, expanding entities as it goes.
+	// Note this should not contian the '<', '>', etc, or they will be transformed into entities!
+	static void PutString(const TIXML_STRING& str, TIXML_OSTREAM* out);
+
+	static void PutString(const TIXML_STRING& str, TIXML_STRING* out);
+
+	// Return true if the next characters in the stream are any of the endTag sequences.
+	// Ignore case only works for english, and should only be relied on when comparing
+	// to English words: StringEqual(p, "version", true) is fine.
+	static bool StringEqual(	const char* p,
+								const char* endTag,
+								bool ignoreCase,
+								TiXmlEncoding encoding);
+
+	static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
+
+	TiXmlCursor location;
+
+		/// Field containing a generic user pointer
+	void*			userData;
+
+	// None of these methods are reliable for any language except English.
+	// Good for approximation, not great for accuracy.
+	static int IsAlpha(unsigned char anyByte, TiXmlEncoding encoding);
+	static int IsAlphaNum(unsigned char anyByte, TiXmlEncoding encoding);
+	inline static int ToLower(int v, TiXmlEncoding encoding)
+	{
+		if (encoding == TIXML_ENCODING_UTF8)
+		{
+			if (v < 128) return tolower(v);
+			return v;
+		}
+		else
+		{
+			return tolower(v);
+		}
+	}
+	static void ConvertUTF32ToUTF8(unsigned long input, char* output, int* length);
+
+private:
+	TiXmlBase(const TiXmlBase&);				// not implemented.
+	void operator=(const TiXmlBase& base);	// not allowed.
+
+	struct Entity
+	{
+		const char*		 str;
+		unsigned int	strLength;
+		char				chr;
+	};
+	enum
+	{
+		NUM_ENTITY = 5,
+		MAX_ENTITY_LENGTH = 6
+
+	};
+	static Entity entity[ NUM_ENTITY ];
+	static bool condenseWhiteSpace;
+};
+
+
+/** The parent class for everything in the Document Object Model.
+	(Except for attributes).
+	Nodes have siblings, a parent, and children. A node can be
+	in a document, or stand on its own. The type of a TiXmlNode
+	can be queried, and it can be cast to its more defined type.
+*/
+class TiXmlNode : public TiXmlBase
+{
+	friend class TiXmlDocument;
+	friend class TiXmlElement;
+
+public:
+	#ifdef TIXML_USE_STL
+
+			/** An input stream operator, for every class. Tolerant of newlines and
+				formatting, but doesn't expect them.
+			*/
+			friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
+
+			/** An output stream operator, for every class. Note that this outputs
+				without any newlines or formatting, as opposed to Print(), which
+				includes tabs and new lines.
+
+				The operator<< and operator>> are not completely symmetric. Writing
+				a node to a stream is very well defined. You'll get a nice stream
+				of output, without any extra whitespace or newlines.
+
+				But reading is not as well defined. (As it always is.) If you create
+				a TiXmlElement (for example) and read that from an input stream,
+				the text needs to define an element or junk will result. This is
+				true of all input streams, but it's worth keeping in mind.
+
+				A TiXmlDocument will read nodes until it reads a root element, and
+			all the children of that root element.
+			*/
+			friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
+
+		/// Appends the XML node or attribute to a std::string.
+		friend std::string& operator<< (std::string& out, const TiXmlNode& base);
+
+	#else
+			// Used internally, not part of the public API.
+			friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base);
+	#endif
+
+	/** The types of XML nodes supported by TinyXml. (All the
+			unsupported types are picked up by UNKNOWN.)
+	*/
+	enum NodeType
+	{
+		DOCUMENT,
+		ELEMENT,
+		COMMENT,
+		UNKNOWN,
+		TEXT,
+		DECLARATION,
+		TYPECOUNT
+	};
+
+	virtual ~TiXmlNode();
+
+	/** The meaning of 'value' changes for the specific type of
+		TiXmlNode.
+		@verbatim
+		Document:	filename of the xml file
+		Element:	name of the element
+		Comment:	the comment text
+		Unknown:	the tag contents
+		Text:		the text string
+		@endverbatim
+
+		The subclasses will wrap this function.
+	*/
+	const char *Value() const { return value.c_str (); }
+
+		#ifdef TIXML_USE_STL
+	/** Return Value() as a std::string. If you only use STL,
+			this is more efficient than calling Value().
+		Only available in STL mode.
+	*/
+	const std::string& ValueStr() const { return value; }
+	#endif
+
+	/** Changes the value of the node. Defined as:
+		@verbatim
+		Document:	filename of the xml file
+		Element:	name of the element
+		Comment:	the comment text
+		Unknown:	the tag contents
+		Text:		the text string
+		@endverbatim
+	*/
+	void SetValue(const char * _value) { value = _value;}
+
+		#ifdef TIXML_USE_STL
+	/// STL std::string form.
+	void SetValue(const std::string& _value)	{ value = _value; }
+	#endif
+
+	/// Delete all the children of this node. Does not affect 'this'.
+	void Clear();
+
+	/// One step up the DOM.
+	TiXmlNode* Parent()							{ return parent; }
+	const TiXmlNode* Parent() const				{ return parent; }
+
+	const TiXmlNode* FirstChild()	const	{ return firstChild; }		///< The first child of this node. Will be null if there are no children.
+	TiXmlNode* FirstChild()					{ return firstChild; }
+	const TiXmlNode* FirstChild(const char * value) const;			///< The first child of this node with the matching 'value'. Will be null if none found.
+	TiXmlNode* FirstChild(const char * value);						///< The first child of this node with the matching 'value'. Will be null if none found.
+
+	const TiXmlNode* LastChild() const	{ return lastChild; }		/// The last child of this node. Will be null if there are no children.
+	TiXmlNode* LastChild()	{ return lastChild; }
+	const TiXmlNode* LastChild(const char * value) const;			/// The last child of this node matching 'value'. Will be null if there are no children.
+	TiXmlNode* LastChild(const char * value);
+
+		#ifdef TIXML_USE_STL
+	const TiXmlNode* FirstChild(const std::string& _value) const	{	return FirstChild (_value.c_str ());	}	///< STL std::string form.
+	TiXmlNode* FirstChild(const std::string& _value)				{	return FirstChild (_value.c_str ());	}	///< STL std::string form.
+	const TiXmlNode* LastChild(const std::string& _value) const	{	return LastChild (_value.c_str ());	}	///< STL std::string form.
+	TiXmlNode* LastChild(const std::string& _value)				{	return LastChild (_value.c_str ());	}	///< STL std::string form.
+	#endif
+
+	/** An alternate way to walk the children of a node.
+		One way to iterate over nodes is:
+		@verbatim
+			for (child = parent->FirstChild(); child; child = child->NextSibling())
+		@endverbatim
+
+		IterateChildren does the same thing with the syntax:
+		@verbatim
+			child = 0;
+			while (child = parent->IterateChildren(child))
+		@endverbatim
+
+		IterateChildren takes the previous child as input and finds
+		the next one. If the previous child is null, it returns the
+		first. IterateChildren will return null when done.
+	*/
+	const TiXmlNode* IterateChildren(const TiXmlNode* previous) const;
+	TiXmlNode* IterateChildren(TiXmlNode* previous);
+
+	/// This flavor of IterateChildren searches for children with a particular 'value'
+	const TiXmlNode* IterateChildren(const char * value, const TiXmlNode* previous) const;
+	TiXmlNode* IterateChildren(const char * value, TiXmlNode* previous);
+
+		#ifdef TIXML_USE_STL
+	const TiXmlNode* IterateChildren(const std::string& _value, const TiXmlNode* previous) const	{	return IterateChildren (_value.c_str (), previous);	}	///< STL std::string form.
+	TiXmlNode* IterateChildren(const std::string& _value, TiXmlNode* previous) {	return IterateChildren (_value.c_str (), previous);	}	///< STL std::string form.
+	#endif
+
+	/** Add a new node related to this. Adds a child past the LastChild.
+		Returns a pointer to the new object or NULL if an error occured.
+	*/
+	TiXmlNode* InsertEndChild(const TiXmlNode& addThis);
+
+
+	/** Add a new node related to this. Adds a child past the LastChild.
+
+		NOTE: the node to be added is passed by pointer, and will be
+		henceforth owned (and deleted) by tinyXml. This method is efficient
+		and avoids an extra copy, but should be used with care as it
+		uses a different memory model than the other insert functions.
+
+		@sa InsertEndChild
+	*/
+	TiXmlNode* LinkEndChild(TiXmlNode* addThis);
+
+	/** Add a new node related to this. Adds a child before the specified child.
+		Returns a pointer to the new object or NULL if an error occured.
+	*/
+	TiXmlNode* InsertBeforeChild(TiXmlNode* beforeThis, const TiXmlNode& addThis);
+
+	/** Add a new node related to this. Adds a child after the specified child.
+		Returns a pointer to the new object or NULL if an error occured.
+	*/
+	TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis);
+
+	/** Replace a child of this node.
+		Returns a pointer to the new object or NULL if an error occured.
+	*/
+	TiXmlNode* ReplaceChild(TiXmlNode* replaceThis, const TiXmlNode& withThis);
+
+	/// Delete a child of this node.
+	bool RemoveChild(TiXmlNode* removeThis);
+
+	/// Navigate to a sibling node.
+	const TiXmlNode* PreviousSibling() const			{ return prev; }
+	TiXmlNode* PreviousSibling()						{ return prev; }
+
+	/// Navigate to a sibling node.
+	const TiXmlNode* PreviousSibling(const char *) const;
+	TiXmlNode* PreviousSibling(const char *);
+
+		#ifdef TIXML_USE_STL
+	const TiXmlNode* PreviousSibling(const std::string& _value) const	{	return PreviousSibling (_value.c_str ());	}	///< STL std::string form.
+	TiXmlNode* PreviousSibling(const std::string& _value)			 {	return PreviousSibling (_value.c_str ());	}	///< STL std::string form.
+	const TiXmlNode* NextSibling(const std::string& _value) const		{	return NextSibling (_value.c_str ());	}	///< STL std::string form.
+	TiXmlNode* NextSibling(const std::string& _value)					 {	return NextSibling (_value.c_str ());	}	///< STL std::string form.
+	#endif
+
+	/// Navigate to a sibling node.
+	const TiXmlNode* NextSibling() const				{ return next; }
+	TiXmlNode* NextSibling()							{ return next; }
+
+	/// Navigate to a sibling node with the given 'value'.
+	const TiXmlNode* NextSibling(const char *) const;
+	TiXmlNode* NextSibling(const char *);
+
+	/** Convenience function to get through elements.
+		Calls NextSibling and ToElement. Will skip all non-Element
+		nodes. Returns 0 if there is not another element.
+	*/
+	const TiXmlElement* NextSiblingElement() const;
+	TiXmlElement* NextSiblingElement();
+
+	/** Convenience function to get through elements.
+		Calls NextSibling and ToElement. Will skip all non-Element
+		nodes. Returns 0 if there is not another element.
+	*/
+	const TiXmlElement* NextSiblingElement(const char *) const;
+	TiXmlElement* NextSiblingElement(const char *);
+
+		#ifdef TIXML_USE_STL
+	const TiXmlElement* NextSiblingElement(const std::string& _value) const	{	return NextSiblingElement (_value.c_str ());	}	///< STL std::string form.
+	TiXmlElement* NextSiblingElement(const std::string& _value)				{	return NextSiblingElement (_value.c_str ());	}	///< STL std::string form.
+	#endif
+
+	/// Convenience function to get through elements.
+	const TiXmlElement* FirstChildElement()	const;
+	TiXmlElement* FirstChildElement();
+
+	/// Convenience function to get through elements.
+	const TiXmlElement* FirstChildElement(const char * value) const;
+	TiXmlElement* FirstChildElement(const char * value);
+
+		#ifdef TIXML_USE_STL
+	const TiXmlElement* FirstChildElement(const std::string& _value) const	{	return FirstChildElement (_value.c_str ());	}	///< STL std::string form.
+	TiXmlElement* FirstChildElement(const std::string& _value)				{	return FirstChildElement (_value.c_str ());	}	///< STL std::string form.
+	#endif
+
+	/** Query the type (as an enumerated value, above) of this node.
+		The possible types are: DOCUMENT, ELEMENT, COMMENT,
+								UNKNOWN, TEXT, and DECLARATION.
+	*/
+	int Type() const	{ return type; }
+
+	/** Return a pointer to the Document this node lives in.
+		Returns null if not in a document.
+	*/
+	const TiXmlDocument* GetDocument() const;
+	TiXmlDocument* GetDocument();
+
+	/// Returns true if this node has no children.
+	bool NoChildren() const						{ return !firstChild; }
+
+	virtual const TiXmlDocument*		ToDocument()		const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual const TiXmlElement*		 ToElement()		 const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual const TiXmlComment*		 ToComment()		 const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual const TiXmlUnknown*		 ToUnknown()		 const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual const TiXmlText*				ToText()				const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+	virtual TiXmlDocument*					ToDocument()		{ return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual TiXmlElement*					 ToElement()			{ return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual TiXmlComment*					 ToComment()		 { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual TiXmlUnknown*					 ToUnknown()			{ return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual TiXmlText*							ToText()				{ return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+	virtual TiXmlDeclaration*			 ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+	/** Create an exact duplicate of this node and return it. The memory must be deleted
+		by the caller.
+	*/
+	virtual TiXmlNode* Clone() const = 0;
+
+protected:
+	TiXmlNode(NodeType _type);
+
+	// Copy to the allocated object. Shared functionality between Clone, Copy constructor,
+	// and the assignment operator.
+	void CopyTo(TiXmlNode* target) const;
+
+	#ifdef TIXML_USE_STL
+			// The real work of the input operator.
+			virtual void StreamIn(TIXML_ISTREAM* in, TIXML_STRING* tag) = 0;
+	#endif
+
+	// Figure out what is at *p, and parse it. Returns null if it is not an xml node.
+	TiXmlNode* Identify(const char* start, TiXmlEncoding encoding);
+
+	TiXmlNode*		parent;
+	NodeType		type;
+
+	TiXmlNode*		firstChild;
+	TiXmlNode*		lastChild;
+
+	TIXML_STRING	value;
+
+	TiXmlNode*		prev;
+	TiXmlNode*		next;
+
+private:
+	TiXmlNode(const TiXmlNode&);				// not implemented.
+	void operator=(const TiXmlNode& base);	// not allowed.
+};
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+	number of attributes, each with a unique name.
+
+	@note The attributes are not TiXmlNodes, since they are not
+			part of the tinyXML document object model. There are other
+			suggested ways to look at this problem.
+*/
+class TiXmlAttribute : public TiXmlBase
+{
+	friend class TiXmlAttributeSet;
+
+public:
+	/// Construct an empty attribute.
+	TiXmlAttribute() : TiXmlBase()
+	{
+		document = 0;
+		prev = next = 0;
+	}
+
+	#ifdef TIXML_USE_STL
+	/// std::string constructor.
+	TiXmlAttribute(const std::string& _name, const std::string& _value)
+	{
+		name = _name;
+		value = _value;
+		document = 0;
+		prev = next = 0;
+	}
+	#endif
+
+	/// Construct an attribute with a name and value.
+	TiXmlAttribute(const char * _name, const char * _value)
+	{
+		name = _name;
+		value = _value;
+		document = 0;
+		prev = next = 0;
+	}
+
+	const char*		Name()	const		{ return name.c_str(); }		///< Return the name of this attribute.
+	const char*		Value() const		{ return value.c_str(); }		///< Return the value of this attribute.
+	#ifdef TIXML_USE_STL
+	const std::string& ValueStr() const	{ return value; }				///< Return the value of this attribute.
+	#endif
+	int				IntValue() const;									///< Return the value of this attribute, converted to an integer.
+	double			DoubleValue() const;								///< Return the value of this attribute, converted to a double.
+
+	// Get the tinyxml string representation
+	const TIXML_STRING& NameTStr() const { return name; }
+
+	/** QueryIntValue examines the value string. It is an alternative to the
+		IntValue() method with richer error checking.
+		If the value is an integer, it is stored in 'value' and
+		the call returns TIXML_SUCCESS. If it is not
+		an integer, it returns TIXML_WRONG_TYPE.
+
+		A specialized but useful call. Note that for success it returns 0,
+		which is the opposite of almost all other TinyXml calls.
+	*/
+	int QueryIntValue(int* _value) const;
+	/// QueryDoubleValue examines the value string. See QueryIntValue().
+	int QueryDoubleValue(double* _value) const;
+
+	void SetName(const char* _name)	{ name = _name; }				///< Set the name of this attribute.
+	void SetValue(const char* _value)	{ value = _value; }				///< Set the value.
+
+	void SetIntValue(int _value);										///< Set the value from an integer.
+	void SetDoubleValue(double _value);								///< Set the value from a double.
+
+		#ifdef TIXML_USE_STL
+	/// STL std::string form.
+	void SetName(const std::string& _name)	{ name = _name; }
+	/// STL std::string form.
+	void SetValue(const std::string& _value)	{ value = _value; }
+	#endif
+
+	/// Get the next sibling attribute in the DOM. Returns null at end.
+	const TiXmlAttribute* Next() const;
+	TiXmlAttribute* Next();
+	/// Get the previous sibling attribute in the DOM. Returns null at beginning.
+	const TiXmlAttribute* Previous() const;
+	TiXmlAttribute* Previous();
+
+	bool operator==(const TiXmlAttribute& rhs) const { return rhs.name == name; }
+	bool operator<(const TiXmlAttribute& rhs)	 const { return name < rhs.name; }
+	bool operator>(const TiXmlAttribute& rhs)	const { return name > rhs.name; }
+
+	/*	Attribute parsing starts: first letter of the name
+						 returns: the next char after the value end quote
+	*/
+	virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+	// Prints this Attribute to a FILE stream.
+	virtual void Print(FILE* cfile, int depth) const;
+
+	virtual void StreamOut(TIXML_OSTREAM * out) const;
+	virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const {}
+
+	// [internal use]
+	// Set the document pointer so the attribute can report errors.
+	void SetDocument(TiXmlDocument* doc)	{ document = doc; }
+
+private:
+	TiXmlAttribute(const TiXmlAttribute&);				// not implemented.
+	void operator=(const TiXmlAttribute& base);	// not allowed.
+
+	TiXmlDocument*	document;	// A pointer back to a document, for error reporting.
+	TIXML_STRING name;
+	TIXML_STRING value;
+	TiXmlAttribute*	prev;
+	TiXmlAttribute*	next;
+};
+
+
+/*	A class used to manage a group of attributes.
+	It is only used internally, both by the ELEMENT and the DECLARATION.
+
+	The set can be changed transparent to the Element and Declaration
+	classes that use it, but NOT transparent to the Attribute
+	which has to implement a next() and previous() method. Which makes
+	it a bit problematic and prevents the use of STL.
+
+	This version is implemented with circular lists because:
+		- I like circular lists
+		- it demonstrates some independence from the (typical) doubly linked list.
+*/
+class TiXmlAttributeSet
+{
+public:
+	TiXmlAttributeSet();
+	~TiXmlAttributeSet();
+
+	void Add(TiXmlAttribute* attribute);
+	void Remove(TiXmlAttribute* attribute);
+
+	const TiXmlAttribute* First()	const	{ return (sentinel.next == &sentinel) ? 0 : sentinel.next; }
+	TiXmlAttribute* First()					{ return (sentinel.next == &sentinel) ? 0 : sentinel.next; }
+	const TiXmlAttribute* Last() const		{ return (sentinel.prev == &sentinel) ? 0 : sentinel.prev; }
+	TiXmlAttribute* Last()					{ return (sentinel.prev == &sentinel) ? 0 : sentinel.prev; }
+
+	const TiXmlAttribute*	Find(const TIXML_STRING& name) const;
+	TiXmlAttribute*	Find(const TIXML_STRING& name);
+
+private:
+	//*ME:	Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
+	//*ME:	this class must be also use a hidden/disabled copy-constructor !!!
+	TiXmlAttributeSet(const TiXmlAttributeSet&);	// not allowed
+	void operator=(const TiXmlAttributeSet&);	// not allowed (as TiXmlAttribute)
+
+	TiXmlAttribute sentinel;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+	and can contain other elements, text, comments, and unknowns.
+	Elements also contain an arbitrary number of attributes.
+*/
+class TiXmlElement : public TiXmlNode
+{
+public:
+	/// Construct an element.
+	TiXmlElement (const char * in_value);
+
+	#ifdef TIXML_USE_STL
+	/// std::string constructor.
+	TiXmlElement(const std::string& _value);
+	#endif
+
+	TiXmlElement(const TiXmlElement&);
+
+	void operator=(const TiXmlElement& base);
+
+	virtual ~TiXmlElement();
+
+	/** Given an attribute name, Attribute() returns the value
+		for the attribute of that name, or null if none exists.
+	*/
+	const char* Attribute(const char* name) const;
+
+	/** Given an attribute name, Attribute() returns the value
+		for the attribute of that name, or null if none exists.
+		If the attribute exists and can be converted to an integer,
+		the integer value will be put in the return 'i', if 'i'
+		is non-null.
+	*/
+	const char* Attribute(const char* name, int* i) const;
+
+	/** Given an attribute name, Attribute() returns the value
+		for the attribute of that name, or null if none exists.
+		If the attribute exists and can be converted to an double,
+		the double value will be put in the return 'd', if 'd'
+		is non-null.
+	*/
+	const char* Attribute(const char* name, double* d) const;
+
+	/** QueryIntAttribute examines the attribute - it is an alternative to the
+		Attribute() method with richer error checking.
+		If the attribute is an integer, it is stored in 'value' and
+		the call returns TIXML_SUCCESS. If it is not
+		an integer, it returns TIXML_WRONG_TYPE. If the attribute
+		does not exist, then TIXML_NO_ATTRIBUTE is returned.
+	*/
+	int QueryIntAttribute(const char* name, int* _value) const;
+	/// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
+	int QueryDoubleAttribute(const char* name, double* _value) const;
+	/// QueryFloatAttribute examines the attribute - see QueryIntAttribute().
+	int QueryFloatAttribute(const char* name, float* _value) const {
+		double d;
+		int result = QueryDoubleAttribute(name, &d);
+		if (result == TIXML_SUCCESS) {
+			*_value = (float)d;
+		}
+		return result;
+	}
+		#ifdef TIXML_USE_STL
+	/** Template form of the attribute query which will try to read the
+		attribute into the specified type. Very easy, very powerful, but
+		be careful to make sure to call this with the correct type.
+
+		@return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
+	*/
+	template< typename T > int QueryValueAttribute(const std::string& name, T* outValue) const
+	{
+		const TiXmlAttribute* node = attributeSet.Find(name);
+		if (!node)
+			return TIXML_NO_ATTRIBUTE;
+
+		std::stringstream sstream(node->ValueStr());
+		sstream >> *outValue;
+		if (!sstream.fail())
+			return TIXML_SUCCESS;
+		return TIXML_WRONG_TYPE;
+	}
+	#endif
+
+	/** Sets an attribute of name to a given value. The attribute
+		will be created if it does not exist, or changed if it does.
+	*/
+	void SetAttribute(const char* name, const char * _value);
+
+		#ifdef TIXML_USE_STL
+	const char* Attribute(const std::string& name) const				{ return Attribute(name.c_str()); }
+	const char* Attribute(const std::string& name, int* i) const		{ return Attribute(name.c_str(), i); }
+	const char* Attribute(const std::string& name, double* d) const	{ return Attribute(name.c_str(), d); }
+	int QueryIntAttribute(const std::string& name, int* _value) const	{ return QueryIntAttribute(name.c_str(), _value); }
+	int QueryDoubleAttribute(const std::string& name, double* _value) const { return QueryDoubleAttribute(name.c_str(), _value); }
+
+	/// STL std::string form.
+	void SetAttribute(const std::string& name, const std::string& _value);
+	///< STL std::string form.
+	void SetAttribute(const std::string& name, int _value);
+	#endif
+
+	/** Sets an attribute of name to a given value. The attribute
+		will be created if it does not exist, or changed if it does.
+	*/
+	void SetAttribute(const char * name, int value);
+
+	/** Sets an attribute of name to a given value. The attribute
+		will be created if it does not exist, or changed if it does.
+	*/
+	void SetDoubleAttribute(const char * name, double value);
+
+	/** Deletes an attribute with the given name.
+	*/
+	void RemoveAttribute(const char * name);
+		#ifdef TIXML_USE_STL
+	void RemoveAttribute(const std::string& name)	{	RemoveAttribute (name.c_str ());	}	///< STL std::string form.
+	#endif
+
+	const TiXmlAttribute* FirstAttribute() const	{ return attributeSet.First(); }		///< Access the first attribute in this element.
+	TiXmlAttribute* FirstAttribute()				 { return attributeSet.First(); }
+	const TiXmlAttribute* LastAttribute()	const	 { return attributeSet.Last(); }		///< Access the last attribute in this element.
+	TiXmlAttribute* LastAttribute()					{ return attributeSet.Last(); }
+
+	/** Convenience function for easy access to the text inside an element. Although easy
+		and concise, GetText() is limited compared to getting the TiXmlText child
+		and accessing it directly.
+
+		If the first child of 'this' is a TiXmlText, the GetText()
+		returns the character string of the Text node, else null is returned.
+
+		This is a convenient method for getting the text of simple contained text:
+		@verbatim
+		<foo>This is text</foo>
+		const char* str = fooElement->GetText();
+		@endverbatim
+
+		'str' will be a pointer to "This is text".
+
+		Note that this function can be misleading. If the element foo was created from
+		this XML:
+		@verbatim
+		<foo><b>This is text</b></foo>
+		@endverbatim
+
+		then the value of str would be null. The first child node isn't a text node, it is
+		another element. From this XML:
+		@verbatim
+		<foo>This is <b>text</b></foo>
+		@endverbatim
+		GetText() will return "This is ".
+
+		WARNING: GetText() accesses a child node - don't become confused with the
+				 similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are
+				 safe type casts on the referenced node.
+	*/
+	const char* GetText() const;
+
+	/// Creates a new Element and returns it - the returned element is a copy.
+	virtual TiXmlNode* Clone() const;
+	// Print the Element to a FILE stream.
+	virtual void Print(FILE* cfile, int depth) const;
+
+	/*	Attribtue parsing starts: next char past '<'
+						 returns: next char past '>'
+	*/
+	virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+	virtual const TiXmlElement*		ToElement()		 const	{ return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+	virtual TiXmlElement*			ToElement()				{ return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected:
+
+	void CopyTo(TiXmlElement* target) const;
+	void ClearThis();	// like clear, but initializes 'this' object as well
+
+	// Used to be public [internal use]
+	#ifdef TIXML_USE_STL
+			virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+	#endif
+	virtual void StreamOut(TIXML_OSTREAM * out) const;
+	virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+	/*	[internal use]
+		Reads the "value" of the element -- another element, or text.
+		This should terminate with the current end tag.
+	*/
+	const char* ReadValue(const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding);
+
+private:
+
+	TiXmlAttributeSet attributeSet;
+};
+
+
+/**	An XML comment.
+*/
+class TiXmlComment : public TiXmlNode
+{
+public:
+	/// Constructs an empty comment.
+	TiXmlComment() : TiXmlNode(TiXmlNode::COMMENT) {}
+	TiXmlComment(const TiXmlComment&);
+	void operator=(const TiXmlComment& base);
+
+	virtual ~TiXmlComment()	{}
+
+	/// Returns a copy of this Comment.
+	virtual TiXmlNode* Clone() const;
+	/// Write this Comment to a FILE stream.
+	virtual void Print(FILE* cfile, int depth) const;
+
+	/*	Attribtue parsing starts: at the ! of the !--
+						 returns: next char past '>'
+	*/
+	virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+	virtual const TiXmlComment*	ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+	virtual TiXmlComment*	ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected:
+	void CopyTo(TiXmlComment* target) const;
+
+	// used to be public
+	#ifdef TIXML_USE_STL
+			virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+	#endif
+	virtual void StreamOut(TIXML_OSTREAM * out) const;
+	virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+private:
+
+};
+
+
+/** XML text. A text node can have 2 ways to output the next. "normal" output
+	and CDATA. It will default to the mode it was parsed from the XML file and
+	you generally want to leave it alone, but you can change the output mode with
+	SetCDATA() and query it with CDATA().
+*/
+class TiXmlText : public TiXmlNode
+{
+	friend class TiXmlElement;
+public:
+	/** Constructor for text element. By default, it is treated as
+		normal, encoded text. If you want it be output as a CDATA text
+		element, set the parameter _cdata to 'true'
+	*/
+	TiXmlText (const char * initValue) : TiXmlNode (TiXmlNode::TEXT)
+	{
+		SetValue(initValue);
+		cdata = false;
+	}
+	virtual ~TiXmlText() {}
+
+	#ifdef TIXML_USE_STL
+	/// Constructor.
+	TiXmlText(const std::string& initValue) : TiXmlNode (TiXmlNode::TEXT)
+	{
+		SetValue(initValue);
+		cdata = false;
+	}
+	#endif
+
+	TiXmlText(const TiXmlText& copy) : TiXmlNode(TiXmlNode::TEXT)	{ copy.CopyTo(this); }
+	void operator=(const TiXmlText& base)								 { base.CopyTo(this); }
+
+	/// Write this text object to a FILE stream.
+	virtual void Print(FILE* cfile, int depth) const;
+
+	/// Queries whether this represents text using a CDATA section.
+	bool CDATA()					{ return cdata; }
+	/// Turns on or off a CDATA representation of text.
+	void SetCDATA(bool _cdata)	{ cdata = _cdata; }
+
+	virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+	virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+	virtual TiXmlText*			 ToText()			 { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected :
+	///	[internal use] Creates a new Element and returns it.
+	virtual TiXmlNode* Clone() const;
+	void CopyTo(TiXmlText* target) const;
+
+	virtual void StreamOut (TIXML_OSTREAM * out) const;
+	virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+	bool Blank() const;	// returns true if all white space and new lines
+	// [internal use]
+	#ifdef TIXML_USE_STL
+			virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+	#endif
+
+private:
+	bool cdata;			// true if this should be input and output as a CDATA style text element
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+	@verbatim
+		<?xml version="1.0" standalone="yes"?>
+	@endverbatim
+
+	TinyXml will happily read or write files without a declaration,
+	however. There are 3 possible attributes to the declaration:
+	version, encoding, and standalone.
+
+	Note: In this version of the code, the attributes are
+	handled as special cases, not generic attributes, simply
+	because there can only be at most 3 and they are always the same.
+*/
+class TiXmlDeclaration : public TiXmlNode
+{
+public:
+	/// Construct an empty declaration.
+	TiXmlDeclaration()	 : TiXmlNode(TiXmlNode::DECLARATION) {}
+
+#ifdef TIXML_USE_STL
+	/// Constructor.
+	TiXmlDeclaration(	const std::string& _version,
+						const std::string& _encoding,
+						const std::string& _standalone);
+#endif
+
+	/// Construct.
+	TiXmlDeclaration(	const char* _version,
+						const char* _encoding,
+						const char* _standalone);
+
+	TiXmlDeclaration(const TiXmlDeclaration& copy);
+	void operator=(const TiXmlDeclaration& copy);
+
+	virtual ~TiXmlDeclaration()	{}
+
+	/// Version. Will return an empty string if none was found.
+	const char *Version() const			{ return version.c_str (); }
+	/// Encoding. Will return an empty string if none was found.
+	const char *Encoding() const		{ return encoding.c_str (); }
+	/// Is this a standalone document?
+	const char *Standalone() const		{ return standalone.c_str (); }
+
+	/// Creates a copy of this Declaration and returns it.
+	virtual TiXmlNode* Clone() const;
+	/// Print this declaration to a FILE stream.
+	virtual void Print(FILE* cfile, int depth) const;
+
+	virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+	virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+	virtual TiXmlDeclaration*			 ToDeclaration()			 { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected:
+	void CopyTo(TiXmlDeclaration* target) const;
+	// used to be public
+	#ifdef TIXML_USE_STL
+			virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+	#endif
+	virtual void StreamOut (TIXML_OSTREAM * out) const;
+	virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+private:
+
+	TIXML_STRING version;
+	TIXML_STRING encoding;
+	TIXML_STRING standalone;
+};
+
+
+/** Any tag that tinyXml doesn't recognize is saved as an
+	unknown. It is a tag of text, but should not be modified.
+	It will be written back to the XML, unchanged, when the file
+	is saved.
+
+	DTD tags get thrown into TiXmlUnknowns.
+*/
+class TiXmlUnknown : public TiXmlNode
+{
+public:
+	TiXmlUnknown() : TiXmlNode(TiXmlNode::UNKNOWN)	{}
+	virtual ~TiXmlUnknown() {}
+
+	TiXmlUnknown(const TiXmlUnknown& copy) : TiXmlNode(TiXmlNode::UNKNOWN)		{ copy.CopyTo(this); }
+	void operator=(const TiXmlUnknown& copy)										{ copy.CopyTo(this); }
+
+	/// Creates a copy of this Unknown and returns it.
+	virtual TiXmlNode* Clone() const;
+	/// Print this Unknown to a FILE stream.
+	virtual void Print(FILE* cfile, int depth) const;
+
+	virtual const char* Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding);
+
+	virtual const TiXmlUnknown*		 ToUnknown()		 const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+	virtual TiXmlUnknown*					 ToUnknown()			{ return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected:
+	void CopyTo(TiXmlUnknown* target) const;
+
+	#ifdef TIXML_USE_STL
+			virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+	#endif
+	virtual void StreamOut (TIXML_OSTREAM * out) const;
+	virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+
+private:
+
+};
+
+
+/** Always the top level node. A document binds together all the
+	XML pieces. It can be saved, loaded, and printed to the screen.
+	The 'value' of a document node is the xml file name.
+*/
+class TiXmlDocument : public TiXmlNode
+{
+public:
+	/// Create an empty document, that has no name.
+	TiXmlDocument();
+	/// Create a document with a name. The name of the document is also the filename of the xml.
+	TiXmlDocument(const char * documentName);
+
+	#ifdef TIXML_USE_STL
+	/// Constructor.
+	TiXmlDocument(const std::string& documentName);
+	#endif
+
+	TiXmlDocument(const TiXmlDocument& copy);
+	void operator=(const TiXmlDocument& copy);
+
+	virtual ~TiXmlDocument() {}
+
+	/** Load a file using the current document value.
+		Returns true if successful. Will delete any existing
+		document data before loading.
+	*/
+	bool LoadFile(TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+	/// Save a file using the current document value. Returns true if successful.
+	bool SaveFile() const;
+	/// Load a file using the given filename. Returns true if successful.
+	bool LoadFile(const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+	/// Save a file using the given filename. Returns true if successful.
+	bool SaveFile(const char * filename) const;
+	/** Load a file using the given FILE*. Returns true if successful. Note that this method
+		doesn't stream - the entire object pointed at by the FILE*
+		will be interpreted as an XML file. TinyXML doesn't stream in XML from the current
+		file location. Streaming may be added in the future.
+	*/
+	bool LoadFile(FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+	/// Save a file using the given FILE*. Returns true if successful.
+	bool SaveFile(FILE*) const;
+
+	#ifdef TIXML_USE_STL
+	bool LoadFile(const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING)			///< STL std::string version.
+	{
+		StringToBuffer f(filename);
+		return (f.buffer && LoadFile(f.buffer, encoding));
+	}
+	bool SaveFile(const std::string& filename) const		///< STL std::string version.
+	{
+		StringToBuffer f(filename);
+		return (f.buffer && SaveFile(f.buffer));
+	}
+	/** Write the document to a string using formatted printing ("pretty print").
+	@return the document as a formatted standard string.
+	*/
+	std::string GetAsString();
+	#endif
+
+	/** Write the document to a string using formatted printing ("pretty print").
+	@param buffer A character buffer that should be big enough to hold your document.
+	@param bufferSize The size of @p buffer.
+
+	@return True if the document could be copied into @p buffer, else false.
+	*/
+	bool GetAsCharBuffer(char* buffer, size_t bufferSize);
+
+	/** Parse the given null terminated block of xml data. Passing in an encoding to this
+		method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
+		to use that encoding, regardless of what TinyXml might otherwise try to detect.
+	*/
+	virtual const char* Parse(const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING);
+
+	/** Get the root element -- the only top level element -- of the document.
+		In well formed XML, there should only be one. TinyXml is tolerant of
+		multiple elements at the document level.
+	*/
+	const TiXmlElement* RootElement() const		{ return FirstChildElement(); }
+	TiXmlElement* RootElement()					{ return FirstChildElement(); }
+
+	/** If an error occurs, Error will be set to true. Also,
+		- The ErrorId() will contain the integer identifier of the error (not generally useful)
+		- The ErrorDesc() method will return the name of the error. (very useful)
+		- The ErrorRow() and ErrorCol() will return the location of the error (if known)
+	*/
+	bool Error() const						{ return error; }
+
+	/// Contains a textual (english) description of the error if one occurs.
+	const char * ErrorDesc() const	{ return errorDesc.c_str (); }
+
+	/** Generally, you probably want the error string (ErrorDesc()). But if you
+		prefer the ErrorId, this function will fetch it.
+	*/
+	int ErrorId()	const				{ return errorId; }
+
+	/** Returns the location (if known) of the error. The first column is column 1,
+		and the first row is row 1. A value of 0 means the row and column wasn't applicable
+		(memory errors, for example, have no row/column) or the parser lost the error. (An
+		error in the error reporting, in that case.)
+
+		@sa SetTabSize, Row, Column
+	*/
+	int ErrorRow()	{ return errorLocation.row+1; }
+	int ErrorCol()	{ return errorLocation.col+1; }	///< The column where the error occured. See ErrorRow()
+
+	/** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())
+		to report the correct values for row and column. It does not change the output
+		or input in any way.
+
+		By calling this method, with a tab size
+		greater than 0, the row and column of each node and attribute is stored
+		when the file is loaded. Very useful for tracking the DOM back in to
+		the source file.
+
+		The tab size is required for calculating the location of nodes. If not
+		set, the default of 4 is used. The tabsize is set per document. Setting
+		the tabsize to 0 disables row/column tracking.
+
+		Note that row and column tracking is not supported when using operator>>.
+
+		The tab size needs to be enabled before the parse or load. Correct usage:
+		@verbatim
+		TiXmlDocument doc;
+		doc.SetTabSize(8);
+		doc.Load("myfile.xml");
+		@endverbatim
+
+		@sa Row, Column
+	*/
+	void SetTabSize(int _tabsize)		{ tabsize = _tabsize; }
+
+	int TabSize() const	{ return tabsize; }
+
+	/** If you have handled the error, it can be reset with this call. The error
+		state is automatically cleared if you Parse a new XML block.
+	*/
+	void ClearError()						{	error = false;
+												errorId = 0;
+												errorDesc = "";
+												errorLocation.row = errorLocation.col = 0;
+												//errorLocation.last = 0;
+											}
+
+	/** Dump the document to standard out. */
+	void Print() const						{ Print(stdout, 0); }
+
+	/// Print this Document to a FILE stream.
+	virtual void Print(FILE* cfile, int depth = 0) const;
+	// [internal use]
+	void SetError(int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding);
+
+	virtual const TiXmlDocument*		ToDocument()		const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+	virtual TiXmlDocument*					ToDocument()					{ return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+protected :
+	virtual void StreamOut (TIXML_OSTREAM * out) const;
+	virtual void FormattedStreamOut(TIXML_OSTREAM * stream, int depth) const;
+	// [internal use]
+	virtual TiXmlNode* Clone() const;
+	#ifdef TIXML_USE_STL
+			virtual void StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag);
+	#endif
+
+private:
+	void CopyTo(TiXmlDocument* target) const;
+
+	bool error;
+	int	errorId;
+	TIXML_STRING errorDesc;
+	int tabsize;
+	TiXmlCursor errorLocation;
+	bool useMicrosoftBOM;		// the UTF-8 BOM were found when read. Note this, and try to write.
+};
+
+
+/**
+	A TiXmlHandle is a class that wraps a node pointer with null checks; this is
+	an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
+	DOM structure. It is a separate utility class.
+
+	Take an example:
+	@verbatim
+	<Document>
+		<Element attributeA = "valueA">
+			<Child attributeB = "value1" />
+			<Child attributeB = "value2" />
+		</Element>
+	<Document>
+	@endverbatim
+
+	Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+	easy to write a *lot* of code that looks like:
+
+	@verbatim
+	TiXmlElement* root = document.FirstChildElement("Document");
+	if (root)
+	{
+		TiXmlElement* element = root->FirstChildElement("Element");
+		if (element)
+		{
+			TiXmlElement* child = element->FirstChildElement("Child");
+			if (child)
+			{
+				TiXmlElement* child2 = child->NextSiblingElement("Child");
+				if (child2)
+				{
+					// Finally do something useful.
+	@endverbatim
+
+	And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
+	of such code. A TiXmlHandle checks for null	pointers so it is perfectly safe
+	and correct to use:
+
+	@verbatim
+	TiXmlHandle docHandle(&document);
+	TiXmlElement* child2 = docHandle.FirstChild("Document").FirstChild("Element").Child("Child", 1).Element();
+	if (child2)
+	{
+		// do something useful
+	@endverbatim
+
+	Which is MUCH more concise and useful.
+
+	It is also safe to copy handles - internally they are nothing more than node pointers.
+	@verbatim
+	TiXmlHandle handleCopy = handle;
+	@endverbatim
+
+	What they should not be used for is iteration:
+
+	@verbatim
+	int i=0;
+	while (true)
+	{
+		TiXmlElement* child = docHandle.FirstChild("Document").FirstChild("Element").Child("Child", i).Element();
+		if (!child)
+			break;
+		// do something
+		++i;
+	}
+	@endverbatim
+
+	It seems reasonable, but it is in fact two embedded while loops. The Child method is
+	a linear walk to find the element, so this code would iterate much more than it needs
+	to. Instead, prefer:
+
+	@verbatim
+	TiXmlElement* child = docHandle.FirstChild("Document").FirstChild("Element").FirstChild("Child").Element();
+
+	for (child; child; child=child->NextSiblingElement())
+	{
+		// do something
+	}
+	@endverbatim
+*/
+class TiXmlHandle
+{
+public:
+	/// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+	TiXmlHandle(TiXmlNode* _node)					{ this->node = _node; }
+	/// Copy constructor
+	TiXmlHandle(const TiXmlHandle& ref)			{ this->node = ref.node; }
+	TiXmlHandle operator=(const TiXmlHandle& ref) { this->node = ref.node; return *this; }
+
+	/// Return a handle to the first child node.
+	TiXmlHandle FirstChild() const;
+	/// Return a handle to the first child node with the given name.
+	TiXmlHandle FirstChild(const char * value) const;
+	/// Return a handle to the first child element.
+	TiXmlHandle FirstChildElement() const;
+	/// Return a handle to the first child element with the given name.
+	TiXmlHandle FirstChildElement(const char * value) const;
+
+	/** Return a handle to the "index" child with the given name.
+		The first child is 0, the second 1, etc.
+	*/
+	TiXmlHandle Child(const char* value, int index) const;
+	/** Return a handle to the "index" child.
+		The first child is 0, the second 1, etc.
+	*/
+	TiXmlHandle Child(int index) const;
+	/** Return a handle to the "index" child element with the given name.
+		The first child element is 0, the second 1, etc. Note that only TiXmlElements
+		are indexed: other types are not counted.
+	*/
+	TiXmlHandle ChildElement(const char* value, int index) const;
+	/** Return a handle to the "index" child element.
+		The first child element is 0, the second 1, etc. Note that only TiXmlElements
+		are indexed: other types are not counted.
+	*/
+	TiXmlHandle ChildElement(int index) const;
+
+	#ifdef TIXML_USE_STL
+	TiXmlHandle FirstChild(const std::string& _value) const				{ return FirstChild(_value.c_str()); }
+	TiXmlHandle FirstChildElement(const std::string& _value) const		{ return FirstChildElement(_value.c_str()); }
+
+	TiXmlHandle Child(const std::string& _value, int index) const			{ return Child(_value.c_str(), index); }
+	TiXmlHandle ChildElement(const std::string& _value, int index) const	{ return ChildElement(_value.c_str(), index); }
+	#endif
+
+	/// Return the handle as a TiXmlNode. This may return null.
+	TiXmlNode* Node() const			{ return node; }
+	/// Return the handle as a TiXmlElement. This may return null.
+	TiXmlElement* Element() const	{ return ((node && node->ToElement()) ? node->ToElement() : 0); }
+	/// Return the handle as a TiXmlText. This may return null.
+	TiXmlText* Text() const			{ return ((node && node->ToText()) ? node->ToText() : 0); }
+	/// Return the handle as a TiXmlUnknown. This may return null;
+	TiXmlUnknown* Unknown() const			{ return ((node && node->ToUnknown()) ? node->ToUnknown() : 0); }
+
+private:
+	TiXmlNode* node;
+};
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif
+
diff --git a/plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp b/plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp
new file mode 100644
index 0000000000..3c42354d11
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinyxmlerror.cpp
@@ -0,0 +1,76 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier)copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. 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.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinyxmlerror.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifdef USE_MMGR
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "mmgr.h"
+#endif
+
+#include "tinyxml.h"
+
+// The goal of the seperate error file is to make the first
+// step towards localization. tinyxml (currently) only supports
+// english error messages, but the could now be translated.
+//
+// It also cleans up the code a bit.
+//
+
+const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =
+{
+	"No error",
+	"Error",
+	"Failed to open file",
+	"Memory allocation failed.",
+	"Error parsing Element.",
+	"Failed to read Element name",
+	"Error reading Element value.",
+	"Error reading Attributes.",
+	"Error: empty tag.",
+	"Error reading end tag.",
+	"Error parsing Unknown.",
+	"Error parsing Comment.",
+	"Error parsing Declaration.",
+	"Error document empty.",
+	"Error null (0) or unexpected EOF found in input stream.",
+	"Error parsing CDATA.",
+	"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
+};
diff --git a/plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp b/plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp
new file mode 100644
index 0000000000..73f2c18679
--- /dev/null
+++ b/plugins/UserInfoEx/src/ex_import/tinyxmlparser.cpp
@@ -0,0 +1,1613 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier)copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. 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.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+
+===============================================================================
+
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/ex_import/tinyxmlparser.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include <ctype.h>
+#include <stddef.h>
+
+#ifdef USE_MMGR
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include "mmgr.h"
+#endif
+
+#include "tinyxml.h"
+
+//#define DEBUG_PARSER
+#if defined(DEBUG_PARSER)
+#	if defined(DEBUG) && defined(_MSC_VER)
+#		include <windows.h>
+#		define TIXML_LOG OutputDebugString
+#	else
+#		define TIXML_LOG printf
+#	endif
+#endif
+
+// Note tha "PutString" hardcodes the same list. This
+// is less flexible than it appears. Changing the entries
+// or order will break putstring.	
+TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = 
+{
+	{ "&amp;",	5, '&' },
+	{ "&lt;",	 4, '<' },
+	{ "&gt;",	 4, '>' },
+	{ "&quot;", 6, '\"' },
+	{ "&apos;", 6, '\'' }
+};
+
+// Bunch of unicode info at:
+//		http://www.unicode.org/faq/utf_bom.html
+// Including the basic of this table, which determines the #bytes in the
+// sequence from the lead byte. 1 placed for invalid sequences --
+// although the result will be junk, pass it through as much as possible.
+// Beware of the non-characters in UTF-8:	
+//				ef bb bf (Microsoft "lead bytes")
+//				ef bf be
+//				ef bf bf 
+
+const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+const int TiXmlBase::utf8ByteTable[256] = 
+{
+	//	0	1	2	3	4	5	6	7	8	9	a	b	c	d	e	f
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x00
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x10
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x20
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x30
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x40
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x50
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x60
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x70	End of ASCII range
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x80 0x80 to 0xc1 invalid
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x90 
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0xa0 
+		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0xb0 
+		1,	1,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	// 0xc0 0xc2 to 0xdf 2 byte
+		2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	// 0xd0
+		3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	// 0xe0 0xe0 to 0xef 3 byte
+		4,	4,	4,	4,	4,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1	// 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
+};
+
+
+void TiXmlBase::ConvertUTF32ToUTF8(unsigned long input, char* output, int* length)
+{
+	const unsigned long BYTE_MASK = 0xBF;
+	const unsigned long BYTE_MARK = 0x80;
+	const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+	if (input < 0x80) 
+		*length = 1;
+	else if (input < 0x800)
+		*length = 2;
+	else if (input < 0x10000)
+		*length = 3;
+	else if (input < 0x200000)
+		*length = 4;
+	else
+		{ *length = 0; return; }	// This code won't covert this correctly anyway.
+
+	output += *length;
+
+	// Scary scary fall throughs.
+	switch (*length) 
+	{
+		case 4:
+			--output; 
+			*output = (char)((input | BYTE_MARK) & BYTE_MASK); 
+			input >>= 6;
+		case 3:
+			--output; 
+			*output = (char)((input | BYTE_MARK) & BYTE_MASK); 
+			input >>= 6;
+		case 2:
+			--output; 
+			*output = (char)((input | BYTE_MARK) & BYTE_MASK); 
+			input >>= 6;
+		case 1:
+			--output; 
+			*output = (char)(input | FIRST_BYTE_MARK[*length]);
+	}
+}
+
+
+/*static*/ int TiXmlBase::IsAlpha(unsigned char anyByte, TiXmlEncoding /*encoding*/)
+{
+	// This will only work for low-ascii, everything else is assumed to be a valid
+	// letter. I'm not sure this is the best approach, but it is quite tricky trying
+	// to figure out alhabetical vs. not across encoding. So take a very 
+	// conservative approach.
+
+//	if (encoding == TIXML_ENCODING_UTF8)
+//	{
+		if (anyByte < 127)
+			return isalpha(anyByte);
+		else
+			return 1;	// What else to do? The unicode set is huge...get the english ones right.
+//	}
+//	else
+//	{
+//		return isalpha(anyByte);
+//	}
+}
+
+
+/*static*/ int TiXmlBase::IsAlphaNum(unsigned char anyByte, TiXmlEncoding /*encoding*/)
+{
+	// This will only work for low-ascii, everything else is assumed to be a valid
+	// letter. I'm not sure this is the best approach, but it is quite tricky trying
+	// to figure out alhabetical vs. not across encoding. So take a very 
+	// conservative approach.
+
+//	if (encoding == TIXML_ENCODING_UTF8)
+//	{
+		if (anyByte < 127)
+			return isalnum(anyByte);
+		else
+			return 1;	// What else to do? The unicode set is huge...get the english ones right.
+//	}
+//	else
+//	{
+//		return isalnum(anyByte);
+//	}
+}
+
+
+class TiXmlParsingData
+{
+	friend class TiXmlDocument;
+	public:
+	void Stamp(const char* now, TiXmlEncoding encoding);
+
+	const TiXmlCursor& Cursor()	{ return cursor; }
+
+	private:
+	// Only used by the document!
+	TiXmlParsingData(const char* start, int _tabsize, int row, int col)
+	{
+		assert(start);
+		stamp = start;
+		tabsize = _tabsize;
+		cursor.row = row;
+		cursor.col = col;
+	}
+
+	TiXmlCursor		cursor;
+	const char*		stamp;
+	int				tabsize;
+};
+
+
+void TiXmlParsingData::Stamp(const char* now, TiXmlEncoding encoding)
+{
+	assert(now);
+
+	// Do nothing if the tabsize is 0.
+	if (tabsize < 1)
+	{
+		return;
+	}
+
+	// Get the current row, column.
+	int row = cursor.row;
+	int col = cursor.col;
+	const char* p = stamp;
+	assert(p);
+
+	while (p < now)
+	{
+		// Treat p as unsigned, so we have a happy compiler.
+		const unsigned char* pU = (const unsigned char*)p;
+
+		// Code contributed by Fletcher Dunn: (modified by lee)
+		switch (*pU) {
+			case 0:
+				// We *should* never get here, but in case we do, don't
+				// advance past the terminating null character, ever
+				return;
+
+			case '\r':
+				// bump down to the next line
+				++row;
+				col = 0;				
+				// Eat the character
+				++p;
+
+				// Check for \r\n sequence, and treat this as a single character
+				if (*p == '\n') {
+					++p;
+				}
+				break;
+
+			case '\n':
+				// bump down to the next line
+				++row;
+				col = 0;
+
+				// Eat the character
+				++p;
+
+				// Check for \n\r sequence, and treat this as a single
+				// character.	(Yes, this bizarre thing does occur still
+				// on some arcane platforms...)
+				if (*p == '\r') {
+					++p;
+				}
+				break;
+
+			case '\t':
+				// Eat the character
+				++p;
+
+				// Skip to next tab stop
+				col = (col / tabsize + 1) * tabsize;
+				break;
+
+			case TIXML_UTF_LEAD_0:
+				if (encoding == TIXML_ENCODING_UTF8)
+				{
+					if (*(p+1) && *(p+2))
+					{
+						// In these cases, don't advance the column. These are
+						// 0-width spaces.
+						if (*(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2)
+							p += 3;	
+						else if (*(pU+1)==0xbfU && *(pU+2)==0xbeU)
+							p += 3;	
+						else if (*(pU+1)==0xbfU && *(pU+2)==0xbfU)
+							p += 3;	
+						else
+							{ p +=3; ++col; }	// A normal character.
+					}
+				}
+				else
+				{
+					++p;
+					++col;
+				}
+				break;
+
+			default:
+				if (encoding == TIXML_ENCODING_UTF8)
+				{
+					// Eat the 1 to 4 byte utf8 character.
+					int step = TiXmlBase::utf8ByteTable[*((unsigned char*)p)];
+					if (step == 0)
+						step = 1;		// Error case from bad encoding, but handle gracefully.
+					p += step;
+
+					// Just advance one column, of course.
+					++col;
+				}
+				else
+				{
+					++p;
+					++col;
+				}
+				break;
+		}
+	}
+	cursor.row = row;
+	cursor.col = col;
+	assert(cursor.row >= -1);
+	assert(cursor.col >= -1);
+	stamp = p;
+	assert(stamp);
+}
+
+
+const char* TiXmlBase::SkipWhiteSpace(const char* p, TiXmlEncoding encoding)
+{
+	if (!p || !*p)
+	{
+		return 0;
+	}
+	if (encoding == TIXML_ENCODING_UTF8)
+	{
+		while (*p)
+		{
+			const unsigned char* pU = (const unsigned char*)p;
+			
+			// Skip the stupid Microsoft UTF-8 Byte order marks
+			if (	*(pU+0)==TIXML_UTF_LEAD_0
+				 && *(pU+1)==TIXML_UTF_LEAD_1 
+				 && *(pU+2)==TIXML_UTF_LEAD_2)
+			{
+				p += 3;
+				continue;
+			}
+			else if (*(pU+0)==TIXML_UTF_LEAD_0
+				 && *(pU+1)==0xbfU
+				 && *(pU+2)==0xbeU)
+			{
+				p += 3;
+				continue;
+			}
+			else if (*(pU+0)==TIXML_UTF_LEAD_0
+				 && *(pU+1)==0xbfU
+				 && *(pU+2)==0xbfU)
+			{
+				p += 3;
+				continue;
+			}
+
+			if (IsWhiteSpace(*p) || *p == '\n' || *p =='\r')		// Still using old rules for white space.
+				++p;
+			else
+				break;
+		}
+	}
+	else
+	{
+		while (*p && IsWhiteSpace(*p) || *p == '\n' || *p =='\r')
+			++p;
+	}
+
+	return p;
+}
+
+#ifdef TIXML_USE_STL
+/*static*/ bool TiXmlBase::StreamWhiteSpace(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+	for (;;)
+	{
+		if (!in->good()) return false;
+
+		int c = in->peek();
+		// At this scope, we can't get to a document. So fail silently.
+		if (!IsWhiteSpace(c) || c <= 0)
+			return true;
+
+		*tag += (char) in->get();
+	}
+}
+
+/*static*/ bool TiXmlBase::StreamTo(TIXML_ISTREAM * in, int character, TIXML_STRING * tag)
+{
+	//assert(character > 0 && character < 128);	// else it won't work in utf-8
+	while (in->good())
+	{
+		int c = in->peek();
+		if (c == character)
+			return true;
+		if (c <= 0)		// Silent failure: can't get document at this scope
+			return false;
+
+		in->get();
+		*tag += (char) c;
+	}
+	return false;
+}
+#endif
+
+const char* TiXmlBase::ReadName(const char* p, TIXML_STRING * name, TiXmlEncoding encoding)
+{
+	*name = "";
+	assert(p);
+
+	// Names start with letters or underscores.
+	// Of course, in unicode, tinyxml has no idea what a letter *is*. The
+	// algorithm is generous.
+	//
+	// After that, they can be letters, underscores, numbers,
+	// hyphens, or colons. (Colons are valid ony for namespaces,
+	// but tinyxml can't tell namespaces from names.)
+	if (	 p && *p 
+		 && (IsAlpha((unsigned char) *p, encoding) || *p == '_'))
+	{
+		while (		p && *p
+				&&	(		IsAlphaNum((unsigned char) *p, encoding) 
+						 || *p == '_'
+						 || *p == '-'
+						 || *p == '.'
+						 || *p == ':'))
+		{
+			(*name) += *p;
+			++p;
+		}
+		return p;
+	}
+	return 0;
+}
+
+const char* TiXmlBase::GetEntity(const char* p, char* value, int* length, TiXmlEncoding encoding)
+{
+	// Presume an entity, and pull it out.
+		TIXML_STRING ent;
+	int i;
+	*length = 0;
+
+	if (*(p+1) && *(p+1) == '#' && *(p+2))
+	{
+		unsigned long ucs = 0;
+		ptrdiff_t delta = 0;
+		unsigned mult = 1;
+
+		if (*(p+2) == 'x')
+		{
+			// Hexadecimal.
+			if (!*(p+3)) return 0;
+
+			const char* q = p+3;
+			q = strchr(q, ';');
+
+			if (!q || !*q) return 0;
+
+			delta = q-p;
+			--q;
+
+			while (*q != 'x')
+			{
+				if (*q >= '0' && *q <= '9')
+					ucs += mult * (*q - '0');
+				else if (*q >= 'a' && *q <= 'f')
+					ucs += mult * (*q - 'a' + 10);
+				else if (*q >= 'A' && *q <= 'F')
+					ucs += mult * (*q - 'A' + 10);
+				else 
+					return 0;
+				mult *= 16;
+				--q;
+			}
+		}
+		else
+		{
+			// Decimal.
+			if (!*(p+2)) return 0;
+
+			const char* q = p+2;
+			q = strchr(q, ';');
+
+			if (!q || !*q) return 0;
+
+			delta = q-p;
+			--q;
+
+			while (*q != '#')
+			{
+				if (*q >= '0' && *q <= '9')
+					ucs += mult * (*q - '0');
+				else 
+					return 0;
+				mult *= 10;
+				--q;
+			}
+		}
+		if (encoding == TIXML_ENCODING_UTF8)
+		{
+			// convert the UCS to UTF-8
+			ConvertUTF32ToUTF8(ucs, value, length);
+		}
+		else
+		{
+			*value = (char)ucs;
+			*length = 1;
+		}
+		return p + delta + 1;
+	}
+
+	// Now try to match it.
+	for (i=0; i<NUM_ENTITY; ++i)
+	{
+		if (strncmp(entity[i].str, p, entity[i].strLength) == 0)
+		{
+			assert(strlen(entity[i].str) == entity[i].strLength);
+			*value = entity[i].chr;
+			*length = 1;
+			return (p + entity[i].strLength);
+		}
+	}
+
+	// So it wasn't an entity, its unrecognized, or something like that.
+	*value = *p;	// Don't put back the last one, since we return it!
+	//*length = 1;	// Leave unrecognized entities - this doesn't really work.
+					// Just writes strange XML.
+	return p+1;
+}
+
+
+bool TiXmlBase::StringEqual(const char* p,
+							 const char* tag,
+							 bool ignoreCase,
+							 TiXmlEncoding encoding)
+{
+	assert(p);
+	assert(tag);
+	if (!p || !*p)
+	{
+		assert(0);
+		return false;
+	}
+
+	const char* q = p;
+
+	if (ignoreCase)
+	{
+		while (*q && *tag && ToLower(*q, encoding) == ToLower(*tag, encoding))
+		{
+			++q;
+			++tag;
+		}
+
+		if (*tag == 0)
+			return true;
+	}
+	else
+	{
+		while (*q && *tag && *q == *tag)
+		{
+			++q;
+			++tag;
+		}
+
+		if (*tag == 0)		// Have we found the end of the tag, and everything equal?
+			return true;
+	}
+	return false;
+}
+
+const char* TiXmlBase::ReadText(	const char* p, 
+									TIXML_STRING * text, 
+									bool trimWhiteSpace, 
+									const char* endTag, 
+									bool caseInsensitive,
+									TiXmlEncoding encoding)
+{
+		*text = "";
+	if (	 !trimWhiteSpace			// certain tags always keep whitespace
+		 || !condenseWhiteSpace)	// if true, whitespace is always kept
+	{
+		// Keep all the white space.
+		while (		 p && *p
+				&& !StringEqual(p, endTag, caseInsensitive, encoding)
+			 )
+		{
+			int len;
+			char cArr[4] = { 0, 0, 0, 0 };
+			p = GetChar(p, cArr, &len, encoding);
+			text->append(cArr, len);
+		}
+	}
+	else
+	{
+		bool whitespace = false;
+
+		// Remove leading white space:
+		p = SkipWhiteSpace(p, encoding);
+		while (		 p && *p
+				&& !StringEqual(p, endTag, caseInsensitive, encoding))
+		{
+			if (*p == '\r' || *p == '\n')
+			{
+				whitespace = true;
+				++p;
+			}
+			else if (IsWhiteSpace(*p))
+			{
+				whitespace = true;
+				++p;
+			}
+			else
+			{
+				// If we've found whitespace, add it before the
+				// new character. Any whitespace just becomes a space.
+				if (whitespace)
+				{
+					(*text) += ' ';
+					whitespace = false;
+				}
+				int len;
+				char cArr[4] = { 0, 0, 0, 0 };
+				p = GetChar(p, cArr, &len, encoding);
+				if (len == 1)
+					(*text) += cArr[0];	// more efficient
+				else
+					text->append(cArr, len);
+			}
+		}
+	}
+	return p + strlen(endTag);
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlDocument::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+	// The basic issue with a document is that we don't know what we're
+	// streaming. Read something presumed to be a tag (and hope), then
+	// identify it, and call the appropriate stream method on the tag.
+	//
+	// This "pre-streaming" will never read the closing ">" so the
+	// sub-tag can orient itself.
+
+	if (!StreamTo(in, '<', tag)) 
+	{
+		SetError(TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return;
+	}
+
+	while (in->good())
+	{
+		int tagIndex = (int) tag->length();
+		while (in->good() && in->peek() != '>')
+		{
+			int c = in->get();
+			if (c <= 0)
+			{
+				SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+				break;
+			}
+			(*tag) += (char) c;
+		}
+
+		if (in->good())
+		{
+			// We now have something we presume to be a node of 
+			// some sort. Identify it, and call the node to
+			// continue streaming.
+			TiXmlNode* node = Identify(tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING);
+
+			if (node)
+			{
+				node->StreamIn(in, tag);
+				bool isElement = node->ToElement() != 0;
+				delete node;
+				node = 0;
+
+				// If this is the root element, we're done. Parsing will be
+				// done by the >> operator.
+				if (isElement)
+				{
+					return;
+				}
+			}
+			else
+			{
+				SetError(TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN);
+				return;
+			}
+		}
+	}
+	// We should have returned sooner.
+	SetError(TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN);
+}
+
+#endif
+
+const char* TiXmlDocument::Parse(const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding)
+{
+	ClearError();
+
+	// Parse away, at the document level. Since a document
+	// contains nothing but other tags, most of what happens
+	// here is skipping white space.
+	if (!p || !*p)
+	{
+		SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return 0;
+	}
+
+	// Note that, for a document, this needs to come
+	// before the while space skip, so that parsing
+	// starts from the pointer we are given.
+	location.Clear();
+	if (prevData)
+	{
+		location.row = prevData->cursor.row;
+		location.col = prevData->cursor.col;
+	}
+	else
+	{
+		location.row = 0;
+		location.col = 0;
+	}
+	TiXmlParsingData data(p, TabSize(), location.row, location.col);
+	location = data.Cursor();
+
+	if (encoding == TIXML_ENCODING_UNKNOWN)
+	{
+		// Check for the Microsoft UTF-8 lead bytes.
+		const unsigned char* pU = (const unsigned char*)p;
+		if (	*(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
+			 && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
+			 && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2)
+		{
+			encoding = TIXML_ENCODING_UTF8;
+			useMicrosoftBOM = true;
+		}
+	}
+
+		p = SkipWhiteSpace(p, encoding);
+	if (!p)
+	{
+		SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN);
+		return 0;
+	}
+
+	while (p && *p)
+	{
+		TiXmlNode* node = Identify(p, encoding);
+		if (node)
+		{
+			p = node->Parse(p, &data, encoding);
+			LinkEndChild(node);
+		}
+		else
+		{
+			break;
+		}
+
+		// Did we get encoding info?
+		if (	 encoding == TIXML_ENCODING_UNKNOWN
+			 && node->ToDeclaration())
+		{
+			TiXmlDeclaration* dec = node->ToDeclaration();
+			const char* enc = dec->Encoding();
+			assert(enc);
+
+			if (*enc == 0)
+				encoding = TIXML_ENCODING_UTF8;
+			else if (StringEqual(enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN))
+				encoding = TIXML_ENCODING_UTF8;
+			else if (StringEqual(enc, "UTF8", true, TIXML_ENCODING_UNKNOWN))
+				encoding = TIXML_ENCODING_UTF8;	// incorrect, but be nice
+			else 
+				encoding = TIXML_ENCODING_LEGACY;
+		}
+
+		p = SkipWhiteSpace(p, encoding);
+	}
+
+	// Was this empty?
+	if (!firstChild) {
+		SetError(TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding);
+		return 0;
+	}
+
+	// All is well.
+	return p;
+}
+
+void TiXmlDocument::SetError(int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding)
+{	
+	// The first error in a chain is more accurate - don't set again!
+	if (error)
+		return;
+
+	assert(err > 0 && err < TIXML_ERROR_STRING_COUNT);
+	error	 = true;
+	errorId = err;
+	errorDesc = errorString[ errorId ];
+
+	errorLocation.Clear();
+	if (pError && data)
+	{
+		data->Stamp(pError, encoding);
+		errorLocation = data->Cursor();
+	}
+}
+
+
+TiXmlNode* TiXmlNode::Identify(const char* p, TiXmlEncoding encoding)
+{
+	TiXmlNode* returnNode = 0;
+
+	p = SkipWhiteSpace(p, encoding);
+	if (!p || !*p || *p != '<')
+	{
+		return 0;
+	}
+
+	TiXmlDocument* doc = GetDocument();
+	p = SkipWhiteSpace(p, encoding);
+
+	if (!p || !*p)
+	{
+		return 0;
+	}
+
+	// What is this thing? 
+	// - Elements start with a letter or underscore, but xml is reserved.
+	// - Comments: <!--
+	// - Decleration: <?xml
+	// - Everthing else is unknown to tinyxml.
+	//
+
+	const char* xmlHeader = { "<?xml" };
+	const char* commentHeader = { "<!--" };
+	const char* dtdHeader = { "<!" };
+	const char* cdataHeader = { "<![CDATA[" };
+
+	if (StringEqual(p, xmlHeader, true, encoding))
+	{
+		#ifdef DEBUG_PARSER
+			TIXML_LOG("XML parsing Declaration\n");
+		#endif
+		returnNode = new TiXmlDeclaration();
+	}
+	else if (StringEqual(p, commentHeader, false, encoding))
+	{
+		#ifdef DEBUG_PARSER
+			TIXML_LOG("XML parsing Comment\n");
+		#endif
+		returnNode = new TiXmlComment();
+	}
+	else if (StringEqual(p, cdataHeader, false, encoding))
+	{
+		#ifdef DEBUG_PARSER
+			TIXML_LOG("XML parsing CDATA\n");
+		#endif
+		TiXmlText* text = new TiXmlText("");
+		text->SetCDATA(true);
+		returnNode = text;
+	}
+	else if (StringEqual(p, dtdHeader, false, encoding))
+	{
+		#ifdef DEBUG_PARSER
+			TIXML_LOG("XML parsing Unknown(1)\n");
+		#endif
+		returnNode = new TiXmlUnknown();
+	}
+	else if (	 IsAlpha(*(p+1), encoding)
+				|| *(p+1) == '_')
+	{
+		#ifdef DEBUG_PARSER
+			TIXML_LOG("XML parsing Element\n");
+		#endif
+		returnNode = new TiXmlElement("");
+	}
+	else
+	{
+		#ifdef DEBUG_PARSER
+			TIXML_LOG("XML parsing Unknown(2)\n");
+		#endif
+		returnNode = new TiXmlUnknown();
+	}
+
+	if (returnNode)
+	{
+		// Set the parent, so it can report errors
+		returnNode->parent = this;
+	}
+	else
+	{
+		if (doc)
+			doc->SetError(TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN);
+	}
+	return returnNode;
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlElement::StreamIn (TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+	// We're called with some amount of pre-parsing. That is, some of "this"
+	// element is in "tag". Go ahead and stream to the closing ">"
+	while (in->good())
+	{
+		int c = in->get();
+		if (c <= 0)
+		{
+			TiXmlDocument* document = GetDocument();
+			if (document)
+				document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+			return;
+		}
+		(*tag) += (char) c ;
+		
+		if (c == '>')
+			break;
+	}
+
+	if (tag->length() < 3) return;
+
+	// Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
+	// If not, identify and stream.
+
+	if (	 tag->at(tag->length() - 1) == '>' 
+		 && tag->at(tag->length() - 2) == '/')
+	{
+		// All good!
+		return;
+	}
+	else if (tag->at(tag->length() - 1) == '>')
+	{
+		// There is more. Could be:
+		//		text
+		//		closing tag
+		//		another node.
+		for (;;)
+		{
+			StreamWhiteSpace(in, tag);
+
+			// Do we have text?
+			if (in->good() && in->peek() != '<') 
+			{
+				// Yep, text.
+				TiXmlText text("");
+				text.StreamIn(in, tag);
+
+				// What follows text is a closing tag or another node.
+				// Go around again and figure it out.
+				continue;
+			}
+
+			// We now have either a closing tag...or another node.
+			// We should be at a "<", regardless.
+			if (!in->good()) return;
+			assert(in->peek() == '<');
+			int tagIndex = (int) tag->length();
+
+			bool closingTag = false;
+			bool firstCharFound = false;
+
+			for (;;)
+			{
+				if (!in->good())
+					return;
+
+				int c = in->peek();
+				if (c <= 0)
+				{
+					TiXmlDocument* document = GetDocument();
+					if (document)
+						document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+					return;
+				}
+				
+				if (c == '>')
+					break;
+
+				*tag += (char) c;
+				in->get();
+
+				if (!firstCharFound && c != '<' && !IsWhiteSpace(c))
+				{
+					firstCharFound = true;
+					if (c == '/')
+						closingTag = true;
+				}
+			}
+			// If it was a closing tag, then read in the closing '>' to clean up the input stream.
+			// If it was not, the streaming will be done by the tag.
+			if (closingTag)
+			{
+				if (!in->good())
+					return;
+
+				int c = in->get();
+				if (c <= 0)
+				{
+					TiXmlDocument* document = GetDocument();
+					if (document)
+						document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+					return;
+				}
+				assert(c == '>');
+				*tag += (char) c;
+
+				// We are done, once we've found our closing tag.
+				return;
+			}
+			else
+			{
+				// If not a closing tag, id it, and stream.
+				const char* tagloc = tag->c_str() + tagIndex;
+				TiXmlNode* node = Identify(tagloc, TIXML_DEFAULT_ENCODING);
+				if (!node)
+					return;
+				node->StreamIn(in, tag);
+				delete node;
+				node = 0;
+
+				// No return: go around from the beginning: text, closing tag, or node.
+			}
+		}
+	}
+}
+#endif
+
+const char* TiXmlElement::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+	p = SkipWhiteSpace(p, encoding);
+	TiXmlDocument* document = GetDocument();
+
+	if (!p || !*p)
+	{
+		if (document) document->SetError(TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding);
+		return 0;
+	}
+
+	if (data)
+	{
+		data->Stamp(p, encoding);
+		location = data->Cursor();
+	}
+
+	if (*p != '<')
+	{
+		if (document) document->SetError(TIXML_ERROR_PARSING_ELEMENT, p, data, encoding);
+		return 0;
+	}
+
+	p = SkipWhiteSpace(p+1, encoding);
+
+	// Read the name.
+	const char* pErr = p;
+
+		p = ReadName(p, &value, encoding);
+	if (!p || !*p)
+	{
+		if (document)	document->SetError(TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding);
+		return 0;
+	}
+
+		TIXML_STRING endTag ("</");
+	endTag += value;
+	endTag += ">";
+
+	// Check for and read attributes. Also look for an empty
+	// tag or an end tag.
+	while (p && *p)
+	{
+		pErr = p;
+		p = SkipWhiteSpace(p, encoding);
+		if (!p || !*p)
+		{
+			if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding);
+			return 0;
+		}
+		if (*p == '/')
+		{
+			++p;
+			// Empty tag.
+			if (*p	!= '>')
+			{
+				if (document) document->SetError(TIXML_ERROR_PARSING_EMPTY, p, data, encoding);		
+				return 0;
+			}
+			return (p+1);
+		}
+		else if (*p == '>')
+		{
+			// Done with attributes (if there were any.)
+			// Read the value -- which can include other
+			// elements -- read the end tag, and return.
+			++p;
+			p = ReadValue(p, data, encoding);		// Note this is an Element method, and will set the error if one happens.
+			if (!p || !*p)
+				return 0;
+
+			// We should find the end tag now
+			if (StringEqual(p, endTag.c_str(), false, encoding))
+			{
+				p += endTag.length();
+				return p;
+			}
+			else
+			{
+				if (document) document->SetError(TIXML_ERROR_READING_END_TAG, p, data, encoding);
+				return 0;
+			}
+		}
+		else
+		{
+			// Try to read an attribute:
+			TiXmlAttribute* attrib = new TiXmlAttribute();
+			if (!attrib)
+			{
+				if (document) document->SetError(TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding);
+				return 0;
+			}
+
+			attrib->SetDocument(document);
+			const char* pErr = p;
+			p = attrib->Parse(p, data, encoding);
+
+			if (!p || !*p)
+			{
+				if (document) document->SetError(TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding);
+				delete attrib;
+				return 0;
+			}
+
+			// Handle the strange case of double attributes:
+			TiXmlAttribute* node = attributeSet.Find(attrib->NameTStr());
+			if (node)
+			{
+				node->SetValue(attrib->Value());
+				delete attrib;
+				return 0;
+			}
+
+			attributeSet.Add(attrib);
+		}
+	}
+	return p;
+}
+
+
+const char* TiXmlElement::ReadValue(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+	TiXmlDocument* document = GetDocument();
+
+	// Read in text and elements in any order.
+	const char* pWithWhiteSpace = p;
+	p = SkipWhiteSpace(p, encoding);
+
+	while (p && *p)
+	{
+		if (*p != '<')
+		{
+			// Take what we have, make a text element.
+			TiXmlText* textNode = new TiXmlText("");
+
+			if (!textNode)
+			{
+				if (document) document->SetError(TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding);
+						return 0;
+			}
+
+			if (TiXmlBase::IsWhiteSpaceCondensed())
+			{
+				p = textNode->Parse(p, data, encoding);
+			}
+			else
+			{
+				// Special case: we want to keep the white space
+				// so that leading spaces aren't removed.
+				p = textNode->Parse(pWithWhiteSpace, data, encoding);
+			}
+
+			if (!textNode->Blank())
+				LinkEndChild(textNode);
+			else
+				delete textNode;
+		} 
+		else 
+		{
+			// We hit a '<'
+			// Have we hit a new element or an end tag? This could also be
+			// a TiXmlText in the "CDATA" style.
+			if (StringEqual(p, "</", false, encoding))
+			{
+				return p;
+			}
+			else
+			{
+				TiXmlNode* node = Identify(p, encoding);
+				if (node)
+				{
+					p = node->Parse(p, data, encoding);
+					LinkEndChild(node);
+				}				
+				else
+				{
+					return 0;
+				}
+			}
+		}
+		pWithWhiteSpace = p;
+		p = SkipWhiteSpace(p, encoding);
+	}
+
+	if (!p)
+	{
+		if (document) document->SetError(TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding);
+	}	
+	return p;
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlUnknown::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+	while (in->good())
+	{
+		int c = in->get();	
+		if (c <= 0)
+		{
+			TiXmlDocument* document = GetDocument();
+			if (document)
+				document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+			return;
+		}
+		(*tag) += (char) c;
+
+		if (c == '>')
+		{
+			// All is well.
+			return;		
+		}
+	}
+}
+#endif
+
+
+const char* TiXmlUnknown::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+	TiXmlDocument* document = GetDocument();
+	p = SkipWhiteSpace(p, encoding);
+
+	if (data)
+	{
+		data->Stamp(p, encoding);
+		location = data->Cursor();
+	}
+	if (!p || !*p || *p != '<')
+	{
+		if (document) document->SetError(TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding);
+		return 0;
+	}
+	++p;
+		value = "";
+
+	while (p && *p && *p != '>')
+	{
+		value += *p;
+		++p;
+	}
+
+	if (!p)
+	{
+		if (document)	document->SetError(TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding);
+	}
+	if (*p == '>')
+		return p+1;
+	return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlComment::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+	while (in->good())
+	{
+		int c = in->get();	
+		if (c <= 0)
+		{
+			TiXmlDocument* document = GetDocument();
+			if (document)
+				document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+			return;
+		}
+
+		(*tag) += (char) c;
+
+		if (c == '>' 
+			 && tag->at(tag->length() - 2) == '-'
+			 && tag->at(tag->length() - 3) == '-')
+		{
+			// All is well.
+			return;		
+		}
+	}
+}
+#endif
+
+
+const char* TiXmlComment::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+	TiXmlDocument* document = GetDocument();
+	value = "";
+
+	p = SkipWhiteSpace(p, encoding);
+
+	if (data)
+	{
+		data->Stamp(p, encoding);
+		location = data->Cursor();
+	}
+	const char* startTag = "<!--";
+	const char* endTag	 = "-->";
+
+	if (!StringEqual(p, startTag, false, encoding))
+	{
+		document->SetError(TIXML_ERROR_PARSING_COMMENT, p, data, encoding);
+		return 0;
+	}
+	p += strlen(startTag);
+	p = ReadText(p, &value, false, endTag, false, encoding);
+	return p;
+}
+
+
+const char* TiXmlAttribute::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+	p = SkipWhiteSpace(p, encoding);
+	if (!p || !*p) return 0;
+
+//	int tabsize = 4;
+//	if (document)
+//		tabsize = document->TabSize();
+
+	if (data)
+	{
+		data->Stamp(p, encoding);
+		location = data->Cursor();
+	}
+	// Read the name, the '=' and the value.
+	const char* pErr = p;
+	p = ReadName(p, &name, encoding);
+	if (!p || !*p)
+	{
+		if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding);
+		return 0;
+	}
+	p = SkipWhiteSpace(p, encoding);
+	if (!p || !*p || *p != '=')
+	{
+		if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);
+		return 0;
+	}
+
+	++p;	// skip '='
+	p = SkipWhiteSpace(p, encoding);
+	if (!p || !*p)
+	{
+		if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);
+		return 0;
+	}
+	
+	const char* end;
+	const char SINGLE_QUOTE = '\'';
+	const char DOUBLE_QUOTE = '\"';
+
+	if (*p == SINGLE_QUOTE)
+	{
+		++p;
+		end = "\'";		// single quote in string
+		p = ReadText(p, &value, false, end, false, encoding);
+	}
+	else if (*p == DOUBLE_QUOTE)
+	{
+		++p;
+		end = "\"";		// double quote in string
+		p = ReadText(p, &value, false, end, false, encoding);
+	}
+	else
+	{
+		// All attribute values should be in single or double quotes.
+		// But this is such a common error that the parser will try
+		// its best, even without them.
+		value = "";
+		while (	 p && *p											// existence
+				&& !IsWhiteSpace(*p) && *p != '\n' && *p != '\r'	// whitespace
+				&& *p != '/' && *p != '>')							// tag end
+		{
+			if (*p == SINGLE_QUOTE || *p == DOUBLE_QUOTE) {
+				// [ 1451649 ] Attribute values with trailing quotes not handled correctly
+				// We did not have an opening quote but seem to have a 
+				// closing one. Give up and throw an error.
+				if (document) document->SetError(TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding);
+				return 0;
+			}
+			value += *p;
+			++p;
+		}
+	}
+	return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlText::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+	if (cdata)
+	{
+		int c = in->get();	
+		if (c <= 0)
+		{
+			TiXmlDocument* document = GetDocument();
+			if (document)
+				document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+			return;
+		}
+
+		(*tag) += (char) c;
+
+		if (c == '>' 
+			 && tag->at(tag->length() - 2) == ']'
+			 && tag->at(tag->length() - 3) == ']')
+		{
+			// All is well.
+			return;		
+		}
+	}
+	else
+	{
+		while (in->good())
+		{
+			int c = in->peek();	
+			if (c == '<')
+				return;
+			if (c <= 0)
+			{
+				TiXmlDocument* document = GetDocument();
+				if (document)
+					document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+				return;
+			}
+
+			(*tag) += (char) c;
+			in->get();
+		}
+	}
+}
+#endif
+
+const char* TiXmlText::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding encoding)
+{
+	value = "";
+	TiXmlDocument* document = GetDocument();
+
+	if (data)
+	{
+		data->Stamp(p, encoding);
+		location = data->Cursor();
+	}
+
+	const char* const startTag = "<![CDATA[";
+	const char* const endTag	 = "]]>";
+
+	if (cdata || StringEqual(p, startTag, false, encoding))
+	{
+		cdata = true;
+
+		if (!StringEqual(p, startTag, false, encoding))
+		{
+			document->SetError(TIXML_ERROR_PARSING_CDATA, p, data, encoding);
+			return 0;
+		}
+		p += strlen(startTag);
+
+		// Keep all the white space, ignore the encoding, etc.
+		while (		 p && *p
+				&& !StringEqual(p, endTag, false, encoding)
+			 )
+		{
+			value += *p;
+			++p;
+		}
+
+		TIXML_STRING dummy; 
+		p = ReadText(p, &dummy, false, endTag, false, encoding);
+		return p;
+	}
+	else
+	{
+		bool ignoreWhite = true;
+
+		const char* end = "<";
+		p = ReadText(p, &value, ignoreWhite, end, false, encoding);
+		if (p)
+			return p-1;	// don't truncate the '<'
+		return 0;
+	}
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlDeclaration::StreamIn(TIXML_ISTREAM * in, TIXML_STRING * tag)
+{
+	while (in->good())
+	{
+		int c = in->get();
+		if (c <= 0)
+		{
+			TiXmlDocument* document = GetDocument();
+			if (document)
+				document->SetError(TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN);
+			return;
+		}
+		(*tag) += (char) c;
+
+		if (c == '>')
+		{
+			// All is well.
+			return;
+		}
+	}
+}
+#endif
+
+const char* TiXmlDeclaration::Parse(const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding)
+{
+	p = SkipWhiteSpace(p, _encoding);
+	// Find the beginning, find the end, and look for
+	// the stuff in-between.
+	TiXmlDocument* document = GetDocument();
+	if (!p || !*p || !StringEqual(p, "<?xml", true, _encoding))
+	{
+		if (document) document->SetError(TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding);
+		return 0;
+	}
+	if (data)
+	{
+		data->Stamp(p, _encoding);
+		location = data->Cursor();
+	}
+	p += 5;
+
+	version = "";
+	encoding = "";
+	standalone = "";
+
+	while (p && *p)
+	{
+		if (*p == '>')
+		{
+			++p;
+			return p;
+		}
+
+		p = SkipWhiteSpace(p, _encoding);
+		if (StringEqual(p, "version", true, _encoding))
+		{
+			TiXmlAttribute attrib;
+			p = attrib.Parse(p, data, _encoding);		
+			version = attrib.Value();
+		}
+		else if (StringEqual(p, "encoding", true, _encoding))
+		{
+			TiXmlAttribute attrib;
+			p = attrib.Parse(p, data, _encoding);		
+			encoding = attrib.Value();
+		}
+		else if (StringEqual(p, "standalone", true, _encoding))
+		{
+			TiXmlAttribute attrib;
+			p = attrib.Parse(p, data, _encoding);		
+			standalone = attrib.Value();
+		}
+		else
+		{
+			// Read over whatever it is.
+			while (p && *p && *p != '>' && !IsWhiteSpace(*p))
+				++p;
+		}
+	}
+	return 0;
+}
+
+bool TiXmlText::Blank() const
+{
+	for (unsigned i=0; i<value.length(); i++)
+		if (!IsWhiteSpace(value[i]))
+			return false;
+	return true;
+}
+
diff --git a/plugins/UserInfoEx/src/init.cpp b/plugins/UserInfoEx/src/init.cpp
new file mode 100644
index 0000000000..3b7b0b8249
--- /dev/null
+++ b/plugins/UserInfoEx/src/init.cpp
@@ -0,0 +1,315 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/init.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "version.h"
+
+#include "mir_menuitems.h"
+#include "ctrl_base.h"
+#include "ctrl_button.h"
+#include "ctrl_contact.h"
+#include "dlg_propsheet.h"
+#include "dlg_anniversarylist.h"
+#include "psp_options.h"
+#include "ex_import/svc_ExImport.h"
+//#include "ex_import/svc_ExImVCF.h"
+#include "svc_avatar.h"
+#include "svc_contactinfo.h"
+#include "svc_email.h"
+#include "svc_gender.h"
+#include "svc_homepage.h"
+#include "svc_phone.h"
+#include "svc_refreshci.h"
+#include "svc_reminder.h"
+#include "svc_timezone.h"
+#include "svc_timezone_old.h"
+#include "flags/svc_flags.h"
+
+static PLUGININFOEX pluginInfo = {
+	sizeof(PLUGININFOEX),
+	__PLUGIN_DISPLAY_NAME,
+	__VERSION_DWORD,
+	__DESC,
+	__AUTHOR,
+	__AUTHOREMAIL,
+	__COPYRIGHT,
+	__AUTHORWEB,
+	UNICODE_AWARE,
+	MIID_UIUSERINFOEX
+};
+
+static HANDLE ghModulesLoadedHook		= NULL;
+static HANDLE ghTopToolBarLoaded		= NULL;
+static HANDLE ghModernToolBarLoaded		= NULL;
+static HANDLE ghShutdownHook			= NULL;
+static HANDLE ghPrebuildStatusMenu		= NULL;
+int hLangpack;
+
+/*
+============================================================================================
+	event hooks
+============================================================================================
+*/
+
+/**
+ * This function is called by the ME_TTB_MODULELOADED event.
+ * It adds a set of buttons to the TopToolbar plugin.
+ *
+ * @param	wParam	- not used
+ * @param	lParam	- not used
+ *
+ * @return	always 0
+ **/
+static INT OnTopToolBarLoaded(WPARAM wParam, LPARAM lParam)
+{
+	DlgAnniversaryListOnTopToolBarLoaded();
+	SvcReminderOnTopToolBarLoaded();
+	return 0;
+}
+
+/**
+ * This function is called by Miranda just after loading all system modules.
+ *
+ * @param	wParam	- not used
+ * @param	lParam	- not used
+ *
+ * @return	always 0
+ **/
+static INT OnModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+	myGlobals.HaveCListExtraIcons		= ServiceExists(MS_CLIST_EXTRA_SET_ICON);
+	myGlobals.ExtraIconsServiceExist	= ServiceExists(MS_EXTRAICON_REGISTER);
+	myGlobals.PopUpActionsExist			= ServiceExists(MS_POPUP_REGISTERACTIONS);
+	myGlobals.MsgAddIconExist			= ServiceExists(MS_MSG_ADDICON);
+
+	// init meta contacts
+	INT_PTR ptr = CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+	myGlobals.szMetaProto = (ptr != CALLSERVICE_NOTFOUND) ? (LPCSTR)ptr : NULL;
+
+	// options
+	OptionsLoadModule();
+	// create services to receive string lists of languages and timezones
+	SvcConstantsLoadModule();
+	// load module to remind user about birthday and a anniversary
+	SvcReminderOnModulesLoaded();
+	// load extended intagration services
+	SvcEMailOnModulesLoaded();
+	SvcHomepageLoadModule();
+	SvcPhoneLoadModule();
+	SvcGenderLoadModule();
+	SvcFlagsOnModulesLoaded();
+
+#ifdef _DEBUG // new feature, not in release jet
+	NServices::NAvatar::OnModulesLoaded();
+#endif
+
+	// build contact's menuitems
+	RebuildMenu();
+	ghPrebuildStatusMenu = HookEvent( ME_CLIST_PREBUILDSTATUSMENU, (MIRANDAHOOK)RebuildAccount);
+
+	// install known modules strings to database
+	DB::Setting::WriteAString(NULL, "KnownModules", MODULELONGNAME, USERINFO","MODNAME","MOD_MBIRTHDAY","MODNAMEFLAGS);
+
+	return 0;
+}
+
+static INT OnShutdown(WPARAM wParam, LPARAM lParam)
+{
+	UnhookEvent(ghShutdownHook);
+	DlgContactInfoUnLoadModule();
+	SvcReminderUnloadModule();
+
+	// uninitialize classes
+	CtrlContactUnLoadModule();
+	CtrlButtonUnloadModule();
+
+	SvcConstantsUnloadModule();
+	UnhookEvent(ghPrebuildStatusMenu);
+	SvcEMailUnloadModule();
+	SvcFlagsUnloadModule();
+	SvcGenderUnloadModule();
+	SvcHomepageUnloadModule();
+	SvcPhoneUnloadModule();
+
+	mir_free(hMenuItemAccount);
+	return 0;
+}
+
+static BOOL CoreCheck()
+{
+	BOOL	bOk = TRUE;
+	CHAR	szVer[260];
+	TCHAR	tszExePath[1024];
+
+	GetModuleFileName(GetModuleHandle(NULL), tszExePath, SIZEOF(tszExePath));
+	CallService(MS_SYSTEM_GETVERSIONTEXT, SIZEOF(szVer), (LPARAM)szVer);
+
+	strlwr(szVer);
+	_tcslwr(tszExePath);
+
+
+	bOk *= (GetVersion() & 0x80000000) == 0;
+	bOk *= strstr(szVer, "unicode") != 0;
+
+
+	bOk *= _tcsstr(_tcsrchr(tszExePath, '\\'), _T("miranda")) != 0;
+	bOk *= !strstr(szVer, "coffee") && strncmp(szVer, "1.", 2) && !strstr(szVer, " 1.");
+	bOk *= myGlobals.mirandaVersion < PLUGIN_MAKE_VERSION(1,0,0,0);
+	return bOk;
+}
+
+/*
+============================================================================================
+	plugin interface & DllEntrypoint
+============================================================================================
+*/
+
+/**
+ * This function is called by Miranda to get some information about this plugin.
+ *
+ * @return	pointer to pluginInfo struct
+ **/
+extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+	myGlobals.mirandaVersion = mirandaVersion;
+	return &pluginInfo;
+}
+
+/**
+ * This function returns the provided interfaces.
+ *
+ * @return	array of interfaces
+ **/
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {
+	MIID_UIUSERINFOEX,		// this is just me
+	MIID_UIUSERINFO,		// replace the default userinfo module
+	MIID_CONTACTINFO,		// indicate, that MS_CONTACT_GETCONTACTINFO service is provided
+	MIID_REMINDER,			// indicate an Reminder of being provided
+	MIID_SREMAIL,			// Send/Receive E-Mail service is provided
+	MIID_LAST
+};
+
+/**
+ * This function is called by Miranda just to make it possible to unload some memory, ...
+ *
+ * @return	0
+ **/
+extern "C" INT __declspec(dllexport) Unload(VOID)
+{
+	return 0;
+}
+
+/**
+ * This function is called by Miranda to initialize the plugin.
+ *
+ * @return	0
+ **/
+extern "C" INT __declspec(dllexport) Load(void)
+{
+	mir_getLP(&pluginInfo);
+	if ( !CoreCheck())
+		return 1;
+
+	// init common controls
+	INITCOMMONCONTROLSEX ccEx;
+	ccEx.dwSize = sizeof(ccEx);
+	ccEx.dwICC = ICC_WIN95_CLASSES|ICC_DATE_CLASSES;
+	InitCommonControlsEx(&ccEx);
+
+	ZeroMemory(&myGlobals, sizeof(MGLOBAL));
+
+	// init clist interface
+	pcli = (CLIST_INTERFACE*)CallService(MS_CLIST_RETRIEVE_INTERFACE, 0, (LPARAM)0);
+
+	// init new miranda timezone interface
+	mir_getTMI(&tmi);
+
+	// init freeimage interface
+	INT_PTR result = CALLSERVICE_NOTFOUND;
+	if(ServiceExists(MS_IMG_GETINTERFACE))
+		result = CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM)&FIP);
+
+	if(FIP == NULL || result != S_OK) {
+		MessageBoxEx(NULL, TranslateT("Fatal error, image services not found. Flags Module will be disabled."), _T("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL, 0);
+		return 1;
+	}
+
+	if (IsWinVerVistaPlus())
+	{
+		HMODULE hDwmApi = LoadLibraryA("dwmapi.dll");
+		if (hDwmApi)
+			dwmIsCompositionEnabled = (pfnDwmIsCompositionEnabled)GetProcAddress(hDwmApi,"DwmIsCompositionEnabled");
+	}
+
+	// check for dbx_tree
+	myGlobals.UseDbxTree = ServiceExists("DBT/Entity/GetRoot");
+
+	// load icon library
+	IcoLib_LoadModule();
+
+	SvcFlagsLoadModule();
+	tmi.getTimeZoneTime ? SvcTimezoneLoadModule() : SvcTimezoneLoadModule_old();
+	SvcContactInfoLoadModule();
+	SvcEMailLoadModule();
+	SvcRefreshContactInfoLoadModule();
+
+	CtrlContactLoadModule();
+	// load my button class
+	CtrlButtonLoadModule();
+	// initializes the Ex/Import Services
+	SvcExImport_LoadModule();
+	// load the UserInfoPropertySheet module
+	DlgContactInfoLoadModule();
+
+	// Anniversary stuff
+	DlgAnniversaryListLoadModule();
+	SvcReminderLoadModule();
+
+	// Now the module is loaded! Start initializing certain things
+	ghModulesLoadedHook = HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+	ghTopToolBarLoaded = HookEvent(ME_TTB_MODULELOADED, OnTopToolBarLoaded);
+	ghShutdownHook = HookEvent(ME_SYSTEM_SHUTDOWN, OnShutdown);
+	return 0;
+}
+
+/**
+ * Windows needs it for loading.
+ *
+ * @return	TRUE
+ **/
+BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID lpvReserved)
+{
+	switch (fdwReason) {
+		case DLL_PROCESS_ATTACH:
+			ghInst = hinst;
+			break;
+	}
+	return TRUE;
+}
diff --git a/plugins/UserInfoEx/src/mir_contactqueue.cpp b/plugins/UserInfoEx/src/mir_contactqueue.cpp
new file mode 100644
index 0000000000..93e3bbd976
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_contactqueue.cpp
@@ -0,0 +1,425 @@
+/*
+Copyright �2006 Ricardo Pescuma Domenecci
+
+Modified  �2008-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_contactqueue.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "mir_contactqueue.h"
+#include <process.h>
+
+/**
+ * This static helper function is used to sort the queue items by time
+ * beginning with the next upcoming item to call the Callback for.
+ *
+ * @param		i1	- the first queue item
+ * @param		i2	- the second queue item
+ *
+ * @return	The function returns the time slack between the two items.
+ **/
+static INT QueueSortItems(const CQueueItem *i1, const CQueueItem *i2)
+{
+	INT rc = i1->check_time - i2->check_time;
+	if (!rc)
+	{
+		rc = i1->hContact != i2->hContact;
+	}
+	return rc;
+}
+
+/**
+ *
+ *
+ **/
+CContactQueue::CContactQueue(INT initialSize)
+	: _queue(initialSize, QueueSortItems)
+{
+	_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	_status = RUNNING;
+
+	InitializeCriticalSection(&_cs);
+
+	mir_forkthread((pThreadFunc)CContactQueue::ThreadProc, this);
+}
+
+/**
+ *
+ *
+ **/
+CContactQueue::~CContactQueue()
+{
+	if (_status == RUNNING)
+	{
+		_status = STOPPING;
+	}
+	SetEvent(_hEvent);
+
+	for (INT count = 0; _status != STOPPED && ++count < 50;)
+	{
+		Sleep(10);
+	}
+
+	for (INT i = 0; i < _queue.getCount(); i++)
+	{
+		mir_free(_queue[i]);
+	}
+	_queue.destroy();
+
+	CloseHandle(_hEvent);
+	DeleteCriticalSection(&_cs);
+}
+
+/**
+ *
+ *
+ **/
+VOID CContactQueue::Lock()
+{
+	EnterCriticalSection(&_cs);
+}
+
+/**
+ *
+ *
+ **/
+VOID CContactQueue::Release()
+{
+	LeaveCriticalSection(&_cs);
+}
+
+/**
+ * This function removes all queue items.
+ *
+ * @param		none
+ *
+ * @return		nothing
+ **/
+VOID CContactQueue::RemoveAll()
+{
+	Lock();
+
+	for (INT i = _queue.getCount() - 1; i >= 0; --i)
+	{
+		mir_free(_queue[i]);
+	}
+	_queue.destroy();
+
+	Release();
+}
+
+/**
+ * This function removes all queue items for the hContact.
+ *
+ * @param		hContact	- the contact whose queue items to delete
+ *
+ * @return	nothing
+ **/
+VOID CContactQueue::RemoveAll(HANDLE hContact)
+{
+	Lock();
+
+	for (INT i = _queue.getCount() - 1; i >= 0; --i)
+	{
+		CQueueItem *qi = _queue[i];
+
+		if (qi->hContact == hContact)
+		{
+			_queue.remove(i);
+			mir_free(qi);
+		}
+	}
+
+	Release();
+}
+
+/**
+ * This function removes all queue items for the hContact considering the correct parameter.
+ *
+ * @param		hContact	- the contact whose queue items to delete
+ * @param		param			- a caller defined parameter passed to the callback function
+ *
+ * @return	nothing
+ **/
+VOID CContactQueue::RemoveAllConsiderParam(HANDLE hContact, PVOID param)
+{
+	Lock();
+
+	for (INT i = _queue.getCount() - 1; i >= 0; --i)
+	{
+		CQueueItem *qi = _queue[i];
+
+		if (qi->hContact == hContact && qi->param == param)
+		{
+			_queue.remove(i);
+			mir_free(qi);
+		}
+	}
+
+	Release();
+}
+
+/**
+ * This method adds the desired new item.
+ *
+ * @param		waitTime	- the time to wait until the callback is desired to run
+ * @param		hContact	- the contact to perform the action for
+ * @param		param			- a caller defined parameter passed to the callback function
+ *
+ * @retval	TRUE	- The item is added to the queue successfully.
+ * @retval	FALSE	- The item is not added to the queue.
+ **/
+BOOL CContactQueue::Add(INT waitTime, HANDLE hContact, PVOID param)
+{
+	BOOL rc;
+
+	Lock();
+
+	rc = InternalAdd(waitTime, hContact, param);
+
+	Release();
+
+	return rc;
+}
+
+/**
+ * This method adds the desired new item only, if the queue does not yet contain
+ * an item for the contact.
+ *
+ * @param		waitTime	- the time to wait until the callback is desired to run
+ * @param		hContact	- the contact to perform the action for
+ * @param		param			- a caller defined parameter passed to the callback function
+ *
+ * @retval	TRUE	- The item is added to the queue successfully.
+ * @retval	FALSE	- The item is not added to the queue.
+ **/
+BOOL CContactQueue::AddIfDontHave(INT waitTime, HANDLE hContact, PVOID param)
+{
+	INT i;
+	BOOL rc;
+
+	Lock();
+
+	for (i = _queue.getCount() - 1; i >= 0; --i)
+	{
+		if (_queue[i]->hContact == hContact)
+		{
+			break;
+		}
+	}
+
+	rc = (i == -1) ? InternalAdd(waitTime, hContact, param) : 0;
+
+	Release();
+
+	return rc;
+}
+
+/**
+ * This method removes all existing queue items for the contact and adds a new queue item
+ * for the given contact. This method might be used to move an existing entry,
+ * whose check_time has changed.
+ *
+ * @param		waitTime	- the time to wait until the callback is desired to run
+ * @param		hContact	- the contact to perform the action for
+ * @param		param			- a caller defined parameter passed to the callback function
+ *
+ * @retval	TRUE	- The item is added to the queue successfully.
+ * @retval	FALSE	- The item is not added to the queue.
+ **/
+BOOL CContactQueue::AddUnique(INT waitTime, HANDLE hContact, PVOID param)
+{
+	BOOL rc;
+
+	Lock();
+
+	RemoveAll(hContact);
+	rc = InternalAdd(waitTime, hContact, param);
+
+	Release();
+
+	return rc;
+}
+
+/**
+ * This method removes all existing queue items for the contact with the same parameter as @e param
+ * and adds a new queue item for the given contact. This method might be used to move an existing
+ * entry, whose check_time has changed.
+ *
+ * @param		waitTime	- the time to wait until the callback is desired to run
+ * @param		hContact	- the contact to perform the action for
+ * @param		param			- a caller defined parameter passed to the callback function
+ *
+ * @retval	TRUE	- The item is added to the queue successfully.
+ * @retval	FALSE	- The item is not added to the queue.
+ **/
+BOOL CContactQueue::AddUniqueConsiderParam(INT waitTime, HANDLE hContact, PVOID param)
+{
+	BOOL rc;
+
+	Lock();
+
+	RemoveAllConsiderParam(hContact, param);
+	rc = InternalAdd(waitTime, hContact, param);
+
+	Release();
+
+	return rc;
+}
+
+/**
+ * This member function really adds an item into the time sorted queue list.
+ *
+ * @param		waitTime	- the time to wait until the callback is desired to run
+ * @param		hContact	- the contact to perform the action for
+ * @param		param			- a caller defined parameter passed to the callback function
+ *
+ * @retval	TRUE	- The item is added to the queue successfully.
+ * @retval	FALSE	- The item is not added to the queue.
+ **/
+BOOL CContactQueue::InternalAdd(INT waitTime, HANDLE hContact, PVOID param)
+{
+	BOOL rc;
+	CQueueItem *qi = (CQueueItem *) mir_alloc(sizeof(CQueueItem));
+
+	qi->hContact = hContact;
+	qi->check_time = GetTickCount() + waitTime;
+	qi->param = param;
+
+	rc = _queue.insert(qi);
+	if (!rc)
+	{
+		mir_free(qi);
+	}
+
+	SetEvent(_hEvent);
+
+	return rc;
+}
+
+/**
+ * This is the real thread callback function. As long as _status
+ * is set to RUNNING it looks for items in the queue to perform
+ * the _pfnCallback function on them. If the queue is empty or the
+ * next upcoming item is located in the future, the thread is suspended
+ * in the meanwhile.
+ *
+ * @param		none
+ *
+ * @return	nothing
+ **/
+VOID CContactQueue::Thread()
+{
+	while (_status == RUNNING)
+	{
+		ResetEvent(_hEvent);
+
+		Lock();
+
+		if (_queue.getCount() <= 0)
+		{
+			// can be used by a derivant
+			OnEmpty();
+
+			// No items, so supend thread
+			Release();
+
+			Suspend(INFINITE);
+		}
+		else
+		{
+			// Take a look at first queue item
+			CQueueItem *qi = _queue[0];
+
+			INT dt = qi->check_time - GetTickCount();
+			if (dt > 0)
+			{
+				// Not time to request yet, wait...
+				Release();
+
+				Suspend(dt);
+			}
+			else
+			{
+				// Will request this queue item
+				_queue.remove(0);
+
+				Release();
+
+				Callback(qi->hContact, qi->param);
+
+				mir_free(qi);
+			}
+		}
+	}
+	_status = STOPPED;
+}
+
+/**
+ * This method suspends the worker thread for the given ammount of time.
+ *
+ * @param		time	- milliseconds to suspend the thread for
+ *
+ * @return	nothing
+ **/
+VOID CContactQueue::Suspend(INT time) const
+{
+	if (_status == RUNNING)
+	{
+		WaitForSingleObject(_hEvent, time);
+	}
+}
+
+/**
+ * This method resumes the worker thread and immitiatly goes on with the next entry.
+ *
+ * @param		none
+ *
+ * @return		nothing
+ **/
+VOID CContactQueue::ContinueWithNext()
+{
+	if (_status == RUNNING)
+	{
+		INT i, c, dt;
+
+		Lock();
+
+		c = _queue.getCount();
+		if (c > 0)
+		{
+			dt = _queue[0]->check_time - GetTickCount() - 3000;
+			if (dt > 0)
+			{
+				for (i = 0; i < c; i++)
+				{
+					_queue[i]->check_time -= dt;
+				}
+			}
+		}
+		SetEvent(_hEvent);
+		Release();
+	}
+}
diff --git a/plugins/UserInfoEx/src/mir_contactqueue.h b/plugins/UserInfoEx/src/mir_contactqueue.h
new file mode 100644
index 0000000000..b8f77530b7
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_contactqueue.h
@@ -0,0 +1,221 @@
+/* 
+Copyright �2006 Ricardo Pescuma Domenecci
+
+Modified  �2008-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_contactqueue.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+
+#ifndef __CONTACTASYNCQUEUE_H__
+#define __CONTACTASYNCQUEUE_H__
+
+#ifndef MIRANDA_VER
+#define MIRANDA_VER 0x0A00
+#endif
+
+#include <windows.h>
+#include <newpluginapi.h>
+#include <m_system_cpp.h>
+
+/**
+ *
+ *
+ **/
+struct CQueueItem
+{
+	DWORD	check_time;
+	HANDLE	hContact;
+	PVOID	param;
+};
+
+/**
+ *
+ *
+ **/
+class CContactQueue
+{
+public:
+
+	enum EQueueStatus
+	{
+		RUNNING		= 0,
+		STOPPING	= 1,
+		STOPPED		= 2
+	};
+
+	CContactQueue				(INT initialSize = 10);
+	~CContactQueue				();
+
+	inline INT			Size	()			const	{ return _queue.getCount();}
+	inline INT			Remove	(INT idx)			{ mir_free(_queue[idx]); return _queue.remove(idx);}
+	inline CQueueItem*	Get		(INT idx) const		{ return _queue[idx];}
+
+
+	VOID RemoveAll();
+	
+	/**
+	 * This function removes all queue items for the hContact.
+	 *
+	 * @param		hContact		- the contact whose queue items to delete
+	 *
+	 * @return	nothing
+	 **/
+	VOID RemoveAll(HANDLE hContact);
+
+	/**
+	 * This function removes all queue items for the hContact considering the correct parameter.
+	 *
+	 * @param		hContact		- the contact whose queue items to delete
+	 * @param		param			- a caller defined parameter passed to the callback function
+	 *
+	 * @return	nothing
+	 **/
+	VOID RemoveAllConsiderParam(HANDLE hContact, PVOID param);
+
+	/**
+	 * This method adds the desired new item.
+	 *
+	 * @param		waitTime		- the time to wait until the callback is desired to run
+	 * @param		hContact		- the contact to perform the action for
+	 * @param		param			- a caller defined parameter passed to the callback function
+	 *
+	 * @retval		TRUE			- The item is added to the queue successfully.
+	 * @retval		FALSE			- The item is not added to the queue.
+	 **/
+	BOOL Add(INT waitTime, HANDLE hContact, PVOID param = NULL);
+
+	/**
+	 * This method adds the desired new item only, if the queue does not yet contain
+	 * an item for the contact.
+	 *
+	 * @param		waitTime		- the time to wait until the callback is desired to run
+	 * @param		hContact		- the contact to perform the action for
+	 * @param		param			- a caller defined parameter passed to the callback function
+	 *
+	 * @retval		TRUE			- The item is added to the queue successfully.
+	 * @retval		FALSE			- The item is not added to the queue.
+	 **/
+	BOOL AddIfDontHave(INT waitTime, HANDLE hContact, PVOID param = NULL);
+
+	/**
+	 * This method removes all existing queue items for the contact and adds a new queue item
+	 * for the given contact. This method might be used to move an existing entry, 
+	 * whose check_time has changed.
+	 *
+	 * @param		waitTime		- the time to wait until the callback is desired to run
+	 * @param		hContact		- the contact to perform the action for
+	 * @param		param			- a caller defined parameter passed to the callback function
+	 *
+	 * @return		nothing
+	 **/
+	BOOL AddUnique(INT waitTime, HANDLE hContact, PVOID param = NULL);
+
+	/**
+	 * This method removes all existing queue items for the contact with the same parameter as @e param
+	 * and adds a new queue item for the given contact. This method might be used to move an existing
+	 * entry, whose check_time has changed.
+	 *
+	 * @param		waitTime		- the time to wait until the callback is desired to run
+	 * @param		hContact		- the contact to perform the action for
+	 * @param		param			- a caller defined parameter passed to the callback function
+	 *
+	 * @return	nothing
+	 **/
+	BOOL AddUniqueConsiderParam	(INT waitTime, HANDLE hContact, PVOID param = NULL);
+
+	/**
+	 * This method resumes the worker thread and immitiatly goes on with the next entry.
+	 *
+	 * @param		none
+	 *
+	 * @return		nothing
+	 **/
+	VOID ContinueWithNext();
+
+protected:
+	
+	virtual VOID OnEmpty		() {};
+	virtual VOID Callback		(HANDLE hContact, PVOID param) = 0;
+
+	/**
+	 * This is the real thread callback function. As long as _status
+	 * is set to RUNNING it looks for items in the queue to perform 
+	 * the _pfnCallback function on them. If the queue is empty or the
+	 * next upcoming item is located in the future, the thread is suspended
+	 * in the meanwhile.
+	 *
+	 * @param		none
+	 *
+	 * @return	nothing
+	 **/
+	VOID Thread();
+	
+	/**
+	 * This is a static method to redirect the thread's callback function
+	 * to the desired class object.
+	 *
+	 * @param		obj	- pointer to the object (instance) of CContactQueue
+	 *
+	 * @return	nothing
+	 **/
+	static VOID ThreadProc(CContactQueue* obj)
+	{
+		obj->Thread();
+	}
+
+	/**
+	 * This method suspends the worker thread for the given ammount of time.
+	 *
+	 * @param		time	- milliseconds to suspend the thread for
+	 *
+	 * @return	nothing
+	 **/
+	VOID Suspend(INT time) const;
+
+private:
+
+	LIST<CQueueItem>	_queue;
+
+	CRITICAL_SECTION	_cs;
+	HANDLE				_hEvent;
+	EQueueStatus		_status;
+
+	VOID Lock();
+	VOID Release();
+
+	/**
+	 * This member function really adds an item into the time sorted queue list.
+	 *
+	 * @param		waitTime	- the time to wait until the callback is desired to run
+	 * @param		hContact	- the contact to perform the action for
+	 * @param		param		- a caller defined parameter passed to the callback function
+	 *
+	 * @retval		TRUE		- The item is added to the queue successfully.
+	 * @retval		FALSE		- The item is not added to the queue.
+	 **/
+	BOOL InternalAdd(INT waitTime, HANDLE hContact, PVOID param);
+};
+
+#endif // __CONTACTASYNCQUEUE_H__
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/mir_db.cpp b/plugins/UserInfoEx/src/mir_db.cpp
new file mode 100644
index 0000000000..e4596df4cc
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_db.cpp
@@ -0,0 +1,1334 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_db.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include <m_metacontacts.h>
+#include "ctrl_base.h"
+#include "mir_string.h"
+#include "mir_db.h"
+
+namespace DB {
+
+namespace MetaContact {
+
+/**
+ *
+ *
+ **/
+INT_PTR	SubCount(HANDLE hMetaContact)
+{
+	INT_PTR result = CallService(MS_MC_GETNUMCONTACTS, (WPARAM) hMetaContact, 0);
+	return (result == CALLSERVICE_NOTFOUND) ? -1 : result;
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR	SubDefNum(HANDLE hMetaContact)
+{
+	INT_PTR result = CallService(MS_MC_GETDEFAULTCONTACTNUM, (WPARAM) hMetaContact, 0);
+	return (result == CALLSERVICE_NOTFOUND) ? -1 : result;
+}
+
+/**
+ *
+ *
+ **/
+HANDLE	Sub(HANDLE hMetaContact, INT idx)
+{
+	if (idx != -1) {
+		INT_PTR result = CallService(MS_MC_GETSUBCONTACT, (WPARAM) hMetaContact, (LPARAM) idx);
+		return (result == CALLSERVICE_NOTFOUND) ? NULL : (HANDLE) result;
+	}
+	return NULL;
+}
+
+/**
+ *
+ *
+ **/
+BOOLEAN	IsSub(HANDLE hContact)
+{
+	return	myGlobals.szMetaProto &&
+			DB::Setting::GetByte(myGlobals.szMetaProto, "Enabled", TRUE) &&
+			DB::Setting::GetByte(hContact, myGlobals.szMetaProto, "IsSubcontact", FALSE);
+}
+
+/**
+ *
+ *
+ **/
+HANDLE	GetMeta(HANDLE hContact)
+{
+	HANDLE result;
+	if (myGlobals.szMetaProto){
+		result = (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM) hContact, 0);
+		if (result == (HANDLE)CALLSERVICE_NOTFOUND) {
+			result = NULL;
+		}
+	}
+	else {
+		result = NULL;
+	}
+	return (HANDLE) result;
+}
+
+} /* namespace MetaContact */
+
+/**
+* This namespace contains all functions used to access or modify contacts in the database.
+**/
+namespace Contact {
+
+/**
+ * This function retrieves the display name for a contact.
+ * @param	hContact	- handle to the contact
+ * @return	Returns the display name of a contact.
+ **/
+LPTSTR	DisplayName(HANDLE hContact)
+{
+	return (LPTSTR) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR);
+}
+
+/**
+ * This function is used to retrieve a contact's basic protocol
+ * @param	hContact	- handle to the contact
+ * @return	This function returns the basic protocol of a contact.
+ **/
+LPSTR	Proto(HANDLE hContact)
+{
+	if (hContact) {
+		INT_PTR result;
+		result = CallService(MS_PROTO_GETCONTACTBASEACCOUNT, (WPARAM) hContact, NULL);
+		return (LPSTR) ((result == CALLSERVICE_NOTFOUND) ? NULL : result);
+	}
+	return NULL;
+}
+
+/**
+ * Gets the number of contacts in the database, which does not count the user
+ * @param	hContact	- handle to the contact
+ * @return	Returns the number of contacts. They can be retrieved using
+ *			contact/findfirst and contact/findnext
+ **/
+INT_PTR	GetCount()
+{
+	return CallService(MS_DB_CONTACT_GETCOUNT, 0, 0);
+}
+
+/**
+ * This function searches the first contact in the database and returns its handle.
+ * @retval	HANDLE		- handle of the next contact in the database	
+ * @retval	NULL		- no more contacts in the database
+ **/
+HANDLE	FindFirst()
+{
+	return (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+}
+
+/**
+ * This function searches the next contact in the database and returns its handle.
+ * @param	hContact	- handle to the contact
+ * @retval	HANDLE		- handle of the next contact in the database
+ * @retval	NULL		- no more contacts in the database
+ **/
+HANDLE	FindNext(HANDLE hContact)
+{
+	return (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+}
+
+/**
+ * Simply adds a new contact without setting up any protocol or something else
+ * @return	HANDLE		The function returns the HANDLE of the new contact
+ **/
+HANDLE	Add()
+{
+	return (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0);
+}
+
+/**
+ * This function deletes a contact from the database.
+ * @param	hContact	- handle to the contact
+ **/
+BYTE	Delete(HANDLE hContact)
+{
+	return CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0) != 0;
+}
+
+/**
+ * This function trys to guess, when an ICQ contact was added to database.
+ **/
+DWORD	WhenAdded(DWORD dwUIN, LPCSTR pszProto)
+{
+	DBEVENTINFO		dbei; 
+	HANDLE			edbe;
+	DWORD			dwEvtUIN;
+
+	ZeroMemory(&dbei, sizeof(dbei));
+	dbei.cbSize = sizeof(dbei);
+	for (edbe = DB::Event::FindFirst(NULL); edbe != NULL; edbe = DB::Event::FindNext(edbe)) {
+		// get eventtype and compare
+		if (!DB::Event::GetInfo(edbe, &dbei) && dbei.eventType == EVENTTYPE_ADDED) {
+			if (!DB::Event::GetInfoWithData(edbe, &dbei)) {
+				// extract UIN and compare with given one
+				CopyMemory(&dwEvtUIN, dbei.pBlob, sizeof(DWORD));
+				MIR_FREE(dbei.pBlob);
+				if (dwEvtUIN == dwUIN) {
+					return dbei.timestamp;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+} /* Contact */
+
+namespace Module {
+
+/**
+ * Deletes all settings in the module.
+ * @param	hContact	- handle to the contact
+ * @param	pszModule	- the module to delete the setting from (e.g. USERINFO)
+ * return:	nothing
+ **/
+VOID	Delete(HANDLE hContact, LPCSTR pszModule)
+{
+	CEnumList	Settings;
+	if (!Settings.EnumSettings(hContact, pszModule)) {
+		INT i;
+		for (i = 0; i < Settings.getCount(); i++) {
+			DB::Setting::Delete(hContact, pszModule, Settings[i]);
+		}
+	}
+}
+
+/**
+ * Enum Proc for DBModule_IsEmpty
+ * @param	pszSetting	- the setting
+ * @param	lParam		- DBCONTACTENUMSETTINGS - (LPARAM)&dbces
+ * @retval	TRUE		- always true
+ **/
+static	INT IsEmptyEnumProc(LPCSTR pszSetting, LPARAM lParam)
+{
+	return 1;
+}
+
+/**
+ * This function tests, whether a module is empty for the given contact or not
+ * @param	hContact	- handle to the contact
+ * @param	pszModule	- the module to read the setting from (e.g. USERINFO)
+ * @retval	TRUE		- the module is empty
+ * @retval	FALSE		- the module contains settings
+ **/
+BOOLEAN	IsEmpty(HANDLE hContact, LPCSTR pszModule)
+{
+	DBCONTACTENUMSETTINGS dbces;
+	dbces.pfnEnumProc	= IsEmptyEnumProc;
+	dbces.szModule		= pszModule;
+	dbces.ofsSettings	= 0;
+	dbces.lParam		= 0;
+	return (0 > CallService(MS_DB_CONTACT_ENUMSETTINGS, (WPARAM)hContact, (LPARAM)&dbces));
+}
+
+/**
+ * This function tests, whether a module belongs to a metacontact protocol
+ * @param	pszModule	- the module to read the setting from (e.g. USERINFO)
+ * @retval	TRUE		- the module belongs to a metacontact protocol
+ * @retval	FALSE		- the module belongs to a other protocol
+ **/
+BOOLEAN	IsMeta(LPCSTR pszModule)
+{
+	if(myGlobals.szMetaProto)
+		return !mir_strcmp(pszModule, myGlobals.szMetaProto);
+	return !mir_strcmp(pszModule, "MetaContacts");
+}
+
+/**
+ * This function tests, whether a module is a meta contact, and user wants to scan it for settings
+ * @param	pszModule	- the module to read the setting from (e.g. USERINFO)
+ * @retval	TRUE		- the module is empty
+ * @retval	FALSE		- the module contains settings
+ **/
+BOOLEAN	IsMetaAndScan	(LPCSTR pszModule)
+{
+	return DB::Setting::GetByte(SET_META_SCAN, TRUE) && IsMeta(pszModule);
+}
+
+} /* namespace Module */
+
+namespace Setting {
+
+/**
+ * This function calls MS_DB_CONTACT_GETSETTING_STR service to get database values. 
+ * @param	hContact	- handle to the contact
+ * @param	pszModule	- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting	- the setting to read
+ * @param	destType	- desired string type (DBVT_ASCIIZ, DBVT_WCHAR, DBVT_UTF8)
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	Get(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE destType)
+{
+	BYTE result;
+	DBCONTACTGETSETTING dgs;
+
+	dgs.szModule	= pszModule;
+	dgs.szSetting	= pszSetting;
+	dgs.pValue		= dbv;
+	dbv->type		= 0;
+	
+	// read value without translation to specific type
+	result = CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM) hContact, (LPARAM) &dgs) != 0;
+
+	// Is value read successfully and destination type set?
+	if (!result && destType) {
+		result = DB::Variant::ConvertString(dbv, destType);
+	}
+	return result;
+}
+
+/**
+ * This function reads a value from the database and returns it as an ansi encoded string.
+ * @param	hContact	- handle to the contact
+ * @param	pszModule	- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting	- the setting to read
+ *
+ * @return	string value
+ **/
+LPSTR	GetAString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+	DBVARIANT dbv;
+	if (GetAString(hContact, pszModule, pszSetting, &dbv) == 0){
+		if (DB::Variant::dbv2String(&dbv, DBVT_WCHAR) == 0) {
+			return dbv.pszVal;
+		}
+		DB::Variant::Free(&dbv);
+	}
+	return NULL;
+}
+
+/**
+ * This function reads a value from the database and returns it as an unicode encoded string. 
+ * @param	hContact	- handle to the contact
+ * @param	pszModule	- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting	- the setting to read
+ *
+ * @return	string value
+ **/
+LPWSTR	GetWString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+	DBVARIANT dbv;
+	if (GetWString(hContact, pszModule, pszSetting, &dbv) == 0) {
+		if (DB::Variant::dbv2String(&dbv, DBVT_WCHAR) == 0) {
+			return dbv.pwszVal;
+		}
+		DB::Variant::Free(&dbv);
+	}
+	return NULL;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_GETSETTING_STR service to get database values. 
+ * It searches in pszModule first and if the setting does not exist there it tries proto to retrieve it.
+ * @param	hContact	- handle to the contact
+ * @param	pszModule	- the module to read the setting from (e.g. USERINFO)
+ * @param	szProto		- the contact's protocol to read the setting from (e.g. ICQ)
+ * @param	szSetting	- the setting to read
+ * @param	destType	- desired string type (DBVT_ASCIIZ, DBVT_WCHAR, DBVT_UTF8)
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	GetEx(HANDLE hContact, LPCSTR pszModule, LPCSTR pszProto, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE destType)
+{
+	BYTE result;
+	result = !pszModule || Get(hContact, pszModule, pszSetting, dbv, destType);
+	// try to read setting from the contact's protocol module 
+	if (result && pszProto) {
+		result = Get(hContact, pszProto, pszSetting, dbv, destType) != 0;
+		// try to get setting from a metasubcontact
+		if (result && DB::Module::IsMetaAndScan(pszProto)) {
+			const INT_PTR def = DB::MetaContact::SubDefNum(hContact);
+			HANDLE hSubContact;
+			// try to get setting from the default subcontact first
+			if (def > -1 && def < INT_MAX) {
+				hSubContact = DB::MetaContact::Sub(hContact, def);
+				if (hSubContact != NULL) {
+					result = DB::Setting::GetEx(hSubContact, pszModule, DB::Contact::Proto(hSubContact), pszSetting, dbv, destType) != 0;
+				}
+			}
+			// scan all subcontacts for the setting
+			if (result) {
+				const INT_PTR cnt = DB::MetaContact::SubCount(hContact);
+				if (cnt < INT_MAX) {
+					INT_PTR i;
+					for (i = 0; result && i < cnt; i++) {
+						if (i != def) {
+							hSubContact = DB::MetaContact::Sub(hContact, i);
+							if (hSubContact != NULL) {
+								result = DB::Setting::GetEx(hSubContact, pszModule, DB::Contact::Proto(hSubContact), pszSetting, dbv, destType) != 0;
+	}	}	}	}	}	}	}
+	return result;
+}
+
+/**
+ * This function is used by the controls of the details dialog and calls MS_DB_CONTACT_GETSETTING_STR service
+ * to get database values. It searches in pszModule first and if the setting does not exist there it tries proto
+ * to retrieve it.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSubModule	- the module to read the setting from a meta subcontract (e.g. USERINFO)
+ * @param	pszProto		- the contact's protocol to read the setting from (e.g. ICQ)
+ * @param	pszSetting		- the setting to read
+ * @param	destType		- desired string type (DBVT_ASCIIZ, DBVT_WCHAR, DBVT_UTF8)
+ *
+ * @return	This function returns the WORD which contains the source of information.
+ **/
+WORD	GetCtrl(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSubModule, LPCSTR pszProto, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE destType)
+{
+	WORD wFlags = 0;
+
+	// read setting from given module
+	if (hContact && pszModule && *pszModule && !Get(hContact, pszModule, pszSetting, dbv, destType)) {
+		wFlags |= CTRLF_HASCUSTOM;
+		if (Exists(hContact, pszProto, pszSetting)) {
+			wFlags |= CTRLF_HASPROTO;
+		}
+	}
+	// read setting from contact's basic protocol
+	else if (pszProto && *pszProto) {
+		// try to read the setting from the basic protocol
+		if (!Get(hContact, pszProto, pszSetting, dbv, destType)) {
+			wFlags |= CTRLF_HASPROTO;
+		}
+		// try to read the setting from the sub contacts' modules
+		else if (DB::Module::IsMetaAndScan(pszProto)) {
+			const INT_PTR def = DB::MetaContact::SubDefNum(hContact);
+			HANDLE hSubContact;
+			// try to get setting from the default subcontact first
+			if (def > -1 && def < INT_MAX) {
+				hSubContact = DB::MetaContact::Sub(hContact, def);
+				if (hSubContact != NULL) {
+					wFlags = GetCtrl(hSubContact, pszSubModule, NULL, DB::Contact::Proto(hSubContact), pszSetting, dbv, destType);
+					if (wFlags != 0) {
+						wFlags &= ~CTRLF_HASCUSTOM;
+						wFlags |= CTRLF_HASMETA;
+					}
+				}
+			}
+			// copy the missing settings from the other subcontacts
+			if (wFlags == 0) {
+				INT_PTR i;
+				const INT_PTR cnt = DB::MetaContact::SubCount(hContact);
+				for (i = 0; i < cnt; i++) {
+					if (i != def) {
+						hSubContact = DB::MetaContact::Sub(hContact, i);
+						if (hSubContact != NULL) {
+							wFlags = GetCtrl(hSubContact, pszSubModule, NULL, DB::Contact::Proto(hSubContact), pszSetting, dbv, destType);
+							if (wFlags != 0) {
+								wFlags &= ~CTRLF_HASCUSTOM;
+								wFlags |= CTRLF_HASMETA;
+								break;
+	}	}	}	}	}	}	}
+	if (wFlags == 0) {
+		dbv->type = DBVT_DELETED;
+	}
+	return wFlags;
+}
+
+/**
+ * This function reads a setting from database into a predefined portion of memory
+ * and convert numbers into a string, too.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to read
+ * @param	pszValue		- buffer, that retrieves the value
+ * @param	cchValue		- number of characters the buffer can take
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	GetStatic(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR pszValue, INT cchValue)
+{
+	DBVARIANT dbv;
+	DBCONTACTGETSETTING sVal;
+
+	if (pszValue && cchValue) {
+		pszValue[0]	= 0;
+		dbv.pszVal	= pszValue;
+		dbv.cchVal	= cchValue;
+		dbv.type	= DBVT_ASCIIZ;
+		
+		sVal.pValue		= &dbv;
+		sVal.szModule	= pszModule;
+		sVal.szSetting	= pszSetting;
+		
+		if (!CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM)hContact, (LPARAM)&sVal)) {
+			switch (dbv.type) {
+			case DBVT_BYTE:
+				_itoa(dbv.bVal, pszValue, 10);
+				break;
+			case DBVT_WORD:
+				_itoa(dbv.wVal, pszValue, 10);
+				break;
+			case DBVT_DWORD:
+				_itoa(dbv.dVal, pszValue, 10);
+			}
+			return (pszValue[0] == 0);
+		}
+	}
+	return 1;
+}
+
+/**
+ * This function reads a byte from the database. If required it converts it to a byte value
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to read
+ * @param	errorValue		- value to return if something goes wrong
+ *
+ * @return	byte value
+ **/
+BYTE	GetByte(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, BYTE errorValue)
+{
+	DBVARIANT dbv;
+	BYTE result;
+	if (GetAsIs(hContact, pszModule, pszSetting, &dbv)) {
+		result = errorValue;
+	}
+	else {
+		switch (dbv.type) {
+		case DBVT_BYTE:
+			result = dbv.bVal;
+			break;
+		case DBVT_WORD:
+			result = (dbv.wVal < 0x0100) ? (BYTE) dbv.wVal : errorValue;
+			break;
+		case DBVT_DWORD:
+			result = (dbv.wVal < 0x00000100) ? (BYTE) dbv.dVal : errorValue;
+			break;
+		default:
+			DB::Variant::Free(&dbv);
+			result = errorValue;
+		}
+	}
+	return result;
+}		
+
+/**
+ * This function reads a word from the database. If required it converts it to a word value
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to read
+ * @param	errorValue		- value to return if something goes wrong
+ *
+ * @return	word value
+ **/
+WORD	GetWord(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, WORD errorValue)
+{
+	DBVARIANT dbv;
+	WORD result;
+	if (GetAsIs(hContact, pszModule, pszSetting, &dbv)) {
+		result = errorValue;
+	}
+	else {
+		switch (dbv.type) {
+		case DBVT_BYTE:
+			result = 0x00ff & dbv.bVal;
+			break;
+		case DBVT_WORD:
+			result = dbv.wVal;
+			break;
+		case DBVT_DWORD:
+			result = (dbv.wVal < 0x00010000) ? (WORD) dbv.dVal : errorValue;
+			break;
+		default:
+			DB::Variant::Free(&dbv);
+			result = errorValue;
+		}
+	}
+	return result;
+}	
+
+/**
+ * This function reads a double word from the database. If required it converts it to a double word value
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to read
+ * @param	errorValue		- value to return if something goes wrong
+ *
+ * @return	double word value
+ **/
+DWORD	GetDWord(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DWORD errorValue)
+{
+	DBVARIANT dbv;
+	DWORD result;
+	if (GetAsIs(hContact, pszModule, pszSetting, &dbv)) {
+		result = errorValue;
+	}
+	else {
+		switch (dbv.type) {
+		case DBVT_BYTE:
+			result = 0x000000ff & dbv.bVal;
+			break;
+		case DBVT_WORD:
+			result = 0x0000ffff & dbv.wVal;
+			break;
+		case DBVT_DWORD:
+			result = dbv.dVal;
+			break;
+		default:
+			DB::Variant::Free(&dbv);
+			result = errorValue;
+		}
+	}
+	return result;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write a DBVARIANT structure to the database.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to write
+ * @param	dbv				- the DBVARIANT to store
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	WriteVariant(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, const DBVARIANT *dbv)
+{
+	DBCONTACTWRITESETTING cws;
+
+	cws.szModule	= pszModule;
+	cws.szSetting	= pszSetting;
+	memcpy(&cws.value, dbv, sizeof(DBVARIANT));
+	return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write a BYTE to the database.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to write
+ * @param	value			- the byte to store
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	WriteByte(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, BYTE value)
+{
+	DBCONTACTWRITESETTING cws;
+
+	cws.szModule	= pszModule;
+	cws.szSetting	= pszSetting;
+	cws.value.type	= DBVT_BYTE;
+	cws.value.bVal	= value;
+	return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write a WORD to the database.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to write
+ * @param	value			- the word to store
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	WriteWord(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, WORD value)
+{
+	DBCONTACTWRITESETTING cws;
+
+	cws.szModule	= pszModule;
+	cws.szSetting	= pszSetting;
+	cws.value.type	= DBVT_WORD;
+	cws.value.wVal	= value;
+	return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write a DWORD to the database.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to write
+ * @param	value			- the double word to store
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	WriteDWord(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DWORD value)
+{
+	DBCONTACTWRITESETTING cws;
+
+	cws.szModule	= pszModule;
+	cws.szSetting	= pszSetting;
+	cws.value.type	= DBVT_DWORD;
+	cws.value.dVal	= value;
+	return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write an ansi string to the database.
+ * @param		hContact	- handle to the contact
+ * @param		pszModule	- the module to read the setting from (e.g. USERINFO)
+ * @param		pszSetting	- the setting to write
+ * @param		value		- the string to store
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	WriteAString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR value)
+{
+	DBCONTACTWRITESETTING cws;
+	cws.szModule		= pszModule;
+	cws.szSetting		= pszSetting;
+	cws.value.type		= DBVT_ASCIIZ;
+	cws.value.pszVal	= value;
+	return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write an unicode string to the database.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to write
+ * @param	value			- the string to store
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	WriteWString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPWSTR value)
+{
+	DBCONTACTWRITESETTING cws;
+	cws.szModule		= pszModule;
+	cws.szSetting		= pszSetting;
+	cws.value.type		= DBVT_WCHAR;
+	cws.value.pwszVal	= value;
+	return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function calls MS_DB_CONTACT_WRITESETTING to write an utf8 string to the database.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to write
+ * @param	value			- the string to store
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	WriteUString(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR value)
+{
+	DBCONTACTWRITESETTING cws;
+	cws.szModule		= pszModule;
+	cws.szSetting		= pszSetting;
+	cws.value.type		= DBVT_UTF8;
+	cws.value.pszVal	= value;
+	return CallService(MS_DB_CONTACT_WRITESETTING, (WPARAM) hContact, (LPARAM) &cws) != 0;
+}
+
+/**
+ * This function checks for the existence of the given setting in the database
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to check
+ *
+ * @retval	TRUE			- setting exists
+ * @retval	FALSE			- setting does not exist
+ **/
+BOOLEAN	Exists(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+	if (pszModule && pszSetting) {
+		DBCONTACTGETSETTING cgs;
+		DBVARIANT dbv;
+		CHAR szDummy[1];
+
+		dbv.pszVal		= szDummy;
+		dbv.cchVal		= sizeof(szDummy);
+		dbv.type		= 0;
+		cgs.pValue		= &dbv;
+		cgs.szModule	= pszModule;
+		cgs.szSetting	= pszSetting;
+		if (!CallService(MS_DB_CONTACT_GETSETTINGSTATIC, (WPARAM) hContact, (LPARAM) &cgs)) {
+			return (dbv.type > DBVT_DELETED);
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * This function deletes the given setting from database 
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszSetting		- the setting to read
+ *
+ * @retval	0 - success
+ * @retval	1 - failure
+ **/
+BYTE	Delete(HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting)
+{
+	DBCONTACTGETSETTING cgs;
+	cgs.szModule	= pszModule;
+	cgs.szSetting	= pszSetting;
+	return CallService(MS_DB_CONTACT_DELETESETTING, (WPARAM) hContact, (LPARAM) &cgs) != 0;
+}
+
+/**
+ * This function deletes all reluctant settings of an setting array such as My-phoneXX.
+ * @param	hContact		- handle to the contact
+ * @param	pszModule		- the module to read the setting from (e.g. USERINFO)
+ * @param	pszFormat		- the format, telling what a array of settings is ment
+ * @param	iStart			- the first index of the setting to delete
+ *
+ * @return	nothing
+ **/
+VOID	DeleteArray(HANDLE hContact, LPCSTR pszModule, LPCSTR pszFormat, INT iStart)
+{
+	CHAR pszSetting[MAXSETTING];
+	do {
+		mir_snprintf(pszSetting, MAXSETTING, pszFormat, iStart++);
+	}
+	while (!DB::Setting::Delete(hContact, pszModule, pszSetting));
+}
+
+/**
+ * This function can prevent a setting from being stored to database permanently.
+ * @param	pszSetting		- the setting to read
+ * @param	enabled			- if set to 'true' the setting will not be stored in database
+ *
+ * @retval	0 - success
+ * @retval	1 - failure
+ **/
+BYTE	Resident(LPCSTR pszSetting, const bool enabled)
+{
+	return CallService(MS_DB_SETSETTINGRESIDENT, (WPARAM) enabled, (LPARAM) pszSetting) != 0;
+}
+
+} /* namespace Setting */
+
+namespace Variant {
+
+BYTE	Free(DBVARIANT *dbv)
+{
+	return CallService(MS_DB_CONTACT_FREEVARIANT, 0, (LPARAM) dbv) != 0;
+}
+
+/**
+ * This function converts a string value of the DBVARIANT to the destination type
+ * but keeps all other values as is.
+ * @param		dbv			- pointer to DBVARIANT structure which is to manipulate
+ * @param		destType	- one of (DBVT_ASCIIZ, DBVT_UTF8 or DBVT_WCHAR)
+ *
+ * @retval		0			- success
+ * @retval		1			- error
+ **/
+BYTE	ConvertString(DBVARIANT* dbv, const BYTE destType)
+{
+	if (dbv) {
+		switch (dbv->type) {
+		// source value is of type "ascii"
+		case DBVT_ASCIIZ:
+			{
+				switch (destType) {
+				// destination type is "utf8"
+				case DBVT_UTF8:
+					{
+						LPSTR tmpBuf = mir_utf8encode(dbv->pszVal);
+						mir_free(dbv->pszVal);
+						dbv->pszVal = tmpBuf;
+						dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+					} break;
+				// destination type is "wchar"
+				case DBVT_WCHAR:
+					{
+						LPWSTR tmpBuf = mir_a2u(dbv->pszVal);
+						mir_free(dbv->pszVal);
+						dbv->pwszVal = tmpBuf;
+						dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+					}
+				}
+			} break;
+		// source value is of type "utf8"
+		case DBVT_UTF8:
+			{
+				switch (destType) {
+				// destination type is "ascii"
+				case DBVT_ASCIIZ:
+					{
+						mir_utf8decode(dbv->pszVal, NULL);
+						dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+					} break;
+				// destination type is "wchar"
+				case DBVT_WCHAR:
+					{
+						LPSTR savePtr = dbv->pszVal;
+						dbv->pszVal = NULL;
+						mir_utf8decode(savePtr, &dbv->pwszVal);
+						mir_free(savePtr);
+						dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+					}
+				}
+			} break;
+		// source value is of type "wchar"
+		case DBVT_WCHAR:
+			{
+			switch (destType) {
+				// destination type is "ascii"
+				case DBVT_ASCIIZ:
+					{
+						LPSTR tmpBuf = mir_u2a(dbv->pwszVal);
+						mir_free(dbv->pwszVal);
+						dbv->pszVal = tmpBuf;
+						dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+					} break;
+				// destination type is "utf8"
+				case DBVT_UTF8:
+					{
+						LPSTR tmpBuf = mir_utf8encodeW(dbv->pwszVal);
+						mir_free(dbv->pwszVal);
+						dbv->pszVal = tmpBuf;
+						dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+					}
+				}
+			}
+		}
+		return dbv->type == DBVT_DELETED;
+	}
+	return 1;
+}
+
+/**
+ * This function completely converts a DBVARIANT to the destination string type.
+ * It includes BYTE, WORD, DWORD and all string types
+ * @param		dbv			- pointer to DBVARIANT structure which is to manipulate
+ * @param		destType	- one of (DBVT_ASCIIZ, DBVT_UTF8 or DBVT_WCHAR)
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+BYTE	dbv2String(DBVARIANT* dbv, const BYTE destType)
+{
+	if (dbv) {
+		switch (destType) {
+		// destination type is "utf8" or "ascii"
+		case DBVT_ASCIIZ:
+		case DBVT_UTF8:
+			{
+				CHAR buf[32];
+				switch (dbv->type) {
+				// source value is of type "byte"
+				case DBVT_BYTE:
+					{
+						_ultoa(dbv->bVal, buf, 10);
+						dbv->pszVal = mir_strdup(buf);
+						dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+					} break;
+				// source value is of type "word"
+				case DBVT_WORD:
+					{
+						_ultoa(dbv->wVal, buf, 10);
+						dbv->pszVal = mir_strdup(buf);
+						dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+					} break;
+				// source value is of type "dword"
+				case DBVT_DWORD:
+					{
+						_ultoa(dbv->dVal, buf, 10);
+						dbv->pszVal = mir_strdup(buf);
+						dbv->type = (dbv->pszVal) ? destType : DBVT_DELETED;
+					} break;
+				// source value is of any string type
+				case DBVT_ASCIIZ:
+				case DBVT_WCHAR:
+				case DBVT_UTF8:
+					{
+						return ConvertString(dbv, destType);
+					}
+				}
+			} break;
+		// destination type is "wchar"
+		case DBVT_WCHAR:
+			{
+				WCHAR buf[32];
+				switch (dbv->type) {
+				// source value is of type "byte"
+				case DBVT_BYTE:
+					{
+						_ultow(dbv->bVal, buf, 10);
+						dbv->pwszVal = mir_wcsdup(buf);
+						dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+					} break;
+				// source value is of type "word"
+				case DBVT_WORD:
+					{
+						_ultow(dbv->wVal, buf, 10);
+						dbv->pwszVal = mir_wcsdup(buf);
+						dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+					} break;
+				// source value is of type "dword"
+				case DBVT_DWORD:
+					{
+						_ultow(dbv->dVal, buf, 10);
+						dbv->pwszVal = mir_wcsdup(buf);
+						dbv->type = (dbv->pwszVal) ? destType : DBVT_DELETED;
+					} break;
+				// source value is of any string type
+				case DBVT_ASCIIZ:
+				case DBVT_WCHAR:
+				case DBVT_UTF8:
+					{
+						return ConvertString(dbv, destType);
+					}
+				}
+			}
+		}
+		return dbv->type != destType;
+	}
+	return 1;
+}
+
+} /* namespace Variant */
+
+namespace Event {
+
+/**
+ * This function searches for the first event for the given contact.
+ * @param	hContact		- the handle of the contact to search events for
+ *
+ * @return	This function returns the HANDLE of the first event for the given contact.
+ **/
+HANDLE	FindFirst(HANDLE hContact)
+{
+	return (HANDLE)CallService(MS_DB_EVENT_FINDFIRST, (WPARAM)hContact, 0);
+}
+
+/**
+ * This function searches for the last event for the given contact.
+ * @param	hContact		- the handle of the contact to search events for
+ *
+ * @return	This function returns the HANDLE of the last event for the given contact.
+ **/
+HANDLE	FindLast(HANDLE hContact)
+{
+	return (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0);
+}
+
+/**
+ * This function searches for the next event in the chain, which follows the given event.
+ * @param	hEvent			- the handle of the event where to continue searching
+ *
+ * @return	This function returns the HANDLE of the next event in the event chain.
+ **/
+HANDLE	FindNext(HANDLE hEvent)
+{
+	return (HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hEvent, 0);
+}
+
+/**
+ * This function searches for the previous event in the chain, which follows the given event.
+ * @param	hEvent			- the handle of the event where to continue searching
+ *
+ * @return	This function returns the HANDLE of the previous event in the event chain.
+ **/
+HANDLE	FindPrev(HANDLE hEvent)
+{
+	return (HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hEvent, 0);
+}
+
+/**
+ * This function initializes the DBEVENTINFO structure and calls 
+ * the MS_DB_EVENT_GET service to retrieve information about an event.
+ * @param	hEvent			- the handle of the event to get information for
+ * @param	dbei			- the pointer to a DBEVENTINFO structure, which retrieves all information.
+ *
+ * @retval	0				- success
+ * @retval	nonezero		- failure
+ **/
+BYTE	GetInfo(HANDLE hEvent, DBEVENTINFO *dbei)
+{
+	dbei->cbSize = sizeof(DBEVENTINFO);
+	dbei->cbBlob = 0;
+	dbei->pBlob  = NULL;
+	return CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)dbei) != 0;
+}
+
+/**
+ * This function initializes the DBEVENTINFO structure and calls 
+ * the MS_DB_EVENT_GET service to retrieve information about an event.
+ * @param	hEvent			- the handle of the event to get information for
+ * @param	dbei			- the pointer to a DBEVENTINFO structure, which retrieves all information.
+ *
+ * @retval	0				- success
+ * @retval	1				- failure
+ **/
+BYTE	GetInfoWithData(HANDLE hEvent, DBEVENTINFO *dbei)
+{
+	BYTE result;
+	dbei->cbSize = sizeof(DBEVENTINFO);
+	if (!dbei->cbBlob) {
+		INT_PTR size = BlobSizeOf(hEvent);
+		dbei->cbBlob = (size != -1) ? (DWORD)size : 0;
+	}
+	if(dbei->cbBlob) {
+		dbei->pBlob = (PBYTE) mir_alloc(dbei->cbBlob);
+		if (dbei->pBlob == NULL) {
+			dbei->cbBlob = 0;
+		}
+	}
+	else {
+		dbei->pBlob = NULL;
+	}
+
+	result = CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)dbei) != 0;
+	if (result && dbei->pBlob) {
+		mir_free(dbei->pBlob);
+		dbei->pBlob = NULL;
+	}
+	return result;
+}
+
+/**
+ * This function returns the timestamp for the given event.
+ * @param	hEvent			- the handle of the event to get the timestamp for
+ *
+ * @retval	0 if no timestamp is available
+ * @retval	timestamp
+ **/
+DWORD	TimeOf(HANDLE hEvent)
+{
+	DBEVENTINFO dbei;
+	if (!GetInfo(hEvent, &dbei)) {
+		return dbei.timestamp;
+	}
+	return 0;
+}
+
+/**
+ * This function returns the number of bytes required to retrieve
+ * binary data associated with the event.
+ * @param	hEvent			- the handle of the event to get the number of bytes for
+ *
+ * @retval	size of event data
+ * @retval	-1 if hEvent is invalid
+ **/
+INT_PTR	BlobSizeOf(HANDLE hEvent)
+{
+	return CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hEvent, 0);
+}
+
+/**
+ * This function compares two DBEVENTINFO structures against each other.
+ * It compares the timestamp, eventType and module names.
+ * @param	d1				- pointer to the first DBEVENTINFO structure
+ * @param	d2				- pointer to the second DBEVENTINFO structure
+ * @param	Data			- default false, if true compare also blob data
+.*
+ * @retval	TRUE			- The structures describe the same event.
+ * @retval	FALSE			- The two structure's events differ from each other.
+ **/
+static	FORCEINLINE
+BOOLEAN	IsEqual(const DBEVENTINFO *d1, const DBEVENTINFO *d2, bool Data)
+{
+	BOOLEAN res = d1 && d2 && 
+				(d1->timestamp == d2->timestamp) && 
+				(d1->eventType == d2->eventType) &&
+				(d1->cbBlob == d2->cbBlob) && 
+				(!d1->szModule || !d2->szModule || !stricmp(d1->szModule, d2->szModule))
+				;
+	if(Data) {
+			return res &&
+				(!d1->pBlob || !d2->pBlob || !memcmp(d1->pBlob,d2->pBlob,d1->cbBlob))
+				;
+	}
+	return res;
+}
+
+/**
+ * This functions searches for an existing event in the database, which matches
+ * the information provided by 'dbei'. In order to fasten up the search e.g. 
+ * while checking many events, this function stars searching from the last
+ * found event.
+ * @param	hContact			- the handle of the contact to search events for
+ * @param	hDbExistingEvent	- an existing database event to start the search from.
+ * @param	dbei				- the pointer to a DBEVENTINFO structure
+ *
+ * @retval	TRUE	- the event identified by its information in @c dbei exists.
+ * @retval	FALSE	- no event with the information of @c dbei exists.
+ *
+ **/
+BOOLEAN	Exists(HANDLE hContact, HANDLE& hDbExistingEvent, DBEVENTINFO *dbei)
+{
+	BOOLEAN		result = FALSE;
+	DBEVENTINFO	edbei;
+	HANDLE		sdbe,
+				edbe;
+
+	if (!hDbExistingEvent) {
+		hDbExistingEvent = FindFirst(hContact);
+		if (hDbExistingEvent) {
+			if (!GetInfo(hDbExistingEvent, &edbei)) {
+				if ((dbei->timestamp < edbei.timestamp)) {
+					return FALSE;
+				}
+				if(IsEqual(dbei, &edbei, false)) {
+					if (!GetInfoWithData(hDbExistingEvent, &edbei)) {
+						if(IsEqual(dbei, &edbei, true)) {
+							mir_free(edbei.pBlob);
+							return TRUE;
+						}
+						mir_free(edbei.pBlob);
+					}
+				}
+			}
+			edbe = FindLast(hContact);
+			if (edbe == hDbExistingEvent) {
+				return FALSE;
+			}
+			hDbExistingEvent = edbe;
+		}
+	}
+	if (hDbExistingEvent) {
+		sdbe = hDbExistingEvent;
+		for (	edbe = sdbe;
+				edbe && !GetInfo(edbe, &edbei) && (dbei->timestamp <= edbei.timestamp);
+				edbe = FindPrev(edbe)) {
+			hDbExistingEvent = edbe;
+			//compare without data (faster)
+			if ( result = IsEqual(dbei, &edbei, false)) {
+				if(NULL == (result = !GetInfoWithData(edbe, &edbei))) continue;
+				//compare with data
+				result = IsEqual(dbei, &edbei, true);
+				mir_free(edbei.pBlob);
+				if (result) {
+					break;
+				}
+			}
+		} /*end for*/
+
+		if (!result) {
+			for (	edbe = FindNext(sdbe);
+					edbe && !GetInfo(edbe, &edbei) && (dbei->timestamp >= edbei.timestamp);
+					edbe = FindNext(edbe)) {
+				hDbExistingEvent = edbe;
+				//compare without data (faster)
+				if ( result = IsEqual(dbei, &edbei, false)) {
+					if(NULL == (result = !GetInfoWithData(edbe, &edbei))) continue;
+					//compare with data
+					result = IsEqual(dbei, &edbei, true);
+					mir_free(edbei.pBlob);
+					if (result) {
+						break;
+					}
+				}
+			}
+		}
+	}
+	return result;
+}
+
+} /* namespace Events */
+
+INT CEnumList::EnumProc(LPCSTR pszName, DWORD ofsModuleName, LPARAM lParam)
+{
+	if (pszName) {
+		((CEnumList*)lParam)->Insert(pszName);
+	}
+	return 0;
+}
+
+INT CEnumList::EnumSettingsProc(LPCSTR pszName, LPARAM lParam)
+{
+	return EnumProc(pszName, 0, lParam);
+}
+
+INT CEnumList::CompareProc(LPCSTR p1, LPCSTR p2)
+{
+	if (p1) {
+		if (p2) {
+			return strcmp(p1, p2);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+CEnumList::CEnumList()	: LIST<CHAR>(50, (FTSortFunc)CEnumList::CompareProc)
+{
+}
+
+CEnumList::~CEnumList() 
+{ 
+	INT i, cnt;
+	LPSTR p;
+
+	for (i = 0, cnt = getCount(); i < cnt; i++) {
+		p = (*this)[i]; 
+		if (p) {
+			mir_free(p);
+		}
+	}
+	destroy();
+}
+
+LPSTR CEnumList::Insert(LPCSTR str)
+{
+	LPSTR p = mir_strdup(str);
+	if (p && !insert(p)) {
+		mir_free(p);
+		p = NULL;
+	}
+	return p;
+}
+
+INT_PTR CEnumList::EnumModules()
+{
+	return CallService(MS_DB_MODULES_ENUM, (WPARAM)this, (LPARAM)CEnumList::EnumProc);
+}
+
+/**
+ * @retval	-1	- no settings to enumerate
+ * @retval	 0	- success
+ **/
+INT_PTR CEnumList::EnumSettings(HANDLE hContact, LPCSTR pszModule)
+{
+	DBCONTACTENUMSETTINGS dbces;
+	
+	dbces.pfnEnumProc = (DBSETTINGENUMPROC)CEnumList::EnumSettingsProc;
+	dbces.szModule = pszModule;
+	dbces.lParam = (LPARAM)this;
+	dbces.ofsSettings = 0;
+	return CallService(MS_DB_CONTACT_ENUMSETTINGS, (WPARAM)hContact, (LPARAM)&dbces);
+}
+
+} /* namespace DB */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/mir_db.h b/plugins/UserInfoEx/src/mir_db.h
new file mode 100644
index 0000000000..e06a8788a6
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_db.h
@@ -0,0 +1,229 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_db.h $
+Revision       : $Revision: 189 $
+Last change on : $Date: 2010-09-11 01:31:38 +0400 (Сб, 11 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#pragma once
+
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_database.h>
+
+namespace DB {
+
+namespace MetaContact{
+	INT_PTR	SubCount      (HANDLE hMetaContact);
+	INT_PTR	SubDefNum     (HANDLE hMetaContact);
+	HANDLE	Sub           (HANDLE hMetaContact, INT idx);
+
+	BOOLEAN IsSub         (HANDLE hContact);
+	HANDLE	GetMeta       (HANDLE hContact);
+
+} /* namespace MetaContact */
+
+/**
+ * This namespace contains all functions used to access
+ * or modify contacts in the database.
+ **/
+namespace Contact {
+	LPTSTR	DisplayName   (HANDLE hContact);
+	LPSTR	Proto         (HANDLE hContact);
+
+	INT_PTR	GetCount      ();
+	HANDLE	FindFirst     ();
+	HANDLE	FindNext      (HANDLE hContact);
+
+	HANDLE	Add();
+	BYTE	Delete        (HANDLE hContact);
+
+	DWORD	WhenAdded     (DWORD dwUIN, LPCSTR szBaseProto);
+
+} /* namespace Contact */
+
+namespace Module {
+	VOID	Delete        (HANDLE hContact, LPCSTR pszModule);
+	BOOLEAN	IsEmpty       (HANDLE hContact, LPCSTR pszModule);
+	BOOLEAN	IsMeta        (LPCSTR pszModule);
+	BOOLEAN	IsMetaAndScan (LPCSTR pszModule);
+
+} /* namespace Module */
+
+/**
+ * This namespace defines all functions used to read and write settings from the database.
+ **/
+namespace Setting {
+
+	BYTE	Get           (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE nType);
+	static	FORCEINLINE
+	BYTE	GetAsIs       (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv) {return Get(hContact, pszModule, pszSetting, dbv, 0); }
+	static	FORCEINLINE
+	BYTE	GetAString    (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv) {return Get(hContact, pszModule, pszSetting, dbv, DBVT_ASCIIZ); }
+	LPSTR	GetAString    (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+	static	FORCEINLINE
+	BYTE	GetWString    (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv) {return Get(hContact, pszModule, pszSetting, dbv, DBVT_WCHAR); }
+	LPWSTR	GetWString    (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+	static	FORCEINLINE
+	BYTE	GetUString    (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DBVARIANT *dbv) {return Get(hContact, pszModule, pszSetting, dbv, DBVT_UTF8); }
+
+	BYTE	GetEx         (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE nType);
+	static	FORCEINLINE
+	BYTE	GetAsIsEx     (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetEx(hContact, pszModule, szProto, pszSetting, dbv, 0); }
+	static	FORCEINLINE
+	LPSTR	GetAStringEx  (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting) { DBVARIANT dbv; return (!GetEx(hContact, pszModule, szProto, pszSetting, &dbv, DBVT_ASCIIZ)	&& dbv.type == DBVT_ASCIIZ)	? dbv.pszVal  : NULL; }
+	static	FORCEINLINE
+	LPWSTR	GetWStringEx  (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting) { DBVARIANT dbv; return (!GetEx(hContact, pszModule, szProto, pszSetting, &dbv, DBVT_WCHAR)	&& dbv.type == DBVT_WCHAR)	? dbv.pwszVal : NULL; }
+	static	FORCEINLINE
+	LPSTR	GetUStringEx  (HANDLE hContact, LPCSTR pszModule, LPCSTR szProto, LPCSTR pszSetting) { DBVARIANT dbv; return (!GetEx(hContact, pszModule, szProto, pszSetting, &dbv, DBVT_UTF8)	&& dbv.type == DBVT_UTF8)	? dbv.pszVal  : NULL; }
+
+	WORD	GetCtrl       (HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv, const BYTE nType);
+	static	FORCEINLINE
+	WORD	GetAsIsCtrl   (HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetCtrl(hContact, pszModule, szSubModule, szProto, pszSetting, dbv, 0); }
+	static	FORCEINLINE
+	WORD	GetAStringCtrl(HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetCtrl(hContact, pszModule, szSubModule, szProto, pszSetting, dbv, DBVT_ASCIIZ); }
+	static	FORCEINLINE
+	WORD	GetWStringCtrl(HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetCtrl(hContact, pszModule, szSubModule, szProto, pszSetting, dbv, DBVT_WCHAR); }
+	static	FORCEINLINE
+	WORD	GetUStringCtrl(HANDLE hContact, LPCSTR pszModule, LPCSTR szSubModule, LPCSTR szProto, LPCSTR pszSetting, DBVARIANT *dbv) { return GetCtrl(hContact, pszModule, szSubModule, szProto, pszSetting, dbv, DBVT_UTF8); }
+
+	BYTE	GetStatic     (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR szValue, INT cchValue);
+
+	BYTE	GetByte       (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, BYTE errorValue);
+	static	FORCEINLINE
+	BYTE	GetByte       (LPCSTR pszModule, LPCSTR pszSetting, BYTE errorValue) { return GetByte(NULL, pszModule, pszSetting, errorValue); }
+	static	FORCEINLINE
+	BYTE	GetByte       (LPCSTR pszSetting, BYTE errorValue) { return GetByte(MODNAME, pszSetting, errorValue); }
+
+	WORD	GetWord       (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, WORD errorValue);
+	static	FORCEINLINE
+	WORD	GetWord       (LPCSTR pszModule, LPCSTR pszSetting, WORD errorValue) { return GetWord(NULL, pszModule, pszSetting, errorValue); }
+	static	FORCEINLINE
+	WORD	GetWord       (LPCSTR pszSetting, WORD errorValue) { return GetWord(MODNAME, pszSetting, errorValue); }
+
+	DWORD	GetDWord      (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DWORD errorValue);
+	static	FORCEINLINE
+	DWORD	GetDWord      (LPCSTR pszModule, LPCSTR pszSetting, DWORD errorValue) { return GetDWord(NULL, pszModule, pszSetting, errorValue); }
+	static	FORCEINLINE
+	DWORD	GetDWord      (LPCSTR pszSetting, DWORD errorValue) { return GetDWord(MODNAME, pszSetting, errorValue); }
+
+	/**
+	 * write values to the database.
+	 **/
+	BYTE	WriteVariant  (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, const DBVARIANT *dbv);
+
+	BYTE	WriteByte     (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, BYTE value);
+	static	FORCEINLINE
+	BYTE	WriteByte     (LPCSTR pszModule, LPCSTR pszSetting, BYTE value) { return WriteByte(NULL, pszModule, pszSetting, value); }
+	static	FORCEINLINE
+	BYTE	WriteByte     (LPCSTR pszSetting, BYTE value) { return WriteByte(MODNAME, pszSetting, value); }
+
+	BYTE	WriteWord     (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, WORD value);
+	static	FORCEINLINE
+	BYTE	WriteWord     (LPCSTR pszModule, LPCSTR pszSetting, WORD value) { return WriteWord(NULL, pszModule, pszSetting, value); }
+	static	FORCEINLINE
+	BYTE	WriteWord     (LPCSTR pszSetting, WORD value) { return WriteWord(MODNAME, pszSetting, value); }
+
+	BYTE	WriteDWord    (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, DWORD value);
+	static	FORCEINLINE
+	BYTE	WriteDWord    (LPCSTR pszModule, LPCSTR pszSetting, DWORD value) { return WriteDWord(NULL, pszModule, pszSetting, value); }
+	static	FORCEINLINE
+	BYTE	WriteDWord    (LPCSTR pszSetting, DWORD value) { return WriteDWord(MODNAME, pszSetting, value); }
+
+	BYTE	WriteAString  (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR value);
+	static	FORCEINLINE
+	BYTE	WriteAString  (LPCSTR pszModule, LPCSTR pszSetting, LPSTR value) { return WriteAString(NULL, pszModule, pszSetting, value); }
+	static	FORCEINLINE
+	BYTE	WriteAString  (LPCSTR pszSetting, LPSTR value) { return WriteAString(MODNAME, pszSetting, value); }
+
+	BYTE	WriteWString  (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPWSTR value);
+	static	FORCEINLINE
+	BYTE	WriteWString  (LPCSTR pszModule, LPCSTR pszSetting, LPWSTR value) { return WriteWString(NULL, pszModule, pszSetting, value); }
+	static	FORCEINLINE
+	BYTE	WriteWString  (LPCSTR pszSetting, LPWSTR value) { return WriteWString(MODNAME, pszSetting, value); }
+
+	BYTE	WriteUString  (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting, LPSTR value);
+	static	FORCEINLINE
+	BYTE	WriteUString  (LPCSTR pszModule, LPCSTR pszSetting, LPSTR value) { return WriteUString(NULL, pszModule, pszSetting, value); }
+	static	FORCEINLINE
+	BYTE	WriteUString  (LPCSTR pszSetting, LPSTR value) { return WriteUString(MODNAME, pszSetting, value); }
+
+
+	#define GetTString			GetWString
+	#define GetTStringEx		GetWStringEx
+	#define GetTStringCtrl		GetWStringCtrl
+	#define WriteTString		WriteWString
+
+
+	/**
+	 * misc operations
+	 **/
+	BOOLEAN	Exists        (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+	BYTE	Delete        (HANDLE hContact, LPCSTR pszModule, LPCSTR pszSetting);
+	VOID	DeleteArray   (HANDLE hContact, LPCSTR pszModule, LPCSTR pszFormat, INT iStart);
+	BYTE	Resident      (LPCSTR pszSetting, const bool enabled);
+
+} /* namespace Setting */
+
+namespace Variant {
+	BYTE	ConvertString (DBVARIANT* dbv, const BYTE destType);
+	BYTE	dbv2String    (DBVARIANT* dbv, const BYTE destType);
+	BYTE	Free          (DBVARIANT* dbv);
+} /* namespace Variant */
+
+namespace Event   {
+
+	HANDLE	FindFirst      (HANDLE hContact);
+	HANDLE	FindLast       (HANDLE hContact);
+	HANDLE	FindNext       (HANDLE hEvent);
+	HANDLE	FindPrev       (HANDLE hEvent);
+	BYTE	GetInfo        (HANDLE hEvent, DBEVENTINFO *dbei);
+	BYTE	GetInfoWithData(HANDLE hEvent, DBEVENTINFO *dbei);
+	DWORD	GetTime        (HANDLE hEvent);
+	INT_PTR	BlobSizeOf     (HANDLE hEvent);
+	BOOLEAN	Exists         (HANDLE hContact, HANDLE& hDbExistingEvent, DBEVENTINFO *dbei);
+} /* namespace Events */
+
+/**
+ * enumerating
+ **/
+class CEnumList : public ::LIST<CHAR>
+{
+	static INT EnumProc        (LPCSTR pszName, DWORD ofsModuleName, LPARAM lParam);
+	static INT EnumSettingsProc(LPCSTR pszName, LPARAM lParam);
+
+	static INT CompareProc     (LPCSTR p1, LPCSTR p2);
+	
+public:
+	CEnumList();
+	~CEnumList();
+
+	LPSTR	Insert(LPCSTR str);
+
+	INT_PTR	EnumModules();
+	INT_PTR	EnumSettings(HANDLE hContact, LPCSTR pszModule);
+};
+
+} /* namespace DB */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/mir_icolib.cpp b/plugins/UserInfoEx/src/mir_icolib.cpp
new file mode 100644
index 0000000000..ef5d22e487
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_icolib.cpp
@@ -0,0 +1,401 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_icolib.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+
+typedef struct _ICODESC 
+{
+	LPSTR	pszName;
+	LPSTR	pszDesc;
+	LPSTR	pszSection;
+	WORD	idResource;
+	BYTE	size;
+} ICODESC;
+
+HICON ghDefIcon = NULL;
+
+static ICODESC icoDesc[] = 
+{
+	// common
+	{ ICO_COMMON_IM,		"IM Naming",				SECT_COMMON,	IDI_MIRANDA,			0	},
+	{ ICO_COMMON_PASSWORD,	"Password",					SECT_COMMON,	IDI_PASSWORD,			0	},
+	{ ICO_COMMON_FEMALE,	"Female",					SECT_COMMON,	IDI_FEMALE,				0	},
+	{ ICO_COMMON_MALE,		"Male",						SECT_COMMON,	IDI_MALE,				0	},
+	{ ICO_COMMON_BIRTHDAY,	"Birthday",					SECT_COMMON,	IDI_BIRTHDAY,			0	},
+	{ ICO_COMMON_CLOCK,		"Timezone",					SECT_COMMON,	IDI_CLOCK,				1	},
+	{ ICO_COMMON_MARITAL,	"Marital status",			SECT_COMMON,	IDI_MARITAL,			0	},
+	{ ICO_COMMON_ADDRESS,	"Address",					SECT_COMMON,	IDI_TREE_ADDRESS,		0	},
+	{ ICO_COMMON_ANNIVERSARY,"Anniversary",				SECT_COMMON,	IDI_ANNIVERSARY,		0	},
+
+	//zodiac
+	{ ICO_ZOD_AQUARIUS,		"Aquarius",					SECT_COMMON,	IDI_ZOD_AQUARIUS,		128	},
+	{ ICO_ZOD_ARIES,		"Aries",					SECT_COMMON,	IDI_ZOD_ARIES,			128	},
+	{ ICO_ZOD_CANCER,		"Cancer",					SECT_COMMON,	IDI_ZOD_CANCER,			128	},
+	{ ICO_ZOD_CAPRICORN,	"Capricorn",				SECT_COMMON,	IDI_ZOD_CAPRICORN,		128	},
+	{ ICO_ZOD_GEMINI,		"Gemini",					SECT_COMMON,	IDI_ZOD_GEMINI,			128	},
+	{ ICO_ZOD_LEO,			"Leo",						SECT_COMMON,	IDI_ZOD_LEO,			128	},
+	{ ICO_ZOD_LIBRA,		"Libra",					SECT_COMMON,	IDI_ZOD_LIBRA,			128	},
+	{ ICO_ZOD_PISCES,		"Pisces",					SECT_COMMON,	IDI_ZOD_PISCES,			128	},
+	{ ICO_ZOD_SAGITTARIUS,	"Sagittarius",				SECT_COMMON,	IDI_ZOD_SAGITTARIUS,	128	},
+	{ ICO_ZOD_SCORPIO,		"Scorpio",					SECT_COMMON,	IDI_ZOD_SCORPIO,		128	},
+	{ ICO_ZOD_TAURUS,		"Taurus",					SECT_COMMON,	IDI_ZOD_TAURUS,			128	},
+	{ ICO_ZOD_VIRGO,		"Virgo",					SECT_COMMON,	IDI_ZOD_VIRGO,			128	},
+	// later with new icon pack version
+	//{ ICO_ZOD_UNKNOWN,		"Unknown",					SECT_COMMON,	IDI_ZOD_UNKNOWN,		128	},
+
+	// lists
+	{ ICO_LST_MODULES,		"Export: Modules",			SECT_COMMON,	IDI_LST_MODULES,		0	},
+	{ ICO_LST_FOLDER,		"Export: Folder",			SECT_COMMON,	IDI_LST_FOLDER,			0	},
+	{ ICO_TREE_DEFAULT,		"Default",					SECT_TREE,		IDI_LST_FOLDER,			0	},
+
+	// dialogs
+	{ ICO_DLG_DETAILS,		"Details Infobar",			SECT_DLG,		IDI_DLG_DETAILS,		48	},
+	{ ICO_DLG_PHONE,		"Phone Infobar",			SECT_DLG,		IDI_DLG_PHONE,			1	},
+	{ ICO_DLG_EMAIL,		"E-Mail Infobar",			SECT_DLG,		IDI_DLG_EMAIL,			1	},
+	{ ICO_DLG_EXPORT,		"Export VCard",				SECT_DLG,		IDI_EXPORT,				1	},
+	{ ICO_DLG_IMPORT,		"Import VCard",				SECT_DLG,		IDI_IMPORT,				1	},
+	{ ICO_DLG_ANNIVERSARY,	"Anniversary Infobar",		SECT_DLG,		IDI_ANNIVERSARY,		1	},
+
+	// button icons
+	{ ICO_BTN_UPDATE,		"Update",					SECT_BUTTONS,	IDI_BTN_UPDATE,			0	},
+	{ ICO_BTN_IMPORT,		"Import",					SECT_BUTTONS,	IDI_IMPORT,				0	},
+	{ ICO_BTN_EXPORT,		"Export",					SECT_BUTTONS,	IDI_EXPORT,				0	},
+	{ ICO_BTN_OK,			"Ok",						SECT_BUTTONS,	IDI_BTN_OK,				0	},
+	{ ICO_BTN_CANCEL,		"Cancel",					SECT_BUTTONS,	IDI_BTN_CLOSE,			0	},
+	{ ICO_BTN_APPLY,		"Apply",					SECT_BUTTONS,	IDI_BTN_APPLY,			0	},
+	{ ICO_BTN_GOTO,			"Goto",						SECT_BUTTONS,	IDI_BTN_GOTO,			0	},
+	{ ICO_BTN_PHONE,		"Phone",					SECT_BUTTONS,	IDI_BTN_PHONE,			0	},
+	{ ICO_BTN_FAX,			"Fax",						SECT_BUTTONS,	IDI_BTN_FAX,			0	},
+	{ ICO_BTN_CELLULAR,		"Cellular",					SECT_BUTTONS,	IDI_BTN_CELLULAR,		0	},
+	{ ICO_BTN_CUSTOMPHONE,	"Custom Phone",				SECT_BUTTONS,	IDI_BTN_CUSTOMPHONE,	0	},
+	{ ICO_BTN_EMAIL,		"e-mail",					SECT_BUTTONS,	IDI_BTN_EMAIL,			0	},
+	{ ICO_BTN_DOWNARROW,	"Down arrow",				SECT_BUTTONS,	IDI_BTN_DOWNARROW,		0	},
+	{ ICO_BTN_ADD,			"Add",						SECT_BUTTONS,	IDI_BTN_ADD,			0	},
+	{ ICO_BTN_EDIT,			"Edit",						SECT_BUTTONS,	IDI_BTN_EDIT,			0	},
+	{ ICO_BTN_DELETE,		"Delete",					SECT_BUTTONS,	IDI_BTN_DELETE,			0	},
+	{ ICO_BTN_SEARCH,		"Search",					SECT_BUTTONS,	IDI_SEARCH,				0	},
+	{ ICO_BTN_EXIMPORT,		"Ex-/Import",				SECT_BUTTONS,	IDI_BTN_EXIMPORT,		0	},
+	{ ICO_BTN_BDAY_BACKUP,	"Backup Birthday",			SECT_BUTTONS,	IDI_BTN_BIRTHDAY_BACKUP,0	},
+	//{ ICO_BTN_YES,			"Yes",					SECT_BUTTONS,	IDI_BTN_YES,			0	},
+	//{ ICO_BTN_NO,			"No",						SECT_BUTTONS,	IDI_BTN_NO,				0	},
+	//{ ICO_BTN_IGNORE,		"Ignore",					SECT_BUTTONS,	IDI_BTN_IGNORE,			0	},
+
+	//birthday and anniversary
+	{ ICO_RMD_DTB0,			"Birthday today",			SECT_REMIND,	IDI_RMD_DTB0,			0	},
+	{ ICO_RMD_DTB1,			"Birthday tomorrow",		SECT_REMIND,	IDI_RMD_DTB1,			0	},
+	{ ICO_RMD_DTB2,			"Birthday in 2 days",		SECT_REMIND,	IDI_RMD_DTB2,			0	},
+	{ ICO_RMD_DTB3,			"Birthday in 3 days",		SECT_REMIND,	IDI_RMD_DTB3,			0	},
+	{ ICO_RMD_DTB4,			"Birthday in 4 days",		SECT_REMIND,	IDI_RMD_DTB4,			0	},
+	{ ICO_RMD_DTB5,			"Birthday in 5 days",		SECT_REMIND,	IDI_RMD_DTB5,			0	},
+	{ ICO_RMD_DTB6,			"Birthday in 6 days",		SECT_REMIND,	IDI_RMD_DTB6,			0	},
+	{ ICO_RMD_DTB7,			"Birthday in 7 days",		SECT_REMIND,	IDI_RMD_DTB7,			0	},
+	{ ICO_RMD_DTB8,			"Birthday in 8 days",		SECT_REMIND,	IDI_RMD_DTB8,			0	},
+	{ ICO_RMD_DTB9,			"Birthday in 9 days",		SECT_REMIND,	IDI_RMD_DTB9,			0	},
+	{ ICO_RMD_DTBX,			"Birthday later",			SECT_REMIND,	IDI_RMD_DTBX,			0	},
+		
+	{ ICO_RMD_DTA0,			"Anniversary today",		SECT_REMIND,	IDI_RMD_DTA0,			0	},
+	{ ICO_RMD_DTA1,			"Anniversary tomorrow",		SECT_REMIND,	IDI_RMD_DTA1,			0	},
+	{ ICO_RMD_DTA2,			"Anniversary in 2 days",	SECT_REMIND,	IDI_RMD_DTA2,			0	},
+	{ ICO_RMD_DTA3,			"Anniversary in 3 days",	SECT_REMIND,	IDI_RMD_DTA3,			0	},
+	{ ICO_RMD_DTA4,			"Anniversary in 4 days",	SECT_REMIND,	IDI_RMD_DTA4,			0	},
+	{ ICO_RMD_DTA5,			"Anniversary in 5 days",	SECT_REMIND,	IDI_RMD_DTA5,			0	},
+	{ ICO_RMD_DTA6,			"Anniversary in 6 days",	SECT_REMIND,	IDI_RMD_DTA6,			0	},
+	{ ICO_RMD_DTA7,			"Anniversary in 7 days",	SECT_REMIND,	IDI_RMD_DTA7,			0	},
+	{ ICO_RMD_DTA8,			"Anniversary in 8 days",	SECT_REMIND,	IDI_RMD_DTA8,			0	},
+	{ ICO_RMD_DTA9,			"Anniversary in 9 days",	SECT_REMIND,	IDI_RMD_DTA9,			0	},
+	{ ICO_RMD_DTAX,			"Anniversary later",		SECT_REMIND,	IDI_RMD_DTAX,			0	},
+};
+
+/**
+ * This function finds the default iconpack file and return its path.
+ *
+ * @param		- none
+ *
+ * @return		This function returns the relative path to an existing icon pack.
+ **/
+LPTSTR IcoLib_GetDefaultIconFileName()
+{
+	static LPTSTR	path[] = {
+		_T("Icons\\uinfoex_icons.dll"),
+		_T("Plugins\\uinfoex_icons.dll"),
+		_T("Customize\\Icons\\uinfoex_icons.dll")
+	};
+	TCHAR absolute[MAX_PATH];
+
+	for (INT i = 0; i < SIZEOF(path); i++)
+	{
+		CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)path[i], (LPARAM)absolute);
+		if (PathFileExists(absolute))
+		{
+			return path[i];
+		}
+	}
+	return NULL;
+}
+
+/**
+ * This function checks the version of an iconpack.
+ * If the icon pack's version differs from the desired one, 
+ * dialog with a warning is displayed.
+ *
+ * @param		szIconPack	- This is the path to the icon pack. 
+ *							  It can be absolute or relative.
+ *
+ * @return	nothing
+ **/
+static VOID IcoLib_CheckIconPackVersion(LPTSTR szIconPack)
+{
+	if (DB::Setting::GetByte(SET_ICONS_CHECKFILEVERSION, TRUE))
+	{
+		if (szIconPack)
+		{
+			TCHAR		szAbsolutePath[MAX_PATH];
+			HMODULE hIconDll;
+
+			CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)szIconPack, (LPARAM)szAbsolutePath);
+
+			hIconDll = LoadLibrary(szAbsolutePath);
+			if (hIconDll) 
+			{
+				CHAR szFileVersion[64];
+
+				if (!LoadStringA(hIconDll, IDS_ICOPACKVERSION, szFileVersion, sizeof(szFileVersion)) ||
+						mir_strcmp(szFileVersion, "__UserInfoEx_IconPack_1.2__"))
+				{
+					MsgErr(NULL, LPGENT("Warning: Your current IconPack's version differs from the one UserInfoEx is designed for.\nSome icons may not be displayed correctly"));
+				}
+				FreeLibrary(hIconDll);
+			}
+		}
+		else
+		{
+			MsgErr(NULL, LPGENT("Warning: No IconPack found in one of the following directories: 'customize\\icons', 'icons' or 'plugins'!"));
+		}
+	}
+}
+
+/**
+ * Returns a icon, identified by a name
+ *
+ * @param	pszIcon	- name of the icon
+ *
+ * @return:	HICON if the icon is loaded, NULL otherwise
+ **/
+HICON IcoLib_GetIcon(LPCSTR pszIcon)
+{
+	return (pszIcon) ? Skin_GetIcon(pszIcon) : NULL;
+}
+
+/**
+ * Returns a icon, identified by a name
+ *
+ * @param	hIconItem	- this is the pointer to an IconItem structure in icolib.
+ *
+ * @return:	HICON if the icon is loaded, NULL otherwise
+ **/
+HICON IcoLib_GetIconByHandle(HANDLE hIconItem)
+{
+	return Skin_GetIconByHandle(hIconItem);
+}
+
+/**
+ * Set the icon of each control in the list
+ *
+ * @param	hDlg		- handle to the dialog control, that owns the controls
+ * @param	pCtrl		- list to all controls and its icon names
+ * @param	numCtrls	- number of elements in the pCtrl list
+ *
+ * @return	nothing
+ **/
+VOID IcoLib_SetCtrlIcons(HWND hDlg, const ICONCTRL* pCtrl, BYTE numCtrls)
+{
+	HICON	hIcon;
+	BYTE	i;
+	HWND	hCtrl;
+
+	for (i = 0; i < numCtrls; i++) 
+	{
+		hIcon = IcoLib_GetIcon(pCtrl[i].pszIcon);
+		if (pCtrl[i].idCtrl)	
+		{
+			hCtrl = GetDlgItem(hDlg, pCtrl[i].idCtrl);
+			switch (pCtrl[i].Message) 
+			{
+				case STM_SETICON:
+				case STM_SETIMAGE:
+						{
+						ShowWindow(hCtrl, hIcon ? SW_SHOW : SW_HIDE);
+					}
+				case BM_SETIMAGE:
+					{
+						SendMessage(hCtrl, pCtrl[i].Message, IMAGE_ICON, (LPARAM) hIcon);
+					}
+			}
+		}
+		else
+		{
+			SendMessage(hDlg, pCtrl[i].Message, ICON_BIG, (LPARAM) hIcon);
+		}
+	}
+}
+
+/**
+ * This function manually registers a single icon from the default icon library.
+ *
+ * @param		szIconID		- This is the uniquely identifying string for an icon. 
+ *								  This string is the setting name in the database and should 
+ *								  only use ASCII characters.
+ * @param		szDescription	- This is the description displayed in the options dialog.
+ * @param		szSection		- This is the subsection, where the icon is organized in the options dialog.
+ * @param		szDefaultFile	- This is the validated path to the default icon file.
+ * @param		idIcon			- This is the ResourceID of the icon in the default file.
+ * @param		Size			- This is the desired size of the icon to load.
+ *								  0:	default size for small icons (16x16)
+ *								  1:	default size for normal icons (32x32)
+ * @param		hDefIcon		- This is the default icon to use if the default icon
+ *								  file does not exist and no custom icon is set up in the config.
+ *
+ * @return	This function returns the HANDLE of the icon item.
+ **/
+static HANDLE IcoLib_RegisterIconHandleEx(LPSTR szIconID, LPSTR szDescription, LPSTR szSection, LPTSTR szDefaultFile, INT idIcon, INT Size, HICON hDefIcon)
+{
+	HANDLE hIconHandle = NULL;
+
+	if (szIconID && szDescription && szSection) {
+		SKINICONDESC sid = { 0 };
+		sid.cbSize = sizeof(sid);
+		sid.flags = SIDF_ALL_TCHAR;
+		sid.pszName = szIconID;
+		sid.ptszDescription = mir_a2t(szDescription);
+		sid.ptszSection = mir_a2t(szSection);
+
+		if (sid.ptszDescription && sid.ptszSection) {
+			switch (Size) {
+			// small icons (16x16)
+			case 0:
+				sid.cx = GetSystemMetrics(SM_CXSMICON);
+				sid.cy = GetSystemMetrics(SM_CYSMICON);
+				break;
+
+			// normal icons (32x32)
+			case 1:
+				sid.cx = GetSystemMetrics(SM_CXICON);
+				sid.cy = GetSystemMetrics(SM_CYICON);
+				break;
+
+			// custom icon size
+			default:
+				sid.cx = sid.cy = Size;
+				break;
+			}
+
+			sid.ptszDefaultFile = szDefaultFile;
+			if (sid.ptszDefaultFile && sid.ptszDefaultFile[0])
+				sid.iDefaultIndex = -idIcon;
+			else {
+				sid.hDefaultIcon = hDefIcon;
+				sid.iDefaultIndex = -1;
+			}
+			hIconHandle = Skin_AddIcon(&sid);
+		}
+		MIR_FREE(sid.ptszDescription);
+		MIR_FREE(sid.ptszSection);
+	}
+	return hIconHandle;
+}
+
+/**
+ * This function manually registers a single icon from the default icon library.
+ *
+ * @param		szIconID		- This is the uniquely identifying string for an icon. 
+ *								  This string is the setting name in the database and should 
+ *								  only use ASCII characters.
+ * @param		szDescription	- This is the description displayed in the options dialog.
+ * @param		szSection		- This is the subsection, where the icon is organized in the options dialog.
+ * @param		idIcon			- This is the ResourceID of the icon in the default file
+ * @param		Size			- This is the desired size of the icon to load.
+ *								  0:	default size for small icons (16x16)
+ *								  1:	default size for normal icons (32x32)
+ *
+ * @return	This function returns the HANDLE of the icon item.
+ **/
+HANDLE IcoLib_RegisterIconHandle(LPSTR szIconID, LPSTR szDescription, LPSTR szSection, INT idIcon, INT Size)
+{
+	return IcoLib_RegisterIconHandleEx(szIconID, szDescription, szSection, IcoLib_GetDefaultIconFileName(), idIcon, Size, ghDefIcon);
+}
+
+/**
+ * This function manually registers a single icon from the default icon library.
+ *
+ * @param		szIconID		- This is the uniquely identifying string for an icon. 
+ *								  This string is the setting name in the database and should 
+ *								  only use ASCII characters.
+ * @param		szDescription	- This is the description displayed in the options dialog.
+ * @param		szSection		- This is the subsection, where the icon is organized in the options dialog.
+ * @param		idIcon			- This is the ResourceID of the icon in the default file
+ * @param		Size			- This is the desired size of the icon to load.
+ *								  0:	default size for small icons (16x16)
+ *								  1:	default size for normal icons (32x32)
+ *
+ * @return	 This function returns the HICON of the icon itself.
+ **/
+HICON IcoLib_RegisterIcon(LPSTR szIconID, LPSTR szDescription, LPSTR szSection, INT idIcon, INT Size)
+{
+	return IcoLib_GetIconByHandle(IcoLib_RegisterIconHandle(szIconID, szDescription, szSection, idIcon, Size));
+}
+
+/**
+ * Add default icons to the skin library or load customized icons
+ *
+ * @param		none
+ *
+ * @return		nothing
+ **/
+VOID IcoLib_LoadModule()
+{
+	LPTSTR	szDefaultFile;
+	INT_PTR i;
+
+	// search for default icon file
+	szDefaultFile = IcoLib_GetDefaultIconFileName();
+	
+	IcoLib_CheckIconPackVersion(szDefaultFile);
+
+	// load default icon if required
+	ghDefIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, 
+							 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
+
+	for (i = 0; i < SIZEOF(icoDesc); i++) {	
+		IcoLib_RegisterIconHandleEx(
+			icoDesc[i].pszName, icoDesc[i].pszDesc, icoDesc[i].pszSection, 
+			szDefaultFile, icoDesc[i].idResource, icoDesc[i].size, ghDefIcon);
+	}
+}
+
diff --git a/plugins/UserInfoEx/src/mir_icolib.h b/plugins/UserInfoEx/src/mir_icolib.h
new file mode 100644
index 0000000000..d5ac62e9fa
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_icolib.h
@@ -0,0 +1,148 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_icolib.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_ICONS_H_INCLUDED_
+#define _UINFOEX_ICONS_H_INCLUDED_ 1
+
+#include "m_icolib.h"
+
+// sections
+#define SECT_COMMON				"UserInfoEx"
+#define SECT_ZODIAC				"UserInfoEx/Zodiacs"
+#define SECT_DLG				"UserInfoEx/Dialogs"
+#define SECT_BUTTONS			"UserInfoEx/Buttons"
+#define SECT_TREE				"UserInfoEx/TreeView"
+#define SECT_REMIND				"UserInfoEx/Reminder"
+
+// icons
+#define ICO_COMMON_IM			MODNAME"_common_im"
+#define ICO_COMMON_FEMALE		MODNAME"_common_female"
+#define ICO_COMMON_MALE			MODNAME"_common_male"
+#define ICO_COMMON_BIRTHDAY		MODNAME"_common_birthday"
+#define ICO_COMMON_ANNIVERSARY	MODNAME"_common_anniversary"
+#define ICO_COMMON_CLOCK		MODNAME"_common_clock"
+#define ICO_COMMON_MARITAL		MODNAME"_common_marital"
+#define ICO_COMMON_PASSWORD		MODNAME"_common_password"
+#define ICO_COMMON_ADDRESS		MODNAME"_common_address"
+#define ICO_DLG_DETAILS			MODNAME"_dlg_details"
+#define ICO_DLG_PHONE			MODNAME"_dlg_phone"
+#define ICO_DLG_EMAIL			MODNAME"_dlg_email"
+#define ICO_DLG_EXPORT			MODNAME"_dlg_export"
+#define ICO_DLG_IMPORT			MODNAME"_dlg_import"
+#define ICO_DLG_ANNIVERSARY		MODNAME"_dlg_anniversary"
+#define ICO_DLG_SEARCH			MODNAME"_dlg_search"
+#define ICO_LST_MODULES			MODNAME"_lst_modules"
+#define ICO_LST_FOLDER			MODNAME"_lst_folder"
+#define ICO_BTN_UPDATE			MODNAME"_btn_update"
+#define ICO_BTN_OK				MODNAME"_btn_ok"
+#define ICO_BTN_CANCEL			MODNAME"_btn_cancel"
+#define ICO_BTN_APPLY			MODNAME"_btn_apply"
+#define ICO_BTN_GOTO			MODNAME"_btn_goto"
+#define ICO_BTN_ADD				MODNAME"_btn_add"
+#define ICO_BTN_EDIT			MODNAME"_btn_edit"
+#define ICO_BTN_DELETE			MODNAME"_btn_delete"
+#define ICO_BTN_IMPORT			MODNAME"_btn_import"
+#define ICO_BTN_EXPORT			MODNAME"_btn_export"
+#define ICO_BTN_NOTES			MODNAME"_btn_notes"
+#define ICO_BTN_ABOUT			MODNAME"_btn_about"
+#define ICO_BTN_PROFILE			MODNAME"_btn_profile"
+#define ICO_BTN_DOWNARROW		MODNAME"_btn_downarrow"
+#define ICO_BTN_PHONE			MODNAME"_btn_phone"
+#define ICO_BTN_FAX				MODNAME"_btn_fax"
+#define ICO_BTN_CELLULAR		MODNAME"_btn_cellular"
+#define ICO_BTN_CUSTOMPHONE		MODNAME"_btn_customphone"
+#define ICO_BTN_EMAIL			MODNAME"_btn_email"
+#define ICO_BTN_SEARCH			MODNAME"_btn_search"
+#define ICO_BTN_EXIMPORT		MODNAME"_btn_eximport"
+#define ICO_BTN_BDAY_BACKUP		MODNAME"_btn_bdaybackup"
+#define ICO_BTN_YES				MODNAME"_btn_yes"
+#define ICO_BTN_NO				MODNAME"_btn_no"
+#define ICO_BTN_IGNORE			MODNAME"_btn_ignore"
+#define ICO_ZOD_AQUARIUS		MODNAME"_zod_aquarius"
+#define ICO_ZOD_ARIES			MODNAME"_zod_aries"
+#define ICO_ZOD_CANCER			MODNAME"_zod_cancer"
+#define ICO_ZOD_CAPRICORN		MODNAME"_zod_capricorn"
+#define ICO_ZOD_GEMINI			MODNAME"_zod_gemini"
+#define ICO_ZOD_LEO				MODNAME"_zod_leo"
+#define ICO_ZOD_LIBRA			MODNAME"_zod_libra"
+#define ICO_ZOD_PISCES			MODNAME"_zod_pisces"
+#define ICO_ZOD_SAGITTARIUS		MODNAME"_zod_sagittarius"
+#define ICO_ZOD_SCORPIO			MODNAME"_zod_scorpio"
+#define ICO_ZOD_TAURUS			MODNAME"_zod_taurus"
+#define ICO_ZOD_VIRGO			MODNAME"_zod_virgo"
+#define ICO_ZOD_UNKNOWN			MODNAME"_zod_unknown"
+#define ICO_TREE_DEFAULT		MODNAME"_tree_default"
+
+#define ICO_RMD_DTB0			MODNAME"_rmd_dtb0"
+#define ICO_RMD_DTB1			MODNAME"_rmd_dtb1"
+#define ICO_RMD_DTB2			MODNAME"_rmd_dtb2"
+#define ICO_RMD_DTB3			MODNAME"_rmd_dtb3"
+#define ICO_RMD_DTB4			MODNAME"_rmd_dtb4"
+#define ICO_RMD_DTB5			MODNAME"_rmd_dtb5"
+#define ICO_RMD_DTB6			MODNAME"_rmd_dtb6"
+#define ICO_RMD_DTB7			MODNAME"_rmd_dtb7"
+#define ICO_RMD_DTB8			MODNAME"_rmd_dtb8"
+#define ICO_RMD_DTB9			MODNAME"_rmd_dtb9"
+#define ICO_RMD_DTBX			MODNAME"_rmd_dtbx"
+
+#define ICO_RMD_DTA0			MODNAME"_rmd_dta0"
+#define ICO_RMD_DTA1			MODNAME"_rmd_dta1"
+#define ICO_RMD_DTA2			MODNAME"_rmd_dta2"
+#define ICO_RMD_DTA3			MODNAME"_rmd_dta3"
+#define ICO_RMD_DTA4			MODNAME"_rmd_dta4"
+#define ICO_RMD_DTA5			MODNAME"_rmd_dta5"
+#define ICO_RMD_DTA6			MODNAME"_rmd_dta6"
+#define ICO_RMD_DTA7			MODNAME"_rmd_dta7"
+#define ICO_RMD_DTA8			MODNAME"_rmd_dta8"
+#define ICO_RMD_DTA9			MODNAME"_rmd_dta9"
+#define ICO_RMD_DTAX			MODNAME"_rmd_dtax"
+
+#define SET_ICONS_CHECKFILEVERSION		"CheckIconPackVersion"
+#define SET_ICONS_BUTTONS				"ButtonIcons"
+
+#define ICONINDEX(id)	max((min((id), IDI_LASTICON)) - IDI_FIRST_ICON, 0)
+
+typedef struct TIconCtrl 
+{
+	LPCSTR	pszIcon;
+	UINT	Message;
+	WORD	idCtrl;
+} ICONCTRL, *LPICONCTRL;
+
+LPTSTR	IcoLib_GetDefaultIconFileName();
+VOID	IcoLib_SetCtrlIcons(HWND hDlg, const ICONCTRL* pCtrl, BYTE numCtrls);
+
+HANDLE	IcoLib_RegisterIconHandle(LPSTR szName, LPSTR szDescription, LPSTR szSection, INT idIcon, INT Size);
+HICON	IcoLib_RegisterIcon(LPSTR szName, LPSTR szDescription, LPSTR szSection, INT idIcon, INT Size);
+HICON	IcoLib_GetIcon(LPCSTR pszIcon);
+HICON	IcoLib_GetIconByHandle(HANDLE hIconItem);
+
+VOID	IcoLib_LoadModule();
+
+#endif /* _UINFOEX_ICONS_H_INCLUDED_ */
diff --git a/plugins/UserInfoEx/src/mir_menuitems.cpp b/plugins/UserInfoEx/src/mir_menuitems.cpp
new file mode 100644
index 0000000000..9f140fb4b7
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_menuitems.cpp
@@ -0,0 +1,651 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_menuitems.cpp $
+Revision       : $Revision: 210 $
+Last change on : $Date: 2010-10-02 22:27:36 +0400 (Сб, 02 окт 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "svc_Reminder.h"
+
+#include "svc_email.h"
+#include "svc_homepage.h"
+#include ".\ex_import\svc_ExImport.h"
+
+enum ECascadeType {
+	MCAS_DISABLED	= 2,
+	MCAS_ALL		= 4,
+	MCAS_EXIMPORT	= 8,
+	MCAS_NOTINITIATED = 128
+};
+
+INT hMenuItemRefresh		= NULL;
+HGENMENU *hMenuItemAccount	= NULL;
+
+/**
+ * Helper function to remove all menu items backward (first item second group).
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param	pItems		- poiter to HGENMENU array
+ * @param	Count		- number of array elements
+ *
+ * @return	0 on success, -1 on failure
+ **/
+INT_PTR RemoveMenuItems(HGENMENU * pItems, int Count)
+{
+	if (!Count || !pItems) {
+		return -1;
+	}
+	while (Count--) {
+		if (pItems[Count]) {
+			CallService(MO_REMOVEMENUITEM, (WPARAM)pItems[Count], 0);
+			pItems[Count] = NULL;
+		}
+	}
+	return 0;
+}
+
+/**
+ * This function rebuilds the contactmenu. If user selected to cascade menus,
+ * a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID RebuildContact()
+{
+	int flag = 0;
+	BYTE item = 0;
+	CLISTMENUITEM mi;
+
+	HGENMENU mhRoot = HGENMENU_ROOT;
+	HGENMENU mhExIm = HGENMENU_ROOT;
+	static HGENMENU hMenuItem[4] = {NULL, NULL, NULL, NULL };
+
+	SvcEMailRebuildMenu();
+	SvcHomepageRebuildMenu();
+
+	// load options
+	flag = DB::Setting::GetByte(SET_MI_CONTACT, MCAS_NOTINITIATED);
+	if (flag == MCAS_NOTINITIATED){
+		flag = MCAS_EXIMPORT|TRUE;
+		DB::Setting::WriteByte(SET_MI_CONTACT, flag);
+	}
+
+	// delete all MenuItems and set all bytes 0 to avoid problems
+	RemoveMenuItems (hMenuItem, SIZEOF(hMenuItem));
+	ZeroMemory(&mi, sizeof(mi));
+	mi.cbSize = sizeof(mi);
+
+	// support new genmenu style
+	mi.flags = CMIF_ROOTHANDLE;
+	mi.hParentMenu = HGENMENU_ROOT;
+
+	switch (flag)
+	{
+		case 3:
+			//cascade off
+			mhRoot = mhExIm = HGENMENU_ROOT;
+			hMenuItem[item++] = NULL;
+			break;
+		case 5:
+			//cascade all
+			mi.position = 1000050000;
+			mi.popupPosition = 1000050000;
+			mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+			mi.pszName = LPGEN(MODULELONGNAME);
+			mhRoot = Menu_AddContactMenuItem(&mi);
+			hMenuItem[item++] = mhRoot;
+			mhExIm = mhRoot;
+			break;
+		case 9:
+			//cascade Ex/Import
+			mi.position = 1000050100;
+			mi.popupPosition = 1000050100;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+			mi.pszName = LPGEN("Ex-/Import contact");
+			mhExIm = Menu_AddContactMenuItem(&mi);
+			hMenuItem[item++] = mhExIm;
+			mhRoot = HGENMENU_ROOT;
+			break;
+		default:
+			//disable Menue
+			return;
+	}
+	mi.popupPosition = NULL;
+
+	// ContactDetailsPS's menuitem
+	{
+		mi.hParentMenu = mhRoot;
+		mi.pszService = MS_USERINFO_SHOWDIALOG;
+		mi.pszName = LPGEN("User &Details");
+		mi.position = 1000050000;
+		mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+		mi.hotKey = MAKELPARAM(VK_F3, MOD_ALT);
+		hMenuItem[item++] = Menu_AddContactMenuItem(&mi);
+		mi.hotKey = NULL;
+	}
+
+	// VCard's Ex/Import menuitems
+	{	mi.hParentMenu = mhExIm;
+
+		// Export
+		mi.pszService = MS_USERINFO_VCARD_EXPORT;
+		mi.pszName = mhExIm != HGENMENU_ROOT ? LPGEN("&Export") : LPGEN("&Export User Details");
+		mi.position = 1000050200;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+		hMenuItem[item++] = Menu_AddContactMenuItem(&mi);
+
+		// Import
+		mi.pszService = MS_USERINFO_VCARD_IMPORT;
+		mi.pszName = mhExIm != HGENMENU_ROOT ? LPGEN("&Import") : LPGEN("&Import User Details");
+		mi.position = 1000050300;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+		hMenuItem[item++] = Menu_AddContactMenuItem(&mi);
+	}
+}
+
+/**
+ * This function rebuilds the mainmenu. If user selected to cascade menus,
+ * a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID RebuildMain()
+{
+	int flag = 0;
+	BYTE item = 0;
+	CLISTMENUITEM mi;
+
+	HGENMENU mhRoot = HGENMENU_ROOT;
+	HGENMENU mhExIm = HGENMENU_ROOT;
+	static HGENMENU hMenuItem[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+	// load options
+	flag = DB::Setting::GetByte(SET_MI_MAIN, MCAS_NOTINITIATED);
+	if (flag == MCAS_NOTINITIATED){
+		flag = MCAS_ALL|TRUE;
+		DB::Setting::WriteByte(SET_MI_MAIN, flag);
+	}
+
+	// delete all MenuItems and set all bytes 0 to avoid problems
+	RemoveMenuItems (hMenuItem, SIZEOF(hMenuItem));
+	ZeroMemory(&mi, sizeof(mi));
+	mi.cbSize = sizeof(mi);
+
+	// support new genmenu style
+	mi.flags = CMIF_ROOTHANDLE;
+	mi.hParentMenu = HGENMENU_ROOT;
+
+	switch (flag)
+	{
+		case 3:
+			//cascade off
+			mhRoot = mhExIm = HGENMENU_ROOT;
+			hMenuItem[item++] = NULL;
+			break;
+		case 5:
+			//cascade all
+			mi.position = 500050000;
+			mi.popupPosition = 500050000;
+			mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+			mi.pszName = LPGEN(MODULELONGNAME);
+			mhRoot = Menu_AddMainMenuItem(&mi);
+			hMenuItem[item++] = mhRoot;
+			mhExIm = mhRoot;
+			break;
+		case 9:
+			//cascade Ex/Import
+			mi.position = 500050000;
+			mi.popupPosition = 500050000;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+			mi.pszName = LPGEN("Ex-/Import contact");
+			mhExIm = Menu_AddMainMenuItem(&mi);
+			hMenuItem[item++] = mhExIm;
+			mhRoot = HGENMENU_ROOT;
+			break;
+		default:
+			//disable Menue
+			return;
+	}
+	mi.popupPosition = NULL;
+
+	// details dialog
+	{
+		mi.hParentMenu = mhRoot;
+		mi.pszService = MS_USERINFO_SHOWDIALOG;
+		mi.pszName = LPGEN("View/Change My &Details...");
+		mi.position = 500050000;
+		mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+		hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+	}
+
+	// VCard's Ex/Import menuitems
+	{	mi.hParentMenu = mhExIm;
+
+		// Export
+		mi.pszService = MS_USERINFO_VCARD_EXPORTALL;
+		mi.pszName = LPGEN("Export all contacts");
+		mi.position = 500150000;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+		hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+
+		// Import
+		mi.pszService = MS_USERINFO_VCARD_IMPORTALL;
+		mi.pszName = LPGEN("Import all contacts");
+		mi.position = 500151000;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+		hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+	}
+
+	mi.hParentMenu = mhRoot;
+
+	// reminder
+	{
+		const BOOLEAN bRemindMenus =
+			DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED) &&
+			DB::Setting::GetByte(SET_REMIND_MENUENABLED, DEFVAL_REMIND_MENUENABLED);
+		if (bRemindMenus) {
+			// make backup of each protocol based birthday
+			mi.pszService = MS_USERINFO_REMINDER_AGGRASIVEBACKUP;
+			mi.pszName = LPGEN("Backup birthdays");
+			mi.position = 500253000;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_BDAY_BACKUP);
+			hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+			// Check anniversaries
+			mi.pszService = MS_USERINFO_REMINDER_CHECK;
+			mi.pszName = LPGEN("Check anniversaries");
+			mi.position = 500251000;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_SEARCH);
+			hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+		}
+		else {
+			hMenuItem[item++] = NULL;
+			hMenuItem[item++] = NULL;
+		}
+		// Refresh Contact Details
+		mi.pszService = MS_USERINFO_REFRESH;
+		mi.pszName = LPGEN("Refresh Contact Details");
+		mi.position = 500254000;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+		hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+	}
+
+	// anniversary list
+	{
+		mi.pszService = MS_USERINFO_REMINDER_LIST;
+		mi.pszName = LPGEN("Anniversary list");
+		mi.position = 500252000;
+		mi.hIcon = IcoLib_GetIcon(ICO_COMMON_ANNIVERSARY);
+		hMenuItem[item++] = Menu_AddMainMenuItem(&mi);
+	}
+}
+
+/**
+ * This function rebuilds the clist context menu (clist main groupmenu). If user selected to
+ * cascade menus, a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID RebuildGroup()
+{
+	int flag = 0;
+	BYTE item = 0;
+	CLISTMENUITEM mi;
+	GroupMenuParam gmp = {0};
+
+	HGENMENU mhRoot = HGENMENU_ROOT;
+	HGENMENU mhExIm = HGENMENU_ROOT;
+	static HGENMENU hMenuItem[3] = {NULL, NULL, NULL };
+
+	// load options
+	flag = DB::Setting::GetByte(SET_MI_GROUP, MCAS_NOTINITIATED);
+	if (flag == MCAS_NOTINITIATED){
+		flag = MCAS_EXIMPORT|TRUE;
+		DB::Setting::WriteByte(SET_MI_GROUP, flag);
+	}
+
+	// delete all MenuItems and set all bytes 0 to avoid problems
+	RemoveMenuItems (hMenuItem, SIZEOF(hMenuItem));
+	ZeroMemory(&mi, sizeof(mi));
+	mi.cbSize = sizeof(mi);
+
+	// create service name main (prevent to generate {(Null)/Ex-/Import Group} in db) and set pointer to end it
+	char text[ 200 ];
+	strcpy( text, "UserInfo");
+	mi.pszService = text;
+	char* tDest = text + strlen( text );
+
+	// support new genmenu style
+	mi.flags = CMIF_ROOTHANDLE;
+	mi.hParentMenu = HGENMENU_ROOT;
+
+	switch (flag)
+	{
+		case 3:
+			//cascade off
+			mhRoot = mhExIm = HGENMENU_ROOT;
+			hMenuItem[item++] = NULL;
+			break;
+		case 5:
+			//cascade all
+			mi.position = 250000;
+			mi.popupPosition = 250000;
+			mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+			mi.pszName = LPGEN(MODULELONGNAME);
+			mhRoot = Menu_AddGroupMenuItem(0, &mi);
+			hMenuItem[item++] = mhRoot;
+			mhExIm = mhRoot;
+			break;
+		case 9:
+			//cascade Ex/Import
+			mi.position = 250100;
+			mi.popupPosition = 250100;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+			mi.pszName = LPGEN("Ex-/Import contact");
+			mhExIm = Menu_AddGroupMenuItem(0, &mi);
+			hMenuItem[item++] = mhExIm;
+			mhRoot = HGENMENU_ROOT;
+			break;
+		default:
+			//disable Menue
+			return;
+	}
+	mi.popupPosition = NULL;
+
+	// VCard's Ex/Import menuitems
+	{	mi.hParentMenu = mhExIm;
+
+		// Export
+		mi.pszService = MS_USERINFO_VCARD_EXPORTALL;
+		mi.pszName = LPGEN("Export all contacts");
+		mi.position = 250200;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+		hMenuItem[item++] = Menu_AddGroupMenuItem(0, &mi);
+
+		// Import
+		mi.pszService = MS_USERINFO_VCARD_IMPORTALL;
+		mi.pszName = LPGEN("Import all contacts");
+		mi.position = 250300;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+		hMenuItem[item++] = Menu_AddGroupMenuItem(0, &mi);
+	}
+}
+
+/******************************
+ * (Sub)GroupMenu
+ ******************************/
+
+/**
+ * This function rebuilds the group context menu (clist main groupmenu). If user selected to
+ * cascade menus, a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID RebuildSubGroup()
+{
+	int flag = 0;
+	BYTE item = 0;
+	CLISTMENUITEM mi;
+	GroupMenuParam gmp = {0};
+
+	HGENMENU mhRoot = HGENMENU_ROOT;
+	HGENMENU mhExIm = HGENMENU_ROOT;
+	static HGENMENU hMenuItem[3] = {NULL, NULL, NULL };
+
+	// load options
+	flag = DB::Setting::GetByte(SET_MI_SUBGROUP, MCAS_NOTINITIATED);
+	if (flag == MCAS_NOTINITIATED){
+		flag = MCAS_DISABLED|TRUE;
+		DB::Setting::WriteByte(SET_MI_SUBGROUP, flag);
+	}
+
+	// delete all MenuItems and set all bytes 0 to avoid problems
+	RemoveMenuItems (hMenuItem, SIZEOF(hMenuItem));
+	ZeroMemory(&mi, sizeof(mi));
+	mi.cbSize = sizeof(mi);
+
+	// create service name main (prevent to generate {(Null)/Ex-/Import Group} in db) and set pointer to end it
+	char text[ 200 ];
+	strcpy( text, "UserInfo");
+	mi.pszService = text;
+	char* tDest = text + strlen( text );
+
+	// support new genmenu style
+	mi.flags = CMIF_ROOTHANDLE;
+	mi.hParentMenu = HGENMENU_ROOT;
+
+	switch (flag)
+	{
+		case 3:
+			//cascade off
+			mhRoot = mhExIm = HGENMENU_ROOT;
+			hMenuItem[item++] = NULL;
+			break;
+		case 5:
+			//cascade all
+			mi.position = 1050000;
+			mi.popupPosition = 1050000;
+			mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+			mi.pszName = LPGEN(MODULELONGNAME);
+			mhRoot = Menu_AddSubGroupMenuItem(0, &mi);
+			hMenuItem[item++] = mhRoot;
+			mhExIm = mhRoot;
+			break;
+		case 9:
+			//cascade Ex/Import
+			mi.position = 1050100;
+			mi.popupPosition = 1050100;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+			mi.pszName = LPGEN("Ex-/Import Group");
+			mhExIm = Menu_AddSubGroupMenuItem(0, &mi);
+			hMenuItem[item++] = mhExIm;
+			mhRoot = HGENMENU_ROOT;
+			break;
+		default:
+			//disable Menue
+			return;
+	}
+	mi.popupPosition = NULL;
+
+	// VCard's Ex/Import menuitems
+	{	mi.hParentMenu = mhExIm;
+
+		// Export
+		strcpy( tDest, "/ExportGroup");		//mi.pszService
+		if (!ServiceExists(mi.pszService)) CreateServiceFunction(mi.pszService, svcExIm_Group_Service);
+		mi.pszName = mhExIm != HGENMENU_ROOT ? LPGEN("&Export") : LPGEN("&Export Group");
+		mi.position = 1050200;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+		gmp.lParam=0;
+		gmp.wParam=TRUE;
+		hMenuItem[item++] = Menu_AddSubGroupMenuItem(&gmp, &mi);
+
+		// Import
+		strcpy( tDest, "/ImportGroup");		//mi.pszService
+		if (!ServiceExists(mi.pszService)) CreateServiceFunction(mi.pszService, svcExIm_Group_Service);
+		mi.pszName = mhExIm != HGENMENU_ROOT ? LPGEN("&Import") : LPGEN("&Import Group");
+		mi.position = 1050300;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+		gmp.lParam=0;
+		gmp.wParam=FALSE;
+		hMenuItem[item++] = Menu_AddSubGroupMenuItem(&gmp, &mi);
+	}
+}
+
+/******************************
+ * Account Menu
+ ******************************/
+
+/**
+ * This function rebuilds the account context menu (clist status <account>). If user selected to
+ * cascade menus, a root menu item is created which holds the popup for all the other items.
+ * Note: since miranda 0.8 genmenu is part of core (we don't need to check for).
+ *
+ * @param	wParam				- 0 not used
+ * @param	lParam				- clear bit for old menu items
+ *								  0 don't delete old items (its calld by ME_CLIST_PREBUILDSTATUSMENU hook)
+ *								  other then 0 delete old items first
+ *
+ * @return	always 0
+ **/
+INT_PTR RebuildAccount(WPARAM wParam, LPARAM lParam)
+{
+	const BYTE mItems = 3;				// menuitems to create
+	int flag = 0, mProtoCount = 0;
+	BYTE i = 0, item = 0;
+	TCHAR sztName[MAXSETTING];
+	PROTOACCOUNT* pAccountName = NULL;
+	CLISTMENUITEM mi;
+
+	mProtoCount = pcli->menuProtoCount;
+
+	HGENMENU mhRoot = HGENMENU_ROOT;
+	HGENMENU mhExIm = HGENMENU_ROOT;
+
+	// on call by hook or first start
+	if (!lParam || !hMenuItemAccount) {
+		size_t sizeNew = mItems * mProtoCount * sizeof(HGENMENU);
+		hMenuItemAccount = (HGENMENU*) mir_realloc(hMenuItemAccount,sizeNew);
+		// set all bytes 0 to avoid problems
+		memset(hMenuItemAccount, 0, sizeNew);
+	}
+	// on options change
+	else {
+		// delete all MenuItems backward (first item second group)
+		RemoveMenuItems (hMenuItemAccount, mItems * mProtoCount);
+	}
+
+	// load options
+	flag = DB::Setting::GetByte(SET_MI_ACCOUNT, MCAS_NOTINITIATED);
+	if (flag == MCAS_NOTINITIATED){
+		flag = MCAS_EXIMPORT|TRUE;
+		DB::Setting::WriteByte(SET_MI_ACCOUNT, flag);
+	}
+
+
+	// loop for all account names
+	for (i = 0; i < mProtoCount; i++) {
+
+		// set all bytes 0 to avoid problems
+		item = 0;
+		mhRoot = 0;
+		ZeroMemory(&mi, sizeof(mi));
+		mi.cbSize = sizeof(mi);
+
+		mhRoot = pcli->menuProtos[i].pMenu;
+		if ( mhRoot == NULL )
+			break;
+		pAccountName = ProtoGetAccount(pcli->menuProtos[i].szProto);
+
+		// create service name main (account module name) and set pointer to end it
+		char text[ 200 ];
+		strcpy( text, pcli->menuProtos[i].szProto);
+		mi.pszService = text;
+		char* tDest = text + strlen( text );
+
+		// support new genmenu style
+		mi.flags = CMIF_ROOTHANDLE|CMIF_TCHAR|CMIF_KEEPUNTRANSLATED;
+		mi.hParentMenu = mhRoot;
+
+		switch (flag)
+		{
+			case 3:
+				//cascade off
+				mhExIm = mhRoot;
+				// seperator
+				mi.position = 50100;
+				hMenuItemAccount[mItems*i + item++] = Menu_AddStatusMenuItem(&mi);
+				break;
+			case 5:
+				//cascade all
+				mi.position = 50100;
+				mi.hIcon = (HICON)LoadImage(ghInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0);
+				mi.ptszName = TranslateT(MODULELONGNAME);
+				hMenuItemAccount[mItems*i + item] = Menu_AddStatusMenuItem(&mi);
+				mhRoot = hMenuItemAccount[mItems*i + item++];
+				mhExIm = mhRoot;
+				break;
+			case 9:
+				//cascade Ex/Import
+				mi.position = 50100;
+				mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXIMPORT);
+				mir_sntprintf(sztName, SIZEOF(sztName),_T("%s %s"), pAccountName->tszAccountName, TranslateT("Ex-/Import"));
+				mi.ptszName = sztName;
+				hMenuItemAccount[mItems*i + item] = Menu_AddStatusMenuItem(&mi);
+				mhExIm = hMenuItemAccount[mItems*i + item++];
+				break;
+			default:
+				//disable Menue
+				return 0;
+		}
+
+		// VCard's Ex/Import menuitems
+		{
+			mi.hParentMenu = mhExIm;
+
+			// Export
+			strcpy( tDest, "/ExportAccount");		//mi.pszService
+			if (!ServiceExists(mi.pszService)) CreateServiceFunction(mi.pszService, svcExIm_Account_Service);
+			mir_sntprintf(sztName, SIZEOF(sztName),_T("%s %s"), pAccountName->tszAccountName, TranslateT("&Export"));
+			mi.ptszName = sztName;
+			mi.position = 50200;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT);
+			hMenuItemAccount[mItems*i + item++] = Menu_AddStatusMenuItem(&mi);
+
+			// Import
+			strcpy( tDest, "/ImportAccount");		//mi.pszService
+			if (!ServiceExists(mi.pszService)) CreateServiceFunction(mi.pszService, svcExIm_Account_Service);
+			mir_sntprintf(sztName, SIZEOF(sztName),_T("%s %s"), pAccountName->tszAccountName, TranslateT("&Import"));
+			mi.ptszName = sztName;
+			mi.position = 50300;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT);
+			hMenuItemAccount[mItems*i + item++] = Menu_AddStatusMenuItem(&mi);
+		}
+	}
+	return 0;
+}
+
+
+VOID RebuildMenu()
+{
+	RebuildMain();
+	RebuildContact();
+	RebuildGroup();
+	RebuildSubGroup();
+	RebuildAccount(NULL, 1);
+	return;
+}
diff --git a/plugins/UserInfoEx/src/mir_menuitems.h b/plugins/UserInfoEx/src/mir_menuitems.h
new file mode 100644
index 0000000000..1d749ba592
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_menuitems.h
@@ -0,0 +1,47 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_menuitems.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UINFOEX_MENUITEMS_H_INCLUDED_
+#define _UINFOEX_MENUITEMS_H_INCLUDED_
+
+extern INT hMenuItemRefresh;
+extern HGENMENU *hMenuItemAccount;
+
+
+VOID RebuildMenu();
+
+VOID RebuildMain();
+VOID RebuildContact();
+VOID RebuildGroup();
+VOID RebuildSubGroup();
+INT_PTR RebuildAccount(WPARAM wParam, LPARAM lParam);
+
+
+#endif /* _UINFOEX_MENUITEMS_H_INCLUDED_ */
diff --git a/plugins/UserInfoEx/src/mir_string.cpp b/plugins/UserInfoEx/src/mir_string.cpp
new file mode 100644
index 0000000000..7ab895ecc6
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_string.cpp
@@ -0,0 +1,167 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_string.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+
+char	*mir_strncpy(char *pszDest, const char *pszSrc, const size_t cchDest)
+{
+	if (!pszDest || !pszSrc || !cchDest)
+		return NULL;
+	pszDest = strncpy(pszDest, pszSrc, cchDest-1);
+	pszDest[cchDest-1] = 0;
+	return pszDest;
+}
+
+wchar_t	*mir_wcsncpy(wchar_t *pszDest, const wchar_t *pszSrc, const size_t cchDest)
+{
+	if (!pszDest || !pszSrc || !cchDest)
+		return NULL;
+	pszDest = wcsncpy(pszDest, pszSrc, cchDest-1);
+	pszDest[cchDest-1] = 0;
+	return pszDest;
+}
+
+char	*mir_strncat(char *pszDest, const char *pszSrc, const size_t cchDest)
+{
+	if (!pszDest || !pszSrc || !cchDest)
+		return NULL;
+	strncat(pszDest, pszSrc, cchDest-1);
+	pszDest[cchDest-1] = 0;
+	return pszDest;
+}
+
+wchar_t	*mir_wcsncat(wchar_t *pszDest, const wchar_t *pszSrc, const size_t cchDest)
+{
+	if (!pszDest || !pszSrc || !cchDest)
+		return NULL;
+	pszDest = wcsncat(pszDest, pszSrc, cchDest-1);
+	pszDest[cchDest-1] = 0;
+	return pszDest;
+}
+
+char	*mir_strncat_c(char *pszDest, const char cSrc) 
+{
+	size_t size = sizeof(char) * (strlen(pszDest) + 2);		//cSrc = 1 + 1 forNULL temination 
+	if (!pszDest)
+		pszDest = (char *) mir_alloc(size);
+	else
+		pszDest = (char *) mir_realloc(pszDest, size);
+	pszDest[size-2] = cSrc;
+	pszDest[size-1] = 0;
+	return pszDest;
+}
+
+wchar_t	*mir_wcsncat_c(wchar_t *pwszDest, const wchar_t wcSrc) {
+	size_t size = sizeof(wchar_t) * (wcslen(pwszDest) + 2);
+	if (!pwszDest)
+		pwszDest = (wchar_t *) mir_alloc(size);
+	else
+		pwszDest = (wchar_t *) mir_realloc(pwszDest, size);
+	pwszDest[size-2] = wcSrc;
+	pwszDest[size-1] = 0;
+	return pwszDest;
+}
+
+char	*mir_strnerase(char *pszDest, size_t sizeFrom, size_t sizeTo) {
+	char *pszReturn = NULL;
+	size_t sizeNew, sizeLen = strlen(pszDest);
+	if (sizeFrom >= 0 && sizeFrom < sizeLen && sizeTo >= 0 && sizeTo <= sizeLen && sizeFrom < sizeTo) {
+		sizeNew = sizeLen - (sizeTo - sizeFrom);
+		size_t sizeCopy = sizeNew - sizeFrom;
+		pszReturn = (char *) mir_alloc(sizeNew + 1);
+		memcpy(pszReturn, pszDest, sizeFrom);
+		memcpy(pszReturn + sizeFrom, pszDest + sizeTo, sizeCopy);
+		pszReturn[sizeNew] = 0;
+	}
+
+	pszDest = (char *) mir_realloc(pszDest, sizeNew + 1);
+	pszDest = mir_strcpy(pszDest, pszReturn);
+	mir_free(pszReturn);
+	return pszDest;
+}
+
+size_t mir_vsnwprintf(wchar_t *pszDest, const size_t cchDest, const wchar_t *pszFormat, va_list& argList)
+{
+	size_t iRet, cchMax;
+
+	if (!pszDest || !pszFormat || !*pszFormat)
+		return -1;
+
+	cchMax = cchDest - 1;
+	iRet = _vsnwprintf(pszDest, cchMax, pszFormat, argList);
+	if (iRet < 0) pszDest[0] = 0;
+	else if (iRet >= cchMax) {
+		pszDest[cchMax] = 0;
+		iRet = cchMax;
+	}
+	return iRet;
+}
+
+size_t mir_snwprintf(wchar_t *pszDest, const size_t cchDest, const wchar_t *pszFormat, ...)
+{
+	size_t iRet;
+	va_list argList;
+
+	va_start(argList, pszFormat);
+	iRet = mir_vsnwprintf(pszDest, cchDest, pszFormat, argList);
+		va_end(argList);
+	return iRet;
+}
+
+int mir_IsEmptyA(char *str)
+{
+	if(str == NULL || str[0] == 0)
+		return 1;
+	int i = 0;
+	while (str[i]) {
+		if (str[i]!=' ' &&
+			str[i]!='\r' &&
+			str[i]!='\n')
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+int mir_IsEmptyW(wchar_t *str)
+{
+	if(str == NULL || str[0] == 0)
+		return 1;
+	int i = 0;
+	while (str[i]) {
+		if (str[i]!=' ' &&
+			str[i]!='\r' &&
+			str[i]!='\n')
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
diff --git a/plugins/UserInfoEx/src/mir_string.h b/plugins/UserInfoEx/src/mir_string.h
new file mode 100644
index 0000000000..6e2dad0425
--- /dev/null
+++ b/plugins/UserInfoEx/src/mir_string.h
@@ -0,0 +1,88 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/mir_string.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _MIR_STRING_H_INCLUDED_
+#define _MIR_STRING_H_INCLUDED_
+
+#define mir_wcsdup		mir_wstrdup
+
+
+ #define mir_tcslen		mir_wcslen
+ #define mir_tcscpy		mir_wcscpy
+ #define mir_tcsncpy	mir_wcsncpy
+ #define mir_tcsncat	mir_wcsncat
+ #define mir_tcsdup		mir_wcsdup
+ #define mir_tcscmp		mir_wcscmp
+ #define mir_tcsncmp	mir_wcsncmp
+ #define mir_tcsicmp	mir_wcsicmp
+ #define mir_tcsnicmp	mir_wcsnicmp
+ #define mir_tcschr		mir_wcschr
+ #define mir_tcsrchr	mir_wcsrchr
+ #define mir_tcsncat_c	mir_wcsncat_c
+ #define mir_IsEmpty	mir_IsEmptyW
+
+
+
+#define mir_strlen(s)			(((s)!=0)?strlen(s):0)
+#define mir_strcpy(d,s)			(((s)!=0&&(d)!=0)?strcpy(d,s):0)
+#define mir_strcmp(s1,s2)		((s1)==0||(s2)==0||strcmp((s1),(s2)))
+#define mir_strncmp(s1,s2,n)	((s1)==0||(s2)==0||strncmp((s1),(s2),(n)))
+#define mir_stricmp(s1,s2)		((s1)==0||(s2)==0||_stricmp((s1),(s2)))
+#define mir_strnicmp(s1,s2,n)	((s1)==0||(s2)==0||_strnicmp((s1),(s2),(n)))
+#define mir_strchr(s,c)			(((s)!=0)?strchr((s),(c)):0)
+#define mir_strrchr(s,c)		(((s)!=0)?strrchr((s),(c)):0)
+
+#define mir_wcslen(s)			(((s)!=0)?wcslen(s):0)
+#define mir_wcscpy(d,s)			(((s)!=0&&(d)!=0)?wcscpy(d,s):0)
+#define mir_wcscmp(s1,s2)		((s1)==0||(s2)==0||wcscmp((s1),(s2)))
+#define mir_wcsncmp(s1,s2,n)	((s1)==0||(s2)==0||wcsncmp((s1),(s2),(n)))
+#define mir_wcsicmp(s1,s2)		((s1)==0||(s2)==0||_wcsicmp((s1),(s2)))
+#define mir_wcsnicmp(s1,s2,n)	((s1)==0||(s2)==0||_wcsnicmp((s1),(s2),(n)))
+#define mir_wcschr(s,c)			(((s)!=0)?wcschr((s),(c)):0)
+#define mir_wcsrchr(s,c)		(((s)!=0)?wcsrchr((s),(c)):0)
+
+char *		mir_strncpy(char *pszDest, const char *pszSrc, const size_t cchDest);
+wchar_t *	mir_wcsncpy(wchar_t *pszDest, const wchar_t *pszSrc, const size_t cchDest);
+
+char *		mir_strncat(char *pszDest, const char *pszSrc, const size_t cchDest);
+wchar_t *	mir_wcsncat(wchar_t *pszDest, const wchar_t *pszSrc, const size_t cchDest);
+
+char *		mir_strncat_c(char *pszDest, const char cSrc);
+char *		mir_wcsncat_c(char *pszDest, const char cSrc);
+
+size_t		mir_vsnwprintf(wchar_t *pszDest, const size_t cchDest, const wchar_t *pszFormat, va_list& argList);
+size_t		mir_snwprintf (wchar_t *pszDest, const size_t cchDest, const wchar_t *pszFormat, ...);
+
+char *		mir_strnerase(char *pszDest, size_t sizeFrom, size_t sizeTo);
+
+int			mir_IsEmptyA(char *str);
+int			mir_IsEmptyW(wchar_t *str);
+
+#endif /* _MIR_STRING_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/psp_about.cpp b/plugins/UserInfoEx/src/psp_about.cpp
new file mode 100644
index 0000000000..0e8362848e
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_about.cpp
@@ -0,0 +1,124 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_about.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_edit.h"
+#include "psp_base.h"
+
+/**
+ * Dialog procedure for the about/nodes information propertysheetpage
+ *
+ * @param	 hDlg	- handle to the dialog window
+ * @param	 uMsg	- the message to handle
+ * @param	 wParam	- parameter
+ * @param	 lParam	- parameter
+ *
+ * @return	different values
+ **/
+INT_PTR CALLBACK PSPProcEdit(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, const CHAR* pszSetting)
+{
+	switch (uMsg) 
+	{
+	case WM_INITDIALOG:
+		{
+			CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+			if (pCtrlList)
+			{
+				HFONT hBoldFont;
+				PSGetBoldFont(hDlg, hBoldFont);
+				SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+				if (!mir_stricmp(pszSetting, SET_CONTACT_MYNOTES))
+					SetDlgItemText(hDlg, IDC_PAGETITLE, LPGENT("My Notes:"));
+				else
+					SetDlgItemText(hDlg, IDC_PAGETITLE, LPGENT("About:"));
+
+				TranslateDialogDefault(hDlg);
+				
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ABOUT, pszSetting, DBVT_TCHAR));
+
+				// remove static edge in aero mode
+				if (IsAeroMode())
+					SetWindowLongPtr(GetDlgItem(hDlg, EDIT_ABOUT), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hDlg, EDIT_ABOUT), GWL_EXSTYLE)&~WS_EX_STATICEDGE);
+
+				SendDlgItemMessage(hDlg, EDIT_ABOUT, EM_SETEVENTMASK, 0, /*ENM_KEYEVENTS|*/ENM_LINK|ENM_CHANGE);
+				SendDlgItemMessage(hDlg, EDIT_ABOUT, EM_AUTOURLDETECT, TRUE, NULL);
+				if (!lParam) SendDlgItemMessage(hDlg, EDIT_ABOUT, EM_LIMITTEXT, 1024, NULL);
+			}
+		}
+		break;
+		
+	case WM_NOTIFY:
+		{
+			switch (((LPNMHDR)lParam)->idFrom) 
+			{
+			/**
+			 * notification handler for richedit control
+			 **/
+			case EDIT_ABOUT:
+				{
+					switch (((LPNMHDR)lParam)->code) 
+					{
+
+					/**
+					 * notification handler for a link within the richedit control
+					 **/
+					case EN_LINK:
+						return CEditCtrl::GetObj(((LPNMHDR)lParam)->hwndFrom)->LinkNotificationHandler((ENLINK*)lParam);
+					}
+				}
+				return FALSE;
+			}
+		}
+		break;
+
+	case WM_COMMAND:
+		{
+			switch (LOWORD(wParam))
+			{
+			case EDIT_ABOUT:
+				{
+					if (HIWORD(wParam) == EN_CHANGE)
+					{
+						CBaseCtrl *pResult;
+
+						pResult = CBaseCtrl::GetObj((HWND)lParam);
+						if (PtrIsValid(pResult) && (pResult->_cbSize == sizeof(CBaseCtrl)))
+						{
+							pResult->OnChangedByUser(HIWORD(wParam));
+						}
+					}
+				}
+			}
+		}
+		return FALSE;
+	}
+	return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_anniversary.cpp b/plugins/UserInfoEx/src/psp_anniversary.cpp
new file mode 100644
index 0000000000..4872c655db
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_anniversary.cpp
@@ -0,0 +1,347 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_anniversary.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_annivedit.h"
+#include "ctrl_edit.h"
+#include "psp_base.h"
+
+/**
+ * Dialog procedure for the anniversary add/edit dialog
+ *
+ * @param	 hDlg	- handle to the dialog window
+ * @param	 uMsg	- the message to handle
+ * @param	 wParam	- parameter
+ * @param	 lParam	- parameter
+ *
+ * @return	different values
+ **/
+static INT_PTR CALLBACK DlgProc_AnniversaryEditor(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg) 
+	{
+	case WM_INITDIALOG:
+		{
+			MAnnivDate* pDlgEditAnniv = (MAnnivDate*)lParam;
+
+			if (!PtrIsValid(pDlgEditAnniv))
+				break;
+			SetUserData(hDlg, lParam);
+
+			// set icons
+			if (DB::Setting::GetByte(SET_ICONS_BUTTONS, 1))
+			{
+				SendDlgItemMessage(hDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_OK));
+				SendDlgItemMessage(hDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_BTN_CANCEL));
+			}
+			SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)IcoLib_GetIcon(ICO_DLG_ANNIVERSARY));
+
+			// translate controls
+			SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
+			SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+			TranslateDialogDefault(hDlg);
+
+			// init controls
+			EnableWindow(GetDlgItem(hDlg, EDIT_CATEGORY), pDlgEditAnniv->Id() != ANID_BIRTHDAY);
+			SetDlgItemText(hDlg, EDIT_CATEGORY, pDlgEditAnniv->Description());
+			return TRUE;
+		}
+
+	case WM_CTLCOLORSTATIC:
+		{
+			SetBkColor((HDC)wParam, RGB(255, 255, 255));
+		}
+		return (INT_PTR)GetStockObject(WHITE_BRUSH);
+
+	case WM_COMMAND:
+		{
+			switch (LOWORD(wParam))
+			{
+			case EDIT_CATEGORY:
+				{
+					if (HIWORD(wParam) == EN_UPDATE) 
+					{
+						EnableWindow(GetDlgItem(hDlg, IDOK), GetWindowTextLength((HWND)lParam) > 0);
+					}
+				}
+				break;
+
+			case IDOK:
+				{
+					MAnnivDate* pDlgEditAnniv = (MAnnivDate*)GetUserData(hDlg);
+
+					// read new description
+					{
+						HWND hEdit = GetDlgItem(hDlg, EDIT_CATEGORY);
+						INT len = Edit_GetTextLength(hEdit);
+						LPTSTR pszText;
+
+						if (
+							len == 0 ||
+							(pszText = (LPTSTR)_alloca((len + 1) * sizeof(TCHAR))) == NULL ||
+							!Edit_GetText(hEdit, pszText, len + 1)
+					)
+						{
+							MsgErr(hDlg, LPGENT("Please enter a valid Description first!"));
+							break;
+						}
+
+						if (_tcsicmp(pszText, pDlgEditAnniv->Description())) {
+							pDlgEditAnniv->Description(pszText);
+							pDlgEditAnniv->SetFlags(MAnnivDate::MADF_HASCUSTOM|MAnnivDate::MADF_CHANGED);
+						}
+					}
+				}
+			case IDCANCEL:
+				return EndDialog(hDlg, LOWORD(wParam));
+			}
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * Dialog procedure for the anniversary propertysheetpage
+ *
+ * @param	 hDlg	- handle to the dialog window
+ * @param	 uMsg	- the message to handle
+ * @param	 wParam	- parameter
+ * @param	 lParam	- parameter
+ *
+ * @return	different values
+ **/
+INT_PTR CALLBACK PSPProcAnniversary(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg) 
+	{
+	case WM_INITDIALOG:
+		{
+			CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+			if (pCtrlList)
+			{
+				HFONT hBoldFont;
+				PSGetBoldFont(hDlg, hBoldFont);
+				SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+				TranslateDialogDefault(hDlg);
+
+				pCtrlList->insert(CEditCtrl::CreateObj		(hDlg, EDIT_AGE,						SET_CONTACT_AGE,			DBVT_BYTE));
+				pCtrlList->insert(CAnnivEditCtrl::CreateObj	(hDlg, EDIT_ANNIVERSARY_DATE,			NULL));
+
+				// hContact == NULL or reminder disabled
+				CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE)->EnableReminderCtrl(lParam != NULL);
+
+				SendDlgItemMessage(hDlg, EDIT_AGE, EM_LIMITTEXT, 3, 0);
+				SendDlgItemMessage(hDlg, SPIN_AGE, UDM_SETRANGE32, 0, 200);
+			}
+		}
+		break;
+
+	case WM_NOTIFY:
+		{
+			switch (((LPNMHDR)lParam)->idFrom) 
+			{
+			case 0:
+				{
+					switch (((LPNMHDR)lParam)->code) 
+					{
+					case PSN_ICONCHANGED:
+						{
+							const ICONCTRL idIcon[] = {
+								{ ICO_COMMON_BIRTHDAY, STM_SETIMAGE, ICO_BIRTHDAY },
+								{ ICO_BTN_ADD, BM_SETIMAGE, BTN_ADD },
+								{ ICO_BTN_DELETE, BM_SETIMAGE, BTN_DELETE }
+							};
+							IcoLib_SetCtrlIcons(hDlg, idIcon, SIZEOF(idIcon));
+						}
+						return FALSE;
+					}
+				}
+				break; /* case 0 */
+
+			case EDIT_ANNIVERSARY_DATE:
+				{
+					CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+					if (!PspIsLocked(hDlg) && PtrIsValid(pDateCtrl)) 
+					{
+						LPNMHDR lpNmhdr = (LPNMHDR)lParam;
+						
+						switch (lpNmhdr->code) 
+						{
+						case DTN_DATETIMECHANGE:
+							{
+								pDateCtrl->OnDateChanged((LPNMDATETIMECHANGE) lParam);
+							}
+							break;
+						case DTN_DROPDOWN:
+							{
+								HWND hMonthCal = DateTime_GetMonthCal(lpNmhdr->hwndFrom);
+								SetWindowLongPtr(hMonthCal, GWL_STYLE, GetWindowLongPtr(hMonthCal, GWL_STYLE)|MCS_WEEKNUMBERS);
+								InvalidateRect(hMonthCal, NULL, TRUE);
+							}
+						}
+					}
+				}
+				return FALSE;
+			} /* switch (((LPNMHDR)lParam)->idFrom) */
+		}
+		break;
+
+	case WM_COMMAND:
+		{
+			switch (LOWORD(wParam))
+			{
+			case EDIT_REMIND:
+				{
+					if (!PspIsLocked(hDlg) && HIWORD(wParam) == EN_UPDATE)
+					{
+						CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+						if (PtrIsValid(pDateCtrl)) 
+						{
+							pDateCtrl->OnRemindEditChanged();
+						}
+					}
+				}
+				return FALSE;
+
+			/**
+			 * name:	BTN_MENU
+			 * desc:	the button to dropdown the list to show all items is pressed
+			 **/
+			case BTN_MENU:
+				{
+					if (HIWORD(wParam) == BN_CLICKED)
+					{
+						CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+						if (PtrIsValid(pDateCtrl)) 
+						{
+							pDateCtrl->OnMenuPopup();
+						}
+					}
+				}
+				return FALSE;
+
+			/**
+			 * name:	BTN_ADD
+			 * desc:	creates a new dialog to add a new anniversary
+			 **/
+			case BTN_ADD:
+				{
+					CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+					if (HIWORD(wParam) == BN_CLICKED && PtrIsValid(pDateCtrl)) 
+					{
+						MAnnivDate Date;
+						if (IDOK == DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_ANNIVERSARY_EDITOR), hDlg, (DLGPROC)DlgProc_AnniversaryEditor, (LPARAM)&Date)) 
+						{ 
+							SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+							if (!pDateCtrl->AddDate(Date))
+							{
+								pDateCtrl->SetCurSel(pDateCtrl->NumDates() - 1);
+							}
+						}
+					}
+				}
+				return FALSE;
+
+			/**
+			 * name:	BTN_EDIT
+			 * desc:	edit the currently selected anniversary
+			 **/
+			case BTN_EDIT:
+				{
+					CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+					if (HIWORD(wParam) == BN_CLICKED && PtrIsValid(pDateCtrl)) 
+					{
+						MAnnivDate *pDate = pDateCtrl->Current();
+
+						if (!pDate) 
+						{
+							MsgErr(hDlg, LPGENT("No valid date selected for editing!"));
+						}
+						else if (
+							IDOK == DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_ANNIVERSARY_EDITOR), hDlg, DlgProc_AnniversaryEditor, (LPARAM)pDate)&&
+							(pDate->Flags() & (MAnnivDate::MADF_CHANGED|MAnnivDate::MADF_REMINDER_CHANGED))
+					)
+						{ 
+							SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+							pDateCtrl->SetCurSel(pDateCtrl->CurrentIndex());
+						}
+					}
+				}
+				return FALSE;
+
+			/**
+			 * name:	BTN_DELETE
+			 * desc:	user wants to delete an anniversary
+			 **/
+			case BTN_DELETE:
+				{
+					CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+					if (HIWORD(wParam) == BN_CLICKED && PtrIsValid(pDateCtrl))
+					{
+						MAnnivDate *pCurrent;
+
+						pCurrent = pDateCtrl->Current();
+						if (pCurrent)
+						{
+							INT rc = MsgBox(hDlg, MB_YESNO|MB_ICON_QUESTION|MB_NOPOPUP, LPGENT("Delete"), NULL, 
+								LPGENT("Do you really want to delete the %s?"),	pCurrent->Description());
+						
+							if (rc == IDYES)
+							{
+								pDateCtrl->DeleteDate(pDateCtrl->CurrentIndex());
+							}
+						}
+					}
+				}
+				return FALSE;
+
+			/**
+			 * name:	CHECK_REMIND
+			 * desc:	state of reminder checkbox is changed
+			 **/
+			case RADIO_REMIND1:
+			case RADIO_REMIND2:
+			case RADIO_REMIND3:
+				{
+					CAnnivEditCtrl *pDateCtrl = CAnnivEditCtrl::GetObj(hDlg, EDIT_ANNIVERSARY_DATE);
+					if (PtrIsValid(pDateCtrl) && HIWORD(wParam) == BN_CLICKED)
+					{
+						pDateCtrl->OnReminderChecked();
+					}
+				}
+				return FALSE;
+
+			} /* switch (LOWORD(wParam)) */
+		}
+		break;
+	}
+	return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_base.cpp b/plugins/UserInfoEx/src/psp_base.cpp
new file mode 100644
index 0000000000..e015131ad6
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_base.cpp
@@ -0,0 +1,111 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_base.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "ctrl_base.h"
+
+VOID UpDate_CountryIcon(HWND hCtrl, int countryID) {
+	HICON hIcon = LoadFlagIcon(countryID);
+	HICON hOld  = Static_SetIcon(hCtrl, hIcon);
+	ShowWindow(hCtrl, hIcon ? SW_SHOW : SW_HIDE);
+	Skin_ReleaseIcon(hOld);
+}
+
+/**
+ * Default dialog procedure, which handles common functions
+ **/
+INT_PTR CALLBACK PSPBaseProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	CCtrlList *pCtrlList;
+
+	pCtrlList = CCtrlList::GetObj(hDlg);
+	if (PtrIsValid(pCtrlList)) {
+		switch (uMsg) {
+		case WM_INITDIALOG:		return TRUE;
+
+		/**
+		 * set propertysheet page's background white in aero mode
+		 **/
+		case WM_CTLCOLORSTATIC:
+		case WM_CTLCOLORDLG:	{
+				if (IsAeroMode())
+					return (INT_PTR)GetStockBrush(WHITE_BRUSH);
+			} break;
+
+		/**
+		 * Set text color of edit boxes according to the source of information they display.
+		 **/
+		case WM_CTLCOLOREDIT:
+			return pCtrlList->OnSetTextColour((HWND)lParam, (HDC)wParam);
+
+		case WM_NOTIFY:
+			{
+				switch (((LPNMHDR)lParam)->idFrom) {
+				case 0:		{
+						HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+						LPSTR pszProto;
+				
+						switch (((LPNMHDR)lParam)->code) {
+						case PSN_RESET:			{
+								pCtrlList->OnReset();
+							} break;
+
+						case PSN_INFOCHANGED:	{
+								if (PSGetBaseProto(hDlg, pszProto) && *pszProto) {
+									BOOL bChanged = (GetWindowLongPtr(hDlg, DWLP_MSGRESULT)&PSP_CHANGED)|pCtrlList->OnInfoChanged(hContact, pszProto);
+									SetWindowLongPtr(hDlg, DWLP_MSGRESULT,	bChanged ? PSP_CHANGED : 0);
+								}
+							} break;
+
+						case PSN_APPLY:			{
+								if (PSGetBaseProto(hDlg, pszProto) && *pszProto) {
+									pCtrlList->OnApply(hContact, pszProto);
+								}
+							} break;
+						}
+					} break;
+				}
+			} break;
+
+		case WM_COMMAND:
+			{
+				if (!PspIsLocked(hDlg)) {
+					pCtrlList->OnChangedByUser(LOWORD(wParam), HIWORD(wParam));
+				}
+			} break;
+
+		case WM_DESTROY:
+			{
+				// destroy all control objects and the list
+				pCtrlList->Release();
+			}
+		}
+	}
+	return 0;
+}
diff --git a/plugins/UserInfoEx/src/psp_base.h b/plugins/UserInfoEx/src/psp_base.h
new file mode 100644
index 0000000000..f2ddbba23f
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_base.h
@@ -0,0 +1,51 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_base.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UI_PSP_BASE_INCLUDE_
+#define _UI_PSP_BASE_INCLUDE_
+
+INT_PTR CALLBACK PSPBaseProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+INT_PTR CALLBACK PSPProcAnniversary(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcCompany(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcContactHome(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcContactProfile(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcContactWork(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcGeneral(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcOrigin(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK PSPProcEdit(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, const CHAR* pszSetting);
+static FORCEINLINE INT_PTR CALLBACK PSPProcMyNotes(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{ return PSPProcEdit(hDlg, uMsg, wParam, lParam, SET_CONTACT_MYNOTES); }
+static FORCEINLINE INT_PTR CALLBACK PSPProcAbout(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{ return PSPProcEdit(hDlg, uMsg, wParam, lParam, SET_CONTACT_ABOUT); }
+
+VOID UpDate_CountryIcon(HWND hCtrl, int countryID);
+
+#endif /* _UI_PSP_BASE_INCLUDE_ */
diff --git a/plugins/UserInfoEx/src/psp_company.cpp b/plugins/UserInfoEx/src/psp_company.cpp
new file mode 100644
index 0000000000..c77028e9b0
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_company.cpp
@@ -0,0 +1,76 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_company.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+#include "ctrl_edit.h"
+#include "psp_base.h"
+
+/**
+ * Dialog procedure for the company contact information propertysheetpage
+ *
+ * @param hDlg		- handle to the dialog window
+ * @param uMsg		- the message to handle
+ * @param wParam	- parameter
+ * @param lParam	- parameter
+ *
+ * @return	different values
+ **/
+INT_PTR CALLBACK PSPProcCompany(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg) 
+	{
+	case WM_INITDIALOG:
+		{
+			CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+			if (pCtrlList)
+			{
+				LPIDSTRLIST pList;
+				UINT nList;
+				HFONT hBoldFont;
+				PSGetBoldFont(hDlg, hBoldFont);
+				SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+				TranslateDialogDefault(hDlg);
+
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_COMPANY,					SET_CONTACT_COMPANY,			DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_DEPARTMENT,				SET_CONTACT_COMPANY_DEPARTMENT,	DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_OFFICE,					SET_CONTACT_COMPANY_OFFICE,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_POSITION,					SET_CONTACT_COMPANY_POSITION,	DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_SUPERIOR,					SET_CONTACT_COMPANY_SUPERIOR,	DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ASSISTENT,				SET_CONTACT_COMPANY_ASSISTENT,	DBVT_TCHAR));
+
+				GetOccupationList(&nList, &pList);
+				pCtrlList->insert(   CCombo::CreateObj(hDlg, EDIT_OCCUPATION,				SET_CONTACT_COMPANY_OCCUPATION,	DBVT_WORD,	pList, nList));
+			}
+		}
+	}
+	return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}			
diff --git a/plugins/UserInfoEx/src/psp_contact.cpp b/plugins/UserInfoEx/src/psp_contact.cpp
new file mode 100644
index 0000000000..fb38591004
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_contact.cpp
@@ -0,0 +1,347 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_contact.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+#include "ctrl_contact.h"
+#include "ctrl_edit.h"
+#include "psp_base.h"
+
+/**
+ * Dialog procedure for the home contact information propertysheetpage
+ *
+ * @param	 hDlg	- handle to the dialog window
+ * @param	 uMsg	- the message to handle
+ * @param	 wParam	- parameter
+ * @param	 lParam	- parameter
+ *
+ * @return	different values
+ **/
+INT_PTR CALLBACK PSPProcContactHome(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg)
+	{
+	case WM_INITDIALOG:
+		{
+			CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+			if (pCtrlList)
+			{
+				TCHAR szAddr[MAX_PATH];
+				HANDLE hContact = (HANDLE)lParam;
+				LPIDSTRLIST pList;
+				UINT nList;
+
+				HFONT hBoldFont;
+				PSGetBoldFont(hDlg, hBoldFont);
+				SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+				mir_sntprintf(szAddr, MAX_PATH, _T("%s (%s)"), TranslateT("Address"), TranslateT("Home"));
+				SetDlgItemText(hDlg, IDC_PAGETITLE, szAddr);
+				SendDlgItemMessage(hDlg, BTN_GOTO, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open in Browser"), MBF_TCHAR);
+				TranslateDialogDefault(hDlg);
+
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STREET,					SET_CONTACT_STREET,			DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_CITY,						SET_CONTACT_CITY,			DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ZIP,						SET_CONTACT_ZIP,			DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STATE,					SET_CONTACT_STATE,			DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_HOMEPAGE,					SET_CONTACT_HOMEPAGE,		DBVT_TCHAR));
+
+				GetCountryList(&nList, &pList);
+				pCtrlList->insert(   CCombo::CreateObj(hDlg, EDIT_COUNTRY,					SET_CONTACT_COUNTRY,		DBVT_WORD,	pList, nList));
+
+				break;
+			}
+		}
+		return FALSE;
+
+	case WM_NOTIFY:
+		{
+			switch (((LPNMHDR)lParam)->idFrom) 
+			{
+			case 0:
+				{
+					HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+					LPCSTR pszProto;
+					HWND hCtrl;
+			
+					switch (((LPNMHDR)lParam)->code) 
+					{
+					case PSN_INFOCHANGED:
+						{
+							BYTE bChanged = 0;
+
+							if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+							// phone numbers
+							hCtrl = GetDlgItem(hDlg, EDIT_PHONE);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_PHONE, TranslateT(SET_CONTACT_PHONE), hContact, USERINFO, pszProto, SET_CONTACT_PHONE);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_FAX, TranslateT(SET_CONTACT_FAX), hContact, USERINFO, pszProto, SET_CONTACT_FAX);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_CELLULAR, TranslateT(SET_CONTACT_CELLULAR), hContact, USERINFO, pszProto, SET_CONTACT_CELLULAR);
+							bChanged |= CtrlContactAddMyItemsFromDB(hCtrl, ICO_BTN_CUSTOMPHONE, 0, hContact, USERINFO, pszProto, SET_CONTACT_MYPHONE_CAT, SET_CONTACT_MYPHONE_VAL);
+							SendMessage(hCtrl, CBEXM_SETCURSEL, (WPARAM)-1, TRUE);
+								
+							// emails
+							hCtrl = GetDlgItem(hDlg, EDIT_EMAIL);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Primary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_EMAIL);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Secondary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_EMAIL0);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Tertiary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_EMAIL1);
+							bChanged |= CtrlContactAddMyItemsFromDB(hCtrl, ICO_BTN_EMAIL, 0, hContact, USERINFO, pszProto, SET_CONTACT_MYEMAIL_CAT, SET_CONTACT_MYEMAIL_VAL);
+							SendMessage(hCtrl, CBEXM_SETCURSEL, (WPARAM)-1, TRUE);
+							SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bChanged ? PSP_CHANGED : 0);
+						}
+						break;
+
+					case PSN_APPLY:
+						{
+							if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+							hCtrl = GetDlgItem(hDlg, EDIT_PHONE);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_PHONE);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_FAX);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_CELLULAR);
+							CtrlContactWriteMyItemsToDB(hCtrl, 3, hContact, USERINFO, pszProto, SET_CONTACT_MYPHONE_CAT, SET_CONTACT_MYPHONE_VAL);
+							SendMessage(hCtrl, CBEXM_RESETCHANGED, NULL, NULL);
+
+							hCtrl = GetDlgItem(hDlg, EDIT_EMAIL);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_EMAIL);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_EMAIL0);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_EMAIL1);
+							CtrlContactWriteMyItemsToDB(hCtrl, 3, hContact, USERINFO, pszProto, SET_CONTACT_MYEMAIL_CAT, SET_CONTACT_MYEMAIL_VAL);
+							SendMessage(hCtrl, CBEXM_RESETCHANGED, NULL, NULL);
+						}
+						break;
+
+					case PSN_ICONCHANGED:
+						{
+							HICON hIcon;
+
+							hIcon = IcoLib_GetIcon(ICO_BTN_GOTO);
+							SendDlgItemMessage(hDlg, BTN_GOTO, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+							SetDlgItemText(hDlg, BTN_GOTO, hIcon ? _T("") : _T("->"));
+
+							hIcon = IcoLib_GetIcon(ICO_COMMON_ADDRESS);
+							SendDlgItemMessage(hDlg, ICO_ADDRESS, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+							ShowWindow(GetDlgItem(hDlg, ICO_ADDRESS), hIcon ? SW_SHOW : SW_HIDE);
+
+							SendDlgItemMessage(hDlg, EDIT_PHONE, WM_SETICON, NULL, NULL);
+							SendDlgItemMessage(hDlg, EDIT_EMAIL, WM_SETICON, NULL, NULL);
+						}
+						break;
+					}
+				}
+			}
+		}
+		break;
+
+	case WM_COMMAND:
+		{
+			switch (LOWORD(wParam)) 
+			{
+			case EDIT_HOMEPAGE:
+				{
+					if (HIWORD(wParam) == EN_UPDATE) 
+					{
+						EnableWindow(GetDlgItem(hDlg, BTN_GOTO), GetWindowTextLength((HWND)lParam) > 0);
+					}
+				}
+				break;
+
+			case BTN_GOTO:
+				{
+					CEditCtrl::GetObj(hDlg, EDIT_HOMEPAGE)->OpenUrl();
+				}
+				break;
+
+			case EDIT_COUNTRY:
+				if(HIWORD(wParam) == CBN_SELCHANGE) {
+					LPIDSTRLIST pd = (LPIDSTRLIST)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+					UpDate_CountryIcon(GetDlgItem(hDlg, ICO_COUNTRY), pd->nID);
+				}
+				break;
+			}
+		}
+	}
+	return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
+
+/**
+ * Dialog procedure for the company's contact information propertysheetpage
+ *
+ * @param	 hDlg	- handle to the dialog window
+ * @param	 uMsg	- the message to handle
+ * @param	 wParam	- parameter
+ * @param	 lParam	- parameter
+ *
+ * @return	different values
+ **/
+INT_PTR CALLBACK PSPProcContactWork(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg)
+	{
+	case WM_INITDIALOG:
+		{
+			CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+			if (pCtrlList)
+			{
+				TCHAR szAddr[MAX_PATH];
+				HANDLE hContact = (HANDLE)lParam;
+				LPIDSTRLIST pList;
+				UINT nList;
+
+				HFONT hBoldFont;
+				PSGetBoldFont(hDlg, hBoldFont);
+				SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+				mir_sntprintf(szAddr, MAX_PATH, _T("%s (%s)"), TranslateT("Address & Contact"), TranslateT("Company"));
+				SetDlgItemText(hDlg, IDC_PAGETITLE, szAddr);
+				SendDlgItemMessage(hDlg, BTN_GOTO, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open in Browser"), MBF_TCHAR);
+				TranslateDialogDefault(hDlg);
+
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STREET,					SET_CONTACT_COMPANY_STREET,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_CITY,						SET_CONTACT_COMPANY_CITY,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ZIP,						SET_CONTACT_COMPANY_ZIP,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STATE,					SET_CONTACT_COMPANY_STATE,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_HOMEPAGE,					SET_CONTACT_COMPANY_HOMEPAGE,	DBVT_TCHAR));
+
+				GetCountryList(&nList, &pList);
+				pCtrlList->insert(   CCombo::CreateObj(hDlg, EDIT_COUNTRY,					SET_CONTACT_COMPANY_COUNTRY,	DBVT_WORD,	pList, nList));
+			}
+		}
+		break;
+
+	case WM_NOTIFY:
+		{
+			switch (((LPNMHDR)lParam)->idFrom) 
+			{
+			case 0:
+				{
+					HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+					LPCSTR pszProto;
+					HWND hCtrl;
+			
+					switch (((LPNMHDR)lParam)->code) 
+					{
+					case PSN_INFOCHANGED:
+						{
+							BYTE bChanged = 0;
+
+							if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+							// phone numbers
+							hCtrl = GetDlgItem(hDlg, EDIT_PHONE);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_PHONE, TranslateT(SET_CONTACT_PHONE), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_PHONE);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_FAX, TranslateT(SET_CONTACT_FAX), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_FAX);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_CELLULAR, TranslateT(SET_CONTACT_CELLULAR), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_CELLULAR);
+							bChanged |= CtrlContactAddMyItemsFromDB(hCtrl, ICO_BTN_CUSTOMPHONE, 0, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_MYPHONE_CAT, SET_CONTACT_COMPANY_MYPHONE_VAL);
+							SendMessage(hCtrl, CBEXM_SETCURSEL, (WPARAM)-1, TRUE);
+								
+							// emails
+							hCtrl = GetDlgItem(hDlg, EDIT_EMAIL);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Primary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Secondary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL0);
+							bChanged |= CtrlContactAddItemFromDB(hCtrl, ICO_BTN_EMAIL, TranslateT("Tertiary e-mail"), hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL1);
+							bChanged |= CtrlContactAddMyItemsFromDB(hCtrl, ICO_BTN_EMAIL, 0, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_MYEMAIL_CAT, SET_CONTACT_COMPANY_MYEMAIL_VAL);
+							SendMessage(hCtrl, CBEXM_SETCURSEL, (WPARAM)-1, TRUE);
+							SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bChanged ? PSP_CHANGED : 0);
+						}
+						break;
+
+					case PSN_APPLY:
+						{
+							if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+							hCtrl = GetDlgItem(hDlg, EDIT_PHONE);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_PHONE);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_FAX);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_CELLULAR);
+							CtrlContactWriteMyItemsToDB(hCtrl, 3, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_MYPHONE_CAT, SET_CONTACT_COMPANY_MYPHONE_VAL);
+							SendMessage(hCtrl, CBEXM_RESETCHANGED, NULL, NULL);
+
+							hCtrl = GetDlgItem(hDlg, EDIT_EMAIL);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL0);
+							CtrlContactWriteItemToDB(hCtrl, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_EMAIL1);
+							CtrlContactWriteMyItemsToDB(hCtrl, 3, hContact, USERINFO, pszProto, SET_CONTACT_COMPANY_MYEMAIL_CAT, SET_CONTACT_COMPANY_MYEMAIL_VAL);
+							SendMessage(hCtrl, CBEXM_RESETCHANGED, NULL, NULL);
+						}
+						break;
+
+					case PSN_ICONCHANGED:
+						{
+							HICON hIcon;
+
+							hIcon = IcoLib_GetIcon(ICO_BTN_GOTO);
+							SendDlgItemMessage(hDlg, BTN_GOTO, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+							SetDlgItemText(hDlg, BTN_GOTO, hIcon ? _T("") : _T("->"));
+
+							hIcon = IcoLib_GetIcon(ICO_COMMON_ADDRESS);
+							SendDlgItemMessage(hDlg, ICO_ADDRESS, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+							ShowWindow(GetDlgItem(hDlg, ICO_ADDRESS), hIcon ? SW_SHOW : SW_HIDE);
+
+							SendDlgItemMessage(hDlg, EDIT_PHONE, WM_SETICON, NULL, NULL);
+							SendDlgItemMessage(hDlg, EDIT_EMAIL, WM_SETICON, NULL, NULL);
+						}
+						break;
+					}
+				}
+			}
+		}
+		break;
+
+	case WM_COMMAND:
+		{
+			switch (LOWORD(wParam)) 
+			{
+			case EDIT_HOMEPAGE:
+				{
+					if (HIWORD(wParam) == EN_UPDATE) 
+					{
+						EnableWindow(GetDlgItem(hDlg, BTN_GOTO), GetWindowTextLength((HWND)lParam) > 0);
+					}
+				}
+				break;
+
+			case BTN_GOTO:
+				{
+					CEditCtrl::GetObj(hDlg, EDIT_HOMEPAGE)->OpenUrl();
+				}
+				break;
+
+			case EDIT_COUNTRY:
+				if(HIWORD(wParam) == CBN_SELCHANGE) {
+					LPIDSTRLIST pd = (LPIDSTRLIST)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+					UpDate_CountryIcon(GetDlgItem(hDlg, ICO_COUNTRY), pd->nID);
+				}
+				break;
+			}
+		}
+	}
+	return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_general.cpp b/plugins/UserInfoEx/src/psp_general.cpp
new file mode 100644
index 0000000000..9b9a15a58e
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_general.cpp
@@ -0,0 +1,211 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_general.cpp $
+Revision       : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+#include "ctrl_edit.h"
+#include "ctrl_tzcombo.h"
+#include "psp_base.h"
+#include "svc_reminder.h"
+
+/**
+ * Dialog procedure for the contact information propertysheetpage
+ *
+ * @param	 hDlg	- handle to the dialog window
+ * @param	 uMsg	- the message to handle
+ * @param	 wParam	- parameter
+ * @param	 lParam	- parameter
+ *
+ * @return	different values
+ **/
+INT_PTR CALLBACK PSPProcGeneral(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg)
+	{
+	case WM_INITDIALOG:
+		{
+			CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+			if (pCtrlList)
+			{
+				LPIDSTRLIST pList;
+				UINT nList;
+				HFONT hBoldFont;
+				
+				PSGetBoldFont(hDlg, hBoldFont);
+				SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+				TranslateDialogDefault(hDlg);
+
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_TITLE,					SET_CONTACT_TITLE,			DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_FIRSTNAME,				SET_CONTACT_FIRSTNAME,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_SECONDNAME,				SET_CONTACT_SECONDNAME,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_LASTNAME,					SET_CONTACT_LASTNAME,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_NICK,						SET_CONTACT_NICK,			DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_DISPLAYNAME,	"CList",	SET_CONTACT_MYHANDLE,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_PARTNER,					SET_CONTACT_PARTNER,		DBVT_TCHAR));
+
+				GetNamePrefixList(&nList, &pList);
+				pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_PREFIX, SET_CONTACT_PREFIX, DBVT_BYTE, pList, nList));
+
+				// marital groupbox
+				GetMaritalList(&nList, &pList);
+				pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_MARITAL, SET_CONTACT_MARITAL, DBVT_BYTE, pList, nList));
+
+				GetLanguageList(&nList, &pList);
+				pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_LANG1, SET_CONTACT_LANG1, DBVT_TCHAR, pList, nList));
+				pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_LANG2, SET_CONTACT_LANG2, DBVT_TCHAR, pList, nList));
+				pCtrlList->insert(CCombo::CreateObj(hDlg, EDIT_LANG3, SET_CONTACT_LANG3, DBVT_TCHAR, pList, nList));
+			}
+		}
+		break;
+
+	case WM_NOTIFY:
+		{
+			switch (((LPNMHDR)lParam)->idFrom) 
+			{
+			case 0:
+				{
+					HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+					char* pszProto;
+
+					switch (((LPNMHDR)lParam)->code) 
+					{
+					case PSN_INFOCHANGED:
+						{
+							BOOLEAN bEnable;
+							DBVARIANT dbv;
+							CCtrlFlags Flags;
+		
+							if (PSGetBaseProto(hDlg, pszProto) && *pszProto)
+							{
+								Flags.W = DB::Setting::GetTStringCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_GENDER, &dbv);
+								if (Flags.B.hasCustom || Flags.B.hasProto || Flags.B.hasMeta)
+								{
+									if (dbv.type == DBVT_BYTE) 
+									{
+										CheckDlgButton(hDlg, RADIO_FEMALE, (dbv.bVal == 'F'));
+										CheckDlgButton(hDlg, RADIO_MALE, (dbv.bVal == 'M'));
+
+										bEnable = !hContact || Flags.B.hasCustom || !DB::Setting::GetByte(SET_PROPSHEET_PCBIREADONLY, 0);
+										EnableWindow(GetDlgItem(hDlg, RADIO_FEMALE), bEnable);
+										EnableWindow(GetDlgItem(hDlg, RADIO_MALE), bEnable);
+									}
+									else
+									{
+										DB::Variant::Free(&dbv);
+									}
+								}
+							}
+						}
+						break;
+
+					case PSN_APPLY:
+						{
+							if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+							 // gender
+							{
+								BYTE gender
+									= SendDlgItemMessage(hDlg, RADIO_FEMALE, BM_GETCHECK, NULL, NULL)
+									? 'F'
+									: SendDlgItemMessage(hDlg, RADIO_MALE, BM_GETCHECK, NULL, NULL)
+									? 'M'
+									: 0;
+
+								if (gender) DB::Setting::WriteByte(hContact, hContact ? USERINFO : pszProto, SET_CONTACT_GENDER, gender);
+								else DB::Setting::Delete(hContact, hContact ? USERINFO : pszProto, SET_CONTACT_GENDER);
+							}
+						}
+						break;
+
+					case PSN_ICONCHANGED:
+						{
+							const ICONCTRL idIcon[] = {
+								{ ICO_COMMON_FEMALE,	STM_SETIMAGE,	ICO_FEMALE },
+								{ ICO_COMMON_MALE,		STM_SETIMAGE,	ICO_MALE },
+								{ ICO_COMMON_MARITAL,	STM_SETIMAGE,	ICO_MARITAL },
+							};
+							IcoLib_SetCtrlIcons(hDlg, idIcon, SIZEOF(idIcon));
+						}
+					}
+				}
+			}
+		}
+		break;
+
+	case WM_COMMAND:
+		{
+			HANDLE hContact;
+			LPCSTR pszProto;
+
+			switch (LOWORD(wParam))
+			{
+			case RADIO_FEMALE:
+				{
+					if (!PspIsLocked(hDlg) && HIWORD(wParam) == BN_CLICKED) 
+					{
+						DBVARIANT dbv;
+
+						PSGetContact(hDlg, hContact);
+						PSGetBaseProto(hDlg, pszProto);
+
+						if (!DB::Setting::GetAsIsCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_GENDER, &dbv) ||
+							dbv.type != DBVT_BYTE ||
+							(dbv.bVal != 'F' && SendMessage((HWND)lParam, BM_GETCHECK, NULL, NULL))
+				 )
+						{
+							SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+						}
+					}
+				}
+				break;
+
+			case RADIO_MALE:
+				{
+					if (!PspIsLocked(hDlg) && HIWORD(wParam) == BN_CLICKED)
+					{
+						DBVARIANT dbv;
+
+						PSGetContact(hDlg, hContact);
+						PSGetBaseProto(hDlg, pszProto);
+
+						if (!DB::Setting::GetAsIsCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_GENDER, &dbv) ||
+							dbv.type != DBVT_BYTE ||
+							(dbv.bVal != 'M' && SendMessage((HWND)lParam, BM_GETCHECK, NULL, NULL))
+				 )
+						{
+							SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+						}
+					}
+				}
+			}
+		}
+	}
+	return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_options.cpp b/plugins/UserInfoEx/src/psp_options.cpp
new file mode 100644
index 0000000000..9399e90f63
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_options.cpp
@@ -0,0 +1,1570 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_options.cpp $
+Revision       : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "dlg_propsheet.h"
+#include "mir_menuitems.h"
+
+#include "svc_Contactinfo.h"
+#include "svc_Avatar.h"
+#include "svc_Email.h"
+#include "svc_Gender.h"
+#include "svc_Homepage.h"
+#include "svc_Phone.h"
+#include "svc_Refreshci.h"
+#include "svc_Reminder.h"
+#include "svc_Timezone.h"
+#include "svc_Timezone_old.h"
+#include "flags\svc_flags.h"
+#include "psp_options.h"
+
+#define		PSM_ENABLE_TABITEM	(WM_USER+106)
+
+static MenuOptionsList ctrl_Menu[]= 
+{
+	{SET_MI_MAIN,		CHECK_OPT_MI_MAIN,		RADIO_OPT_MI_MAIN_NONE,		RADIO_OPT_MI_MAIN_ALL,		RADIO_OPT_MI_MAIN_EXIMPORT},
+	{SET_MI_CONTACT,	CHECK_OPT_MI_CONTACT,	RADIO_OPT_MI_CONTACT_NONE,	RADIO_OPT_MI_CONTACT_ALL,	RADIO_OPT_MI_CONTACT_EXIMPORT},
+	{SET_MI_GROUP,		CHECK_OPT_MI_GROUP,		RADIO_OPT_MI_GROUP_NONE,	RADIO_OPT_MI_GROUP_ALL,		RADIO_OPT_MI_GROUP_EXIMPORT},
+	{SET_MI_SUBGROUP,	CHECK_OPT_MI_SUBGROUP,	RADIO_OPT_MI_SUBGROUP_NONE,	RADIO_OPT_MI_SUBGROUP_ALL,	RADIO_OPT_MI_SUBGROUP_EXIMPORT},
+//	{SET_MI_STATUS,		CHECK_OPT_MI_STATUS,	RADIO_OPT_MI_STATUS_NONE,	RADIO_OPT_MI_STATUS_ALL,	RADIO_OPT_MI_STATUS_EXIMPORT},
+	{SET_MI_ACCOUNT,	CHECK_OPT_MI_ACCOUNT,	RADIO_OPT_MI_ACCOUNT_NONE,	RADIO_OPT_MI_ACCOUNT_ALL,	RADIO_OPT_MI_ACCOUNT_EXIMPORT},
+};
+
+/**
+ *
+ *
+ **/
+static FORCEINLINE VOID NotifyParentOfChange(HWND hDlg)
+{
+	SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
+}
+
+/**
+ * This function clears all CList extra icons from the given column.
+ *
+ * @param	ExtraIconColumnType		- the clist column to clear
+ *
+ * @return	nothing
+ **/
+static VOID ClearAllExtraIcons(INT ExtraIconColumnType)
+{
+	IconExtraColumn iec;
+	HANDLE hContact;
+
+	iec.cbSize = sizeof(IconExtraColumn);
+	iec.hImage = INVALID_HANDLE_VALUE;
+	iec.ColumnType = ExtraIconColumnType;
+	
+	//walk through all the contacts stored in the DB
+	for (hContact = DB::Contact::FindFirst();	hContact != NULL;	hContact = DB::Contact::FindNext(hContact))
+	{
+		CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+	}
+}
+
+/**
+ * Sends a PSN_INFOCHANGED notify to the handle.
+ *
+ * @param	hWnd					- the dialog's window handle
+ *
+ * @return	nothing
+ **/
+static VOID SendNotify_InfoChanged(HWND hDlg)
+{
+	PSHNOTIFY pshn;
+
+	// extended setting
+	pshn.hdr.idFrom = 0;
+	pshn.hdr.code = PSN_EXPERTCHANGED;
+	pshn.lParam = SendMessage(GetParent(GetParent(hDlg)), PSM_ISEXPERT, NULL, NULL) ? TRUE : FALSE;
+	SendMessage(hDlg, WM_NOTIFY, 0, (LPARAM)&pshn);
+
+	// send info changed message
+	pshn.hdr.code = PSN_INFOCHANGED;
+	SendMessage(hDlg, WM_NOTIFY, NULL, (LPARAM)&pshn); 
+}
+
+/**
+ *
+ *
+ **/
+static INT FORCEINLINE ComboBox_FindByItemDataPtr(HWND hCombo, LPARAM pData)
+{
+	INT nItemIndex;
+
+	for (nItemIndex = ComboBox_GetCount(hCombo); 
+			 (nItemIndex >= 0) && (ComboBox_GetItemData(hCombo, nItemIndex) != pData); 
+			 nItemIndex--);
+	return nItemIndex;
+}
+
+/**
+ *
+ *
+ **/
+static VOID FORCEINLINE ComboBox_SetCurSelByItemDataPtr(HWND hCombo, LPARAM pData)
+{
+	ComboBox_SetCurSel(hCombo, ComboBox_FindByItemDataPtr(hCombo, pData)); 
+}
+
+/**
+ *
+ *
+ **/
+static VOID FORCEINLINE ComboBox_AddItemWithData(HWND hCombo, LPTSTR ptszText, LPARAM pData)
+{
+	ComboBox_SetItemData(hCombo, ComboBox_AddString(hCombo, TranslateTS(ptszText)), pData);  
+}
+
+/**
+ * This function fills a combobox with the advanced column names for clist extra icons.
+ *
+ * @param	hCombo					- the combobox's window handle
+ *
+ * @return	nothing
+ **/
+static VOID ComboBox_FillExtraIcons(HWND hCombo)
+{
+	if (hCombo)
+	{
+		ComboBox_AddItemWithData(hCombo, LPGENT("<none>"), -1);
+
+		/* check if Clist Nicer */
+		if (ServiceExists("CListFrame/SetSkinnedFrame"))
+		{
+			const struct CComboList {
+				INT			nColumn;
+				LPTSTR	ptszName;
+			} ExtraIcons[] = {
+				{ EXTRA_ICON_ADV1, LPGENT("Advanced #1 (ICQ X-Status)")},
+				{ EXTRA_ICON_ADV2, LPGENT("Advanced #2")},
+				{ EXTRA_ICON_ADV3, LPGENT("Advanced #3")},
+				{ EXTRA_ICON_ADV4, LPGENT("Advanced #4")},
+				{ EXTRA_ICON_RES0, LPGENT("Reserved, unused")},
+				{ EXTRA_ICON_RES1, LPGENT("Reserved #1")},
+				{ EXTRA_ICON_RES2, LPGENT("Reserved #2")},
+				{ EXTRA_ICON_CLIENT, LPGENT("Client (fingerprint required)")},
+			};
+
+			for (BYTE i = 0; i < SIZEOF(ExtraIcons); i++)
+			{
+				ComboBox_AddItemWithData(hCombo,
+					TranslateTS(ExtraIcons[i].ptszName), 
+					ExtraIcons[i].nColumn );
+			}
+		}
+		/* check if Clist modern*/
+		else if (ServiceExists("CList/HideContactAvatar")) 
+		{
+			const struct CComboList {
+				INT			nColumn;
+				LPTSTR	ptszName;
+			} ExtraIcons[] = {
+				{ EXTRA_ICON_ADV1, LPGENT("Advanced #1")},
+				{ EXTRA_ICON_ADV2, LPGENT("Advanced #2")},
+				{ EXTRA_ICON_ADV3, LPGENT("Advanced #3")},
+				{ EXTRA_ICON_ADV4, LPGENT("Advanced #4")},
+				{ EXTRA_ICON_CLIENT, LPGENT("Client (fingerprint required)")},
+				{ EXTRA_ICON_PROTO, LPGENT("Protocol")},
+				{ EXTRA_ICON_VISMODE, LPGENT("Visibility/Chat activity")},
+			};
+
+			for (BYTE i = 0; i < SIZEOF(ExtraIcons); i++)
+			{
+				ComboBox_AddItemWithData(hCombo,
+					TranslateTS(ExtraIcons[i].ptszName), 
+					ExtraIcons[i].nColumn );
+			}
+		}
+		/*check if Clist MW*/
+		else if (ServiceExists("CLUI/GetConnectingIconForProtocol"))
+		{
+			const struct CComboList {
+				INT			nColumn;
+				LPTSTR	ptszName;
+			} ExtraIcons[] = {
+				{ EXTRA_ICON_ADV1, LPGENT("Advanced #1")},
+				{ EXTRA_ICON_ADV2, LPGENT("Advanced #2")},
+				{ EXTRA_ICON_ADV3, LPGENT("Advanced #3")},
+				{ EXTRA_ICON_ADV4, LPGENT("Advanced #4")},
+				{ EXTRA_ICON_CLIENT, LPGENT("Client (fingerprint required)")},
+				{ EXTRA_ICON_PROTO, LPGENT("Protocol Type")},
+			};
+
+			for (BYTE i = 0; i < SIZEOF(ExtraIcons); i++)
+			{
+				ComboBox_AddItemWithData(hCombo,
+					TranslateTS(ExtraIcons[i].ptszName), 
+					ExtraIcons[i].nColumn );
+			}
+		}
+		ComboBox_SetCurSel(hCombo, NULL);
+	}
+}
+
+/**
+ * This function enables a dialog item
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the dialog item's identifier
+ * @param	bEnabled		- TRUE if the item should be enabled, FALSE if disabled
+ *
+ * @retval	TRUE on success
+ * @retval	FALSE on failure
+ **/
+static BOOLEAN EnableDlgItem(HWND hDlg, const INT idCtrl, BOOLEAN bEnabled)
+{
+	return EnableWindow(GetDlgItem(hDlg, idCtrl), bEnabled);
+}
+
+/**
+ * This function enables a list of dialog items, if they were enabled in the resource editor.
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the array of dialog items' identifiers
+ * @param	countCtrl		- the number of items in the array of dialog items
+ * @param	bEnabled		- TRUE if the item should be enabled, FALSE if disabled
+ *
+ * @return	bEnabled
+ **/
+static BOOLEAN InitialEnableControls(HWND hDlg, const INT *idCtrl, int countCtrl, BOOLEAN bEnabled)
+{
+	HWND hCtrl;
+
+	while (countCtrl-- > 0) 
+	{
+		hCtrl = GetDlgItem(hDlg, idCtrl[countCtrl]);
+		EnableWindow(hCtrl, IsWindowEnabled(hCtrl) && bEnabled);
+	}
+	return bEnabled;
+}
+
+/**
+ * This function enables a list of dialog items.
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the array of dialog items' identifiers
+ * @param	countCtrl		- the number of items in the array of dialog items
+ * @param	bEnabled		- TRUE if the item should be enabled, FALSE if disabled
+ *
+ * @return	bEnabled
+ **/
+static BOOLEAN EnableControls(HWND hDlg, const INT *idCtrl, int countCtrl, BOOLEAN bEnabled)
+{
+	while (countCtrl-- > 0) 
+	{
+		EnableDlgItem(hDlg, idCtrl[countCtrl], bEnabled);
+	}
+	return bEnabled;
+}
+
+/**
+ * This function checks an dialog button according to the value, read from the database
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the dialog item's identifier
+ * @param	pszSetting		- the setting from the database to use
+ * @param	bDefault		- the default value to use, if no database setting exists
+ *
+ * @return	This function returns the value from database or the default value.
+ **/
+static BOOLEAN DBGetCheckBtn(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, BOOLEAN bDefault)
+{
+	BOOLEAN val = (DB::Setting::GetByte(pszSetting, bDefault) & 1) == 1;
+	CheckDlgButton(hDlg, idCtrl, val);
+	return val;
+}
+
+/**
+ * This function writes a byte (flag = 1) to database according to the checkstate
+ * of the dialog button identified by 'idCtrl'.
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the dialog item's identifier
+ * @param	pszSetting		- the setting to write the button state to
+ *
+ * @return	checkstate
+ **/
+static BOOLEAN DBWriteCheckBtn(HWND hDlg, const INT idCtrl, LPCSTR pszSetting)
+{
+	BOOLEAN val = IsDlgButtonChecked(hDlg, idCtrl);
+	int Temp = DB::Setting::GetByte(pszSetting, 0);
+	Temp &= ~1;
+	DB::Setting::WriteByte(pszSetting, Temp |= val );
+	return val;
+}
+
+/**
+ * This function reads a DWORD from database and interprets it as an color value
+ * to set to the color control.
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the dialog item's identifier
+ * @param	pszSetting		- the setting from the database to use
+ * @param	bDefault		- the default value to use, if no database setting exists
+ *
+ * @return	nothing
+ **/
+static VOID DBGetColor(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, DWORD bDefault)
+{
+	SendDlgItemMessage(hDlg, idCtrl, CPM_SETCOLOUR, 0, DB::Setting::GetDWord(pszSetting, bDefault));
+}
+
+/**
+ * This function writes a DWORD to database according to the value
+ * of the color control identified by 'idCtrl'.
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the dialog item's identifier
+ * @param	pszSetting		- the setting to write the button state to
+ *
+ * @return	nothing
+ **/
+static VOID DBWriteColor(HWND hDlg, const INT idCtrl, LPCSTR pszSetting)
+{
+	DB::Setting::WriteDWord(pszSetting, SendDlgItemMessage(hDlg, idCtrl, CPM_GETCOLOUR, 0, 0));
+}
+
+/**
+ * This function writes a BYTE to database according to the value
+ * read from the edit control identified by 'idCtrl'.
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the dialog item's identifier
+ * @param	pszSetting		- the setting to write the button state to
+ * @param	defVal			- this is the default value used by the GetByte() function in order 
+ *							  to check whether updating the value is required or not.
+ *
+ * @retval	TRUE			- the database value was updated
+ * @retval	FALSE			- no database update needed
+ **/
+static BOOLEAN DBWriteEditByte(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, BYTE defVal)
+{
+	BYTE v;
+	BOOL t;
+
+	v = (BYTE)GetDlgItemInt(hDlg, idCtrl, &t, FALSE);
+	if (t && (v != DB::Setting::GetByte(pszSetting, defVal)))
+	{
+		return DB::Setting::WriteByte(pszSetting, v) == 0;
+	}
+	return FALSE;
+}
+
+/**
+ * This function writes a WORD to database according to the value
+ * read from the edit control identified by 'idCtrl'.
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the dialog item's identifier
+ * @param	pszSetting		- the setting to write the button state to
+ * @param	defVal			- this is the default value used by the GetWord() function in order 
+ *							  to check whether updating the value is required or not.
+ *
+ * @retval	TRUE			- the database value was updated
+ * @retval	FALSE			- no database update needed
+ **/
+static BOOLEAN DBWriteEditWord(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, WORD defVal)
+{
+	WORD v;
+	BOOL t;
+
+	v = (WORD)GetDlgItemInt(hDlg, idCtrl, &t, FALSE);
+	if (t && (v != DB::Setting::GetWord(pszSetting, defVal)))
+	{
+		return DB::Setting::WriteWord(pszSetting, v) == 0;
+	}
+	return FALSE;
+}
+
+/**
+ * This function writes a BYTE to database according to the currently
+ * selected item of a combobox identified by 'idCtrl'.
+ *
+ * @param	hWnd			- the dialog's window handle
+ * @param	idCtrl			- the dialog item's identifier
+ * @param	pszSetting		- the setting to write the button state to
+ * @param	defVal			- this is the default value used by the GetByte() function in order 
+ *							  to check whether updating the value is required or not.
+ *
+ * @retval	TRUE			- the database value was updated
+ * @retval	FALSE			- no database update needed
+ **/
+static BOOLEAN DBWriteComboByte(HWND hDlg, const INT idCtrl, LPCSTR pszSetting, BYTE defVal)
+{
+	BYTE v;
+
+	v = (BYTE)SendDlgItemMessage(hDlg, idCtrl, CB_GETCURSEL, NULL, NULL);
+	if (v != DB::Setting::GetByte(pszSetting, defVal))
+	{
+		return DB::Setting::WriteByte(pszSetting, v) == 0;
+	}
+	return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc_CommonOpts(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	static BOOLEAN	bInitialized = 0;
+
+	switch (uMsg) 
+	{
+		case WM_INITDIALOG:
+			{
+				//this controls depend on clist-typ
+				//enable control if myGlobals.ExtraIconsServiceExist else disable
+				const int idExtraIcons[] = {
+					GROUP_OPT_EXTRAICONS,
+					TXT_OPT_GENDER,			COMBO_OPT_GENDER,
+					/*TXT_OPT_FLAGS,*/			COMBO_OPT_FLAGS,
+//					CHECK_OPT_FLAGSUNKNOWN,	CHECK_OPT_FLAGSMSGSTATUS,
+					TXT_OPT_DEFAULTICONS,
+					CHECK_OPT_HOMEPAGEICON,	CHECK_OPT_PHONEICON, CHECK_OPT_EMAILICON,
+					CHECK_OPT_ZODIACAVATAR
+				};
+
+				TranslateDialogDefault(hDlg);
+
+#ifdef _DEBUG	// new feature, not in release yet
+				ShowWindow(GetDlgItem(hDlg, CHECK_OPT_ZODIACAVATAR),SW_SHOW);
+#else
+				ShowWindow(GetDlgItem(hDlg, CHECK_OPT_ZODIACAVATAR),SW_HIDE);
+#endif
+
+				// init extra icons options
+				ShowWindow(GetDlgItem(hDlg, TXT_OPT_EXTRAICONS),myGlobals.ExtraIconsServiceExist?SW_SHOW:SW_HIDE);
+				ShowWindow(GetDlgItem(hDlg, TXT_OPT_GENDER),	myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+				ShowWindow(GetDlgItem(hDlg, COMBO_OPT_GENDER),	myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+				ShowWindow(GetDlgItem(hDlg, CHECK_OPT_GENDER),	myGlobals.ExtraIconsServiceExist?SW_SHOW:SW_HIDE);
+				ShowWindow(GetDlgItem(hDlg, TXT_OPT_FLAGS),		myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+				ShowWindow(GetDlgItem(hDlg, COMBO_OPT_FLAGS),	myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+				if (InitialEnableControls(hDlg, idExtraIcons, SIZEOF(idExtraIcons), myGlobals.HaveCListExtraIcons) && !myGlobals.ExtraIconsServiceExist) 
+				{
+					ComboBox_FillExtraIcons(GetDlgItem(hDlg, COMBO_OPT_GENDER));
+					ComboBox_FillExtraIcons(GetDlgItem(hDlg, COMBO_OPT_FLAGS));
+				}
+
+				SendNotify_InfoChanged(hDlg);
+			}
+			return TRUE;
+
+		case WM_NOTIFY:
+			switch (((LPNMHDR)lParam)->code) 
+			{
+				case PSN_INFOCHANGED:
+					{
+						bInitialized = 0;
+						
+						// menu item settings
+						for (int i = 0; i < SIZEOF(ctrl_Menu); i++) {
+							int flag = DB::Setting::GetByte(ctrl_Menu[i].pszKey, 2);
+							// check button and enable / disable control
+							int idEnable[] = { ctrl_Menu[i].idCheckbox + 1, ctrl_Menu[i].idNONE, ctrl_Menu[i].idALL, ctrl_Menu[i].idEXIMPORT };
+							EnableControls(hDlg, idEnable, SIZEOF(idEnable), DBGetCheckBtn(hDlg, ctrl_Menu[i].idCheckbox, ctrl_Menu[i].pszKey,0));
+							// set radio button state
+							int id = ctrl_Menu[i].idNONE;	//default
+							if ((flag & 4) == 4) id = ctrl_Menu[i].idALL;
+							else if ((flag & 8) == 8) id = ctrl_Menu[i].idEXIMPORT;
+							CheckRadioButton(hDlg, ctrl_Menu[i].idNONE, ctrl_Menu[i].idEXIMPORT, id);
+						}
+
+						// extra icon settings
+						if (!myGlobals.ExtraIconsServiceExist)
+						{
+							ComboBox_SetCurSelByItemDataPtr(GetDlgItem(hDlg, COMBO_OPT_GENDER),
+								(LPARAM)DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER, DEFVAL_CLIST_EXTRAICON_GENDER));
+							ComboBox_SetCurSelByItemDataPtr(GetDlgItem(hDlg, COMBO_OPT_FLAGS),
+								(LPARAM)DB::Setting::GetByte(MODNAMEFLAGS,"ExtraImgFlagColumn", SETTING_EXTRAIMGFLAGCOLUMN_DEFAULT));
+						}
+						else
+						{
+							DBGetCheckBtn(hDlg, CHECK_OPT_GENDER,	SET_CLIST_EXTRAICON_GENDER2, 0);
+						}
+						DBGetCheckBtn (hDlg, CHECK_OPT_HOMEPAGEICON,	SET_CLIST_EXTRAICON_HOMEPAGE,	DEFVAL_CLIST_EXTRAICON_HOMEPAGE);
+						DBGetCheckBtn (hDlg, CHECK_OPT_EMAILICON,		SET_CLIST_EXTRAICON_EMAIL,		DEFVAL_CLIST_EXTRAICON_EMAIL);
+						DBGetCheckBtn (hDlg, CHECK_OPT_PHONEICON,		SET_CLIST_EXTRAICON_PHONE,		DEFVAL_CLIST_EXTRAICON_PHONE);
+						CheckDlgButton(hDlg, CHECK_OPT_FLAGSUNKNOWN,	gFlagsOpts.bUseUnknownFlag);
+						CheckDlgButton(hDlg, CHECK_OPT_FLAGSMSGSTATUS,	gFlagsOpts.bShowStatusIconFlag);
+
+						// misc
+						DBGetCheckBtn(hDlg, CHECK_OPT_ZODIACAVATAR,	SET_ZODIAC_AVATARS, FALSE);
+						
+						bInitialized = 1;
+					}
+					break;
+
+				case PSN_APPLY: 
+					// menu item settings
+					{
+						for (int i = 0; i < SIZEOF(ctrl_Menu); i++) {
+							int flag = IsDlgButtonChecked(hDlg, ctrl_Menu[i].idCheckbox);
+							flag |= IsDlgButtonChecked(hDlg, ctrl_Menu[i].idNONE)? 2:0;
+							flag |= IsDlgButtonChecked(hDlg, ctrl_Menu[i].idALL)? 4:0;
+							flag |= IsDlgButtonChecked(hDlg, ctrl_Menu[i].idEXIMPORT)? 8:0;
+							DB::Setting::WriteByte(ctrl_Menu[i].pszKey, (BYTE) flag);
+						}
+
+						RebuildMenu();
+					}
+
+					// extra icon settings
+					BOOL FlagsClistChange = 0;
+					BOOL FlagsMsgWndChange = 0;
+					{
+						BYTE valNew;
+						valNew = IsDlgButtonChecked(hDlg, CHECK_OPT_FLAGSUNKNOWN);
+						if (gFlagsOpts.bUseUnknownFlag != valNew) {
+							gFlagsOpts.bUseUnknownFlag  = valNew;
+							DB::Setting::WriteByte(MODNAMEFLAGS,"UseUnknownFlag",valNew);
+							FlagsClistChange++;
+							FlagsMsgWndChange++;
+						}
+						valNew = IsDlgButtonChecked(hDlg, CHECK_OPT_FLAGSMSGSTATUS);
+						if (gFlagsOpts.bShowStatusIconFlag != valNew) {
+							gFlagsOpts.bShowStatusIconFlag  = valNew;
+							DB::Setting::WriteByte(MODNAMEFLAGS,"ShowStatusIconFlag",valNew);
+							FlagsMsgWndChange++;
+						}
+
+						if (!myGlobals.ExtraIconsServiceExist)
+						{
+							// Enable/Disable extra icon gender modules and write new values to database
+							BYTE bOldColumn = DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER, DEFVAL_CLIST_EXTRAICON_GENDER);
+							BYTE bNewColumn = (BYTE)ComboBox_GetItemData(
+								GetDlgItem(hDlg,COMBO_OPT_GENDER),
+								SendDlgItemMessage(hDlg, COMBO_OPT_GENDER, CB_GETCURSEL, NULL, NULL));
+							if (bOldColumn != bNewColumn) {
+								ClearAllExtraIcons(bOldColumn);
+								SvcGenderEnableExtraIcons(bNewColumn, TRUE);
+							}
+
+							// Enable/Disable extra icon Flags and write new values to database
+							bNewColumn = (BYTE)ComboBox_GetItemData(
+								GetDlgItem(hDlg,COMBO_OPT_FLAGS),
+								SendDlgItemMessage(hDlg, COMBO_OPT_FLAGS, CB_GETCURSEL, NULL, NULL));
+							if (gFlagsOpts.idExtraColumn != bNewColumn ||
+								gFlagsOpts.bShowExtraImgFlag!=(bNewColumn!=((BYTE)-1))) {
+								SvcFlagsEnableExtraIcons(bNewColumn, TRUE);
+								FlagsClistChange++;
+							}
+						}
+						else
+						{
+							SvcGenderEnableExtraIcons(IsDlgButtonChecked(hDlg, CHECK_OPT_GENDER)? 1:-1, TRUE);
+						}
+
+						if(FlagsClistChange)  EnsureExtraImages();
+						if(FlagsMsgWndChange) UpdateStatusIcons(NULL);
+
+						SvcHomepageEnableExtraIcons(IsDlgButtonChecked(hDlg, CHECK_OPT_HOMEPAGEICON), TRUE);
+						SvcEMailEnableExtraIcons(IsDlgButtonChecked(hDlg, CHECK_OPT_EMAILICON), TRUE);
+						SvcPhoneEnableExtraIcons(IsDlgButtonChecked(hDlg, CHECK_OPT_PHONEICON), TRUE);
+
+					}
+
+					// misc
+					BOOLEAN bEnabled = IsDlgButtonChecked(hDlg, CHECK_OPT_ZODIACAVATAR);
+					DB::Setting::WriteByte(SET_ZODIAC_AVATARS, bEnabled);
+					NServices::NAvatar::Enable(bEnabled);
+			}
+			break;
+
+		case WM_COMMAND:
+			{
+				switch (LOWORD(wParam)) 
+				{
+					case COMBO_OPT_GENDER:
+					case COMBO_OPT_FLAGS:
+						{
+							if (HIWORD(wParam) == CBN_SELCHANGE && bInitialized) 
+							{
+								NotifyParentOfChange(hDlg);
+							}
+						}
+						break;
+					case CHECK_OPT_MI_MAIN:
+					case CHECK_OPT_MI_CONTACT:
+					case CHECK_OPT_MI_GROUP:
+					case CHECK_OPT_MI_SUBGROUP:
+//					case CHECK_OPT_MI_STATUS:
+					case CHECK_OPT_MI_ACCOUNT:
+						{
+							for (int i = 0; i < SIZEOF(ctrl_Menu); i++) {
+								if (ctrl_Menu[i].idCheckbox == LOWORD(wParam)) 
+								{
+									const INT idMenuItems[] = { ctrl_Menu[i].idCheckbox + 1, ctrl_Menu[i].idNONE, ctrl_Menu[i].idALL, ctrl_Menu[i].idEXIMPORT };
+									EnableControls(hDlg, idMenuItems, SIZEOF(idMenuItems), 
+										Button_GetCheck((HWND)lParam) && ServiceExists(MS_CLIST_REMOVEMAINMENUITEM));
+									break;
+								}
+							}
+							if (bInitialized) NotifyParentOfChange(hDlg);
+						}
+						break;
+					case RADIO_OPT_MI_MAIN_ALL:
+					case RADIO_OPT_MI_MAIN_NONE:
+					case RADIO_OPT_MI_MAIN_EXIMPORT:
+					case RADIO_OPT_MI_CONTACT_ALL:
+					case RADIO_OPT_MI_CONTACT_NONE:
+					case RADIO_OPT_MI_CONTACT_EXIMPORT:
+					case RADIO_OPT_MI_GROUP_ALL:
+					case RADIO_OPT_MI_GROUP_NONE:
+					case RADIO_OPT_MI_GROUP_EXIMPORT:
+					case RADIO_OPT_MI_SUBGROUP_ALL:
+					case RADIO_OPT_MI_SUBGROUP_NONE:
+					case RADIO_OPT_MI_SUBGROUP_EXIMPORT:
+//					case RADIO_OPT_MI_STATUS_ALL:
+//					case RADIO_OPT_MI_STATUS_NONE:
+//					case RADIO_OPT_MI_STATUS_EXIMPORT:
+					case RADIO_OPT_MI_ACCOUNT_ALL:
+					case RADIO_OPT_MI_ACCOUNT_NONE:
+					case RADIO_OPT_MI_ACCOUNT_EXIMPORT:
+					case CHECK_OPT_HOMEPAGEICON:
+					case CHECK_OPT_EMAILICON:
+					case CHECK_OPT_PHONEICON:
+					case CHECK_OPT_GENDER:
+					case CHECK_OPT_FLAGSUNKNOWN:
+					case CHECK_OPT_FLAGSMSGSTATUS:
+					case CHECK_OPT_ZODIACAVATAR:
+						{
+							if (bInitialized)
+							{
+								NotifyParentOfChange(hDlg);
+							}
+						}
+				}
+			}
+	}
+	return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc_AdvancedOpts(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	static BOOLEAN	bInitialized = 0;
+
+	switch (uMsg) {
+		case WM_INITDIALOG:
+			{
+				TranslateDialogDefault(hDlg);
+
+				EnableDlgItem(hDlg, CHECK_OPT_METASCAN, myGlobals.szMetaProto != NULL);
+
+				SendNotify_InfoChanged(hDlg);
+			} return TRUE;
+
+		case WM_NOTIFY:
+			{
+				switch (((LPNMHDR)lParam)->code) {
+					case PSN_INFOCHANGED:
+						{
+							bInitialized = 0;
+
+							DBGetCheckBtn(hDlg, CHECK_OPT_ICOVERSION, SET_ICONS_CHECKFILEVERSION, TRUE);
+							DBGetCheckBtn(hDlg, CHECK_OPT_BUTTONICONS, SET_ICONS_BUTTONS, TRUE);
+							DBGetCheckBtn(hDlg, CHECK_OPT_METASCAN, SET_META_SCAN, TRUE);
+							DBGetCheckBtn(hDlg, CHECK_OPT_SREMAIL_ENABLED, SET_EXTENDED_EMAILSERVICE, TRUE);
+							if(tmi.getTimeZoneTime) {
+								CheckDlgButton(hDlg, CHECK_OPT_AUTOTIMEZONE, TRUE);
+								EnableWindow(GetDlgItem(hDlg, CHECK_OPT_AUTOTIMEZONE),	FALSE);
+							}
+							else {
+								DBGetCheckBtn(hDlg, CHECK_OPT_AUTOTIMEZONE, SET_OPT_AUTOTIMEZONE, TRUE);
+							}
+
+							bInitialized = 1;
+						} break;
+
+					case PSN_APPLY:
+						{
+							DBWriteCheckBtn(hDlg, CHECK_OPT_ICOVERSION, SET_ICONS_CHECKFILEVERSION);
+							DBWriteCheckBtn(hDlg, CHECK_OPT_BUTTONICONS, SET_ICONS_BUTTONS);
+							DBWriteCheckBtn(hDlg, CHECK_OPT_METASCAN, SET_META_SCAN);
+
+							DBWriteCheckBtn(hDlg, CHECK_OPT_SREMAIL_ENABLED, SET_EXTENDED_EMAILSERVICE);
+							if (!tmi.getTimeZoneTime) {
+								DBWriteCheckBtn(hDlg, CHECK_OPT_AUTOTIMEZONE, SET_OPT_AUTOTIMEZONE);
+								if (IsDlgButtonChecked(hDlg, CHECK_OPT_AUTOTIMEZONE)) {
+									SvcTimezoneSyncWithWindows();
+								}
+							}
+						}
+				}
+			} break;
+
+		case WM_COMMAND:
+			{
+				switch (LOWORD(wParam)) {
+					case CHECK_OPT_ICOVERSION:
+					case CHECK_OPT_BUTTONICONS:
+					case CHECK_OPT_METASCAN:
+					case CHECK_OPT_SREMAIL_ENABLED:
+					case CHECK_OPT_AUTOTIMEZONE:
+						{
+							if (bInitialized) {
+								NotifyParentOfChange(hDlg);
+							}
+						} break;
+
+					case BTN_OPT_RESET:
+						{
+							BOOLEAN WantReset;
+
+							WantReset = MsgBox(hDlg, 
+								MB_ICON_WARNING|MB_YESNO, 
+								LPGENT("Question"), 
+								LPGENT("Reset factory defaults"),
+								LPGENT("This will delete all settings, you've made!\nAll TreeView settings, window positions and any other settings!\n\nAre you sure to procceed?"));
+
+							if (WantReset) {
+								HANDLE hContact;
+								DB::CEnumList	Settings;
+
+								// delete all skin icons
+								if (!Settings.EnumSettings(NULL, "SkinIcons")) {
+									INT i;
+									LPSTR s;
+
+									for (i = 0; i < Settings.getCount(); i++) {
+										s = Settings[i];
+										if (!mir_strnicmp(s, "UserInfoEx", 10)) {
+											DB::Setting::Delete(NULL, "SkinIcons", s);
+										}
+									}
+								}
+								// delete global settings
+								DB::Module::Delete(NULL, USERINFO"Ex");
+								DB::Module::Delete(NULL, USERINFO"ExW");
+
+								// delete old contactsettings
+								for (hContact = DB::Contact::FindFirst();	hContact != NULL; hContact = DB::Contact::FindNext(hContact)) {
+									DB::Setting::Delete(hContact, USERINFO, "PListColWidth0");
+									DB::Setting::Delete(hContact, USERINFO, "PListColWidth1");
+									DB::Setting::Delete(hContact, USERINFO, "PListColWidth2");
+									DB::Setting::Delete(hContact, USERINFO, "EMListColWidth0");
+									DB::Setting::Delete(hContact, USERINFO, "EMListColWidth1");
+									DB::Setting::Delete(hContact, USERINFO, "BirthRemind");
+									DB::Setting::Delete(hContact, USERINFO, "RemindBirthday");
+									DB::Setting::Delete(hContact, USERINFO, "RemindDaysErlier");
+									DB::Setting::Delete(hContact, USERINFO, "vCardPath");
+
+									DB::Module::Delete(hContact, USERINFO"Ex");
+									DB::Module::Delete(hContact, USERINFO"ExW");
+								}
+
+								SendMessage(GetParent(hDlg), PSM_FORCECHANGED, NULL, NULL);
+								MsgBox(hDlg, MB_ICON_INFO, 
+									LPGENT("Ready"),
+									LPGENT("Everything is done!"),
+									LPGENT("All settings are reset to default values now!"));
+							}
+						}
+				}
+			}
+	}
+	return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc_DetailsDlgOpts(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	static BOOLEAN	bInitialized = 0;
+
+	switch (uMsg) 
+	{
+		case WM_INITDIALOG:
+			{
+				TranslateDialogDefault(hDlg);
+				SendNotify_InfoChanged(hDlg);
+			}
+			return TRUE;
+
+		case WM_NOTIFY:
+			switch (((LPNMHDR)lParam)->code) 
+			{
+				case PSN_INFOCHANGED:
+				{
+					bInitialized = 0;
+
+					// init colors
+					DBGetCheckBtn(hDlg, CHECK_OPT_CLR, SET_PROPSHEET_SHOWCOLOURS, TRUE);
+					SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_OPT_CLR, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, CHECK_OPT_CLR));
+					DBGetColor(hDlg, CLR_NORMAL, SET_PROPSHEET_CLRNORMAL, RGB(90, 90, 90));
+					DBGetColor(hDlg, CLR_USER, SET_PROPSHEET_CLRCUSTOM, RGB(0, 10, 130));
+					DBGetColor(hDlg, CLR_BOTH, SET_PROPSHEET_CLRBOTH, RGB(0, 160, 10));
+					DBGetColor(hDlg, CLR_CHANGED, SET_PROPSHEET_CLRCHANGED, RGB(190, 0, 0));
+					DBGetColor(hDlg, CLR_META, SET_PROPSHEET_CLRMETA, RGB(120, 40, 130));
+
+					// treeview options
+					DBGetCheckBtn(hDlg, CHECK_OPT_GROUPS, SET_PROPSHEET_GROUPS, TRUE);
+					DBGetCheckBtn(hDlg, CHECK_OPT_SORTTREE, SET_PROPSHEET_SORTITEMS, FALSE);
+					DBGetCheckBtn(hDlg, CHECK_OPT_AEROADAPTION, SET_PROPSHEET_AEROADAPTION, TRUE);
+
+					// common options
+					DBGetCheckBtn(hDlg, CHECK_OPT_READONLY, SET_PROPSHEET_PCBIREADONLY, FALSE);
+					DBGetCheckBtn(hDlg, CHECK_OPT_CHANGEMYDETAILS, SET_PROPSHEET_CHANGEMYDETAILS, FALSE);
+					Button_Enable(GetDlgItem(hDlg, CHECK_OPT_CHANGEMYDETAILS), myGlobals.CanChangeDetails);
+
+					bInitialized = 1;
+					break;
+				}
+
+				case PSN_APPLY:
+				{
+					DBWriteCheckBtn(hDlg, CHECK_OPT_CLR, SET_PROPSHEET_SHOWCOLOURS);
+					DBWriteCheckBtn(hDlg, CHECK_OPT_GROUPS, SET_PROPSHEET_GROUPS);
+					DBWriteCheckBtn(hDlg, CHECK_OPT_SORTTREE, SET_PROPSHEET_SORTITEMS);
+					DBWriteCheckBtn(hDlg, CHECK_OPT_READONLY, SET_PROPSHEET_PCBIREADONLY);
+					DBWriteCheckBtn(hDlg, CHECK_OPT_AEROADAPTION, SET_PROPSHEET_AEROADAPTION);
+					DBWriteCheckBtn(hDlg, CHECK_OPT_CHANGEMYDETAILS, SET_PROPSHEET_CHANGEMYDETAILS);
+
+					DBWriteColor(hDlg, CLR_NORMAL, SET_PROPSHEET_CLRNORMAL);
+					DBWriteColor(hDlg, CLR_USER, SET_PROPSHEET_CLRCUSTOM);
+					DBWriteColor(hDlg, CLR_BOTH, SET_PROPSHEET_CLRBOTH);
+					DBWriteColor(hDlg, CLR_CHANGED, SET_PROPSHEET_CLRCHANGED);
+					DBWriteColor(hDlg, CLR_META, SET_PROPSHEET_CLRMETA);
+				}
+			}
+			break;
+
+		case WM_COMMAND:
+			{
+				switch (LOWORD(wParam)) 
+				{
+					case CHECK_OPT_CLR:
+						{
+							if (HIWORD(wParam) == BN_CLICKED) 
+							{
+								BOOL bChecked = SendMessage((HWND)lParam, BM_GETCHECK, NULL, NULL);
+								const INT idCtrl[] = { CLR_NORMAL, CLR_USER, CLR_BOTH, CLR_CHANGED, CLR_META, TXT_OPT_CLR_NORMAL,
+												 TXT_OPT_CLR_USER, TXT_OPT_CLR_BOTH, TXT_OPT_CLR_CHANGED, TXT_OPT_CLR_META };
+								
+								EnableControls(hDlg, idCtrl, SIZEOF(idCtrl), bChecked);
+							}
+						}
+					case CHECK_OPT_GROUPS:
+					case CHECK_OPT_SORTTREE:
+					case CHECK_OPT_AEROADAPTION:
+					case CHECK_OPT_READONLY:
+					case CHECK_OPT_CHANGEMYDETAILS:
+					case CHECK_OPT_MI_CONTACT:
+						{
+							if (bInitialized && HIWORD(wParam) == BN_CLICKED) 
+							{
+								NotifyParentOfChange(hDlg);
+							}
+						}
+						break;
+
+					default:
+						{
+							if (bInitialized && HIWORD(wParam) == CPN_COLOURCHANGED)
+							{
+								NotifyParentOfChange(hDlg);
+							}
+						}
+				}
+			}
+	}
+	return FALSE;
+}
+
+
+static INT_PTR CALLBACK DlgProc_ReminderOpts(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	static BOOLEAN	bInitialized = 0;
+
+	switch (uMsg) {
+		
+		case WM_INITDIALOG:
+			{
+				HWND hCtrl;
+
+				TranslateDialogDefault(hDlg);
+
+				ShowWindow(GetDlgItem(hDlg, TXT_REMIND5), myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+				ShowWindow(GetDlgItem(hDlg, EDIT_EXTRAICON), myGlobals.ExtraIconsServiceExist?SW_HIDE:SW_SHOW);
+				ShowWindow(GetDlgItem(hDlg, CHECK_REMIND_SECURED), myGlobals.UseDbxTree?SW_HIDE:SW_SHOW);
+
+				SendDlgItemMessage(hDlg, ICO_BIRTHDAY, STM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon(ICO_DLG_ANNIVERSARY));
+
+				// set colours			
+				SendDlgItemMessage(hDlg, EDIT_REMIND, EM_LIMITTEXT, 2, 0);
+				SendDlgItemMessage(hDlg, SPIN_REMIND, UDM_SETRANGE32, 0, 50);
+				SendDlgItemMessage(hDlg, EDIT_REMIND2, EM_LIMITTEXT, 4, 0);
+				SendDlgItemMessage(hDlg, SPIN_REMIND2, UDM_SETRANGE32, 1, 8760);
+				SendDlgItemMessage(hDlg, EDIT_REMIND_SOUNDOFFSET, EM_LIMITTEXT, 2, 0);
+				SendDlgItemMessage(hDlg, SPIN_REMIND_SOUNDOFFSET, UDM_SETRANGE32, 0, 50);
+
+				ComboBox_FillExtraIcons(GetDlgItem(hDlg, EDIT_EXTRAICON));
+
+				if (hCtrl = GetDlgItem(hDlg, EDIT_REMIND_ENABLED)) 
+				{
+					ComboBox_AddString(hCtrl, TranslateT("Reminder disabled"));
+					ComboBox_AddString(hCtrl, TranslateT("Anniversaries only"));
+					ComboBox_AddString(hCtrl, TranslateT("Bithdays only"));
+					ComboBox_AddString(hCtrl, TranslateT("everything"));
+				}
+				if (hCtrl = GetDlgItem(hDlg, EDIT_BIRTHMODULE)) 
+				{
+					ComboBox_AddString(hCtrl, TranslateT("mBirthday"));
+					ComboBox_AddString(hCtrl, TranslateT("UserInfo (default)"));
+				}
+				SendNotify_InfoChanged(hDlg);
+			}
+			return TRUE;
+
+		case WM_NOTIFY:
+			switch (((LPNMHDR)lParam)->code) 
+			{
+				case PSN_INFOCHANGED:
+					{
+						bInitialized = 0;
+						BOOLEAN bEnabled;
+
+						// set reminder options
+						bEnabled = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED);
+						SendDlgItemMessage(hDlg, EDIT_REMIND_ENABLED, CB_SETCURSEL, bEnabled, NULL);
+						DlgProc_ReminderOpts(hDlg, WM_COMMAND, MAKEWPARAM(EDIT_REMIND_ENABLED, CBN_SELCHANGE),
+							(LPARAM)GetDlgItem(hDlg, EDIT_REMIND_ENABLED));
+
+						DBGetCheckBtn(hDlg, CHECK_REMIND_MI, SET_REMIND_MENUENABLED, DEFVAL_REMIND_MENUENABLED);
+						DBGetCheckBtn(hDlg, CHECK_REMIND_FLASHICON, SET_REMIND_FLASHICON, FALSE);
+						DBGetCheckBtn(hDlg, CHECK_REMIND_VISIBLEONLY, SET_REMIND_CHECKVISIBLE, DEFVAL_REMIND_CHECKVISIBLE);
+						DBGetCheckBtn(hDlg, CHECK_REMIND_STARTUP, SET_REMIND_CHECKON_STARTUP, FALSE);
+						DBGetCheckBtn(hDlg, CHECK_REMIND_SECURED, SET_REMIND_SECUREBIRTHDAY, FALSE);
+
+						SetDlgItemInt(hDlg, EDIT_REMIND, DB::Setting::GetWord(NULL, MODNAME, SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET), FALSE);
+						SetDlgItemInt(hDlg, EDIT_REMIND_SOUNDOFFSET, DB::Setting::GetByte(SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET), FALSE);
+						SetDlgItemInt(hDlg, EDIT_REMIND2, DB::Setting::GetWord(NULL, MODNAME, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL), FALSE);
+						if (!myGlobals.ExtraIconsServiceExist) {
+							for (int i = 0; i < ComboBox_GetCount(GetDlgItem(hDlg, EDIT_EXTRAICON)); i++)
+							{
+								if ((BYTE)ComboBox_GetItemData(GetDlgItem(hDlg,EDIT_EXTRAICON),i) == DB::Setting::GetByte(SET_REMIND_EXTRAICON, 1))
+								{
+									SendDlgItemMessage(hDlg, EDIT_EXTRAICON, CB_SETCURSEL, i, NULL);
+									break;
+								}
+							}
+						}
+						SendDlgItemMessage(hDlg, EDIT_BIRTHMODULE, CB_SETCURSEL, DB::Setting::GetByte(SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE), NULL);
+						{
+							MTime mtLast;
+							TCHAR szTime[MAX_PATH];
+
+							mtLast.DBGetStamp(NULL, MODNAME, SET_REMIND_LASTCHECK);
+							mtLast.UTCToLocal();
+							mtLast.TimeFormat(szTime, SIZEOF(szTime));
+
+							SetDlgItemText(hDlg, TXT_REMIND_LASTCHECK, szTime);
+						}
+						
+						bInitialized = 1;
+					}
+					break;
+
+				case PSN_APPLY:
+					{
+						BYTE bColumn, bNewVal;
+						BOOLEAN bReminderCheck = FALSE;
+
+						// save checkbox options
+						DBWriteCheckBtn(hDlg, CHECK_REMIND_MI, SET_REMIND_MENUENABLED);
+						DBWriteCheckBtn(hDlg, CHECK_REMIND_FLASHICON, SET_REMIND_FLASHICON);
+						DBWriteCheckBtn(hDlg, CHECK_REMIND_VISIBLEONLY, SET_REMIND_CHECKVISIBLE);
+						DBWriteCheckBtn(hDlg, CHECK_REMIND_STARTUP, SET_REMIND_CHECKON_STARTUP);
+						DBWriteCheckBtn(hDlg, CHECK_REMIND_SECURED, SET_REMIND_SECUREBIRTHDAY);
+						
+						DBWriteEditByte(hDlg, EDIT_REMIND_SOUNDOFFSET, SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET);
+						DBWriteEditWord(hDlg, EDIT_REMIND2, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL);
+						bReminderCheck = DBWriteEditWord(hDlg, EDIT_REMIND, SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET);							
+
+						// save primary birthday module
+						BYTE bOld = DB::Setting::GetByte(SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE);  //    = 1
+						BYTE bNew = (BYTE)ComboBox_GetCurSel(GetDlgItem(hDlg,EDIT_BIRTHMODULE));
+						if (bOld != bNew) {
+							//keep the database clean
+							MAnnivDate mdb;
+							HANDLE hContact = NULL;
+							DBWriteComboByte(hDlg, EDIT_BIRTHMODULE, SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE);
+							//walk through all the contacts stored in the DB
+							for (hContact = DB::Contact::FindFirst();
+								 hContact != NULL;
+								 hContact = DB::Contact::FindNext(hContact))
+							{
+								mdb.DBMoveBirthDate(hContact,bOld,bNew);
+							}
+						}
+
+						// save new clist extra icon
+						bColumn = DB::Setting::GetByte(SET_REMIND_EXTRAICON, 1);
+						bNewVal = (BYTE)ComboBox_GetItemData(
+							GetDlgItem(hDlg,EDIT_EXTRAICON),
+							SendDlgItemMessage(hDlg, EDIT_EXTRAICON, CB_GETCURSEL, NULL, NULL));
+						if (bColumn != bNewVal) 
+						{
+							ClearAllExtraIcons(bColumn);
+							DB::Setting::WriteByte(SET_REMIND_EXTRAICON, bNewVal);
+							bReminderCheck = TRUE;
+						}
+
+						// update current reminder state
+						bNewVal = (BYTE)SendDlgItemMessage(hDlg, EDIT_REMIND_ENABLED, CB_GETCURSEL, NULL, NULL);
+						if (DB::Setting::GetByte(SET_REMIND_ENABLED, 1) != bNewVal) 
+						{
+							DB::Setting::WriteByte(SET_REMIND_ENABLED, bNewVal);
+							if (bNewVal == REMIND_OFF) 
+							{
+								ClearAllExtraIcons(bColumn);
+								SvcReminderEnable(FALSE);
+								bReminderCheck = FALSE;
+							}
+							else {
+								bReminderCheck = TRUE;
+							}
+						}
+						// update all contact list extra icons
+						if (bReminderCheck) 
+						{
+							SvcReminderEnable(TRUE);							// reinit reminder options from db
+							SvcReminderCheckAll(NOTIFY_CLIST);		// notify
+						}
+						RebuildMain();
+					}
+			}
+			break;
+
+		case WM_COMMAND:
+			{
+				switch (LOWORD(wParam)) 
+				{
+					case EDIT_REMIND_ENABLED:
+						{
+							if (HIWORD(wParam) == CBN_SELCHANGE) 
+							{
+								INT bEnabled = ComboBox_GetCurSel((HWND)lParam) > 0;
+								const INT idCtrl[] = {
+									CHECK_REMIND_MI, EDIT_REMIND, EDIT_REMIND2, SPIN_REMIND, SPIN_REMIND2, TXT_REMIND,
+									TXT_REMIND2, TXT_REMIND3, TXT_REMIND4, TXT_REMIND6, TXT_REMIND7, TXT_REMIND8, TXT_REMIND9,
+									TXT_REMIND_LASTCHECK, CHECK_REMIND_FLASHICON, EDIT_BIRTHMODULE, CHECK_REMIND_VISIBLEONLY,
+									CHECK_REMIND_SECURED, CHECK_REMIND_STARTUP, EDIT_REMIND_SOUNDOFFSET, SPIN_REMIND_SOUNDOFFSET
+								};
+								
+								EnableControls(hDlg, idCtrl, SIZEOF(idCtrl), bEnabled);
+								bEnabled &= myGlobals.HaveCListExtraIcons;
+
+								EnableDlgItem(hDlg, TXT_REMIND5, bEnabled);
+								EnableDlgItem(hDlg, EDIT_EXTRAICON, bEnabled);
+							}
+						}
+
+					case EDIT_EXTRAICON:
+					case EDIT_BIRTHMODULE:
+						{
+							if (bInitialized && HIWORD(wParam) == CBN_SELCHANGE) 
+							{
+								NotifyParentOfChange(hDlg);
+							}
+						}
+						break;
+
+					case CHECK_REMIND_MI:
+					case CHECK_REMIND_FLASHICON:
+					case CHECK_REMIND_VISIBLEONLY:
+					case CHECK_REMIND_STARTUP:
+					case CHECK_REMIND_SECURED:
+						{
+							if (bInitialized && HIWORD(wParam) == BN_CLICKED) 
+							{
+								NotifyParentOfChange(hDlg);
+							}
+						}
+						break;
+
+					/*
+					 * The user changes the number of days in advance of an anniversary to be notified by popups and clist extra icon.
+						*/
+					case EDIT_REMIND:
+						{
+							if (bInitialized && HIWORD(wParam) == EN_UPDATE) 
+							{
+								BOOL t;
+								WORD v;
+								
+								v = (WORD)GetDlgItemInt(hDlg, LOWORD(wParam), &t, FALSE);
+								if (t && (v != DB::Setting::GetWord(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET)))
+								{
+									NotifyParentOfChange(hDlg);
+								}
+							}
+						}
+						break;
+
+					/*
+					 * The user changes the number of days in advance of an anniversary to be notified by sound.
+						*/
+					case EDIT_REMIND_SOUNDOFFSET:
+						{
+							if (bInitialized && HIWORD(wParam) == EN_UPDATE) 
+							{
+								BOOL t;
+								BYTE v;
+								
+								v = (BYTE)GetDlgItemInt(hDlg, LOWORD(wParam), &t, FALSE);
+								if (t && (v != DB::Setting::GetByte(SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET)))
+								{
+									NotifyParentOfChange(hDlg);
+								}
+							}
+						}
+						break;
+
+					/*
+					 * The user changes the notification interval
+						*/
+					case EDIT_REMIND2:
+						{
+							if (bInitialized && HIWORD(wParam) == EN_UPDATE) 
+							{
+								BOOL t;
+								WORD v;
+								
+								v = (WORD)GetDlgItemInt(hDlg, LOWORD(wParam), &t, FALSE);
+								if (t && (v != DB::Setting::GetWord(NULL, MODNAME, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL)))
+								{
+									NotifyParentOfChange(hDlg);
+								}
+							}
+						}
+				}
+			}
+	}
+	return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc_Popups(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	static BOOLEAN	bInitialized = 0;
+
+	switch (uMsg) 
+	{		
+	case WM_INITDIALOG:
+		{
+			TranslateDialogDefault(hDlg);
+			SendNotify_InfoChanged(hDlg);
+		}
+		return TRUE;
+
+	case WM_NOTIFY:
+		{
+			switch (((LPNMHDR)lParam)->code) 
+			{
+			case PSN_INFOCHANGED:
+				{
+					BYTE bDelay, isEnabled;
+
+					bInitialized = 0;
+
+					DBGetCheckBtn(hDlg, CHECK_OPT_POPUP_MSGBOX, SET_POPUPMSGBOX, DEFVAL_POPUPMSGBOX);
+					DBGetCheckBtn(hDlg, CHECK_OPT_POPUP_PROGRESS, "PopupProgress", FALSE);
+					// disable if popup plugin dos not sopport buttons inside popop
+					if (!myGlobals.PopUpActionsExist) {
+						EnableDlgItem(hDlg, CHECK_OPT_POPUP_MSGBOX, FALSE);
+						EnableDlgItem(hDlg, CHECK_OPT_POPUP_PROGRESS, FALSE);
+					}
+					else if (!(DB::Setting::GetDWord(0, "PopUp","Actions", 0) & 1)) {
+						EnableDlgItem(hDlg, CHECK_OPT_POPUP_MSGBOX, FALSE);
+					}
+
+					// enable/disable popups
+					isEnabled = DBGetCheckBtn(hDlg, CHECK_OPT_POPUP_ENABLED, SET_POPUP_ENABLED, DEFVAL_POPUP_ENABLED);
+					SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_OPT_POPUP_ENABLED, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, CHECK_OPT_POPUP_ENABLED));
+
+					// set colortype checkboxes and color controls
+					DBGetColor(hDlg, CLR_BBACK, SET_POPUP_BIRTHDAY_COLOR_BACK, RGB(192, 180, 30));
+					DBGetColor(hDlg, CLR_BTEXT, SET_POPUP_BIRTHDAY_COLOR_TEXT, RGB(0, 0, 0));
+					switch (DB::Setting::GetByte(SET_POPUP_BIRTHDAY_COLORTYPE, POPUP_COLOR_CUSTOM))
+					{
+					case POPUP_COLOR_DEFAULT:
+						{
+							CheckDlgButton(hDlg, CHECK_OPT_POPUP_DEFCLR, TRUE);
+						}
+						break;
+
+					case POPUP_COLOR_WINDOWS:
+						{
+							CheckDlgButton(hDlg, CHECK_OPT_POPUP_WINCLR, TRUE);
+						}
+					}
+
+					DBGetColor(hDlg, CLR_ABACK, SET_POPUP_ANNIVERSARY_COLOR_BACK, RGB(90, 190, 130));
+					DBGetColor(hDlg, CLR_ATEXT, SET_POPUP_ANNIVERSARY_COLOR_TEXT, RGB(0, 0, 0));
+					switch (DB::Setting::GetByte(SET_POPUP_ANNIVERSARY_COLORTYPE, POPUP_COLOR_CUSTOM)) 
+					{
+						case POPUP_COLOR_DEFAULT:
+							{
+								CheckDlgButton(hDlg, CHECK_OPT_POPUP_ADEFCLR, TRUE);
+							}
+							break;
+						case POPUP_COLOR_WINDOWS:
+							{
+								CheckDlgButton(hDlg, CHECK_OPT_POPUP_AWINCLR, TRUE);
+							}
+					}
+					
+					if (isEnabled) 
+					{
+						SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_OPT_POPUP_DEFCLR, BN_CLICKED), NULL);
+						SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_OPT_POPUP_ADEFCLR, BN_CLICKED), NULL);
+					}
+					// set delay values
+					bDelay = DB::Setting::GetByte(SET_POPUP_DELAY, 0);
+					switch (bDelay) 
+					{
+					case 0:
+						{
+							CheckDlgButton(hDlg, RADIO_OPT_POPUP_DEFAULT, TRUE);
+							if (isEnabled)
+							{
+								EnableDlgItem(hDlg, EDIT_DELAY, FALSE);
+							}
+						}
+						break;
+
+					case 255:
+						{
+							CheckDlgButton(hDlg, RADIO_OPT_POPUP_PERMANENT, TRUE);
+							if (isEnabled)
+							{
+								EnableDlgItem(hDlg, EDIT_DELAY, FALSE);
+							}
+						}
+						break;
+
+					default:
+						{
+							CheckDlgButton(hDlg, RADIO_OPT_POPUP_CUSTOM, TRUE);
+							SetDlgItemInt(hDlg, EDIT_DELAY, bDelay, FALSE);
+						}
+					}
+					bInitialized = TRUE;
+				}
+				break;
+			
+			case PSN_APPLY:
+				{
+					DBWriteCheckBtn(hDlg, CHECK_OPT_POPUP_MSGBOX, SET_POPUPMSGBOX);
+					DBWriteCheckBtn(hDlg, CHECK_OPT_POPUP_PROGRESS, "PopupProgress");
+					DBWriteCheckBtn(hDlg, CHECK_OPT_POPUP_ENABLED, SET_POPUP_ENABLED);
+
+					// save popup style for birthdays
+					DBWriteColor(hDlg, CLR_BBACK, SET_POPUP_BIRTHDAY_COLOR_BACK);
+					DBWriteColor(hDlg, CLR_BTEXT, SET_POPUP_BIRTHDAY_COLOR_TEXT);
+					DB::Setting::WriteByte(SET_POPUP_BIRTHDAY_COLORTYPE, 
+						SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_DEFCLR, BM_GETCHECK, NULL, NULL) 
+							? POPUP_COLOR_DEFAULT 
+							: SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_WINCLR, BM_GETCHECK, NULL, NULL) 
+								? POPUP_COLOR_WINDOWS 
+								: POPUP_COLOR_CUSTOM);
+
+					// save popup style for anniversaries
+					DBWriteColor(hDlg, CLR_ABACK, SET_POPUP_ANNIVERSARY_COLOR_BACK);
+					DBWriteColor(hDlg, CLR_ATEXT, SET_POPUP_ANNIVERSARY_COLOR_TEXT);
+					DB::Setting::WriteByte(SET_POPUP_ANNIVERSARY_COLORTYPE, 
+						SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_ADEFCLR, BM_GETCHECK, NULL, NULL) 
+							? POPUP_COLOR_DEFAULT 
+							: SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_AWINCLR, BM_GETCHECK, NULL, NULL) 
+								? POPUP_COLOR_WINDOWS 
+								: POPUP_COLOR_CUSTOM);
+
+					// save delay
+					if (SendDlgItemMessage(hDlg, RADIO_OPT_POPUP_PERMANENT, BM_GETCHECK, NULL, NULL))
+					{
+						DB::Setting::WriteByte(SET_POPUP_DELAY, 255);
+					}
+					else if (SendDlgItemMessage(hDlg, RADIO_OPT_POPUP_CUSTOM, BM_GETCHECK, NULL, NULL)) 
+					{
+						TCHAR szDelay[4];
+						GetDlgItemText(hDlg, EDIT_DELAY, szDelay, SIZEOF(szDelay));
+						DB::Setting::WriteByte(SET_POPUP_DELAY, (BYTE)_tcstol(szDelay, NULL, 10));
+					}
+					else
+					{
+						DB::Setting::Delete(NULL, MODNAME, SET_POPUP_DELAY);
+					}
+				}
+			}
+		}
+		break;
+
+	case WM_COMMAND:
+		{
+			switch (LOWORD(wParam)) 
+			{
+			case BTN_PREVIEW:
+				{
+					POPUPDATAT_V2 ppd;
+
+					ZeroMemory(&ppd, sizeof(POPUPDATAT_V2));
+					ppd.iSeconds = (INT)DB::Setting::GetByte(SET_POPUP_DELAY, 0);
+					mir_tcsncpy(ppd.lptzText, TranslateT("This is the reminder message"), MAX_SECONDLINE);
+
+					// Birthday
+					mir_tcsncpy(ppd.lptzContactName, TranslateT("Birthday"), SIZEOF(ppd.lptzContactName));
+					ppd.lchIcon = IcoLib_GetIcon(ICO_RMD_DTB0);
+					if (IsDlgButtonChecked(hDlg, CHECK_OPT_POPUP_WINCLR))
+					{
+						ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+						ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+					}
+					else if (!IsDlgButtonChecked(hDlg, CHECK_OPT_POPUP_DEFCLR))
+					{
+						ppd.colorBack = SendDlgItemMessage(hDlg, CLR_BBACK, CPM_GETCOLOUR, 0, 0);
+						ppd.colorText = SendDlgItemMessage(hDlg, CLR_BTEXT, CPM_GETCOLOUR, 0, 0);
+					}
+					PUAddPopUpT(&ppd);
+
+					// Anniversary
+					mir_tcsncpy(ppd.lptzContactName, TranslateT("Anniversary"), SIZEOF(ppd.lptzContactName));
+					ppd.lchIcon = IcoLib_GetIcon(ICO_RMD_DTAX);
+					if (IsDlgButtonChecked(hDlg, CHECK_OPT_POPUP_WINCLR))
+					{
+						ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+						ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+					}
+					else if (IsDlgButtonChecked(hDlg, CHECK_OPT_POPUP_DEFCLR))
+					{
+						ppd.colorBack = 0;
+						ppd.colorText = 0;
+					}
+					else
+					{
+						ppd.colorBack = SendDlgItemMessage(hDlg, CLR_ABACK, CPM_GETCOLOUR, 0, 0);
+						ppd.colorText = SendDlgItemMessage(hDlg, CLR_ATEXT, CPM_GETCOLOUR, 0, 0);
+					}
+					PUAddPopUpT(&ppd);
+				}
+				break;
+
+			case CHECK_OPT_POPUP_MSGBOX:
+			case CHECK_OPT_POPUP_PROGRESS:
+				{
+					if (bInitialized)
+					{
+						NotifyParentOfChange(hDlg);
+					}
+				}
+				break;
+
+			case CHECK_OPT_POPUP_ENABLED:
+				{
+					if (HIWORD(wParam) == BN_CLICKED) 
+					{
+						const BOOL bEnabled = SendMessage((HWND)lParam, BM_GETCHECK, NULL, NULL);
+						const INT idCtrl[] = { 
+							CHECK_OPT_POPUP_DEFCLR, CHECK_OPT_POPUP_WINCLR, 
+							CLR_BBACK, TXT_OPT_POPUP_CLR_BACK, 
+							CLR_BTEXT, TXT_OPT_POPUP_CLR_TEXT,
+							CHECK_OPT_POPUP_ADEFCLR, CHECK_OPT_POPUP_AWINCLR,
+							CLR_ABACK, TXT_OPT_POPUP_CLR_ABACK,
+							CLR_ATEXT, TXT_OPT_POPUP_CLR_ATEXT,
+							RADIO_OPT_POPUP_DEFAULT, RADIO_OPT_POPUP_CUSTOM, 
+							RADIO_OPT_POPUP_PERMANENT, EDIT_DELAY
+						};
+						
+						EnableControls(hDlg, idCtrl, SIZEOF(idCtrl), bEnabled);
+
+						if (bInitialized)
+						{
+							NotifyParentOfChange(hDlg);
+						}
+					}
+				}
+				break;
+
+			case CHECK_OPT_POPUP_DEFCLR:
+			case CHECK_OPT_POPUP_WINCLR:
+				{
+					if (HIWORD(wParam) == BN_CLICKED) 
+					{
+						INT bDefClr = SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_DEFCLR, BM_GETCHECK, NULL, NULL);
+						INT bWinClr = SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_WINCLR, BM_GETCHECK, NULL, NULL);
+
+						EnableDlgItem(hDlg, CHECK_OPT_POPUP_DEFCLR, !bWinClr);
+						EnableDlgItem(hDlg, CHECK_OPT_POPUP_WINCLR, !bDefClr);
+						EnableDlgItem(hDlg, CLR_BBACK, !(bDefClr || bWinClr));
+						EnableDlgItem(hDlg, TXT_OPT_POPUP_CLR_BACK, !(bDefClr || bWinClr));
+						EnableDlgItem(hDlg, CLR_BTEXT, !(bDefClr || bWinClr));
+						EnableDlgItem(hDlg, TXT_OPT_POPUP_CLR_TEXT, !(bDefClr || bWinClr));
+						if (bInitialized)
+						{
+							NotifyParentOfChange(hDlg);
+						}
+					}
+				}
+				break;
+
+			case CHECK_OPT_POPUP_ADEFCLR:
+			case CHECK_OPT_POPUP_AWINCLR:
+				{
+					if (HIWORD(wParam) == BN_CLICKED) 
+					{
+						INT bDefClr = SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_ADEFCLR, BM_GETCHECK, NULL, NULL);
+						INT bWinClr = SendDlgItemMessage(hDlg, CHECK_OPT_POPUP_AWINCLR, BM_GETCHECK, NULL, NULL);
+
+						EnableDlgItem(hDlg, CHECK_OPT_POPUP_ADEFCLR, !bWinClr);
+						EnableDlgItem(hDlg, CHECK_OPT_POPUP_AWINCLR, !bDefClr);
+						EnableDlgItem(hDlg, CLR_ABACK, !(bDefClr || bWinClr));
+						EnableDlgItem(hDlg, TXT_OPT_POPUP_CLR_ABACK, !(bDefClr || bWinClr));
+						EnableDlgItem(hDlg, CLR_ATEXT, !(bDefClr || bWinClr));
+						EnableDlgItem(hDlg, TXT_OPT_POPUP_CLR_ATEXT, !(bDefClr || bWinClr));
+						if (bInitialized)
+						{
+							NotifyParentOfChange(hDlg);
+						}
+					}
+				}
+				break;
+
+			case RADIO_OPT_POPUP_DEFAULT:
+				if (HIWORD(wParam) == BN_CLICKED) 
+				{
+					EnableDlgItem(hDlg, EDIT_DELAY, FALSE);
+					if (bInitialized)
+					{
+						NotifyParentOfChange(hDlg);
+					}
+				}
+				break;
+			case RADIO_OPT_POPUP_CUSTOM:
+				if (HIWORD(wParam) == BN_CLICKED)
+				{
+					EnableDlgItem(hDlg, EDIT_DELAY, TRUE);
+					if (bInitialized) 
+					{
+						NotifyParentOfChange(hDlg);
+					}
+				}
+				break;
+			case RADIO_OPT_POPUP_PERMANENT:
+				if (HIWORD(wParam) == BN_CLICKED)
+				{
+					EnableDlgItem(hDlg, EDIT_DELAY, FALSE);
+					if (bInitialized)
+					{
+						NotifyParentOfChange(hDlg);
+					}
+				}
+				break;
+			case EDIT_DELAY:
+				if (bInitialized && HIWORD(wParam) == EN_UPDATE)
+				{
+					NotifyParentOfChange(hDlg);
+				}
+				break;
+			default:
+				if (bInitialized && HIWORD(wParam) == CPN_COLOURCHANGED)
+				{
+					NotifyParentOfChange(hDlg);
+				}
+			}
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * This hook handler function is called on opening the options dialog
+ * to tell miranda, which pages userinfoex wants to add.
+ *
+ * @param	wParam			- options dialog's internal datastructure,
+ * @param	lParam			- not used
+ *
+ * @retval	MIR_OK
+ **/
+static INT OnInitOptions(WPARAM wParam, LPARAM lParam)
+{
+	DlgContactInfoInitTreeIcons();
+
+	OPTIONSDIALOGPAGE odp = { 0 };
+	odp.position = 95400;
+	odp.hInstance = ghInst;
+	odp.pszTitle = MODNAME;
+	odp.pszGroup = LPGEN("Plugins");
+	odp.cbSize = sizeof(odp);
+
+	// Common page
+	odp.pszTab = LPGEN("Common");
+	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_COMMON);
+	odp.pfnDlgProc = DlgProc_CommonOpts;
+	odp.flags = ODPF_BOLDGROUPS;
+	Options_AddPage(wParam, &odp);
+
+	// Advanced page
+	odp.pszTab = LPGEN("Advanced");
+	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ADVANCED);
+	odp.pfnDlgProc = DlgProc_AdvancedOpts;
+	odp.flags = ODPF_BOLDGROUPS|ODPF_EXPERTONLY;
+	Options_AddPage(wParam, &odp);
+
+	// Details Dialog page
+	odp.pszTab = LPGEN("Details Dialog");
+	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_DETAILSDLG);
+	odp.pfnDlgProc = DlgProc_DetailsDlgOpts;
+	odp.flags = ODPF_BOLDGROUPS;
+	Options_AddPage(wParam, &odp);
+
+	// Reminder page
+	odp.pszTab = LPGEN("Reminder");
+	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_REMINDER);
+	odp.pfnDlgProc = DlgProc_ReminderOpts;
+	odp.flags = ODPF_BOLDGROUPS;
+	Options_AddPage(wParam, &odp);
+
+	// Popups page
+	if (ServiceExists(MS_POPUP_ADDPOPUPT)) {
+		odp.pszTitle = MODNAME;
+		odp.pszGroup = LPGEN("Popups");
+		odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_POPUP);
+		odp.pfnDlgProc = DlgProc_Popups;
+		odp.flags = ODPF_BOLDGROUPS|ODPF_EXPERTONLY;
+		Options_AddPage(wParam, &odp);
+	}
+	return MIR_OK;
+}
+
+/**
+ * This function loads the options module.
+ *
+ * @param	none
+ *
+ * @retval	nothing
+ **/
+VOID OptionsLoadModule()
+{
+	HookEvent(ME_OPT_INITIALISE, OnInitOptions);
+}
diff --git a/plugins/UserInfoEx/src/psp_options.h b/plugins/UserInfoEx/src/psp_options.h
new file mode 100644
index 0000000000..fbed92549a
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_options.h
@@ -0,0 +1,43 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_options.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _UINFOEX_OPTIONS_H_INCLUDED_
+#define _UINFOEX_OPTIONS_H_INCLUDED_
+
+struct MenuOptionsList {
+	LPCSTR		pszKey;
+	const int	idCheckbox;
+	const int	idNONE;
+	const int	idALL;
+	const int	idEXIMPORT;
+};
+
+VOID OptionsLoadModule();
+#endif /* _UINFOEX_OPTIONS_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/psp_origin.cpp b/plugins/UserInfoEx/src/psp_origin.cpp
new file mode 100644
index 0000000000..3859a4e7bc
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_origin.cpp
@@ -0,0 +1,176 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_origin.cpp $
+Revision       : $Revision: 191 $
+Last change on : $Date: 2010-09-20 13:52:01 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "ctrl_combo.h"
+#include "ctrl_edit.h"
+#include "ctrl_tzcombo.h"
+#include "psp_base.h"
+#include "svc_reminder.h"
+
+/**
+ * This is the dialog procedure for the advanced contact information propertysheetpage.
+ *
+ * @param		hDlg		- handle to the dialog window
+ * @param		uMsg		- the message to handle
+ * @param		wParam	- parameter
+ * @param		lParam	- parameter
+ *
+ * @return	different values
+ **/
+INT_PTR CALLBACK PSPProcOrigin(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg) 
+	{
+	case WM_INITDIALOG:
+		{
+			CCtrlList *pCtrlList = CCtrlList::CreateObj(hDlg);
+			if (pCtrlList)
+			{
+				LPIDSTRLIST pList;
+				UINT nList;
+
+				HFONT hBoldFont;
+				PSGetBoldFont(hDlg, hBoldFont);
+				SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hBoldFont, 0);
+
+				TranslateDialogDefault(hDlg);
+				SetTimer(hDlg, 1, 5000, NULL);
+
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STREET,	SET_CONTACT_ORIGIN_STREET,	DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_ZIP,		SET_CONTACT_ORIGIN_ZIP,		DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_CITY,		SET_CONTACT_ORIGIN_CITY,	DBVT_TCHAR));
+				pCtrlList->insert(CEditCtrl::CreateObj(hDlg, EDIT_STATE,	SET_CONTACT_ORIGIN_STATE,	DBVT_TCHAR));
+
+				GetCountryList(&nList, &pList);
+				pCtrlList->insert(CCombo::CreateObj  (hDlg, EDIT_COUNTRY,	SET_CONTACT_ORIGIN_COUNTRY,	DBVT_WORD,	pList, nList));
+
+				pCtrlList->insert(CTzCombo::CreateObj(hDlg, EDIT_TIMEZONE,	NULL));
+			}
+		}
+		break;
+
+	case WM_NOTIFY:
+		{
+			switch (((LPNMHDR) lParam)->idFrom) 
+			{
+			case 0:
+				{
+					HANDLE hContact = (HANDLE)((LPPSHNOTIFY) lParam)->lParam;
+					LPCSTR pszProto;
+					
+					switch (((LPNMHDR) lParam)->code) 
+					{
+					case PSN_INFOCHANGED:
+						{
+							BYTE bChanged = 0;
+
+							if (!PSGetBaseProto(hDlg, pszProto) || *pszProto == 0) break;
+
+							if (hContact)
+							{
+								MTime mt;
+								
+								if (mt.DBGetStamp(hContact, USERINFO, SET_CONTACT_ADDEDTIME) && strstr(pszProto, "ICQ"))
+								{
+									DWORD dwStamp;
+									
+									dwStamp = DB::Contact::WhenAdded(DB::Setting::GetDWord(hContact, pszProto, "UIN", 0), pszProto);
+									if (dwStamp > 0)
+									{
+										mt.FromStampAsUTC(dwStamp);
+									}
+								}
+								if (mt.IsValid()) 
+								{
+									TCHAR szTime[MAX_PATH];
+									LPTSTR ptr;
+									
+									mt.UTCToLocal();
+									mt.DateFormatLong(szTime, SIZEOF(szTime));
+									
+									_tcscat(szTime, _T(" - "));
+									ptr = szTime + _tcslen(szTime);
+									mt.TimeFormat(ptr, SIZEOF(szTime) - (ptr - szTime));
+									SetDlgItemText(hDlg, TXT_DATEADDED, szTime);
+								}
+							}
+						 
+							SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bChanged ? PSP_CHANGED : 0);
+						}
+						break;
+				
+					case PSN_ICONCHANGED:
+						{
+							const ICONCTRL idIcon[] = {
+								{ ICO_COMMON_ADDRESS,	STM_SETIMAGE,	ICO_ADDRESS	},
+								{ ICO_COMMON_CLOCK,		STM_SETIMAGE,	ICO_CLOCK },
+							};
+
+							IcoLib_SetCtrlIcons(hDlg, idIcon, SIZEOF(idIcon));
+						}
+					}
+				}
+			} /* switch (((LPNMHDR)lParam)->idFrom) */
+		}
+		break;
+
+	case WM_COMMAND:
+		{
+			switch (LOWORD(wParam)) 
+			{
+			case EDIT_COUNTRY:
+				if(HIWORD(wParam) == CBN_SELCHANGE) {
+					LPIDSTRLIST pd = (LPIDSTRLIST)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+					UpDate_CountryIcon(GetDlgItem(hDlg, ICO_COUNTRY), pd->nID);
+				}
+				break;
+			}
+		}
+		break;
+
+	case WM_TIMER:
+		{
+			TCHAR szTime[32];
+			CTzCombo::GetObj(hDlg, EDIT_TIMEZONE)->GetTime(szTime, SIZEOF(szTime));
+			SetDlgItemText(hDlg, TXT_TIME, szTime);
+			break;
+		}
+
+	case WM_DESTROY:
+		{
+			KillTimer(hDlg, 1);
+		}
+	}
+	return PSPBaseProc(hDlg, uMsg, wParam, lParam);
+}
diff --git a/plugins/UserInfoEx/src/psp_profile.cpp b/plugins/UserInfoEx/src/psp_profile.cpp
new file mode 100644
index 0000000000..e964db40f6
--- /dev/null
+++ b/plugins/UserInfoEx/src/psp_profile.cpp
@@ -0,0 +1,1464 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/psp_profile.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * system & local includes
+ **/
+#include "commonheaders.h"
+#include "ctrl_base.h"
+
+#define LVF_EDITLABEL	8
+
+#define CELIF_CUSTOM	8		// entry is userdefined (e.g. MyPhoneXX)
+#define CELIF_SMS		16		// phone with ability to receive sms
+#define LIF_TIPVISIBLE	32		// set if infotip is visible
+
+typedef struct TCECListItem : CTRL {
+	LPIDSTRLIST	idstrList;
+	INT			idstrListCount;
+	INT			iListItem;
+	LPTSTR		pszText[2];
+} LCITEM, *LPLCITEM;
+
+typedef struct TListCtrl : CTRL {
+	HWND		hList;
+	HWND		hTip;
+	POINT		ptTip;
+	INT			iHotItem;
+	INT			iHotSubItem;
+	HFONT		hFont;
+
+	struct {
+		HWND		hEdit;		// handle to edit window
+		HWND		hBtn;		// button to open dropdown list
+		RECT		rcCombo;
+		struct {
+			HWND	hDrop;		// dropdown list
+			INT		iItem;		// currently selected item of the dropdown
+		} dropDown; 
+		LPLCITEM	pItem;		// the item beiing edited
+		INT			iItem;		// zero based index to item in the listview
+		INT			iSubItem;	// zero based index to subitem
+		INT			iTopIndex;	// zero based index to first visible item on list
+	} labelEdit;
+} LISTCTRL, *LPLISTCTRL;
+
+typedef INT (*MISERVICE)(WPARAM wParam, LPARAM lParam);
+
+typedef struct TProfileEntries {
+	LPTSTR	szGroup;
+	LPCSTR	szCatFmt;
+	LPCSTR	szValFmt;
+	MIRANDASERVICE	GetList;
+} PROFILEENTRY, *LPPROFILEENTRY;
+
+static const PROFILEENTRY pFmt[3] = {
+	{ LPGENT("Past"),			 "Past%d",				 "Past%dText",				 (MIRANDASERVICE)GetPastList				 },
+	{ LPGENT("Affiliation"),"Affiliation%d",	"Affiliation%dText",	(MIRANDASERVICE)GetAffiliationsList },
+	{ LPGENT("Interest"),	 "Interest%dCat",	"Interest%dText",		 (MIRANDASERVICE)GetInterestsList		}
+};
+
+static	WNDPROC		OldListViewProc = NULL;	// listview control's default window procedure
+static	WNDPROC		OldEditProc = NULL;		// edit control's default window procedure
+static	WNDPROC		OldDropdownProc = NULL;	// listbox control's default window procedure
+
+static INT_PTR CALLBACK ProfileList_LabelEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+extern COLORREF clrBoth;
+extern COLORREF clrChanged;
+extern COLORREF clrCustom;
+extern COLORREF clrNormal;
+extern COLORREF clrMeta;
+
+
+/**
+ * name:	ProfileList_AddGroup
+ * desc:	add a group to the listview
+ * param:	hList	- handle to listview control
+ *			pszText	- text of new group
+ * return:	index the where group was added
+ **/
+static INT ProfileList_AddGroup(HWND hList, LPTSTR pszText, INT iItem)
+{
+	LVITEM lvi;
+	lvi.mask = LVIF_TEXT|LVIF_PARAM;
+	lvi.iItem = iItem;
+	lvi.iSubItem = 0;
+	lvi.pszText = pszText;
+	lvi.lParam = NULL;
+	return ListView_InsertItem(hList, &lvi);
+}
+
+/**
+ * name:	ProfileList_GetItemText
+ * desc:	returns the text of a listview item
+ * param:	hList	 - handle to listview control
+ *			iItem	 - item index
+ *			iSubItem - subitem (column) index
+ *			pszText	 - pointer of a buffer to retrieve the text
+ *			ccText	 - number of maximal characters pszText can take
+ * return:	number of read characters
+ **/
+static INT ProfileList_GetItemText(HWND hList, INT iItem, INT iSubItem, LPTSTR pszText, INT ccText)
+{
+	LVITEM lvi;
+	TCHAR szNull[2];
+
+	lvi.mask = LVIF_TEXT;
+	lvi.iItem = iItem;
+	lvi.iSubItem = iSubItem;
+	lvi.cchTextMax = ccText ? ccText : 2;
+	lvi.pszText = pszText ? pszText : szNull;
+	return SNDMSG(hList, LVM_GETITEMTEXT, iItem, (LPARAM)&lvi);
+}
+
+/**
+ * name:	ProfileList_GetItemData
+ * desc:	return the infostructure associated with the desired item
+ * param:	hList	- handle to listview control
+ *			iItem	- item index
+ * return:	LPLCITEM structure
+ **/
+static LPLCITEM ProfileList_GetItemData(HWND hList, INT iItem)
+{
+	LVITEM lvi;
+
+	if (iItem < 0) return NULL;
+	lvi.mask = LVIF_PARAM;
+	lvi.iSubItem = 0;
+	lvi.iItem = iItem;
+	return (SendMessage(hList, LVM_GETITEM, NULL, (LPARAM)&lvi)) ? (LPLCITEM)lvi.lParam : NULL;
+}
+
+/**
+ * name:	ProfileList_DeleteItem
+ * desc:	delete an item from the listview
+ * param:	hList	- handle to listview control
+ *			iItem	- item index
+ *	
+ * return:	nothing
+ **/
+static VOID ProfileList_DeleteItem(HWND hList, INT iItem)
+{
+	LPLCITEM pItem = ProfileList_GetItemData(hList, iItem);
+	
+	if (PtrIsValid(pItem)) {
+		if (pItem->pszText[0])
+			mir_free(pItem->pszText[0]);
+		if (pItem->pszText[1])
+			mir_free(pItem->pszText[1]);
+		mir_free(pItem);
+	}
+	ListView_DeleteItem(hList, iItem);
+}
+
+/**
+ * name:	ProfileList_Clear
+ * desc:	delete all list items and their data
+ *
+ **/
+static VOID ProfileList_Clear(HWND hList)
+{
+	LVITEM lvi;
+
+	lvi.iItem = 0;
+	lvi.iSubItem = 0;
+	lvi.mask = LVIF_PARAM;
+
+	while (ListView_GetItem(hList, &lvi)) {
+		if (PtrIsValid(lvi.lParam)) {
+			if (((LPLCITEM)lvi.lParam)->pszText[0])
+				mir_free(((LPLCITEM)lvi.lParam)->pszText[0]);
+			if (((LPLCITEM)lvi.lParam)->pszText[1])
+				mir_free(((LPLCITEM)lvi.lParam)->pszText[1]);
+			mir_free((LPVOID)lvi.lParam);
+		}
+		lvi.iItem++;
+	}
+	ListView_DeleteAllItems(hList);
+}
+
+/**
+ * name:	ProfileList_EndLabelEdit
+ * desc:	destroys the edit control and saves the text to the list control if desired
+ * param:	hList	- handle to listview control
+ *			bSave	- tells, whether to save changes (TRUE) or not (FALSE)
+ * return:	returns 0 on success or nonzero
+ **/
+static INT ProfileList_EndLabelEdit(LPLISTCTRL pList, BOOLEAN bSave)
+{
+	HWND hEdit;
+
+	// check if labeledit is enabled
+	if (!PtrIsValid(pList) || !pList->hList || !pList->labelEdit.hEdit)
+		return 1;
+	// set hEdit NULL to indicate the endlabeledit call and prevent other calls
+	hEdit = pList->labelEdit.hEdit;
+	pList->labelEdit.hEdit = NULL;
+
+	if (bSave != FALSE && pList->labelEdit.pItem) {
+		WORD ccText;
+		LPTSTR	szEdit = NULL;
+		BOOLEAN bChanged = FALSE;
+
+		// an list element was selected
+		if (pList->labelEdit.iSubItem && pList->labelEdit.dropDown.iItem != pList->labelEdit.pItem->iListItem && pList->labelEdit.dropDown.iItem >= 0 && pList->labelEdit.dropDown.iItem < pList->labelEdit.pItem->idstrListCount) {
+			pList->labelEdit.pItem->iListItem = pList->labelEdit.dropDown.iItem;
+			if (pList->labelEdit.pItem->pszText[0]) {
+				mir_free(pList->labelEdit.pItem->pszText[0]);
+				pList->labelEdit.pItem->pszText[0] = NULL;
+			}
+			bChanged = TRUE;
+		}
+		// value was edited
+		else {
+			if ((ccText = GetWindowTextLength(hEdit)) > 0 && (szEdit = (LPTSTR)mir_alloc((ccText + 2) * sizeof(TCHAR)))) {
+				GetWindowText(hEdit, szEdit, ccText + 1);
+				szEdit[ccText + 1] = 0;
+
+				if (!pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem]) {
+					pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem] = szEdit;
+					bChanged = TRUE;
+				}
+				else
+				if (_tcscmp(pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem], szEdit)) {
+					mir_free(pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem]);
+					pList->labelEdit.pItem->pszText[pList->labelEdit.iSubItem] = szEdit;
+					bChanged = TRUE;
+				}
+				else
+					mir_free(szEdit);
+			}
+		}
+		if (bChanged) {
+			pList->labelEdit.pItem->wFlags |= CTRLF_CHANGED;
+			pList->wFlags |= CTRLF_CHANGED;
+			SendMessage(GetParent(GetParent(pList->hList)), PSM_CHANGED, 0, 0);
+		}
+	}
+	if (pList->labelEdit.hBtn) DestroyWindow(pList->labelEdit.hBtn);
+	if (pList->labelEdit.dropDown.hDrop) DestroyWindow(pList->labelEdit.dropDown.hDrop);
+	DestroyWindow(hEdit);
+	ListView_RedrawItems(pList->hList, pList->labelEdit.iItem, pList->labelEdit.iItem);
+	ZeroMemory(&pList->labelEdit, sizeof(pList->labelEdit));
+	SetFocus(pList->hList);
+	return 0;
+}
+
+static INT ProfileList_EndLabelEdit(HWND hList, BOOLEAN bSave)
+{
+	return ProfileList_EndLabelEdit((LPLISTCTRL)GetUserData(hList), bSave);
+}
+
+/**
+ * name:	ProfileList_BeginLabelEdit
+ * desc:	create an edit control to edit the label of the selected item
+ * param:	pList	 - handle to listview control's info structure
+ *			iItem	 - item index
+ *			iSubItem - subitem (column) index
+ * return:	handle to the edit control
+ **/
+static HWND ProfileList_BeginLabelEdit(LPLISTCTRL pList, INT iItem, INT iSubItem)
+{
+	LVITEM lvi;
+	LPLCITEM pItem;
+	HANDLE hContact;
+	RECT rcList;
+	
+	if (!PtrIsValid(pList)) 
+		return NULL;
+	if (pList->labelEdit.hEdit)
+		ProfileList_EndLabelEdit(pList, FALSE);
+
+	lvi.mask = LVIF_PARAM|LVIF_STATE;
+	lvi.stateMask = 0xFFFFFFFF;
+	lvi.iItem = iItem;
+	lvi.iSubItem = iSubItem;
+
+	if (!ListView_GetItem(pList->hList, &lvi))
+		return NULL;
+
+	pItem = (LPLCITEM)lvi.lParam;
+
+	PSGetContact(GetParent(pList->hList), hContact);
+			
+	// do not edit deviders or protocol based contact information
+	if (!(lvi.state & LVIS_SELECTED) || !PtrIsValid(pItem) || (hContact && (pItem->wFlags & CTRLF_HASPROTO))) 
+		return NULL;
+
+	ListView_EnsureVisible(pList->hList, iItem, FALSE);
+	ListView_GetSubItemRect(pList->hList, iItem, iSubItem, LVIR_BOUNDS, &pList->labelEdit.rcCombo);
+		
+	if (lvi.iSubItem == 0) {
+		RECT rc2;
+		
+		ListView_GetSubItemRect(pList->hList, iItem, 1, LVIR_BOUNDS, &rc2);
+		pList->labelEdit.rcCombo.right = rc2.left;
+	}
+	GetClientRect(pList->hList, &rcList);
+	pList->labelEdit.rcCombo.right = min(pList->labelEdit.rcCombo.right, rcList.right);
+	pList->labelEdit.rcCombo.left = max(pList->labelEdit.rcCombo.left, rcList.left);
+	InflateRect(&pList->labelEdit.rcCombo, -1, -1);
+
+	// create the button control for the combobox
+	if (!iSubItem && pItem->idstrList) {
+		pList->labelEdit.hBtn = CreateWindowEx(WS_EX_NOPARENTNOTIFY, UINFOBUTTONCLASS, NULL, 
+					WS_VISIBLE|WS_CHILD|MBS_DOWNARROW,
+					pList->labelEdit.rcCombo.right - (pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top), pList->labelEdit.rcCombo.top,
+					pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top,
+					pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top,
+					pList->hList, NULL, ghInst, NULL);
+		if (pList->labelEdit.hBtn) {
+			SetWindowLongPtr(pList->labelEdit.hBtn, GWLP_ID, BTN_EDIT);
+			pList->labelEdit.rcCombo.right -= pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top;
+		}
+	}
+	else {
+		pList->labelEdit.rcCombo.bottom = 3 * pList->labelEdit.rcCombo.bottom - 2 * pList->labelEdit.rcCombo.top;
+		if (rcList.bottom < pList->labelEdit.rcCombo.bottom) {
+			OffsetRect(&pList->labelEdit.rcCombo, 0, rcList.bottom - pList->labelEdit.rcCombo.bottom - 2);
+		}
+	}
+	// create the edit control
+	pList->labelEdit.hEdit = CreateWindowEx(WS_EX_NOPARENTNOTIFY|WS_EX_CLIENTEDGE,
+				_T("EDIT"),
+				(!iSubItem && pItem->idstrList && pItem->iListItem > 0 && pItem->iListItem < pItem->idstrListCount) 
+					? pItem->idstrList[pItem->iListItem].ptszTranslated
+					: (iSubItem >= 0 && iSubItem < 2 && pItem->pszText[iSubItem] && *pItem->pszText[iSubItem])
+						? pItem->pszText[iSubItem] 
+						: _T(""), 
+						WS_VISIBLE|WS_CHILD|(iSubItem ? (WS_VSCROLL|ES_MULTILINE|ES_AUTOVSCROLL) : ES_AUTOHSCROLL),
+				pList->labelEdit.rcCombo.left, pList->labelEdit.rcCombo.top,
+				pList->labelEdit.rcCombo.right - pList->labelEdit.rcCombo.left,
+				pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top,
+				pList->hList, NULL, ghInst, NULL);
+	if (!pList->labelEdit.hEdit)
+		return NULL;
+	SendMessage(pList->labelEdit.hEdit, WM_SETFONT, (WPARAM)(pList->hFont), 0);
+	SendMessage(pList->labelEdit.hEdit, EM_SETSEL, 0, (LPARAM)-1);
+	SetUserData(pList->labelEdit.hEdit, pList);
+	pList->labelEdit.dropDown.iItem = pItem->iListItem;
+	pList->labelEdit.iItem = iItem;
+	pList->labelEdit.iSubItem = iSubItem;
+	pList->labelEdit.iTopIndex = ListView_GetTopIndex(pList->hList);
+	pList->labelEdit.pItem = pItem;
+	SetFocus(pList->labelEdit.hEdit);
+	OldEditProc = (WNDPROC)SetWindowLongPtr(pList->labelEdit.hEdit, GWLP_WNDPROC, (LONG_PTR)ProfileList_LabelEditProc);
+	return pList->labelEdit.hEdit;
+}
+
+/**
+ * name:	ProfileList_BeginLabelEdit
+ * desc:	create an edit control to edit the label of the selected item
+ * param:	hList	 - handle to listview control
+ *			iItem	 - item index
+ *			iSubItem - subitem (column) index
+ * return:	handle to the edit control
+ **/
+static HWND ProfileList_BeginLabelEdit(HWND hList, INT iItem, INT iSubItem)
+{
+	return ProfileList_BeginLabelEdit((LPLISTCTRL)GetUserData(hList), iItem, iSubItem);
+}
+
+/**
+ * name:	ProfileList_GetInsertIndex
+ * desc:	finds index to add the new item to and adds an devider if necessary
+ * param:	hList	- handle to listcontrol to search for the index in
+ *			pszList	- database settings string, that identifies this category
+ * 
+ * return:	zero based index for the new item or -1 on failure
+ **/
+static INT ProfileList_GetInsertIndex(HWND hList, LPTSTR pszGroup)
+{
+	LVFINDINFO	lfi;
+	INT			iDevider, 
+				iItem;
+	
+	// search for the devider to add the new item under
+	lfi.flags = LVFI_STRING;
+	lfi.psz = pszGroup;
+	iDevider = SendMessage(hList, LVM_FINDITEM, (WPARAM)-1, (LPARAM)&lfi);
+	
+	// devider does not exist yet, add it!
+	if (iDevider == -1) {
+		LVITEM	lvi;
+
+		lvi.mask = LVIF_PARAM|LVIF_TEXT;
+		lvi.iSubItem = 0;
+		lvi.iItem = 0xFFFF;
+		lvi.lParam = NULL;
+		lvi.pszText = (LPTSTR)lfi.psz;
+		if ((iItem = ListView_InsertItem(hList, &lvi)) == -1) {
+			return -1;
+		}
+		iItem++;
+	}
+	else {
+		// search next devider to add new item just before
+		lfi.flags = LVFI_PARAM;
+		lfi.lParam = NULL;
+		if ((iItem = ListView_FindItem(hList, iDevider, &lfi)) == -1)
+			iItem = 0xFFFF;
+	}
+	return iItem;
+}
+
+/**
+ * name:	ProfileList_AddNewItem
+ * desc:	Ask's user for a type and adds new item to the list view
+ * param:	pList	- pointer to the listview's data structure
+ *			pszList	- database settings string, that identifies this category
+ *
+ * return:	TRUE or FALSE
+ **/
+static BOOLEAN ProfileList_AddNewItem(HWND hDlg, LPLISTCTRL pList, const PROFILEENTRY *pEntry)
+{
+	LPLCITEM pItem;
+	LVITEM lvi;
+	HANDLE hContact;
+
+	if (PtrIsValid(pList) && (pItem = (LPLCITEM)mir_alloc(sizeof(LCITEM)))) {
+		PSGetContact(hDlg, hContact);
+		pItem->nType = CTRL_LIST_ITEM;
+		pItem->wFlags = hContact ? CTRLF_HASCUSTOM : 0;
+		pItem->iListItem = 0;
+		pItem->pszText[0] = NULL;
+		pItem->pszText[1] = NULL;
+		// get category list
+		pEntry->GetList((WPARAM)&pItem->idstrListCount, (LPARAM)&pItem->idstrList);
+
+		lvi.mask = LVIF_PARAM|LVIF_STATE;
+		lvi.stateMask = 0xFFFFFFFF;
+		lvi.state = LVIS_FOCUSED|LVIS_SELECTED;
+		lvi.iItem = ProfileList_GetInsertIndex(pList->hList, pEntry->szGroup);
+		lvi.iSubItem = 0;
+		lvi.lParam = (LPARAM)pItem;
+		if ((lvi.iItem = ListView_InsertItem(pList->hList, &lvi)) >= 0) {
+			ProfileList_BeginLabelEdit(pList, lvi.iItem, 0);
+			return TRUE;
+		}
+		mir_free(pItem);
+		MsgErr(hDlg, LPGENT("Sorry, but there is a problem with adding a new item of type \"%s\""), pEntry->szGroup);
+	}	
+	return FALSE;
+}
+
+/**
+ * name:	ProfileList_AddItemlistFromDB
+ * desc:	reads an zero based indexed szList from the database of hContacts pszModule and fills a hList
+ * param:	hList		- HANDLE to the list to fill with two columns
+ *			iItem		- index of new listviewitem
+ *			iImage		- image to draw from a imagelist associated with the listview
+ *			hContact	- handle to the contact, whose information are read
+ *			pszModule	- the module the information are stored in
+ *			szCatFormat	- name of a database setting that holds the categorytext
+ *			szValFormat - name of a database setting that holds the valuetext
+ *			wFlags		- flags to set for a new allocated item
+ *
+ * return	number of added rows or -1 if listview's changed flag is set
+ **/
+static INT ProfileList_AddItemlistFromDB(
+				LPLISTCTRL pList,
+				INT &iItem,
+				LPIDSTRLIST idList,
+				UINT nList,
+				HANDLE hContact,
+				LPCSTR pszModule,
+				LPCSTR szCatFormat,
+				LPCSTR szValFormat,
+				WORD wFlags)
+{
+	DBVARIANT	dbvVal, dbvCat;
+	LPLCITEM	pItem;
+	LVITEM		lvi;
+	UINT		i, j = 0;
+	CHAR		pszSetting[MAXSETTING];
+
+	lvi.iSubItem = 0;
+	lvi.mask = LVIF_PARAM;
+
+	for (i = 0, lvi.iItem = iItem; ; i++) {
+		// read the setting from db
+		mir_snprintf(pszSetting, MAXSETTING, szValFormat, i);
+		if (DB::Setting::GetTString(hContact, pszModule, pszSetting, &dbvVal)) break;
+		if (dbvVal.type != DBVT_TCHAR) continue;
+		mir_snprintf(pszSetting, MAXSETTING, szCatFormat, i);
+		DB::Setting::GetAString(hContact, pszModule, pszSetting, &dbvCat);
+		// create the itemobject
+		if (!(pItem = (LPLCITEM)mir_alloc(sizeof(LCITEM)))) {
+			DB::Variant::Free(&dbvCat);
+			DB::Variant::Free(&dbvVal);
+			break;
+		}
+		// fill item struct
+		pItem->nType = CTRL_LIST_ITEM;
+		pItem->idstrList = idList;
+		pItem->idstrListCount = nList;
+		pItem->iListItem = 0;
+		pItem->pszText[0] = NULL;
+		pItem->pszText[1] = dbvVal.ptszVal;
+		pItem->wFlags = wFlags;
+		lvi.lParam = (LPARAM)pItem;
+		
+		// get id-str-list-item for the category string
+		if (idList != NULL) {
+			for (j = 0; j < nList; j++) {
+				switch (dbvCat.type) {
+					case DBVT_BYTE:
+						if (dbvCat.bVal != (BYTE)idList[j].nID)
+							continue;
+						break;
+					case DBVT_WORD:
+						if (dbvCat.wVal != (WORD)idList[j].nID)
+							continue;
+						break;
+					case DBVT_DWORD:
+						if (dbvCat.dVal != (DWORD)idList[j].nID)
+							continue;
+						break;
+					case DBVT_ASCIIZ:			
+						if (strcmp(dbvCat.pszVal, idList[j].pszText)) 
+							continue;
+						break;
+				}
+				pItem->iListItem = j;
+				break;
+			}
+		}
+		// item not found in the predefined category list?
+		if ((idList == NULL || j == nList) && dbvCat.type == DBVT_ASCIIZ) {
+			pItem->pszText[0] = mir_a2t(dbvCat.pszVal);
+			DB::Variant::Free(&dbvCat);
+		}
+		if ((lvi.iItem = ListView_InsertItem(pList->hList, &lvi)) < 0) {
+			mir_free(pItem);
+			DB::Variant::Free(&dbvCat);
+			DB::Variant::Free(&dbvVal);
+			break;
+		}
+		lvi.iItem++;
+		dbvCat.type = dbvVal.type = DBVT_DELETED;
+	}
+	iItem = lvi.iItem;
+	return i;
+}
+
+/**
+ * name:	ProfileList_DropdownProc
+ * desc:	procedure to catch messages for a listbox control for my own combobox
+ * param:	hwnd	- handle to the listview control's window
+ *			msg		- message sent to the control
+ *			wParam	- message specific parameter
+ *			lParam	- message specific parameter
+ * return:	message specific
+ **/
+static INT_PTR CALLBACK ProfileList_DropdownProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
+{
+	LPLISTCTRL pList;
+
+	switch (msg) {
+		case WM_KEYDOWN:
+			switch (wParam) {
+				case VK_ESCAPE:
+					SetFocus(GetParent(hwnd));
+					return 0;
+
+				case VK_RETURN:
+				case VK_F4:
+				{
+					LPIDSTRLIST pItem;
+
+					if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) return CB_ERR;
+					pList->labelEdit.dropDown.iItem = ListBox_GetCurSel(hwnd);
+					if (pList->labelEdit.dropDown.iItem >= 0 && PtrIsValid(pItem = (LPIDSTRLIST)ListBox_GetItemData(hwnd, pList->labelEdit.dropDown.iItem)))
+						SetWindowText(pList->labelEdit.hEdit, pItem->ptszTranslated);
+					else
+						pList->labelEdit.dropDown.iItem = -1;
+					SetFocus(pList->labelEdit.hEdit);
+					return 0;
+				}
+			}
+			break;
+		case WM_LBUTTONUP:
+		{
+			POINT pt;
+			LPIDSTRLIST pItem;
+
+			if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) return CB_ERR;
+			CallWindowProc(OldDropdownProc, hwnd, msg, wParam, lParam);
+
+						pt.x = (short)LOWORD(lParam);
+						pt.y = (short)HIWORD(lParam);
+						ClientToScreen(hwnd, &pt);
+
+			if (SendMessage(hwnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)) == HTVSCROLL) 
+				return CB_ERR;
+						
+			pList->labelEdit.dropDown.iItem = SendMessage(hwnd, LB_GETCURSEL, 0, 0);
+			
+			if (pList->labelEdit.dropDown.iItem >= 0 && PtrIsValid(pItem = (LPIDSTRLIST)ListBox_GetItemData(hwnd, pList->labelEdit.dropDown.iItem))) {	
+				SetWindowText(pList->labelEdit.hEdit, pItem->ptszTranslated);
+			}
+			else
+				pList->labelEdit.dropDown.iItem = -1;
+
+			ProfileList_EndLabelEdit(pList->hList, TRUE);
+			return 0;
+		}
+		case WM_KILLFOCUS:
+			if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+				if (GetFocus() == pList->labelEdit.hEdit) {
+					ShowWindow(hwnd, SW_HIDE);
+					return 0;
+				}
+				ProfileList_EndLabelEdit(pList, FALSE);
+			}
+			return 0;
+	}
+	return CallWindowProc(OldDropdownProc, hwnd, msg, wParam, lParam);
+}
+
+/**
+ * name:	ProfileList_LabelEditProc
+ * desc:	procedure to catch messages for an edit control for my own combobox
+ * param:	hwnd	- handle to the listview control's window
+ *			msg		- message sent to the control
+ *			wParam	- message specific parameter
+ *			lParam	- message specific parameter
+ * return:	message specific
+ **/
+static INT_PTR CALLBACK ProfileList_LabelEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	LPLISTCTRL pList;
+
+	switch (msg) {
+		case WM_KEYDOWN:
+			switch (wParam) {
+				case VK_ESCAPE:
+					if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)))
+						ProfileList_EndLabelEdit(pList, FALSE);
+					return 0;
+				case VK_F3:
+					if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)) && pList->labelEdit.hBtn)
+						SendMessage(pList->hList, WM_COMMAND, MAKEWPARAM(BTN_EDIT, BN_CLICKED), (LPARAM)pList->labelEdit.hBtn);
+					return 0;
+				case VK_RETURN:
+				{
+					BOOLEAN bEditNext;
+					INT iItem;
+		
+					if (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_WANTRETURN && !(GetKeyState(VK_CONTROL) & 0x8000))
+						break;
+					if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+						bEditNext = !pList->labelEdit.iSubItem && !ProfileList_GetItemText(pList->hList, pList->labelEdit.iItem, 1, NULL, NULL);
+						iItem = pList->labelEdit.iItem;
+						ProfileList_EndLabelEdit(pList->hList, TRUE);
+						if (bEditNext) ProfileList_BeginLabelEdit(pList->hList, pList->labelEdit.iItem, 1);
+					}
+					return 0;
+				}
+				case VK_TAB:
+					if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+						LVITEM lvi;
+
+						lvi.mask = LVIF_STATE;
+						lvi.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
+						lvi.iItem = pList->labelEdit.iItem;
+
+						if (wParam == VK_TAB && !pList->labelEdit.iSubItem) {
+							lvi.iSubItem = 1;
+							lvi.state = LVIS_FOCUSED|LVIS_SELECTED;
+							ProfileList_EndLabelEdit(pList->hList, TRUE);
+						}
+						else {
+							UINT iSubItem = (wParam == VK_TAB) ? 0 : pList->labelEdit.iSubItem;
+
+							lvi.iSubItem = 0;
+							lvi.state = 0;
+
+							ProfileList_EndLabelEdit(pList, TRUE);
+
+							// unselect current list item
+							if (!ListView_SetItem(pList->hList, &lvi))
+								return 0;
+
+							// search for next valid list item (skip deviders)
+							lvi.iSubItem = iSubItem;
+							lvi.mask = LVIF_PARAM;
+							do {
+								if (wParam == VK_UP) 
+									lvi.iItem--;
+								else
+									lvi.iItem++;
+
+								if (lvi.iItem == -1 || !ListView_GetItem(pList->hList, &lvi)) {
+									return 0;
+								}
+							} while (!lvi.lParam);
+
+							// select new list item
+							lvi.mask = LVIF_STATE;
+							lvi.state = LVIS_FOCUSED|LVIS_SELECTED;
+							if (!ListView_SetItem(pList->hList, &lvi)) {
+								return 0;
+							}
+						}
+						ProfileList_BeginLabelEdit(pList->hList, lvi.iItem, lvi.iSubItem);
+						return 0;
+					}
+					return 1;
+			}
+			break;
+		case WM_GETDLGCODE:
+			return DLGC_WANTALLKEYS | CallWindowProc(OldEditProc, hwnd, msg, wParam, lParam);
+		case WM_KILLFOCUS:
+		{
+			HWND hwndFocus = GetFocus();
+
+			if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)) && 
+				hwndFocus != pList->labelEdit.dropDown.hDrop && 
+				hwndFocus != pList->labelEdit.hEdit &&
+				hwndFocus != pList->labelEdit.hBtn)
+				ProfileList_EndLabelEdit(pList, hwndFocus == pList->hList);
+			return 0;
+		}
+	}
+	return CallWindowProc(OldEditProc, hwnd, msg, wParam, lParam);
+}
+
+/**
+ * name:	ListSubclassProc
+ * desc:	procedure to catch messages for a listview control, to handle tooltips
+ * param:	hwnd	- handle to the listview control's window
+ *			msg		- message sent to the control
+ *			wParam	- message specific parameter
+ *			lParam	- message specific parameter
+ * return:	message specific
+ **/
+static INT_PTR CALLBACK ProfileList_SubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
+{
+	LPLISTCTRL pList;
+
+	switch (msg) {
+		case WM_KEYDOWN:
+		{
+			INT nCurSel, newSel;
+			LVITEM lvi;
+
+			switch (wParam) {
+				case VK_F2:
+					nCurSel = ListView_GetSelectionMark(hwnd);
+					if (nCurSel == -1) break;
+					ProfileList_BeginLabelEdit(hwnd, nCurSel, 0);
+					return 0;
+				case VK_F3:
+					nCurSel = ListView_GetSelectionMark(hwnd);
+					if (nCurSel == -1) break;
+					ProfileList_BeginLabelEdit(hwnd, nCurSel, 1);
+					return 0;
+				case VK_UP:
+				case VK_DOWN:
+					lvi.iItem = nCurSel = ListView_GetSelectionMark(hwnd);
+					lvi.iSubItem = 0;
+
+					// find next valid item to select
+					lvi.mask = LVIF_PARAM;
+					do {
+						if (wParam == VK_UP) lvi.iItem--;
+						else lvi.iItem++;
+						if (lvi.iItem == -1 || !ListView_GetItem(hwnd, &lvi)) {
+							return 0;
+						}
+					} while (!lvi.lParam);
+
+					ListView_EnsureVisible(hwnd, lvi.iItem, FALSE);
+					newSel = lvi.iItem;
+					lvi.iItem = nCurSel;
+					lvi.mask = LVIF_STATE;
+					lvi.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
+					lvi.state = 0;
+					ListView_SetItem(hwnd, &lvi);
+					lvi.iItem = newSel;
+					lvi.state = LVIS_FOCUSED|LVIS_SELECTED;
+					ListView_SetItem(hwnd, &lvi);
+					ListView_SetSelectionMark(hwnd, lvi.iItem);
+					return 0;
+			}
+			break;
+		}
+		case WM_MOUSEMOVE:
+			if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+				HDC hDC;
+				RECT rchWnd, rcItem;
+				SIZE textSize;
+				LVHITTESTINFO hi;
+				TOOLINFO ti;
+				BOOLEAN bReposition;
+				LPLCITEM pItem;
+							
+				hi.pt.x = GET_X_LPARAM(lParam);
+				hi.pt.y = GET_Y_LPARAM(lParam);
+				ListView_SubItemHitTest(hwnd, &hi);
+
+				// show tip only if pointer is over an item
+				if (pList->iHotItem != hi.iItem || pList->iHotSubItem != hi.iSubItem) {
+					bReposition = pList->iHotItem != -1 || pList->iHotSubItem != -1;
+					pList->iHotItem = hi.iItem;
+					pList->iHotSubItem = hi.iSubItem;
+					
+					if ((hi.flags & LVHT_ONITEMLABEL) && PtrIsValid(pItem = ProfileList_GetItemData(hwnd, hi.iItem))) {
+						GetWindowRect(hwnd, &rchWnd);
+						ListView_GetSubItemRect(hwnd, hi.iItem, hi.iSubItem, LVIR_BOUNDS, &rcItem);
+						// calculate size of text on the screen
+						if ((hDC = GetDC(GetParent(hwnd)))) {
+							SelectObject(hDC, (HFONT)SendMessage(GetParent(hwnd), WM_GETFONT, NULL, NULL));
+							GetTextExtentPoint32(hDC, pItem->pszText[hi.iSubItem], lstrlen(pItem->pszText[hi.iSubItem]), &textSize);
+							ReleaseDC(GetParent(hwnd), hDC);
+						}
+						// show tip only for text that is larger than te listview can display
+						if (textSize.cx > rchWnd.right - rchWnd.left || textSize.cx > rcItem.right - rcItem.left) {
+							ZeroMemory(&ti, sizeof(TOOLINFO));
+							ti.cbSize = sizeof(TOOLINFO);
+							ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS|TTF_TRANSPARENT;
+							ti.hinst = ghInst;
+							ti.hwnd = hwnd;
+							ti.uId = (UINT_PTR)hwnd;
+							ti.lpszText = pItem->pszText[hi.iSubItem];
+							ti.rect = rcItem;
+							SendMessage(pList->hTip, TTM_SETMAXTIPWIDTH, 0, 300);
+							SendMessage(pList->hTip, TTM_SETTOOLINFO, NULL, (LPARAM)&ti);
+							if (pList->iHotSubItem > 0) {
+								SendMessage(pList->hTip, TTM_SETTITLE, 1, (LPARAM)
+									((pItem->idstrList && pItem->iListItem > 0 && pItem->iListItem < pItem->idstrListCount)
+									? pItem->idstrList[pItem->iListItem].ptszTranslated
+										: (pItem->pszText[0] && *pItem->pszText[0])
+											? pItem->pszText[0]
+											: TranslateT("<empty>"))
+							);
+								InvalidateRect(pList->hTip, NULL, TRUE);
+							}
+							else
+								SendMessage(pList->hTip, TTM_SETTITLE, 0, (LPARAM)"");
+							SendMessage(pList->hTip, TTM_ACTIVATE, TRUE, (LPARAM)&ti);
+							pList->ptTip.x = rchWnd.left + GET_X_LPARAM(lParam) - 16;
+							pList->ptTip.y = rchWnd.top + rcItem.top;
+							// no TTN_SHOW is called if bReposition is TRUE, so repose here!
+							if (bReposition) {
+								RECT rcTip;
+								GetClientRect(pList->hTip, &rcTip);
+								SetWindowPos(pList->hTip, hwnd, pList->ptTip.x, pList->ptTip.y - rcTip.bottom, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
+							}
+							pList->wFlags |= LIF_TIPVISIBLE;
+							return 0;
+						}
+					}
+					if (pList->wFlags & LIF_TIPVISIBLE) {
+						SendMessage(pList->hTip, TTM_ACTIVATE, FALSE, (LPARAM)&ti);
+						pList->wFlags &= ~LIF_TIPVISIBLE;
+					}
+				}
+			}
+			return 0;
+
+			// begin label edit
+			case WM_LBUTTONDBLCLK:
+			{
+				LVHITTESTINFO hi;
+
+				hi.pt.x = GET_X_LPARAM(lParam);
+				hi.pt.y = GET_Y_LPARAM(lParam);
+				if (ListView_SubItemHitTest(hwnd, &hi)) {
+					ProfileList_BeginLabelEdit(hwnd, hi.iItem, hi.iSubItem);
+				}
+				return TRUE;
+			}
+			
+		case WM_NOTIFY:
+			if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)))
+				break;
+		
+			// ensure position of tooltip is on the topline of the item
+			if (((LPNMHDR)lParam)->hwndFrom == pList->hTip) {
+				RECT rcTip;
+				GetClientRect(pList->hTip, &rcTip);
+				
+				switch (((LPNMHDR)lParam)->code) {
+					case TTN_SHOW:
+						SetWindowPos(pList->hTip, hwnd, pList->ptTip.x, pList->ptTip.y - rcTip.bottom, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
+						return TRUE;
+				}
+			}
+			break;
+		case WM_COMMAND:
+		{
+			switch (LOWORD(wParam)) {
+
+				// show dropdown menu for category list
+				case BTN_EDIT:
+				{
+					INT i;
+					TCHAR szEdit[MAX_PATH];
+
+					if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) break;
+					GetWindowText(pList->labelEdit.hEdit, szEdit, MAX_PATH);
+
+					// need to create the dropdown list?
+					if (pList->labelEdit.dropDown.hDrop == NULL) {
+						const INT listHeight = 120;
+						RECT rc, rcList;
+						INT add;
+
+						// dropdown rect
+						GetClientRect(pList->hList, &rcList);
+						rc.left = pList->labelEdit.rcCombo.left;
+						rc.right = pList->labelEdit.rcCombo.right + pList->labelEdit.rcCombo.bottom - pList->labelEdit.rcCombo.top; 
+						
+						if (rcList.bottom < pList->labelEdit.rcCombo.bottom + listHeight) {
+							rc.bottom = pList->labelEdit.rcCombo.bottom - 7; // don't ask me why!
+							rc.top = rc.bottom - listHeight;
+						}
+						else {
+							rc.top = pList->labelEdit.rcCombo.bottom;
+							rc.bottom = rc.top + listHeight;
+						}
+
+						pList->labelEdit.dropDown.hDrop = CreateWindowEx(0,
+									_T("LISTBOX"), NULL, WS_CHILD|WS_BORDER|WS_VSCROLL|LBS_COMBOBOX|LBS_HASSTRINGS,
+									rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
+									hwnd, NULL, ghInst, NULL);
+						if (!pList->labelEdit.dropDown.hDrop) return FALSE;
+						SetUserData(pList->labelEdit.dropDown.hDrop, pList);
+						OldDropdownProc = (WNDPROC)SetWindowLongPtr(pList->labelEdit.dropDown.hDrop, GWLP_WNDPROC, (LONG_PTR)ProfileList_DropdownProc);
+						SetWindowLongPtr(pList->labelEdit.dropDown.hDrop, GWLP_ID, LIST_DROPDOWN);
+						SendMessage(pList->labelEdit.dropDown.hDrop, WM_SETFONT, (WPARAM)SendMessage(GetParent(pList->hList), WM_GETFONT, 0, 0), 0);
+						
+						// add items
+						for (i = 0; i < pList->labelEdit.pItem->idstrListCount; i++) {
+							add = ListBox_AddString(pList->labelEdit.dropDown.hDrop, pList->labelEdit.pItem->idstrList[i].ptszTranslated);
+							ListBox_SetItemData(pList->labelEdit.dropDown.hDrop, add, pList->labelEdit.pItem->idstrList + i);
+							if (!_tcscmp(szEdit, pList->labelEdit.pItem->idstrList[i].ptszTranslated))
+								ListBox_SetCurSel(pList->labelEdit.dropDown.hDrop, add);
+						}
+					}
+					else {
+						LPIDSTRLIST lpidList;
+
+						i = 0;
+						while (PtrIsValid(lpidList = (LPIDSTRLIST)ListBox_GetItemData(pList->labelEdit.dropDown.hDrop, i))) {
+							if (!_tcscmp(szEdit, lpidList->ptszTranslated)) {
+								ListBox_SetCurSel(pList->labelEdit.dropDown.hDrop, i);
+								break;
+							}
+							i++;
+						}
+						if (i == pList->labelEdit.pItem->idstrListCount) 
+							ListBox_SetCurSel(pList->labelEdit.dropDown.hDrop, -1);
+					}
+					if (IsWindowVisible(pList->labelEdit.dropDown.hDrop)) {
+						SetFocus(pList->labelEdit.hEdit);
+					}
+					else {
+						ShowWindow(pList->labelEdit.dropDown.hDrop, SW_SHOW);
+						//SetFocus(pList->labelEdit.dropDown.hDrop);
+					}
+					break;
+				}
+			}
+			break;
+		}
+		case WM_MOUSEWHEEL:
+		case WM_VSCROLL:
+		case WM_HSCROLL:
+		{
+			if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)))
+				ProfileList_EndLabelEdit(pList, FALSE);
+			break;
+		}
+
+		case WM_KILLFOCUS:
+		{
+			HWND hwndFocus = GetFocus();
+
+			if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd)) &&
+				pList->labelEdit.hEdit != hwndFocus && 
+				pList->labelEdit.dropDown.hDrop != hwndFocus && 
+				pList->labelEdit.hBtn != hwndFocus)
+				ProfileList_EndLabelEdit(pList, FALSE);
+			break;
+		}
+
+		case WM_DESTROY:
+			if (PtrIsValid(pList = (LPLISTCTRL)GetUserData(hwnd))) {
+				HFONT hFont;
+
+				ProfileList_EndLabelEdit(pList, FALSE);
+				ProfileList_Clear(hwnd);
+				if (PtrIsValid(hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0)) && hFont != pList->hFont)
+					DeleteObject(hFont);
+				DestroyWindow(pList->hTip);
+				mir_free(pList);
+			}
+			break;
+	}
+	return CallWindowProc(OldListViewProc, hwnd, msg, wParam, lParam);
+}
+
+/**
+ * name:	DlgProcPspAbout()
+ * desc:	dialog procedure
+ *
+ * return:	0 or 1
+ **/
+INT_PTR CALLBACK PSPProcContactProfile(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	HWND hList = GetDlgItem(hDlg, LIST_PROFILE);
+	LPLISTCTRL pList;
+
+	switch (uMsg) {
+		case WM_INITDIALOG:
+		{
+			LVCOLUMN lvc;
+			RECT rc;
+			LOGFONT lf;
+			HFONT hFont;
+			TOOLINFO ti;
+
+			if (!hList || !(pList = (LPLISTCTRL)mir_alloc(sizeof(LISTCTRL)))) 
+				return FALSE;
+			ZeroMemory(pList, sizeof(LISTCTRL));
+
+			TranslateDialogDefault(hDlg);
+			Ctrl_InitTextColours();
+
+			// init info structure
+			pList->hList = hList;
+			pList->nType = CTRL_LIST_PROFILE;
+			ZeroMemory(&pList->labelEdit, sizeof(pList->labelEdit));
+			SetUserData(hList, pList);
+
+			// set new window procedure
+			OldListViewProc = (WNDPROC)SetWindowLongPtr(hList, GWLP_WNDPROC, (LONG_PTR)&ProfileList_SubclassProc);
+			
+			// remove static edge in aero mode
+			if (IsAeroMode())
+				SetWindowLongPtr(hList, GWL_EXSTYLE, GetWindowLongPtr(hList, GWL_EXSTYLE)&~WS_EX_STATICEDGE);
+
+			// insert columns into the listboxes					
+			ListView_SetExtendedListViewStyle(hList, LVS_EX_FULLROWSELECT);
+
+
+			PSGetBoldFont(hDlg, hFont);
+			SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)hFont, 0);
+
+			// set listfont
+			pList->hFont = (HFONT)SendMessage(hList, WM_GETFONT, 0, 0);
+			pList->wFlags |= LVF_EDITLABEL;
+			GetObject(pList->hFont, sizeof(lf), &lf);
+			lf.lfHeight -= 6;
+			hFont = CreateFontIndirect(&lf);
+			SendMessage(hList, WM_SETFONT, (WPARAM)hFont, 0);
+
+			GetClientRect(hList, &rc);
+			rc.right -= GetSystemMetrics(SM_CXVSCROLL);
+
+			// initiate the tooltips
+			pList->hTip = CreateWindowEx(WS_EX_TOPMOST,	TOOLTIPS_CLASS, NULL,
+				WS_POPUP|TTS_BALLOON|TTS_NOPREFIX|TTS_ALWAYSTIP,
+				CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+				hList, NULL, ghInst, NULL);
+			if (pList->hTip) {
+				SetWindowPos(pList->hTip, HWND_TOPMOST,
+					CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+					SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+				ZeroMemory(&ti, sizeof(TOOLINFO));
+				ti.cbSize = sizeof(TOOLINFO);
+				ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS|TTF_TRANSPARENT;
+				ti.hinst = ghInst;
+				ti.hwnd = hList;
+				ti.uId = (UINT_PTR)hList;
+				SendMessage(pList->hTip, TTM_ADDTOOL, NULL, (LPARAM)&ti);
+				SendMessage(pList->hTip, TTM_ACTIVATE, FALSE, (LPARAM)&ti);
+			}		
+
+			// insert columns into the listboxes					
+			lvc.mask = LVCF_WIDTH;
+			lvc.cx = rc.right / 8 * 3;
+			ListView_InsertColumn(hList, 0, &lvc);
+			lvc.cx = rc.right / 8 * 5;
+			ListView_InsertColumn(hList, 1, &lvc);
+			return TRUE;
+		}
+
+		case WM_CTLCOLORSTATIC:
+		case WM_CTLCOLORDLG:
+			if (IsAeroMode())
+				return (INT_PTR)GetStockBrush(WHITE_BRUSH);
+			break;
+
+		case WM_NOTIFY:
+			switch (((LPNMHDR)lParam)->idFrom) {
+				case 0:
+				{
+					HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+					LPCSTR pszProto;
+					
+					if (!PtrIsValid(pList = (LPLISTCTRL)GetUserData(hList))) break;
+
+					switch (((LPNMHDR)lParam)->code) {
+						// some account data may have changed so reread database
+						case PSN_INFOCHANGED:
+						{
+							BYTE msgResult = 0;
+							LPIDSTRLIST idList;
+							UINT nList;
+							BYTE i;
+							INT iItem = 0,
+								iGrp = 0,
+								numProtoItems,
+								numUserItems;
+
+							if (!(pList->wFlags & CTRLF_CHANGED) && PSGetBaseProto(hDlg, pszProto) && *pszProto != 0) {
+								ProfileList_Clear(hList);
+
+								// insert the past information
+								for (i = 0; i < 3; i++) {
+									pFmt[i].GetList((WPARAM)&nList, (LPARAM)&idList);
+									if ((numProtoItems = ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hContact, pszProto, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASPROTO)) < 0)
+										return FALSE;
+
+									// scan all basic protocols for the subcontacts
+									if (DB::Module::IsMetaAndScan(pszProto)) {
+										INT iDefault = CallService(MS_MC_GETDEFAULTCONTACTNUM, (WPARAM)hContact, NULL);
+										HANDLE hSubContact, hDefContact;
+										LPCSTR pszSubBaseProto;
+										INT j, numSubs;
+										
+										if ((hDefContact = (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hContact, iDefault)) &&
+											 (pszSubBaseProto = DB::Contact::Proto(hDefContact)))
+										{
+											if ((numProtoItems += ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hDefContact, pszSubBaseProto, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASMETA|CTRLF_HASPROTO)) < 0)
+												return FALSE;
+
+											// copy the missing settings from the other subcontacts
+											numSubs = CallService(MS_MC_GETNUMCONTACTS, (WPARAM)hContact, NULL);
+											for (j = 0; j < numSubs; j++) {
+												if (j == iDefault) continue;
+												if (!(hSubContact = (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hContact, j))) continue;
+												if (!(pszSubBaseProto = DB::Contact::Proto(hSubContact))) continue;
+												if ((numProtoItems += ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hSubContact, pszSubBaseProto, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASMETA|CTRLF_HASPROTO)) < 0)
+													return FALSE;
+												//if ((numUserItems += ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hSubContact, USERINFO, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASMETA|CTRLF_HASPROTO)) < 0)
+												//	return FALSE;
+											}
+										}
+									}
+									if ((numUserItems = ProfileList_AddItemlistFromDB(pList, iItem, idList, nList, hContact, USERINFO, pFmt[i].szCatFmt, pFmt[i].szValFmt, CTRLF_HASCUSTOM)) < 0)
+										return FALSE;
+									if (numUserItems || numProtoItems) {
+										msgResult = PSP_CHANGED;
+										ProfileList_AddGroup(hList, pFmt[i].szGroup, iGrp);
+										iGrp = ++iItem;
+									}
+								}
+							}
+							SetWindowLongPtr(hDlg, DWLP_MSGRESULT, msgResult);
+							break;
+						}
+						// user swiches to another propertysheetpage
+						case PSN_KILLACTIVE:
+							ProfileList_EndLabelEdit(hList, TRUE);
+							break;
+						// user selected to apply settings to the database
+						case PSN_APPLY:
+							if (pList->wFlags & CTRLF_CHANGED) {
+								BYTE iFmt = -1;
+								INT iItem;
+								LVITEM lvi;
+								TCHAR szGroup[MAX_PATH];
+								CHAR pszSetting[MAXSETTING];
+								LPLCITEM pItem;
+								LPSTR pszModule = USERINFO;
+
+								if (!hContact) PSGetBaseProto(hDlg, pszModule);
+
+								*szGroup = 0;
+								lvi.mask = LVIF_TEXT|LVIF_PARAM;
+								lvi.pszText = szGroup;
+								lvi.cchTextMax = MAX_PATH;
+
+								for (iItem = lvi.iItem = lvi.iSubItem = 0; ListView_GetItem(hList, &lvi); lvi.iItem++) {
+									if (!PtrIsValid(pItem = (LPLCITEM)lvi.lParam)) {
+										// delete reluctant items
+										if (iFmt >= 0 && iFmt < SIZEOF(pFmt)) {
+											DB::Setting::DeleteArray(hContact, pszModule, pFmt[iFmt].szCatFmt, iItem);
+											DB::Setting::DeleteArray(hContact, pszModule, pFmt[iFmt].szValFmt, iItem);
+										}
+										// find information about the group
+										for (iFmt = 0; iFmt < SIZEOF(pFmt); iFmt++) {
+											if (!_tcscmp(szGroup, pFmt[iFmt].szGroup)) {
+												break;
+											}
+										}
+										// indicate, no group was found. should not happen!!
+										if (iFmt == SIZEOF(pFmt)) {
+											*szGroup = 0;
+											iFmt = -1;
+										}
+										iItem = 0;
+									}
+									else
+									if (iFmt >= 0 && iFmt < SIZEOF(pFmt)) {
+										// save value
+										if (!pItem->pszText[1] || !*pItem->pszText[1])
+											continue;
+										if (!(pItem->wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA))) {
+											mir_snprintf(pszSetting, MAXSETTING, pFmt[iFmt].szValFmt, iItem);
+											DB::Setting::WriteTString(hContact, pszModule, pszSetting, pItem->pszText[1]);
+											// save category
+											mir_snprintf(pszSetting, MAXSETTING, pFmt[iFmt].szCatFmt, iItem);
+											if (pItem->idstrList && pItem->iListItem > 0 && pItem->iListItem < pItem->idstrListCount)
+												DB::Setting::WriteAString(hContact, pszModule, pszSetting, (LPSTR)pItem->idstrList[pItem->iListItem].pszText);
+											else 
+											if (pItem->pszText[0] && *pItem->pszText[0])
+												DB::Setting::WriteTString(hContact, pszModule, pszSetting, (LPTSTR)pItem->pszText[0]);
+											else									
+												DB::Setting::Delete(hContact, pszModule, pszSetting);
+											// redraw the item if required
+											if (pItem->wFlags & CTRLF_CHANGED) {
+												pItem->wFlags &= ~CTRLF_CHANGED;
+												ListView_RedrawItems(hList, lvi.iItem, lvi.iItem);
+											}
+											iItem++;
+										}
+									}
+								}
+								// delete reluctant items
+								if (iFmt >= 0 && iFmt < SIZEOF(pFmt)) {
+									DB::Setting::DeleteArray(hContact, pszModule, pFmt[iFmt].szCatFmt, iItem);
+									DB::Setting::DeleteArray(hContact, pszModule, pFmt[iFmt].szValFmt, iItem);
+								}
+
+								pList->wFlags &= ~CTRLF_CHANGED;
+							}
+							break;
+					}
+					break;
+				}
+
+				//
+				// handle notification messages from the list control
+				//
+				case LIST_PROFILE:
+				{
+					LPLISTCTRL pList = (LPLISTCTRL)GetUserData(((LPNMHDR)lParam)->hwndFrom);
+
+					switch (((LPNMHDR)lParam)->code) {
+						case NM_RCLICK:
+						{
+							HMENU hMenu = CreatePopupMenu();
+							MENUITEMINFO mii;
+							HANDLE hContact;
+							LVHITTESTINFO hi;
+							LPLCITEM pItem;
+							POINT pt;
+							
+							if (!hMenu) return 1;
+							PSGetContact(hDlg, hContact);
+							GetCursorPos(&pt);
+							hi.pt = pt;
+							ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hi.pt);
+							ListView_SubItemHitTest(((LPNMHDR)lParam)->hwndFrom, &hi);
+							pItem = ProfileList_GetItemData(((LPNMHDR)lParam)->hwndFrom, hi.iItem);
+
+							// insert menuitems
+							ZeroMemory(&mii, sizeof(MENUITEMINFO));
+							mii.cbSize = sizeof(MENUITEMINFO);
+							mii.fMask = MIIM_ID|MIIM_STRING;
+							// insert "Add" Menuitem
+							mii.wID = BTN_ADD_intEREST;
+							mii.dwTypeData = TranslateT("Add Interest");
+							InsertMenuItem(hMenu, 0, TRUE, &mii);
+							mii.wID = BTN_ADD_AFFLIATION;
+							mii.dwTypeData = TranslateT("Add Affliation");
+							InsertMenuItem(hMenu, 1, TRUE, &mii);
+							mii.wID = BTN_ADD_PAST;
+							mii.dwTypeData = TranslateT("Add Past");
+							InsertMenuItem(hMenu, 2, TRUE, &mii);
+
+							if (hi.iItem != -1 && PtrIsValid(pItem) && !(hContact && (pItem->wFlags & CTRLF_HASPROTO))) {
+								// insert separator
+								mii.fMask = MIIM_FTYPE;
+								mii.fType = MFT_SEPARATOR;
+								InsertMenuItem(hMenu, 3, TRUE, &mii);
+								// insert "Delete" Menuitem
+								mii.fMask = MIIM_ID|MIIM_STRING;
+								mii.wID = BTN_EDIT_CAT;
+								mii.dwTypeData = TranslateT("Edit Category");
+								InsertMenuItem(hMenu, 4, TRUE, &mii);
+								mii.wID = BTN_EDIT_VAL;
+								mii.dwTypeData = TranslateT("Edit Value");
+								InsertMenuItem(hMenu, 5, TRUE, &mii);
+								mii.fMask = MIIM_FTYPE;
+								mii.fType = MFT_SEPARATOR;
+								InsertMenuItem(hMenu, 6, TRUE, &mii);
+								// insert "Delete" Menuitem
+								mii.fMask = MIIM_ID|MIIM_STRING;
+								mii.wID = BTN_DEL;
+								mii.dwTypeData = TranslateT("Delete");
+								InsertMenuItem(hMenu, 7, TRUE, &mii);
+							}
+							TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hDlg, 0);
+							DestroyMenu(hMenu);
+							return 0;
+						}
+						/*case LVN_BEGINSCROLL:
+							SetFocus(((LPNMHDR)lParam)->hwndFrom);
+							break;
+							*/
+						case LVN_GETDISPINFO:
+							if (pList->labelEdit.iTopIndex != ListView_GetTopIndex(hList))
+								ProfileList_EndLabelEdit(((LPNMHDR)lParam)->hwndFrom, FALSE);
+							break;
+						case NM_CUSTOMDRAW:
+						{
+							LPNMLVCUSTOMDRAW cd = (LPNMLVCUSTOMDRAW)lParam;
+							LPLCITEM pItem = (LPLCITEM)cd->nmcd.lItemlParam;
+							RECT rc;
+
+							switch (cd->nmcd.dwDrawStage) {
+								case CDDS_PREPAINT:
+									SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_NOTIFYITEMDRAW);
+									return TRUE;
+								
+								case CDDS_ITEMPREPAINT:
+									ListView_GetItemRect(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec, &rc, LVIR_BOUNDS);
+									if (!PtrIsValid(pItem)) {
+										HFONT hBold, hFont;
+										TCHAR szText[MAX_PATH];
+										
+										PSGetBoldFont(hDlg, hBold);
+										hFont = (HFONT)SelectObject(cd->nmcd.hdc, hBold);
+										SetTextColor(cd->nmcd.hdc, GetSysColor(COLOR_3DSHADOW));
+										ProfileList_GetItemText(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec, 0, szText, MAX_PATH);
+										rc.left += 6;
+										DrawText(cd->nmcd.hdc, TranslateTS(szText), -1, &rc, DT_NOCLIP|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER);
+
+										rc.bottom -= 2;
+										rc.top = rc.bottom - 1;
+										rc.left -= 6;
+										DrawEdge(cd->nmcd.hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
+
+										SelectObject(cd->nmcd.hdc, hFont);
+										SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+										return TRUE;
+									}
+									// draw selected item
+									if ((cd->nmcd.uItemState & CDIS_SELECTED) || (pList->labelEdit.iItem == cd->nmcd.dwItemSpec)) {
+										SetTextColor(cd->nmcd.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+										FillRect(cd->nmcd.hdc, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
+									}
+									// draw background of unselected item
+									else {
+										SetTextColor(cd->nmcd.hdc, 
+											(pItem->wFlags & CTRLF_CHANGED) 
+												? clrChanged : (pItem->wFlags & CTRLF_HASMETA)
+													? clrMeta : ((pItem->wFlags & (CTRLF_HASCUSTOM)) && (pItem->wFlags & CTRLF_HASPROTO))
+														? clrBoth : (pItem->wFlags & CTRLF_HASCUSTOM)
+															? clrCustom	: clrNormal);
+										FillRect(cd->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
+									}
+									SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_NEWFONT|CDRF_NOTIFYSUBITEMDRAW);
+									return TRUE;
+								
+								case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
+								{
+									HFONT hoFont = (HFONT)SelectObject(cd->nmcd.hdc, pList->hFont);
+
+									ListView_GetSubItemRect(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec, cd->iSubItem, LVIR_BOUNDS, &rc);
+									if (cd->iSubItem == 0) {
+										RECT rc2;
+										ListView_GetSubItemRect(cd->nmcd.hdr.hwndFrom, cd->nmcd.dwItemSpec, 1, LVIR_BOUNDS, &rc2);
+										rc.right = rc2.left;
+									}
+									rc.left += 3;
+									DrawText(cd->nmcd.hdc,
+										pItem->pszText[cd->iSubItem] 
+											? pItem->pszText[cd->iSubItem] 
+											: (cd->iSubItem == 0 && pItem->idstrList && pItem->iListItem > 0 && pItem->iListItem < pItem->idstrListCount)
+												? pItem->idstrList[pItem->iListItem].ptszTranslated
+												: TranslateT("<empty>"),
+										-1, &rc, DT_END_ELLIPSIS|DT_NOCLIP|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER);
+									SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+									return TRUE;
+								}
+							} /* switch (cd->nmcd.dwDrawStage) */
+							break;
+						} /* case NM_CUSTOMDRAW: */
+					} /* (((LPNMHDR)lParam)->code) */
+					break;
+				}
+			}
+			break; /* case WM_NOTIFY: */
+
+		case WM_COMMAND:
+		{
+			switch (LOWORD(wParam)) {
+				case BTN_ADD_intEREST:
+					return ProfileList_AddNewItem(hDlg, (LPLISTCTRL)GetUserData(hList), &pFmt[2]);
+				case BTN_ADD_AFFLIATION:
+					return ProfileList_AddNewItem(hDlg, (LPLISTCTRL)GetUserData(hList), &pFmt[1]);
+				case BTN_ADD_PAST:
+					return ProfileList_AddNewItem(hDlg, (LPLISTCTRL)GetUserData(hList), &pFmt[0]);
+				case BTN_EDIT_CAT:
+					ProfileList_BeginLabelEdit(hList, ListView_GetSelectionMark(hList), 0);
+					break;
+				case BTN_EDIT_VAL:
+					ProfileList_BeginLabelEdit(hList, ListView_GetSelectionMark(hList), 1);
+					break;
+				case BTN_DEL:
+					if (IDYES == MsgBox(hDlg, MB_YESNO|MB_ICON_QUESTION, LPGENT("Question"), LPGENT("Delete an entry"), LPGENT("Do you really want to delete this entry?"))) {
+						INT iItem = ListView_GetSelectionMark(hList);
+						LPLISTCTRL pList = (LPLISTCTRL)GetUserData(hList);
+
+						ProfileList_DeleteItem(hList, iItem);
+						if (PtrIsValid(pList)) pList->wFlags |= CTRLF_CHANGED;
+						SendMessage(GetParent(hDlg), PSM_CHANGED, NULL, NULL);
+						// check if to delete any devider
+						if (!ProfileList_GetItemData(hList, iItem--) && !ProfileList_GetItemData(hList, iItem))
+							ListView_DeleteItem(hList, iItem);
+					}
+					break;
+			}
+			break;
+		}
+	}
+	return FALSE;
+}
diff --git a/plugins/UserInfoEx/src/resdefines.h b/plugins/UserInfoEx/src/resdefines.h
new file mode 100644
index 0000000000..f92a65f805
--- /dev/null
+++ b/plugins/UserInfoEx/src/resdefines.h
@@ -0,0 +1,5 @@
+#ifdef UNICODE
+#define RICHEDIT_CLASS "RichEdit20W"
+#else
+#define RICHEDIT_CLASS "RichEdit20A"
+#endif
diff --git a/plugins/UserInfoEx/src/resource.h b/plugins/UserInfoEx/src/resource.h
new file mode 100644
index 0000000000..1419fcc501
--- /dev/null
+++ b/plugins/UserInfoEx/src/resource.h
@@ -0,0 +1,333 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by resource.rc
+//
+#define IDSKIP                          20
+#define IDALL                           21
+#define IDAPPLY                         22
+#define IDI_MAIN                        110
+#define IDD_EXPORT                      601
+#define IDB_FLAGSPNG                    602
+#define IDD_ANNIVERSARY_EDITOR          603
+#define IDD_ANNIVERSARY_LIST            604
+#define CURSOR_ADDGROUP                 605
+#define IDR_VERSION2                    608
+#define IDD_CHOSE_PROTOCOL              614
+#define IDI_DEFAULT                     618
+#define IDD_DETAILS                     1000
+#define IDD_CONTACT_GENERAL             1001
+#define IDD_CONTACT_ORIGIN              1002
+#define IDD_CONTACT_COMPANY             1003
+#define IDD_CONTACT_ABOUT               1004
+#define CHECK_OPT_FLAGSUNKNOWN          1005
+#define IDD_ADDPHONE                    1006
+#define CHECK_OPT_FLAGSMSGSTATUS        1006
+#define IDD_ADDEMAIL                    1007
+#define IDD_MSGBOX                      1008
+#define IDD_MSGBOX2                     1008
+#define IDD_OPT_ADVANCED                1009
+#define IDD_OPT_POPUP                   1010
+#define IDD_CONTACT_HOME                1011
+#define IDD_CONTACT_ADDRESS             1011
+#define IDD_CONTACT_PROFILE             1012
+#define IDD_OPT_REMINDER                1015
+#define IDD_OPT_T                       1015
+#define IDD_OPT_DETAILSDLG              1016
+#define TXT_TIME                        1017
+#define IDD_CONTACT_HISTORY             1017
+#define TXT_ABOUT                       1018
+#define IDD_COPYPROGRESS                1019
+#define IDD_EXPORT_DATAHISTORY          1020
+#define IDD_OPT_CLIST                   1021
+#define IDD_OPT_COMMON                  1021
+#define IDD_CONTACT_ANNIVERSARY         1022
+#define IDD_REFRESHDETAILS              1023
+#define IDD_MSGBOXDUMMI                 1024
+#define STATIC_WHITERECT                1100
+#define STATIC_TREE                     1101
+#define STATIC_LINE1                    1102
+#define STATIC_LINE2                    1103
+#define ICO_DLGLOGO                     1105
+#define ICO_DLGLOGO2                    1106
+#define ICO_MSGDLG                      1106
+#define ICO_ADDRESS                     1107
+#define ICO_FEMALE                      1108
+#define ICO_MALE                        1109
+#define ICO_MARITAL                     1110
+#define ICO_BIRTHDAY                    1111
+#define ICO_PASSWORD                    1112
+#define ICO_CLOCK                       1113
+#define TXT_NAME                        1114
+#define TXT_DESCRIPTION                 1115
+#define TXT_UPDATING                    1116
+#define TXT_NICK                        1119
+#define TXT_PASSWORD                    1120
+#define TXT_AGE                         1121
+#define TXT_REMIND                      1122
+#define TXT_FEMALE                      1122
+#define TXT_REMIND2                     1123
+#define TXT_MALE                        1123
+#define TXT_REMIND3                     1124
+#define TXT_NUMCONTACT                  1124
+#define TXT_NUMBIRTH                    1125
+#define TXT_REMIND4                     1125
+#define TXT_REMIND5                     1126
+#define TXT_MESSAGE                     1126
+#define TXT_OPT_CLR_NORMAL              1127
+#define TXT_REMIND6                     1127
+#define TXT_OPT_CLR_USER                1128
+#define TXT_REMIND_LASTCHECK            1128
+#define TXT_OPT_CLR_BOTH                1129
+#define TXT_REMIND7                     1129
+#define TXT_OPT_CLR_CHANGED             1130
+#define TXT_REMIND8                     1130
+#define TXT_OPT_POPUP_CLR_BACK          1131
+#define TXT_OPT_CLR_META                1131
+#define TXT_REMIND9                     1131
+#define TXT_OPT_POPUP_CLR_TEXT          1132
+#define TXT_OPT_POPUP_CLR_ABACK         1133
+#define TXT_OPT_POPUP_CLR_ATEXT         1134
+#define EDIT_TITLE                      1201
+#define EDIT_FIRSTNAME                  1202
+#define EDIT_SECONDNAME                 1203
+#define EDIT_LASTNAME                   1204
+#define EDIT_PREFIX                     1205
+#define EDIT_NICK                       1206
+#define EDIT_DISPLAYNAME                1207
+#define EDIT_PASSWORD                   1208
+#define EDIT_STREET                     1209
+#define EDIT_ZIP                        1210
+#define EDIT_CITY                       1211
+#define EDIT_STATE                      1212
+#define EDIT_COUNTRY                    1213
+#define EDIT_POSITION                   1214
+#define EDIT_SUPERIOR                   1215
+#define EDIT_ASSISTENT                  1216
+#define EDIT_COMPANY                    1217
+#define EDIT_DEPARTMENT                 1218
+#define EDIT_ANNIVERSARY_DATE           1219
+#define EDIT_DEPARTMENT2                1219
+#define EDIT_POSITION2                  1219
+#define EDIT_OFFICE                     1219
+#define EDIT_AGE                        1220
+#define EDIT_REMIND                     1221
+#define EDIT_REMIND2                    1222
+#define EDIT_LANG1                      1223
+#define EDIT_REMIND_SOUNDOFFSET         1223
+#define EDIT_LANG2                      1224
+#define EDIT_LANG3                      1225
+#define EDIT_MARITAL                    1226
+#define EDIT_PARTNER                    1227
+#define EDIT_TIMEZONE                   1228
+#define EDIT_HOMEPAGE                   1229
+#define EDIT_CATEGORY                   1230
+#define EDIT_AREA                       1231
+#define EDIT_NUMBER                     1232
+#define EDIT_PHONE                      1233
+#define EDIT_EMAIL                      1234
+#define EDIT_ABOUT                      1235
+#define EDIT_NOTES                      1236
+#define EDIT_OCCUPATION                 1237
+#define EDIT_DELAY                      1238
+#define LIST_PROFILE                    1239
+#define LIST_PHONE                      1240
+#define LIST_EMAIL                      1241
+#define EDIT_TABAPPEAREANCE             1242
+#define SPIN_AGE                        1301
+#define SPIN_REMIND                     1302
+#define SPIN_REMIND2                    1303
+#define SPIN_REMIND_SOUNDOFFSET         1304
+#define CLR_NORMAL                      1400
+#define CLR_USER                        1401
+#define CLR_BOTH                        1402
+#define CLR_CHANGED                     1403
+#define CLR_BBACK                       1404
+#define CLR_META                        1404
+#define CLR_BTEXT                       1405
+#define CLR_ATEXT                       1407
+#define IDC_CUSTOM1                     1502
+#define BTN_DELETE                      1502
+#define BTN_SEARCH                      1502
+#define IDC_HEADERBAR                   1502
+#define IDC_CUSTOM2                     1503
+#define IDC_TREE1                       1504
+#define IDC_TREE                        1504
+#define IDC_COMBO1                      1506
+#define EDIT_SORT                       1506
+#define EDIT_EXTRAICON                  1506
+#define EDIT_METASUBCONTACTS            1506
+#define EDIT_HISTORY_GROUPING           1506
+#define COMBO_VIEW                      1506
+#define EDIT_BIRTHMODULE                1507
+#define EDIT_PATH                       1512
+#define BTN_BROWSE                      1513
+#define BTN_CHECK                       1514
+#define BTN_UNCHECK                     1515
+#define BTN_OPT_RESET                   1515
+#define TEXT_ZODIAC                     1516
+#define LIST_DROPDOWN                   1517
+#define TXT_CHANGED                     1522
+#define TXT_MODULE                      1523
+#define CLR_ABACK                       1523
+#define IDC_LINE                        1526
+#define EDIT_REMIND_ENABLED             1528
+#define IDC_ZODIAC                      1529
+#define STATIC_ADDRESS                  1530
+#define LIST_META                       1531
+#define BTN_DEFAULT                     1533
+#define EDIT_MESSAGE                    1538
+#define BTN_HISTORY_PASSWORD            1548
+#define CHECK_HISTORY_ENABLED           1550
+#define TXT_HISTORY1                    1551
+#define GROUP_STATS                     1553
+#define BTN_BROWSEDIR                   1555
+#define CHECK_OPT_AUTOTIMEZONE          1561
+#define CHECK_OPT_SREMAIL_ENABLED       1562
+#define CHECK_OPT_SENDSMS_MENUITEMS3    1563
+#define IDNONE                          1565
+#define CHECK_OPT_READONLYLABEL         1566
+#define TXT_DATEADDED                   1566
+#define IDC_PROGRESS                    1567
+#define IDC_CHECK1                      1567
+#define IDC_PROGRESS2                   1568
+#define IDC_CHECK2                      1568
+#define STATIC_OPT_METAGROUP            1568
+#define TXT_CONTACT                     1569
+#define EDIT_DAYS                       1569
+#define TXT_SETTING                     1570
+#define CHECK_DAYS                      1570
+#define IDC_INFO                        1571
+#define TXT_DAYS                        1571
+#define CHECK_POPUP                     1572
+#define GROUP_FILTER                    1573
+#define GROUP_REMINDER                  1574
+#define COMBO_OPT_GENDER                1575
+#define COMBO_OPT_FLAGS                 1576
+#define TXT_OPT_GENDER                  1577
+#define GROUP_OPT_EXTRAICONS            1578
+#define TXT_OPT_DEFAULTICONS            1579
+#define TXT_OPT_DEFAULTICONS2           1580
+#define ICO_COUNTRY                     1580
+#define GROUP_OPT_EXTRAICONS2           1581
+#define IDC_PERCENT                     1581
+#define TITLE_ZODIAC                    1582
+#define TXT_OPT_GENDER2                 1582
+#define TXT_OPT_FLAGS                   1582
+#define TITLE_AGE                       1583
+#define FRAME_AGE                       1584
+#define IDC_STATIC_GROUP                1585
+#define CHECK_OPT_GENDER                1586
+#define TXT_OPT_EXTRAICONS              1587
+#define BTN_PREVIEW                     1588
+#define IDC_PAGETITLE                   1589
+#define RADIO_REMIND1                   1590
+#define RADIO_REMIND2                   1591
+#define IDC_PAGETITLEBG                 1591
+#define RADIO_REMIND3                   1592
+#define IDC_PAGETITLEBG2                1592
+#define CHECK_OPT_MI_MAIN               1610
+#define TXT_OPT_MI_MAIN                 1611
+#define RADIO_OPT_MI_MAIN_NONE          1612
+#define RADIO_OPT_MI_MAIN_ALL           1613
+#define RADIO_OPT_MI_MAIN_EXIMPORT      1614
+#define CHECK_OPT_MI_CONTACT            1620
+#define TXT_OPT_MI_CONTACT              1621
+#define RADIO_OPT_MI_CONTACT_NONE       1622
+#define RADIO_OPT_MI_CONTACT_ALL        1623
+#define RADIO_OPT_MI_CONTACT_EXIMPORT   1624
+#define CHECK_OPT_MI_GROUP              1630
+#define TXT_OPT_MI_GROUP                1631
+#define RADIO_OPT_MI_GROUP_NONE         1632
+#define RADIO_OPT_MI_GROUP_ALL          1633
+#define RADIO_OPT_MI_GROUP_EXIMPORT     1634
+#define CHECK_OPT_MI_SUBGROUP           1640
+#define TXT_OPT_MI_SUBGROUP             1641
+#define RADIO_OPT_MI_SUBGROUP_NONE      1642
+#define RADIO_OPT_MI_SUBGROUP_ALL       1643
+#define RADIO_OPT_MI_SUBGROUP_EXIMPORT  1644
+#define CHECK_OPT_MI_STATUS             1650
+#define TXT_OPT_MI_STATUS               1651
+#define RADIO_OPT_MI_STATUS_NONE        1652
+#define RADIO_OPT_MI_STATUS_ALL         1653
+#define RADIO_OPT_MI_STATUS_EXIMPORT    1654
+#define CHECK_OPT_MI_ACCOUNT            1660
+#define TXT_OPT_MI_ACCOUNT              1661
+#define RADIO_OPT_MI_ACCOUNT_NONE       1662
+#define RADIO_OPT_MI_ACCOUNT_ALL        1663
+#define RADIO_OPT_MI_ACCOUNT_EXIMPORT   1664
+#define CHECK_OPT_MI_RESTART            1690
+#define BTN_UPDATE                      40001
+#define BTN_ADD                         40002
+#define BTN_IMPORT                      40002
+#define BTN_ADD_PHONE                   40003
+#define BTN_IMPORT2                     40003
+#define BTN_EXPORT                      40003
+#define BTN_MENU                        40003
+#define BTN_ADD_intEREST                40004
+#define BTN_EDIT                        40004
+#define BTN_ADD_AFFLIATION              40005
+#define BTN_ADD_PAST                    40006
+#define BTN_EDIT_CAT                    40007
+#define BTN_EDIT_VAL                    40008
+#define BTN_EDIT_MAIL                   40009
+#define BTN_EDIT_PHONE                  40010
+#define BTN_DEL                         40011
+#define BTN_DEL_MAIL                    40012
+#define BTN_DELALL                      40012
+#define BTN_DEL_PHONE                   40013
+#define BTN_COPY_MAIL                   40014
+#define BTN_COPY_PHONE                  40015
+#define BTN_GOTO                        40016
+#define BTN_OK                          40017
+#define BTN_CANCEL                      40018
+#define BTN_APPLY                       40019
+#define CHECK_REMIND                    40101
+#define CHECK_REMIND_MI                 40102
+#define CHECK_REMIND_FLASHICON          40103
+#define CHECK_SMS                       40104
+#define CHECK_REMIND_STARTUP            40104
+#define CHECK_OPT_DETECTUTF             40105
+#define CHECK_REMIND_SECURED            40105
+#define CHECK_REMIND_HIDDEN             40106
+#define CHECK_REMIND_VISIBLEONLY        40106
+#define CHECK_OPT_ICOVERSION            40108
+#define CHECK_OPT_HOMEPAGEICON          40108
+#define CHECK_OPT_CLR                   40109
+#define CHECK_OPT_REPLACECONTACTS       40109
+#define CHECK_OPT_GROUPS                40110
+#define CHECK_OPT_EMAILICON             40110
+#define CHECK_OPT_SORTTREE              40111
+#define CHECK_OPT_PHONEICON             40111
+#define CHECK_OPT_READONLY              40112
+#define CHECK_OPT_ZODIACAVATAR          40112
+#define CHECK_OPT_CHANGEMYDETAILS       40113
+#define CHECK_OPT_METACPY               40114
+#define CHECK_OPT_AEROADAPTION          40114
+#define CHECK_OPT_METASCAN              40115
+#define CHECK_OPT_POPUP_WINCLR          40116
+#define CHECK_OPT_BUTTONICONS           40116
+#define CHECK_OPT_POPUP_DEFCLR          40117
+#define CHECK_OPT_POPUP_ENABLED         40118
+#define CHECK_OPT_POPUP_AWINCLR         40119
+#define CHECK_OPT_POPUP_ADEFCLR         40120
+#define RADIO_OPT_POPUP_DEFAULT         40121
+#define RADIO_OPT_POPUP_CUSTOM          40122
+#define RADIO_OPT_POPUP_PERMANENT       40123
+#define RADIO_MALE                      40124
+#define CHECK_OPT_POPUP_ENABLED2        40124
+#define CHECK_OPT_POPUP_PROGRESS        40124
+#define RADIO_FEMALE                    40125
+#define CHECK_OPT_POPUP_PROGRESS2       40125
+#define CHECK_OPT_POPUP_MSGBOX          40125
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC                     1
+#define _APS_NEXT_RESOURCE_VALUE        623
+#define _APS_NEXT_COMMAND_VALUE         40201
+#define _APS_NEXT_CONTROL_VALUE         1589
+#define _APS_NEXT_SYMED_VALUE           601
+#endif
+#endif
diff --git a/plugins/UserInfoEx/src/svc_avatar.cpp b/plugins/UserInfoEx/src/svc_avatar.cpp
new file mode 100644
index 0000000000..f9d5515f52
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_avatar.cpp
@@ -0,0 +1,247 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_avatar.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#include "commonheaders.h"
+#include "m_protocols.h"
+#include "m_png.h"
+#include "m_avatars.h"
+
+namespace NServices
+{
+	namespace NAvatar
+	{
+
+		static HANDLE ghChangedHook = NULL;
+
+		static INT GetContactAvatarFileName(LPCTSTR zodiac, LPSTR szFileName, INT cchFileName)
+		{
+			if (!CallService(MS_DB_GETPROFILEPATH, (WPARAM)cchFileName, (LPARAM)szFileName))
+			{
+				size_t len = mir_strlen(szFileName);
+
+				CHAR tmp[64];
+
+				if (WideCharToMultiByte(CP_ACP, 0, zodiac, 64, tmp, SIZEOF(tmp),0,0) > 0)
+				{
+					mir_snprintf(szFileName + len, cchFileName - len, "\\avatars\\%s.png", tmp);
+				}
+
+				return !PathFileExistsA(szFileName);
+			}
+			return 1;
+		}
+
+		/**
+		 *
+		 *
+		 **/
+		static VOID SetZodiacAvatar(HANDLE hContact)
+		{
+			MAnnivDate mtb;
+
+			// try to load birthday for contact
+			if (!mtb.DBGetBirthDate(hContact))
+			{
+				MZodiac zodiac;
+				//ICONINFO iinfo;
+				CHAR szFileName[MAX_PATH];
+
+				// get zodiac for birthday
+				zodiac = mtb.Zodiac();
+
+				if (!GetContactAvatarFileName(zodiac.pszName, szFileName, SIZEOF(szFileName)))
+				{
+					// extract the bitmap from the icon
+					//GetIconInfo(zodiac.hIcon, &iinfo);
+
+					// save the bitmap to a file used as avatar later
+					//if (!SaveBitmapAsAvatar(iinfo.hbmColor, szFileName))
+					{
+						if (!CallService(MS_AV_SETAVATAR, (WPARAM)hContact, (LPARAM)szFileName))
+						{
+							DB::Setting::WriteByte(hContact, "ContactPhoto", "IsZodiac", 1);
+						}
+					}
+				}
+			}
+		}
+
+		VOID DeleteAvatar(HANDLE hContact)
+		{
+			if (hContact && DB::Setting::GetByte(hContact, "ContactPhoto", "IsZodiac", FALSE))
+			{
+				//AVATARCACHEENTRY *ace;
+				LPSTR szProto = DB::Contact::Proto(hContact);
+
+				DB::Setting::Delete(hContact, "ContactPhoto", "File");
+				DB::Setting::Delete(hContact, "ContactPhoto", "RFile");
+				DB::Setting::Delete(hContact, "ContactPhoto", "Backup");
+				DB::Setting::Delete(hContact, "ContactPhoto", "ImageHash");
+
+				DB::Setting::WriteByte(hContact, "ContactPhoto", "IsZodiac", 0);
+
+				/*
+				ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, NULL, (LPARAM)szProto);
+				if (ace)
+				{
+					if (!CallService(MS_AV_SETAVATAR, (WPARAM)hContact, (LPARAM)ace->szFilename))
+					{
+						DB::Setting::WriteByte(hContact, "ContactPhoto", "IsZodiac", 0);
+					}
+				}
+				*/
+			}
+		}
+
+
+		/**
+		 *
+		 *
+		 **/
+		static INT OnAvatarChanged(HANDLE hContact, AVATARCACHEENTRY *ace)
+		{
+			if (hContact)
+			{
+				// check valid parameters
+				if (ace)
+				{
+					if (// check for correct structure
+							ace->cbSize == sizeof(AVATARCACHEENTRY) &&
+							// set zodiac as avatar either if the desired avatar is invalid or a general protocol picture
+							((ace->dwFlags & AVS_PROTOPIC) || !(ace->dwFlags & AVS_BITMAP_VALID)))
+					{
+						if (!DB::Setting::GetByte(hContact, "ContactPhoto", "IsZodiac", 0))
+						{
+							SetZodiacAvatar(hContact);
+						}
+					}
+					else
+					{
+						DB::Setting::WriteByte(hContact, "ContactPhoto", "IsZodiac", 0);
+					}
+				}
+
+				// avatar was deleted, so we can set up a zodiac avatar
+				else
+				{
+					SetZodiacAvatar(hContact);
+				}
+			}
+			return 0;
+		}
+
+		/**
+		 *
+		 *
+		 **/
+		VOID Enable(BOOLEAN bEnable)
+		{
+			HANDLE hContact;
+			DBVARIANT dbv;
+
+			if (bEnable && !ghChangedHook)
+			{
+
+				//walk through all the contacts stored in the DB
+				for (hContact = DB::Contact::FindFirst();
+					hContact != NULL;
+					hContact = DB::Contact::FindNext(hContact))
+				{
+					// don't set if avatar is locked!
+					if (!DB::Setting::GetByte(hContact, "ContactPhoto", "Locked", 0))
+					{
+						BOOLEAN bInvalidAvatar = TRUE;
+
+						// the relative file is valid
+						if (!DB::Setting::GetAString(hContact, "ContactPhoto", "RFile", &dbv))
+						{
+							CHAR absolute[MAX_PATH];
+							absolute[0] = '\0';
+
+							// check if file exists
+							if (!CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)dbv.pszVal, (LPARAM)absolute))
+							{
+								FILE *f = fopen(absolute, "rb");
+								if (f) {
+									bInvalidAvatar = FALSE;
+									fclose(f);
+								}
+							}
+							DB::Variant::Free(&dbv);
+						}
+
+						// the absolute file is valid
+						if (bInvalidAvatar && !DBGetContactSetting(hContact, "ContactPhoto", "File", &dbv))
+						{
+							FILE *f = fopen(dbv.pszVal, "rb");
+							if (f) {
+								bInvalidAvatar = FALSE;
+								fclose(f);
+							}
+							DB::Variant::Free(&dbv);
+						}
+
+						// set the zodiac as avatar
+						if (bInvalidAvatar) {
+							SetZodiacAvatar(hContact);
+						}
+					}
+				}
+				ghChangedHook = HookEvent(ME_AV_AVATARCHANGED, (MIRANDAHOOK) OnAvatarChanged);
+			}
+			else if (!bEnable && ghChangedHook)
+			{
+				UnhookEvent(ghChangedHook);
+				ghChangedHook = NULL;
+
+				//walk through all the contacts stored in the DB
+				for (hContact = DB::Contact::FindFirst();
+						 hContact != NULL;
+						 hContact = DB::Contact::FindNext(hContact))
+				{
+					DeleteAvatar(hContact);
+				}
+			}
+		}
+
+
+		/**
+		 * name:	OnModulesLoaded
+		 * desc:	initialize stuff, which require all standard modules to bee loaded
+		 * params:	none
+		 * return:	0
+		 **/
+		VOID OnModulesLoaded()
+		{
+			Enable(DB::Setting::GetByte(SET_ZODIAC_AVATARS, FALSE));
+		}
+
+	} /* namespace NAvatar */
+} /* namespace NServices */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_avatar.h b/plugins/UserInfoEx/src/svc_avatar.h
new file mode 100644
index 0000000000..acf00a83e9
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_avatar.h
@@ -0,0 +1,41 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_avatar.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCAVATAR_H_INCLUDED_
+#define _UINFOEX_SVCAVATAR_H_INCLUDED_
+
+namespace NServices 
+{
+	namespace NAvatar 
+	{
+		VOID Enable					(BOOLEAN bEnable);
+		VOID OnModulesLoaded		();
+	}
+} /* namespace NServices */
+
+#endif /* _UINFOEX_SVCAVATAR_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_constants.cpp b/plugins/UserInfoEx/src/svc_constants.cpp
new file mode 100644
index 0000000000..ed803f79f4
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_constants.cpp
@@ -0,0 +1,436 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_constants.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+static IDSTRLIST TmplLanguages[] = {
+	{ 0, LPGEN("Unspecified"), 0},
+	{55, LPGEN("Afrikaans"), 0},
+	{58, LPGEN("Albanian"), 0},
+	{ 1, LPGEN("Arabic"), 0},
+	{59, LPGEN("Armenian"), 0},
+	{68, LPGEN("Azerbaijani"), 0},
+	{72, LPGEN("Belorussian"), 0},
+	{ 2, LPGEN("Bhojpuri"), 0},
+	{56, LPGEN("Bosnian"), 0},
+	{ 3, LPGEN("Bulgarian"), 0},
+	{ 4, LPGEN("Burmese"), 0},
+	{ 5, LPGEN("Cantonese"), 0},
+	{ 6, LPGEN("Catalan"), 0},
+	{61, LPGEN("Chamorro"), 0},
+	{ 7, LPGEN("Chinese"), 0},
+	{ 8, LPGEN("Croatian"), 0},
+	{ 9, LPGEN("Czech"), 0},
+	{10, LPGEN("Danish"), 0},
+	{11, LPGEN("Dutch"), 0},
+	{12, LPGEN("English"), 0},
+	{13, LPGEN("Esperanto"), 0},
+	{14, LPGEN("Estonian"), 0},
+	{15, LPGEN("Farsi"), 0},
+	{16, LPGEN("Finnish"), 0},
+	{17, LPGEN("French"), 0},
+	{18, LPGEN("Gaelic"), 0},
+	{19, LPGEN("German"), 0},
+	{20, LPGEN("Greek"), 0},
+	{70, LPGEN("Gujarati"), 0},
+	{21, LPGEN("Hebrew"), 0},
+	{22, LPGEN("Hindi"), 0},
+	{23, LPGEN("Hungarian"), 0},
+	{24, LPGEN("Icelandic"), 0},
+	{25, LPGEN("Indonesian"), 0},
+	{26, LPGEN("Italian"), 0},
+	{27, LPGEN("Japanese"), 0},
+	{28, LPGEN("Khmer"), 0},
+	{29, LPGEN("Korean"), 0},
+	{69, LPGEN("Kurdish"), 0},
+	{30, LPGEN("Lao"), 0},
+	{31, LPGEN("Latvian"), 0},
+	{32, LPGEN("Lithuanian"), 0},
+	{65, LPGEN("Macedonian"), 0},
+	{33, LPGEN("Malay"), 0},
+	{63, LPGEN("Mandarin"), 0},
+	{62, LPGEN("Mongolian"), 0},
+	{34, LPGEN("Norwegian"), 0},
+	{57, LPGEN("Persian"), 0},
+	{35, LPGEN("Polish"), 0},
+	{36, LPGEN("Portuguese"), 0},
+	{60, LPGEN("Punjabi"), 0},
+	{37, LPGEN("Romanian"), 0},
+	{38, LPGEN("Russian"), 0},
+	{39, LPGEN("Serbo-Croatian"), 0},
+	{66, LPGEN("Sindhi"), 0},
+	{40, LPGEN("Slovak"), 0},
+	{41, LPGEN("Slovenian"), 0},
+	{42, LPGEN("Somali"), 0},
+	{43, LPGEN("Spanish"), 0},
+	{44, LPGEN("Swahili"), 0},
+	{45, LPGEN("Swedish"), 0},
+	{46, LPGEN("Tagalog"), 0},
+	{64, LPGEN("Taiwanese"), 0},
+	{71, LPGEN("Tamil"), 0},
+	{47, LPGEN("Tatar"), 0},
+	{48, LPGEN("Thai"), 0},
+	{49, LPGEN("Turkish"), 0},
+	{50, LPGEN("Ukrainian"), 0},
+	{51, LPGEN("Urdu"), 0},
+	{52, LPGEN("Vietnamese"), 0},
+	{67, LPGEN("Welsh"), 0},
+	{53, LPGEN("Yiddish"), 0},
+	{54, LPGEN("Yoruba"), 0},
+};
+
+static IDSTRLIST TmplOccupations[] = {
+	{ 0, LPGEN("Unspecified"), 0},
+	{ 1, LPGEN("Academic"), 0},
+	{ 2, LPGEN("Administrative"), 0},
+	{ 3, LPGEN("Art/Entertainment"), 0},
+	{ 4, LPGEN("College Student"), 0},
+	{ 5, LPGEN("Computers"), 0},
+	{ 6, LPGEN("Community & Social"), 0},
+	{ 7, LPGEN("Education"), 0},
+	{ 8, LPGEN("Engineering"), 0},
+	{ 9, LPGEN("Financial Services"), 0},
+	{10, LPGEN("Government"), 0},
+	{11, LPGEN("High School Student"), 0},
+	{12, LPGEN("Home"), 0},
+	{13, LPGEN("ICQ - Providing Help"), 0},
+	{14, LPGEN("Law"), 0},
+	{15, LPGEN("Managerial"), 0},
+	{16, LPGEN("Manufacturing"), 0},
+	{17, LPGEN("Medical/Health"), 0},
+	{18, LPGEN("Military"), 0},
+	{19, LPGEN("Non-Government Organization"), 0},
+	{20, LPGEN("Professional"), 0},
+	{21, LPGEN("Retail"), 0},
+	{22, LPGEN("Retired"), 0},
+	{23, LPGEN("Science & Research"), 0},
+	{24, LPGEN("Sports"), 0},
+	{25, LPGEN("Technical"), 0},
+	{26, LPGEN("University Student"), 0},
+	{27, LPGEN("Web Building"), 0},
+	{99, LPGEN("Other Services"), 0}
+};
+
+static IDSTRLIST TmplInterests[] = {
+	{	0,	LPGEN("Unspecified"), 0},
+	{100, LPGEN("Art"), 0},
+	{101, LPGEN("Cars"), 0},
+	{102, LPGEN("Celebrity Fans"), 0},
+	{103, LPGEN("Collections"), 0},
+	{104, LPGEN("Computers"), 0},
+	{105, LPGEN("Culture & Literature"), 0},
+	{106, LPGEN("Fitness"), 0},
+	{107, LPGEN("Games"), 0},
+	{108, LPGEN("Hobbies"), 0},
+	{109, LPGEN("ICQ - Providing Help"), 0},
+	{110, LPGEN("Internet"), 0},
+	{111, LPGEN("Lifestyle"), 0},
+	{112, LPGEN("Movies/TV"), 0},
+	{113, LPGEN("Music"), 0},
+	{114, LPGEN("Outdoor Activities"), 0},
+	{115, LPGEN("Parenting"), 0},
+	{116, LPGEN("Pets/Animals"), 0},
+	{117, LPGEN("Religion"), 0},
+	{118, LPGEN("Science/Technology"), 0},
+	{119, LPGEN("Skills"), 0},
+	{120, LPGEN("Sports"), 0},
+	{121, LPGEN("Web Design"), 0},
+	{122, LPGEN("Nature and Environment"), 0},
+	{123, LPGEN("News & Media"), 0},
+	{124, LPGEN("Government"), 0},
+	{125, LPGEN("Business & Economy"), 0},
+	{126, LPGEN("Mystics"), 0},
+	{127, LPGEN("Travel"), 0},
+	{128, LPGEN("Astronomy"), 0},
+	{129, LPGEN("Space"), 0},
+	{130, LPGEN("Clothing"), 0},
+	{131, LPGEN("Parties"), 0},
+	{132, LPGEN("Women"), 0},
+	{133, LPGEN("Social science"), 0},
+	{134, LPGEN("60's"), 0},
+	{135, LPGEN("70's"), 0},
+	{136, LPGEN("80's"), 0},
+	{137, LPGEN("50's"), 0},
+	{138, LPGEN("Finance and corporate"), 0},
+	{139, LPGEN("Entertainment"), 0},
+	{140, LPGEN("Consumer electronics"), 0},
+	{141, LPGEN("Retail stores"), 0},
+	{142, LPGEN("Health and beauty"), 0},
+	{143, LPGEN("Media"), 0},
+	{144, LPGEN("Household products"), 0},
+	{145, LPGEN("Mail order catalog"), 0},
+	{146, LPGEN("Business services"), 0},
+	{147, LPGEN("Audio and visual"), 0},
+	{148, LPGEN("Sporting and athletic"), 0},
+	{149, LPGEN("Publishing"), 0},
+	{150, LPGEN("Home automation"), 0}
+};
+
+static IDSTRLIST TmplAffiliations[] = {
+	{	0,	LPGEN("Unspecified"), 0},
+	{200, LPGEN("Alumni Org."), 0},
+	{201, LPGEN("Charity Org."), 0},
+	{202, LPGEN("Club/Social Org."), 0},
+	{203, LPGEN("Community Org."), 0},
+	{204, LPGEN("Cultural Org."), 0},
+	{205, LPGEN("Fan Clubs"), 0},
+	{206, LPGEN("Fraternity/Sorority"), 0},
+	{207, LPGEN("Hobbyists Org."), 0},
+	{208, LPGEN("International Org."), 0},
+	{209, LPGEN("Nature and Environment Org."), 0},
+	{210, LPGEN("Professional Org."), 0},
+	{211, LPGEN("Scientific/Technical Org."), 0},
+	{212, LPGEN("Self Improvement Group"), 0},
+	{213, LPGEN("Spiritual/Religious Org."), 0},
+	{214, LPGEN("Sports Org."), 0},
+	{215, LPGEN("Support Org."), 0},
+	{216, LPGEN("Trade and Business Org."), 0},
+	{217, LPGEN("Union"), 0},
+	{218, LPGEN("Volunteer Org."), 0},
+	{299, LPGEN("Other"), 0},
+};
+
+static IDSTRLIST TmplPast[] = {
+	{	0,	LPGEN("Unspecified"), 0},
+	{300, LPGEN("Elementary School"), 0},
+	{301, LPGEN("High School"), 0},
+	{302, LPGEN("College"), 0},
+	{303, LPGEN("University"), 0},
+	{304, LPGEN("Military"), 0},
+	{305, LPGEN("TmplPast Work Place"), 0},
+	{306, LPGEN("TmplPast Organization"), 0},
+	{399, LPGEN("Other"), 0}
+};
+
+static IDSTRLIST TmplMarital[]={
+	{ 0, LPGEN("Unspecified"), 0},
+	{10, LPGEN("Single"), 0},
+	{11, LPGEN("Close relationships"), 0},
+	{12, LPGEN("Engaged"), 0},
+	{20, LPGEN("Married"), 0},
+	{30, LPGEN("Divorced"), 0},
+	{31, LPGEN("Separated"), 0},
+	{40, LPGEN("Widowed"), 0}
+};
+
+static IDSTRLIST TmplPrefixes[]={
+	{	0,	LPGEN("Unspecified"), 0},
+	{'j', LPGEN("jun."), 0},
+	{'s', LPGEN("sen."), 0}
+};
+
+static IDSTRLIST *MyCountries = NULL;
+static UINT MyCountriesCount = 0;
+
+/**
+ * This is a sort procedure, which compares two items of an IDSTRLIST array.
+ * It is used by qsort in SvcConstantsTranslateList and cares about the
+ * locale, which was set up in OS. This prevents e.g. �,� to be put onto 
+ * the end of the list., but being sorted to the right position.
+ *
+ * @param	p1				- (LPIDSTRLIST) first item to compare
+ * @param	p2				- (LPIDSTRLIST) second item to compare
+ *
+ * returns -1, 0, 1			according to the comparison result of _tcscmp.
+ **/
+static int __cdecl ListSortProc(const LPIDSTRLIST p1, const LPIDSTRLIST p2)
+{
+	return lstrcmpi(p1->ptszTranslated, p2->ptszTranslated);
+}
+
+/**
+ * Translates the text of each item of an IDStrinList to users locale
+ * language and saves result in szTranslated member for later use and 
+ * faster access to translated strings later.
+ *
+ * @param	pList			- pointer to list to translate
+ * @param	nListCount		- number of list items
+ *
+ * @return	nothing
+ **/
+static VOID SvcConstantsTranslateList(LPIDSTRLIST pList, UINT nListCount/*, SortedList *pSorted*/)
+{
+	if (!pList[0].ptszTranslated)
+	{
+		for (UINT i = 0; i < nListCount; i++)	
+		{
+			pList[i].ptszTranslated = (LPTSTR)CallService(MS_LANGPACK_PCHARTOTCHAR, 0, (LPARAM)pList[i].pszText);
+		}
+		// Ignore last item, if it is a "Other" item.
+		if (!strcmp(pList[nListCount-1].pszText, LPGEN("Other"))) nListCount--;
+	
+		// Sort list according translated text and ignore first item.
+		qsort(pList+1, nListCount-1, sizeof(pList[0]), 
+			(INT (*)(const VOID*, const VOID*))ListSortProc);
+	}
+}
+
+/**
+ * This function uses the country list provided by the core to create ower own one.
+ * The core's list is extended by a translated value. The cached translation is meant
+ * to improve speed uppon adding items to a combobox.
+ *
+ * @param	pList			- LPIDSTRLIST pointer, which retrieves the list pointer.
+ * @param	pnListSize		- pointer to an unsigned integer, which retrieves the number of items.
+ *
+ * @retval	MIR_OK			- indicates success
+ * @retval	MIR_FAIL		- indicates error
+ **/
+INT_PTR GetCountryList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+	INT_PTR rc = MIR_OK;
+	if (!MyCountries) 
+	{
+		struct CountryListEntry *country;
+
+		if (!CallService(MS_UTILS_GETCOUNTRYLIST, (WPARAM)&MyCountriesCount, (LPARAM)&country))
+		{
+			MyCountries = (IDSTRLIST*)mir_alloc(MyCountriesCount * sizeof(IDSTRLIST));
+			if (MyCountries) 
+			{
+				for (UINT i = 0; i < MyCountriesCount; i++) 
+				{
+					MyCountries[i].nID = country[i].id;
+					MyCountries[i].pszText = country[i].szName;
+					MyCountries[i].ptszTranslated = (LPTSTR)CallService(MS_LANGPACK_PCHARTOTCHAR, 0, (LPARAM)country[i].szName);
+				}
+				// Sort list according translated text and ignore first item.
+				qsort(MyCountries+1, MyCountriesCount-1, sizeof(MyCountries[0]), 
+					(INT (*)(const VOID*, const VOID*))ListSortProc);
+			}
+			else
+			{
+				rc = MIR_FAIL;
+			}
+		}
+		else
+		{
+			rc = MIR_FAIL;
+		}
+	}
+	*pnListSize = MyCountriesCount;
+	*pList = MyCountries;
+	return rc;
+}
+
+INT_PTR GetMaritalList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+	*pnListSize = SIZEOF(TmplMarital);
+	*pList = TmplMarital;
+	SvcConstantsTranslateList(TmplMarital, *pnListSize);
+	return MIR_OK;
+}
+
+INT_PTR GetLanguageList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+	*pnListSize = SIZEOF(TmplLanguages);
+	*pList = TmplLanguages;
+	SvcConstantsTranslateList(TmplLanguages, *pnListSize);
+	return MIR_OK;
+}
+
+INT_PTR GetOccupationList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+	*pnListSize = SIZEOF(TmplOccupations);
+	*pList = TmplOccupations;
+	SvcConstantsTranslateList(TmplOccupations, *pnListSize);
+	return MIR_OK;
+}
+
+INT_PTR GetInterestsList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+	*pnListSize = SIZEOF(TmplInterests);
+	*pList = TmplInterests;
+	SvcConstantsTranslateList(TmplInterests, *pnListSize);
+	return MIR_OK;
+}
+
+INT_PTR GetPastList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+	*pnListSize = SIZEOF(TmplPast);
+	*pList = TmplPast;
+	SvcConstantsTranslateList(TmplPast, *pnListSize);
+	return MIR_OK;
+}
+
+INT_PTR GetAffiliationsList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+	*pnListSize = SIZEOF(TmplAffiliations);
+	*pList = TmplAffiliations;
+	SvcConstantsTranslateList(TmplAffiliations, *pnListSize);
+	return MIR_OK;
+}
+
+INT_PTR GetNamePrefixList(LPUINT pnListSize, LPIDSTRLIST *pList)
+{
+	*pnListSize = SIZEOF(TmplPrefixes);
+	*pList = TmplPrefixes;
+	SvcConstantsTranslateList(TmplPrefixes, *pnListSize);
+	return MIR_OK;
+}
+
+VOID SvcConstantsLoadModule(VOID)
+{
+	UINT nListSize;
+	LPIDSTRLIST pList;
+
+	// precache translation
+	GetMaritalList(&nListSize, &pList);
+	GetLanguageList(&nListSize, &pList);
+	GetCountryList(&nListSize, &pList);
+	GetOccupationList(&nListSize, &pList);
+	GetInterestsList(&nListSize, &pList);
+	GetPastList(&nListSize, &pList);
+	GetAffiliationsList(&nListSize, &pList);
+	GetNamePrefixList(&nListSize, &pList);
+}
+
+static VOID FORCEINLINE SvcConstantsClearList(UINT pnListSize, LPIDSTRLIST pList)
+{
+	if (pList) 
+	{
+		for (UINT i = 0; i < pnListSize; i++)
+		{
+			MIR_FREE(pList[i].ptszTranslated);
+		}
+	}
+}
+
+VOID SvcConstantsUnloadModule(VOID)
+{
+	SvcConstantsClearList(SIZEOF(TmplMarital), TmplMarital);
+	SvcConstantsClearList(SIZEOF(TmplLanguages), TmplLanguages);
+	SvcConstantsClearList(SIZEOF(TmplOccupations), TmplOccupations);
+	SvcConstantsClearList(SIZEOF(TmplInterests), TmplInterests);
+	SvcConstantsClearList(SIZEOF(TmplPast), TmplPast);
+	SvcConstantsClearList(SIZEOF(TmplAffiliations), TmplAffiliations);
+	SvcConstantsClearList(SIZEOF(TmplPrefixes), TmplPrefixes);
+	SvcConstantsClearList(MyCountriesCount, MyCountries);
+	MIR_FREE(MyCountries);
+}
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_constants.h b/plugins/UserInfoEx/src/svc_constants.h
new file mode 100644
index 0000000000..d2c5a5c135
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_constants.h
@@ -0,0 +1,195 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_constants.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_CONSTANTS_INCLUDED_
+#define _SVC_CONSTANTS_INCLUDED_
+
+#define MODULELONGNAME		"Extended UserInfo"
+#define USERINFO			"UserInfo"
+#define MODNAME				"UserInfoEx"
+#define MODNAMEFLAGS		"Flags"
+
+#define MODULELONGNAMET		_T(MODULELONGNAME)
+#define MODNAMET			_T(MODNAME)
+
+#define MAXDATASIZE			1024		// maximum character count of most static, temporary, ...., strings
+#define MAXCATLEN			64			// maximum character count for a category string (phone, email, interest, ...)
+#define MAXSETTING			255			// maximum character count for a setting string
+#define MAXNAME				260			// maximum character count for a username
+#define MAXUID				260			// maximum character count for a uin
+
+// most important modules
+#define MOD_MBIRTHDAY					"mBirthday"
+#define MOD_CLIST						"CList"
+
+// database settings (propertysheet)
+#define	SET_PROPSHEET_PCBIREADONLY		"PBCIReadOnly"
+#define	SET_PROPSHEET_READONLYLABEL		"TILReadonly"
+#define	SET_PROPSHEET_AEROADAPTION		"AeroAdaption"
+#define	SET_PROPSHEET_SHOWCOLOURS		"ShowColours"
+#define	SET_PROPSHEET_CLRNORMAL			"colourNormal"
+#define	SET_PROPSHEET_CLRCUSTOM			"colourUser"
+#define	SET_PROPSHEET_CLRBOTH			"colourBoth"
+#define	SET_PROPSHEET_CLRCHANGED		"colourChanged"
+#define	SET_PROPSHEET_CLRMETA			"colourMeta"
+#define	SET_PROPSHEET_SAVEVCARD			"vCardOnExit"
+#define	SET_PROPSHEET_GROUPS			"TreeGroups"
+#define	SET_PROPSHEET_SORTITEMS			"TreeSortItems"
+#define	SET_PROPSHEET_CHANGEMYDETAILS	"ChangeMyDetails"
+#define	SET_ABOUT_ACTIVEWINDOW			"AboutActiveWin"
+#define	SET_MI_MAIN						"miMenu"
+#define	SET_MI_CONTACT					"miContact"
+#define	SET_MI_GROUP					"miGroup"
+#define	SET_MI_SUBGROUP					"miSubGroup"
+#define	SET_MI_STATUS					"miStatus"
+#define	SET_MI_ACCOUNT					"miAccount"
+
+#define	SET_EXTENDED_EMAILSERVICE		"emailEx"
+#define	SET_CLIST_EXTRAICON_GENDER		"GenderColumn"
+#define	SET_CLIST_EXTRAICON_GENDER2		"cliGender"
+#define	SET_CLIST_EXTRAICON_COUNTRY		"CountryColumn"
+#define	SET_CLIST_EXTRAICON_HOMEPAGE	"cliHomepage"
+#define	SET_CLIST_EXTRAICON_EMAIL		"cliEmail"
+#define	SET_CLIST_EXTRAICON_PHONE		"cliPhone"
+#define	SET_CLIST_EXTRAICON_FLAGS2		"cliFlags"
+#define	SET_OPT_AUTOTIMEZONE			"AutoTimezone"
+#define	SET_ZODIAC_AVATARS				"ZodicAvatars"
+#define	SET_META_SCAN					"MetaScan"
+// database	settings (general psp)
+#define	SET_ME_PASSWORD					"Password"
+#define	SET_CONTACT_TITLE				"Title"
+#define	SET_CONTACT_FIRSTNAME			"FirstName"
+#define	SET_CONTACT_SECONDNAME			"SecondName"
+#define	SET_CONTACT_LASTNAME			"LastName"
+#define	SET_CONTACT_FIRSTLASTNAME		"FullName"
+#define	SET_CONTACT_PREFIX				"Prefix"
+#define	SET_CONTACT_NICK				"Nick"
+#define	SET_CONTACT_MYHANDLE			"MyHandle"
+#define	SET_CONTACT_STREET				"Street"
+#define	SET_CONTACT_ZIP					"Zip"
+#define	SET_CONTACT_CITY				"City"
+#define	SET_CONTACT_STATE				"State"
+#define	SET_CONTACT_COUNTRY				"Country"
+#define	SET_CONTACT_GENDER				"Gender"
+// database settings (advanced psp)
+#define	SET_CONTACT_ORIGIN_STREET		"OriginStreet"
+#define	SET_CONTACT_ORIGIN_ZIP			"OriginZip"
+#define	SET_CONTACT_ORIGIN_CITY			"OriginCity"
+#define	SET_CONTACT_ORIGIN_STATE		"OriginState"
+#define	SET_CONTACT_ORIGIN_COUNTRY		"OriginCountry"
+#define	SET_CONTACT_LANG1				"Language1"
+#define	SET_CONTACT_LANG2				"Language2"
+#define	SET_CONTACT_LANG3				"Language3"
+#define	SET_CONTACT_MARITAL				"MaritalStatus"
+#define	SET_CONTACT_PARTNER				"Partner"
+#define	SET_CONTACT_ANNIVERSARY			"Anniv"
+#define	SET_CONTACT_AGE					"Age"
+#define	SET_CONTACT_TIMEZONE			"Timezone"
+#define	SET_CONTACT_TIMEZONENAME		"TzName"
+#define	SET_CONTACT_TIMEZONEINDEX		"TzIndex"
+#define	SET_CONTACT_BIRTH				"Birth"
+#define	SET_CONTACT_BIRTHDAY			"BirthDay"
+#define	SET_CONTACT_BIRTHMONTH			"BirthMonth"
+#define	SET_CONTACT_BIRTHYEAR			"BirthYear"
+#define	SET_CONTACT_DOBD				"DOBd"
+#define	SET_CONTACT_DOBM				"DOBm"
+#define	SET_CONTACT_DOBY				"DOBy"
+// database settings (company psp)
+#define	SET_CONTACT_COMPANY_POSITION	"CompanyPosition"
+#define	SET_CONTACT_COMPANY_OCCUPATION	"CompanyOccupation"
+#define	SET_CONTACT_COMPANY_SUPERIOR	"CompanySuperior"
+#define	SET_CONTACT_COMPANY_ASSISTENT	"CompanyAssistent"
+#define	SET_CONTACT_COMPANY				"Company"
+#define	SET_CONTACT_COMPANY_DEPARTMENT	"CompanyDepartment"
+#define	SET_CONTACT_COMPANY_OFFICE		"CompanyOffice"
+#define	SET_CONTACT_COMPANY_STREET		"CompanyStreet"
+#define	SET_CONTACT_COMPANY_ZIP			"CompanyZip"
+#define	SET_CONTACT_COMPANY_CITY		"CompanyCity"
+#define	SET_CONTACT_COMPANY_STATE		"CompanyState"
+#define	SET_CONTACT_COMPANY_COUNTRY		"CompanyCountry"
+#define	SET_CONTACT_COMPANY_HOMEPAGE	"CompanyHomepage"
+// database settings (about psp)
+#define	SET_CONTACT_ABOUT				"About"
+#define	SET_CONTACT_MYNOTES				"MyNotes"
+// database settings (... psp)
+#define	SET_CONTACT_PHONE				"Phone"
+#define	SET_CONTACT_FAX					"Fax"
+#define	SET_CONTACT_CELLULAR			"Cellular"
+#define	SET_CONTACT_EMAIL				"e-mail"
+#define	SET_CONTACT_EMAIL0				"e-mail0"
+#define	SET_CONTACT_EMAIL1				"e-mail1"
+#define	SET_CONTACT_HOMEPAGE			"Homepage"
+#define	SET_CONTACT_COMPANY_PHONE		"CompanyPhone"
+#define	SET_CONTACT_COMPANY_FAX			"CompanyFax"
+#define	SET_CONTACT_COMPANY_CELLULAR	"CompanyCellular"
+#define	SET_CONTACT_COMPANY_EMAIL		"Companye-mail"
+#define	SET_CONTACT_COMPANY_EMAIL0		"Companye-mail0"
+#define	SET_CONTACT_COMPANY_EMAIL1		"Companye-mail1"
+
+#define	SET_CONTACT_MYPHONE_VAL			"MyPhone%d"
+#define	SET_CONTACT_MYPHONE_CAT			"MyPhone%dCat"
+#define	SET_CONTACT_COMPANY_MYPHONE_VAL	"MyCompanyPhone%d"
+#define	SET_CONTACT_COMPANY_MYPHONE_CAT	"MyCompanyPhone%dCat"
+#define	SET_CONTACT_MYEMAIL_VAL			"Mye-mail%d"
+#define	SET_CONTACT_MYEMAIL_CAT			"Mye-mail%dCat"
+#define	SET_CONTACT_COMPANY_MYEMAIL_VAL	"MyCompanye-mail%d"
+#define	SET_CONTACT_COMPANY_MYEMAIL_CAT	"MyCompanye-mail%dCat"
+
+#define	SET_CONTACT_ADDEDTIME			"ContactAddTime"
+// default values for some of the options
+#define DEFVAL_GETCONTACTINFO_ENABLED	1
+
+#define DEFVAL_CLIST_EXTRAICON_GENDER	2
+#define DEFVAL_CLIST_EXTRAICON_COUNTRY	3
+#define DEFVAL_CLIST_EXTRAICON_HOMEPAGE 1
+#define DEFVAL_CLIST_EXTRAICON_EMAIL	1
+#define DEFVAL_CLIST_EXTRAICON_PHONE	1
+
+typedef struct CIDList
+{
+	INT		nID;
+	LPCSTR	pszText;
+	LPTSTR	ptszTranslated;
+
+} IDSTRLIST, *LPIDSTRLIST;
+
+INT_PTR GetMaritalList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetLanguageList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetCountryList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetOccupationList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetInterestsList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetPastList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetAffiliationsList(LPUINT pListSize, LPIDSTRLIST *pList);
+INT_PTR GetNamePrefixList(LPUINT pListSize, LPIDSTRLIST *pList);
+
+VOID SvcConstantsLoadModule(VOID);
+VOID SvcConstantsUnloadModule(VOID);
+
+#endif /* _SVC_CONSTANTS_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_contactinfo.cpp b/plugins/UserInfoEx/src/svc_contactinfo.cpp
new file mode 100644
index 0000000000..6a54880e67
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_contactinfo.cpp
@@ -0,0 +1,798 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_contactinfo.cpp $
+Revision       : $Revision: 203 $
+Last change on : $Date: 2010-09-26 18:21:04 +0400 (Вс, 26 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "m_contacts.h"
+#include "svc_timezone.h"
+#include "svc_timezone_old.h"
+#include "svc_contactinfo.h"
+#include "svc_gender.h"
+#include "Flags\svc_countrylistext.h"
+
+#define	CI_TCHAR(ci)	(((ci)->dwFlag & CNF_UNICODE) ? DBVT_WCHAR : DBVT_ASCIIZ)
+
+#define NAMEORDERCOUNT 8
+static BYTE gNameOrder[NAMEORDERCOUNT];	// name order as set up for contact list
+
+/**
+ * This function translates the DBVARIANT structure to an CONTACTINFO structure
+ * and keeps the original data type.
+ *
+ * @warning	ci MUST NOT be NULL and dbv must be freed by caller on failure!
+ *					
+ * @param	dbv			- DBVARIANT to take the data for translation from
+ * @param	ci			- CONTACTINFO structure to translate to
+ *
+ * @retval	0			- success
+ * @retval	1			- failure
+ **/
+static FORCEINLINE INT_PTR VarToVarCI(const DBVARIANT *dbv, CONTACTINFO *ci)
+{
+	switch (dbv->type) {
+	case DBVT_ASCIIZ:
+	case DBVT_WCHAR:	{
+			// string translation is to be done by caller!!!
+			ci->pszVal = dbv->ptszVal;
+			ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+		} break;
+
+	case DBVT_BYTE:		{
+			ci->type = CNFT_BYTE;
+			ci->bVal = dbv->bVal;
+		} break;
+
+	case DBVT_WORD:		{
+			ci->type = CNFT_WORD;
+			ci->wVal = dbv->wVal;
+		} break;
+
+	case DBVT_DWORD:	{
+			ci->type = CNFT_DWORD;
+			ci->dVal = dbv->dVal;
+		} break;
+
+	default:			{
+			ci->type = 0;
+		}
+	}
+	return ci->type == 0;
+}
+
+/**
+ * This function tries to read a setting from the contact's protocol module.
+ *
+ * @warning	ci MUST NOT be NULL!
+ *
+ * @param	ci			- pointer to a CONTACTINFO structure holding information about the contact
+ * @param	pszSetting	- the desired setting to read
+ *
+ * @retval	0 - if setting was found and read correctly
+ * @retval	1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCIVar(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+	DBVARIANT dbv;
+	
+	if (DB::Setting::Get(ci->hContact, ci->szProto, pszSetting, &dbv, CI_TCHAR(ci)) == 0) {
+		if (VarToVarCI(&dbv, ci)) {
+			// On a error, we need to make sure, read data is cleared out!
+			DB::Variant::Free(&dbv);
+		}
+	}
+	else {
+		ci->type = 0;
+	}
+	return ci->type == 0;
+}
+
+/**
+ * This function tries to read a setting from a certain module (e.g. USERINFO) and if failed it 
+ * tries once again with the baseprotocol of the contact (e.g. ICQ). If nothing was found anyway
+ * and this is an metacontact it can have a look into subcontacts to retrieve the information.
+ * This depends on the settings the user did.
+ *
+ * @warning	ci MUST NOT be NULL!
+ *
+ * @param	ci			- pointer to a CONTACTINFO structure holding information about the contact
+ * @param	pszSetting	- the desired setting to read
+ *
+ * @retval	0 - if setting was found and read correctly
+ * @retval	1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCIVarEx(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+	DBVARIANT dbv;
+
+	if (DB::Setting::GetEx(ci->hContact, USERINFO, ci->szProto, pszSetting, &dbv, CI_TCHAR(ci)) == 0) {
+		if (VarToVarCI(&dbv, ci)) {
+			// On a error, we need to make sure, read data is cleared out!
+			DB::Variant::Free(&dbv);
+		}
+	}
+	else {
+		ci->type = 0;
+	}
+	return ci->type == 0;
+}
+
+/**
+ * This function tries to read a Language from a certain module (e.g. USERINFO) and if failed it 
+ * tries once again with the baseprotocol of the contact (e.g. ICQ). If nothing was found anyway
+ * and this is an metacontact it can have a look into subcontacts to retrieve the information.
+ * This depends on the settings the user did.
+ *
+ * @warning	ci MUST NOT be NULL!
+ *
+ * @param	ci			- pointer to a CONTACTINFO structure holding information about the contact
+ * @param	pszSetting	- the desired setting to read
+ *
+ * @retval	0 - if setting was found and read correctly
+ * @retval	1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCILangEx(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+	if (0 == GCIVarEx(ci, pszSetting)) {
+		if(ci->type!= CNFT_ASCIIZ) {
+			//lang was safed in database as code
+			LPIDSTRLIST pList;
+			UINT nList, i, res = 0;
+			switch (ci->type) {
+				case CNFT_BYTE:		res = ci->bVal;	break;
+				case CNFT_WORD:		res = ci->wVal;	break;
+				case CNFT_DWORD:	res = ci->dVal;	break;
+				default:			return 1;
+			}
+			GetLanguageList(&nList, &pList);
+			for(i = 0; i<nList; i++) {
+				if(pList[i].nID == res)	{
+					//use untranslate string (pszText member)
+					ci->pszVal = (ci->dwFlag & CNF_UNICODE)
+								? (LPTSTR) mir_a2u(pList[i].pszText)
+								: (LPTSTR) mir_strdup(pList[i].pszText);
+					ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+					return 0;
+				}
+			} /*end for*/
+			ci->type	= 0;
+			ci->pszVal	= NULL;
+		}
+	}
+	else {
+		ci->type = 0;
+	}
+	return ci->type == 0;
+}
+
+/**
+ * This function read a setting from the baseprotocol of the contact (e.g. ICQ). 
+ *
+ * @warning	ci MUST NOT be NULL!
+ *
+ * @param	ci			- pointer to a CONTACTINFO structure holding information about the contact
+ * @param	pszSetting	- the desired setting to read
+ *
+ * @retval	0 - if setting was found and read correctly
+ * @retval	1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCIStr(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+	const BYTE type = CI_TCHAR(ci);
+	DBVARIANT dbv;
+	
+	if (DB::Setting::Get(ci->hContact, ci->szProto, pszSetting, &dbv, type) == 0) {
+		if (DB::Variant::dbv2String(&dbv, type) == 0) {
+			ci->pszVal = dbv.ptszVal;
+		}
+		else {
+			DB::Variant::Free(&dbv);
+			ci->pszVal = NULL;
+		}
+	}
+	else {
+		ci->pszVal = NULL;
+	}
+	ci->type = (ci->pszVal) ? CNFT_ASCIIZ : 0;
+	return ci->type == 0;
+}
+
+/**
+ * Format the full name for the contact.
+ *
+ * @params	ci			- CONTACTINFO structure
+ *
+ * @retval	0 - if setting was found and read correctly
+ * @retval	1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCIFirstLast(CONTACTINFO *ci)
+{
+	DBVARIANT dbvf, dbvl;
+	size_t cbf, cbl;
+
+	BYTE type = CI_TCHAR(ci);
+
+	if (DB::Setting::GetEx(ci->hContact, USERINFO, ci->szProto, SET_CONTACT_FIRSTNAME, &dbvf, type))
+	{
+		dbvf.type = DBVT_DELETED;
+	}
+	if (DB::Setting::GetEx(ci->hContact, USERINFO, ci->szProto, SET_CONTACT_LASTNAME, &dbvl, type))
+	{
+		dbvl.type = DBVT_DELETED;
+	}
+
+	if (type == DBVT_WCHAR)
+	{
+		// both firstname and lastname are valid
+		if (dbvf.type == DBVT_WCHAR && dbvl.type == DBVT_WCHAR)
+		{
+			cbf = mir_wcslen(dbvf.pwszVal);
+			cbl = mir_wcslen(dbvl.pwszVal);
+
+			ci->pszVal = (LPTSTR) mir_alloc((cbf + cbl + 2) * sizeof(WCHAR));
+			if (ci->pszVal)
+			{
+				mir_snwprintf((LPWSTR) ci->pszVal, cbf + cbl + 2, L"%s %s", dbvf.pwszVal, dbvl.pwszVal);
+			}
+			DB::Variant::Free(&dbvf);
+			DB::Variant::Free(&dbvl);
+		}
+		// set firstname as result
+		else if (dbvf.type == DBVT_WCHAR)
+		{
+			ci->pszVal = (LPTSTR) dbvf.pwszVal;
+			DB::Variant::Free(&dbvl);
+		}
+		// set lastname as result
+		else if (dbvl.type == DBVT_WCHAR)
+		{
+			ci->pszVal = (LPTSTR) dbvl.pwszVal;
+			DB::Variant::Free(&dbvf);
+		}
+		else
+		{
+			ci->pszVal = NULL;
+			DB::Variant::Free(&dbvf);
+			DB::Variant::Free(&dbvl);
+		}
+	}
+	else
+	{
+		// both firstname and lastname are valid
+		if (dbvf.type == DBVT_ASCIIZ && dbvl.type == DBVT_ASCIIZ)
+		{
+			cbf = mir_strlen(dbvf.pszVal);
+			cbl = mir_strlen(dbvl.pszVal);
+
+			ci->pszVal = (LPTSTR) mir_alloc((cbf + cbl + 2) * sizeof(CHAR));
+			if (ci->pszVal)
+			{
+				mir_snprintf((LPSTR) ci->pszVal, cbf + cbl + 2, "%s %s", dbvf.pszVal, dbvl.pszVal);
+			}
+			DB::Variant::Free(&dbvf);
+			DB::Variant::Free(&dbvl);
+		}
+		// set firstname as result
+		else if (dbvf.type == DBVT_ASCIIZ)
+		{
+			ci->pszVal = (LPTSTR) dbvf.pszVal;
+			DB::Variant::Free(&dbvl);
+		}
+		// set lastname as result
+		else if (dbvl.type == DBVT_ASCIIZ)
+		{
+			ci->pszVal = (LPTSTR) dbvl.pszVal;
+			DB::Variant::Free(&dbvf);
+		}
+		else
+		{
+			ci->pszVal = NULL;
+			DB::Variant::Free(&dbvf);
+			DB::Variant::Free(&dbvl);
+		}
+	}
+	ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+	return ci->type == 0;
+}
+
+/**
+ * return the country name
+ *
+ * @param	ci			- pointer to a CONTACTINFO structure holding information about the contact
+ * @param	pszSetting	- the desired setting to read the countrys id from
+ *
+ * @retval	0 - if setting was found and read correctly
+ * @retval	1 - if any error occured or setting was not found
+ **/
+static FORCEINLINE INT_PTR GCICountry(CONTACTINFO *ci, LPCSTR pszSetting)
+{
+	if (0 == GCIVarEx(ci, pszSetting)) {
+		if (ci->type != CNFT_ASCIIZ) {
+			// country id was safed in database as code
+			UINT res = 0;
+			switch (ci->type) {
+				case CNFT_WORD:		res = ci->wVal;	break;
+				case CNFT_DWORD:	res = ci->dVal;	break;
+				default:			return 1;
+			}
+
+//			LPSTR szCountry = res ? (LPSTR)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, res, 0) : NULL;
+			LPSTR szCountry = res ? (LPSTR)ServiceGetCountryByNumber(res, 0) : NULL;
+			if (szCountry) {
+				ci->pszVal = (ci->dwFlag & CNF_UNICODE) 
+							 ? (LPTSTR) mir_a2u(szCountry) 
+							 : (LPTSTR) mir_strdup(szCountry);
+			}
+			else {
+				ci->pszVal = NULL;
+			}
+			ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+		}
+	}
+	else {
+		ci->type = 0;
+	}
+	return ci->type == 0;
+}
+
+/**
+ * This is the service procedure to retrieve contact information
+ *
+ * @param	wParam		- not used
+ * @param	lParam		- pointer to a CONTACTINFO structure which tells what information is desired
+ *
+ * @retval	0 - if contact information was found and read correctly 
+ * @retval	1 - if any error occured or setting was not found
+ **/
+INT_PTR GetContactInfo(WPARAM wParam, LPARAM lParam) 
+{
+	CONTACTINFO *ci = (CONTACTINFO*) lParam;
+	INT_PTR result;
+
+	if (ci && ci->cbSize == sizeof(CONTACTINFO) && (ci->szProto != NULL || (ci->szProto = DB::Contact::Proto(ci->hContact)) != NULL))
+	{
+		switch (ci->dwFlag & 0x7F) {
+
+		//
+		// contact name
+		//
+		case CNF_FIRSTNAME:		{
+				result = GCIVarEx(ci, SET_CONTACT_FIRSTNAME);
+			} break;
+
+		case CNF_LASTNAME:		{
+				result = GCIVarEx(ci, SET_CONTACT_LASTNAME);
+			} break;
+
+		case CNF_FIRSTLAST:		{
+				result = GCIVarEx(ci, SET_CONTACT_FIRSTLASTNAME);	//first try to read "FullName"
+				if(result) result = GCIFirstLast(ci);				//fallback to "FirstName" + "LastName"
+			} break;
+
+		case CNF_NICK:			{
+				result = GCIVarEx(ci, SET_CONTACT_NICK);
+			} break;
+
+		case CNF_CUSTOMNICK:	{
+				LPSTR s = ci->szProto;
+				ci->szProto = MOD_CLIST;
+				result = GCIVar(ci, SET_CONTACT_MYHANDLE);
+				ci->szProto = s;
+			} break;
+
+		case CNF_LANGUAGE1:			{
+				result = GCILangEx(ci, SET_CONTACT_LANG1);
+			} break;
+
+		case CNF_LANGUAGE2:			{
+				result = GCILangEx(ci, SET_CONTACT_LANG2);
+			} break;
+
+		case CNF_LANGUAGE3:			{
+				result = GCILangEx(ci, SET_CONTACT_LANG3);
+			} break;
+
+		//
+		// private contact
+		//
+		case CNF_STREET:		{
+				result = GCIVarEx(ci, SET_CONTACT_STREET);
+			} break;
+
+		case CNF_ZIP:			{
+				result = GCIVarEx(ci, SET_CONTACT_ZIP); 
+			} break;
+
+		case CNF_CITY:			{
+				result = GCIVarEx(ci, SET_CONTACT_CITY);
+			} break;
+
+		case CNF_STATE:			{
+				result = GCIVarEx(ci, SET_CONTACT_STATE);
+			} break;
+
+		case CNF_COUNTRY:		{
+				result = GCICountry(ci, SET_CONTACT_COUNTRY);
+			} break;
+
+		case CNF_PHONE:			{
+				result = GCIVarEx(ci, SET_CONTACT_PHONE);
+			} break;
+
+		case CNF_FAX:			{
+				result = GCIVarEx(ci, SET_CONTACT_FAX);
+			} break;
+
+		case CNF_CELLULAR:		{
+				result = GCIVarEx(ci, SET_CONTACT_CELLULAR);
+			} break;
+
+		case CNF_EMAIL:			{
+				result = GCIVarEx(ci, SET_CONTACT_EMAIL);
+			} break;
+
+		case CNF_HOMEPAGE:		{
+				result = GCIVarEx(ci, SET_CONTACT_HOMEPAGE);
+			} break;
+
+		//
+		// company information
+		//
+		case CNF_CONAME:		{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY);
+			} break;
+
+		case CNF_CODEPT:		{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_DEPARTMENT);
+			} break;
+
+		case CNF_COPOSITION:	{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_POSITION);
+			} break;
+
+		case CNF_COSTREET:		{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_STREET);
+			} break;
+
+		case CNF_COZIP:			{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_ZIP);
+			} break;
+
+		case CNF_COCITY:		{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_CITY);
+			} break;
+
+		case CNF_COSTATE:		{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_STATE);
+			} break;
+
+		case CNF_COCOUNTRY:		{
+				result = GCICountry(ci, SET_CONTACT_COMPANY_COUNTRY);
+			} break;
+
+		case CNF_COPHONE:		{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_PHONE);
+			} break;
+
+		case CNF_COFAX:			{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_FAX);
+			} break;
+
+		case CNF_COCELLULAR:	{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_CELLULAR);
+			} break;
+
+		case CNF_COEMAIL:		{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_EMAIL);
+			} break;
+
+		case CNF_COHOMEPAGE:	{
+				result = GCIVarEx(ci, SET_CONTACT_COMPANY_HOMEPAGE);
+			} break;
+
+		//
+		// personal information
+		//
+		case CNF_ABOUT:			{
+				result = GCIVarEx(ci, SET_CONTACT_ABOUT);
+			} break;
+
+		case CNF_MYNOTES:		{
+				result = GCIVarEx(ci, SET_CONTACT_MYNOTES);
+			} break;
+
+		case CNF_AGE:			{
+				result = GCIVarEx(ci, SET_CONTACT_AGE);
+			} break;	  // returns age (byte, 0==unspecified) ??
+
+		case CNF_GENDER:		{
+				ci->bVal = GenderOf(ci->hContact, ci->szProto);
+				ci->type = (ci->bVal != 0) ? CNFT_BYTE : 0;
+				result = ci->type == 0;
+			} break;
+
+		case CNF_BIRTHDAY:		{
+				MAnnivDate mda;
+				result = mda.DBGetBirthDate(ci->hContact, ci->szProto);
+				if (result == 0) {
+					ci->bVal = (BYTE) mda.Day();
+					ci->type = CNFT_BYTE;
+				}
+			} break;
+
+		case CNF_BIRTHMONTH:	{
+				MAnnivDate mda;
+				result = mda.DBGetBirthDate(ci->hContact, ci->szProto);
+				if (result == 0) {
+					ci->bVal = (BYTE) mda.Month();
+					ci->type = CNFT_BYTE;
+				}
+			} break;
+
+		case CNF_BIRTHYEAR:		{
+				MAnnivDate mda;
+				result = mda.DBGetBirthDate(ci->hContact, ci->szProto);
+				if (result == 0) {
+					ci->wVal = (WORD) mda.Year();
+					ci->type = CNFT_WORD;
+				}
+			} break;
+
+		case CNF_BIRTHDATE:		{
+				MAnnivDate mda;
+				result = mda.DBGetBirthDate(ci->hContact, ci->szProto);
+				if (result == 0) {
+					SYSTEMTIME st = mda.SystemTime();
+					ci->pszVal = NULL;
+					if (ci->dwFlag & CNF_UNICODE) {
+						WCHAR wszDate[80];
+						if (GetDateFormatW(LOCALE_USER_DEFAULT, wParam == 1 ? DATE_LONGDATE : DATE_SHORTDATE, &st, NULL, wszDate, SIZEOF(wszDate))) {
+							ci->pszVal = (LPTSTR)mir_wcsdup(wszDate);
+						}
+					}
+					else {
+						CHAR szDate[80];
+						if (GetDateFormatA(LOCALE_USER_DEFAULT, wParam == 1 ? DATE_LONGDATE : DATE_SHORTDATE, &st, NULL, szDate, SIZEOF(szDate))) {
+							ci->pszVal = (LPTSTR)mir_strdup(szDate);
+						}
+					}
+					ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+					result = ci->type == 0;
+				}
+			} break;
+
+		case CNF_TIMEZONE:		{
+				//use new core tz interface
+				if(tmi.prepareList) {
+					HANDLE hTz = tmi.createByContact(ci->hContact, TZF_KNOWNONLY);
+					if (hTz) {
+						LPTIME_ZONE_INFORMATION tzi = tmi.getTzi(hTz);
+						int offset = tzi->Bias + tzi->StandardBias;
+
+						char str[80];
+						mir_snprintf(str, SIZEOF(str), offset ? "UTC%+d:%02d" : "UTC", offset / -60, abs(offset % 60));
+						ci->pszVal =	ci->dwFlag & CNF_UNICODE
+										? (TCHAR*)mir_a2u(str) 
+										: (TCHAR*)mir_strdup(str);
+						ci->type = CNFT_ASCIIZ;
+						return 0;
+					}
+					else {
+						ci->pszVal = NULL;
+					}
+				}
+				//fallback use old UIEX method
+				else {
+					CTimeZone* ptz = GetContactTimeZone(ci->hContact, ci->szProto);
+					if (ptz) {
+						if (ci->dwFlag & CNF_UNICODE) {
+							ci->pszVal = (LPTSTR) mir_t2u(ptz->ptszDisplay);
+						}
+						else {
+							ci->pszVal = (LPTSTR) mir_t2a(ptz->ptszDisplay);
+						}
+					}
+					else {
+						/* If a timezone does not exist in CTzMgr, it is a invalid timezone,
+						because Windows and CTzMgr know all existing timezones and it
+						would not be shown anywhere anyway as UserInfoEx displays only 
+						known windows timezones in the details dialog!
+						*/
+						ci->pszVal = NULL;
+					}
+				}
+				ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+				result = ci->type == 0;
+			} break;
+
+		//
+		// information about IM specific stuff
+		//
+		case CNF_UNIQUEID:		{
+				// protocol must define a PFLAG_UNIQUEIDSETTING
+				result = CallProtoService(ci->szProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+				if (result != CALLSERVICE_NOTFOUND && result != NULL) {
+					result = GCIVar(ci, (LPCSTR) result);
+				}
+			} break;
+
+		case CNF_DISPLAYUID:	{
+				if (!GCIVar(ci, "display_uid"))
+					result=0;
+				else {
+					result = CallProtoService(ci->szProto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0);
+					if (result != CALLSERVICE_NOTFOUND && result != NULL) {
+						result = GCIVar(ci, (LPCSTR) result);
+					}
+				}
+			} break;
+
+		case CNF_DISPLAYNC:
+		case CNF_DISPLAY:		{
+				INT i;
+				for (i = 0; i < NAMEORDERCOUNT; i++) {
+					switch (gNameOrder[i]) {
+					case 0: // custom name
+						{
+							// make sure we aren't in CNF_DISPLAYNC mode
+							// don't get custom name for NULL contact
+							if (ci->hContact != NULL && (ci->dwFlag & 0x7F) == CNF_DISPLAY) {
+								BYTE dwFlag = ci->dwFlag;
+								ci->dwFlag = (ci->dwFlag & CNF_UNICODE) | CNF_CUSTOMNICK;
+								if (!GetContactInfo(NULL, (LPARAM)ci)) {
+									ci->dwFlag = dwFlag;
+									return 0;
+								}
+								ci->dwFlag = dwFlag;
+							}
+						} break;
+					case 1: // nick
+						{
+							if (!GCIVarEx(ci, SET_CONTACT_NICK))
+								return 0;
+						} break;
+					case 2: // First Name
+						{
+							if (!GCIVarEx(ci, SET_CONTACT_FIRSTNAME))
+								return 0;
+						} break;
+					case 3: // E-mail
+						{
+							if (!GCIVarEx(ci, SET_CONTACT_EMAIL))
+								return 0;
+						} break;
+					case 4: // Last Name
+						{
+							if (!GCIVarEx(ci, SET_CONTACT_LASTNAME))
+								return 0;
+						} break;
+					case 5: // Unique id
+						{
+							// protocol must define a PFLAG_UNIQUEIDSETTING
+							result = CallProtoService(ci->szProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+							if (result != CALLSERVICE_NOTFOUND && result != NULL) {
+								if (!GCIStr(ci, (LPCSTR) result))
+									return 0;
+							}
+						} break;
+					case 6: // first + last name
+						{
+							if (!GCIFirstLast(ci))
+								return 0;
+						} break;
+					default: // unknown contact
+						{
+							if (ci->dwFlag & CNF_UNICODE) {
+								ci->pszVal = (LPTSTR) mir_wcsdup(TranslateW(L"'(Unknown Contact)'"));
+							}
+							else {
+								ci->pszVal = (LPTSTR) mir_strdup(Translate("'(Unknown Contact)'"));
+							}
+							ci->type = (ci->pszVal != NULL) ? CNFT_ASCIIZ : 0;
+							return ci->type == 0;
+						}
+					}
+				}
+			}
+
+		default:				{
+				result = 1;
+			}
+		}
+	}
+	else
+	{
+		result = 1;
+	}
+	return result;
+}
+
+/**
+ * This is the implementation of the MS_DB_CONTACT_GETSETTING_STR_EX service.
+ *
+ * @param	wParam		- handle of the contact a setting was written for (must be NULL in this case)
+ * @param	lParam		- DBCONTACTGETSETTING structure holding information about the setting to read
+ *
+ * @retval	0 - success
+ * @retval	1 - error
+ **/
+static INT_PTR GetContactSettingStrExService(WPARAM wParam, LPARAM lParam)
+{
+	DBCONTACTGETSETTING *cgs = (DBCONTACTGETSETTING*)lParam;
+	return DB::Setting::GetEx((HANDLE)wParam, USERINFO, 
+		cgs->szModule, cgs->szSetting, cgs->pValue, cgs->pValue->type);
+}
+
+/**
+ * If the user changes the name order update the global value.
+ *
+ * @param	wParam		- handle of the contact a setting was written for (must be NULL in this case)
+ * @param	lParam		- DBCONTACTWRITESETTING structure holding information about the written data
+ * @return	0
+ **/
+static INT OnSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+	if ((HANDLE)wParam == NULL) {
+		DBCONTACTWRITESETTING *pdbcws = (DBCONTACTWRITESETTING*) lParam;
+		if (!mir_strcmp(pdbcws->szModule, "Contact") &&
+			!mir_strcmp(pdbcws->szSetting, "NameOrder"))
+		{
+			memcpy(gNameOrder, pdbcws->value.pbVal,pdbcws->value.cpbVal);
+		}
+	}
+	return 0;
+}
+
+/**
+ * Loads the module at startup and replaces the service.
+ *
+ * @param	none
+ * @return	nothing
+ **/
+VOID SvcContactInfoLoadModule()
+{
+	CreateServiceFunction(MS_DB_CONTACT_GETSETTING_STR_EX, GetContactSettingStrExService);
+	CreateServiceFunction(MS_CONTACT_GETCONTACTINFO, GetContactInfo);
+
+	DBVARIANT dbv;
+	if (DB::Setting::GetAString(NULL, "Contact", "NameOrder", &dbv)) {
+		BYTE i;
+		for (i = 0; i < NAMEORDERCOUNT; i++) {
+			gNameOrder[i] = i;
+		}
+	}
+	else {
+		memcpy(gNameOrder, dbv.pbVal, dbv.cpbVal);
+		DB::Variant::Free(&dbv);
+	}
+
+	HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnSettingChanged);
+}
diff --git a/plugins/UserInfoEx/src/svc_contactinfo.h b/plugins/UserInfoEx/src/svc_contactinfo.h
new file mode 100644
index 0000000000..3509858fcd
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_contactinfo.h
@@ -0,0 +1,36 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_contactinfo.h $
+Revision       : $Revision: 199 $
+Last change on : $Date: 2010-09-22 17:21:44 +0400 (Ср, 22 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCCONTACTS_H_INCLUDED_
+#define _UINFOEX_SVCCONTACTS_H_INCLUDED_
+
+INT_PTR		GetContactInfo(WPARAM wParam, LPARAM lParam);
+VOID		SvcContactInfoLoadModule(VOID);
+
+#endif /* _UINFOEX_SVCCONTACTS_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_email.cpp b/plugins/UserInfoEx/src/svc_email.cpp
new file mode 100644
index 0000000000..a9a0b47686
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_email.cpp
@@ -0,0 +1,409 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_email.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_email.h"
+
+static HANDLE ghMenuItem			= NULL;
+static HANDLE ghExtraIconDef		= INVALID_HANDLE_VALUE;
+static HANDLE ghExtraIconSvc		= INVALID_HANDLE_VALUE;
+
+static HANDLE hChangedHook			= NULL;
+static HANDLE hApplyIconHook		= NULL;
+static HANDLE hRebuildIconsHook		= NULL;
+
+/**
+ * This function reads the email address of the contact.
+ *
+ * @param	hContact		- handle to contact to read email from
+ *
+ * @retval	email address
+ * @retval	NULL, if contact does not provide any email address
+ **/
+static LPSTR Get(HANDLE hContact)
+{
+	// ignore owner
+	if (hContact != NULL) 
+	{
+		LPCSTR pszProto = DB::Contact::Proto(hContact);
+		
+		if (pszProto != NULL) 
+		{
+			LPCSTR	e[2][4] = {
+				{ SET_CONTACT_EMAIL,			SET_CONTACT_EMAIL0,			SET_CONTACT_EMAIL1,			"Mye-mail0"},
+				{ SET_CONTACT_COMPANY_EMAIL,	SET_CONTACT_COMPANY_EMAIL0,	SET_CONTACT_COMPANY_EMAIL1,	"MyCompanye-mail0"}
+			};
+
+			INT i, j;
+			LPSTR pszEMail;
+
+			for (i = 0; i < 2; i++)
+			{
+				for (j = 0; j < 4; j++)
+				{
+					pszEMail = DB::Setting::GetAStringEx(hContact, USERINFO, pszProto, e[i][j]);
+					if (pszEMail)
+					{
+						if (strchr(pszEMail, '@'))
+						{
+							return pszEMail;
+						}
+						mir_free(pszEMail);
+					}
+				}
+			}
+		}
+	}
+	return NULL;
+}
+
+/**
+ * Service function that sends emails
+ *
+ * @param	wParam			- handle to contact to send an email to
+ * @param	lParam			- not used
+ *
+ * @retval	0 if email was sent
+ * @retval	1 if no email can be sent
+ **/
+static INT_PTR MenuCommand(WPARAM wParam,LPARAM lParam)
+{
+	INT result;
+	LPSTR val = NULL;
+
+	__try 
+	{
+		val = Get((HANDLE) wParam);
+		if (val)
+		{
+			LPSTR szUrl;
+			INT_PTR len;
+
+			len = mir_strlen(val) + strlen("mailto:");
+
+			szUrl = (LPSTR)_alloca(len + 1);
+
+			mir_snprintf(szUrl, len + 1, "mailto:%s", val);
+			mir_free(val);
+
+			result = CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+		}
+		else
+		{
+			result = 1;
+			MsgBox((HWND)lParam, MB_OK, LPGENT("Send e-mail"), NULL, LPGENT("Memory allocation error!"));
+		}
+	}
+	__except(GetExceptionCode()==EXCEPTION_ACCESS_VIOLATION ? 
+		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) 
+	{
+		if (val)
+		{
+			mir_free(val);
+		}
+
+		result = 1;
+		MsgErr((HWND)lParam, LPGENT("Memory allocation error!"));
+	}
+	return result;
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+static INT OnCListRebuildIcons(WPARAM wParam, LPARAM lParam)
+{
+	HICON hIcon		= IcoLib_GetIcon(ICO_BTN_EMAIL);
+	ghExtraIconDef	= (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+	Skin_ReleaseIcon(hIcon);
+	return 0;
+}
+
+/**
+ * Notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param	wParam			- handle to the contact whose extra icon is to apply
+ * @param	lParam			- not used
+ **/
+static INT OnCListApplyIcons(WPARAM wParam, LPARAM lParam)
+{
+	LPSTR val = Get((HANDLE)wParam);
+
+	if (!myGlobals.ExtraIconsServiceExist)
+	{
+		IconExtraColumn iec;
+
+		iec.cbSize = sizeof(IconExtraColumn);
+		iec.ColumnType = EXTRA_ICON_EMAIL;
+		if (val) 
+		{
+			iec.hImage = ghExtraIconDef;
+			mir_free(val);
+		}
+		else 
+		{
+			iec.hImage = INVALID_HANDLE_VALUE;
+			mir_free(val);
+		}
+		CallService(MS_CLIST_EXTRA_SET_ICON, wParam, (LPARAM)&iec);
+	}
+	else
+	{
+		EXTRAICON ico;
+		ico.cbSize=sizeof(ico);
+		ico.hContact=(HANDLE)wParam;
+		ico.hExtraIcon=ghExtraIconSvc;
+		ico.icoName=val?ICO_BTN_EMAIL:(char *)0;
+		mir_free(val);
+		CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+	}
+	return 0;
+}
+
+/**
+ * Notification handler for changed contact settings
+ *
+ * @param	wParam			- (HANDLE)hContact
+ * @param	lParam			- (DBCONTACTWRITESETTING*)pdbcws
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+	if (hContact && pdbcws && pdbcws->szSetting && 
+			((pdbcws->value.type & DBVTF_VARIABLELENGTH) || (pdbcws->value.type == DBVT_DELETED)) &&
+			(!mir_strncmp(pdbcws->szSetting, SET_CONTACT_EMAIL, 6) ||
+			 !mir_strncmp(pdbcws->szSetting, SET_CONTACT_COMPANY_EMAIL, 13) ||
+			 !mir_strncmp(pdbcws->szSetting, "mye-mail0", 9)))
+	{
+		OnCListApplyIcons((WPARAM)hContact, 0);
+	}
+	return 0;
+}
+
+/**
+ * This function decides whether to show menuitem for sending emails or not.
+ *
+ * @param	wParam			- handle to contact to send an email to
+ * @param	lParam			- not used
+ *
+ * @return	always 0
+ **/
+static INT OnPreBuildMenu(WPARAM wParam, LPARAM lParam)
+{
+	CLISTMENUITEM mi;
+	LPSTR val;
+	
+	ZeroMemory(&mi, sizeof(mi));
+	mi.cbSize = sizeof(mi);
+	mi.flags = CMIM_FLAGS;
+
+	val = Get((HANDLE)wParam);
+	if (val) 
+	{
+		mir_free(val);
+	}
+	else 
+	{
+		mi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+	}
+	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)ghMenuItem, (LPARAM)&mi);
+	return 0;
+}
+
+/***********************************************************************************************************
+ * public Module Interface functions
+ ***********************************************************************************************************/
+
+/**
+ * This function enables or disables menuitems.
+ **/
+VOID SvcEMailRebuildMenu()
+{
+	static HANDLE hPrebuildMenuHook = NULL;
+
+	if (DB::Setting::GetByte(SET_EXTENDED_EMAILSERVICE, TRUE)) 
+	{
+		if (!hPrebuildMenuHook) 
+		{
+			hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildMenu);
+		}
+
+		if (!ghMenuItem) 
+		{
+			CLISTMENUITEM mi;
+
+			// insert contact menuitem
+			ZeroMemory(&mi, sizeof(mi));
+			mi.cbSize = sizeof(mi);
+			mi.position = -2000010000;
+			mi.hIcon = IcoLib_GetIcon(ICO_BTN_EMAIL);
+			mi.pszName = "&E-mail";
+			mi.pszService = MS_EMAIL_SENDEMAIL;
+			ghMenuItem = Menu_AddContactMenuItem(&mi);
+		}
+	}
+	else 
+	{
+		if (hPrebuildMenuHook) 
+		{
+			UnhookEvent(ME_CLIST_PREBUILDCONTACTMENU);
+			hPrebuildMenuHook = NULL;
+		}
+		if (ghMenuItem) 
+		{
+			CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)ghMenuItem, NULL);
+			ghMenuItem = NULL;
+		}
+	}
+}
+
+/**
+ * Force all icons to be reloaded.
+ *
+ * @param	wParam			- handle to the contact whose extra icon is to apply
+ * @param	lParam			- not used
+ **/
+VOID SvcEMailApplyCListIcons()
+{
+	HANDLE hContact;
+
+	//walk through all the contacts stored in the DB
+	for (hContact = DB::Contact::FindFirst();	hContact != NULL;	hContact = DB::Contact::FindNext(hContact))
+	{
+		OnCListApplyIcons((WPARAM)hContact, 0);
+	}
+}
+
+/**
+ * Enable or disable the replacement of clist extra icons.
+ *
+ * @param	bEnable			- determines whether icons are enabled or not
+ * @param	bUpdateDB		- if true the database setting is updated, too.
+ **/
+VOID SvcEMailEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB) 
+{
+	if (myGlobals.HaveCListExtraIcons)
+	{
+		if (bUpdateDB)
+		{
+			DB::Setting::WriteByte(SET_CLIST_EXTRAICON_EMAIL, bEnable);
+		}
+
+		if (bEnable)	// E-mail checkt
+		{
+			// hook events
+			if (hChangedHook == NULL) 
+			{
+				hChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+			}
+			if (hApplyIconHook == NULL) 
+			{
+				hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, OnCListApplyIcons);
+			}
+			if (myGlobals.ExtraIconsServiceExist)
+			{
+				if (ghExtraIconSvc == INVALID_HANDLE_VALUE)
+				{
+					EXTRAICON_INFO ico;
+					
+					ZeroMemory(&ico, sizeof(ico));
+					ico.cbSize = sizeof(ico);
+					ico.type = EXTRAICON_TYPE_ICOLIB;
+					ico.name = "email";	//must be the same as the group name in extraicon
+					ico.description= "E-mail (uinfoex)";
+					ico.descIcon = ICO_BTN_EMAIL;
+					ghExtraIconSvc = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+					ZeroMemory(&ico,sizeof(ico));
+				}
+			}
+			else if (hRebuildIconsHook == NULL) 
+			{
+				hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, OnCListRebuildIcons);
+				OnCListRebuildIcons(0, 0);
+			}
+		}
+		else	// E-mail uncheckt
+		{
+			if (hChangedHook)
+			{
+				UnhookEvent(hChangedHook); 
+				hChangedHook = NULL;
+			}			
+			if (hApplyIconHook)
+			{
+				UnhookEvent(hApplyIconHook); 
+				hApplyIconHook = NULL;
+			}			
+			if (hRebuildIconsHook)
+			{
+				UnhookEvent(hRebuildIconsHook); 
+				hRebuildIconsHook = NULL;
+			}
+		}
+		SvcEMailApplyCListIcons();
+	}
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcEMailOnModulesLoaded()
+{
+	SvcEMailEnableExtraIcons(
+		myGlobals.ExtraIconsServiceExist || 
+		DB::Setting::GetByte(SET_CLIST_EXTRAICON_EMAIL, 
+		DEFVAL_CLIST_EXTRAICON_EMAIL), FALSE);
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcEMailLoadModule()
+{
+	if (DB::Setting::GetByte(SET_EXTENDED_EMAILSERVICE, TRUE)) {
+		// create own email send command
+		if (!myDestroyServiceFunction(MS_EMAIL_SENDEMAIL))
+			CreateServiceFunction(MS_EMAIL_SENDEMAIL, MenuCommand);
+	}
+}
+
+/**
+ * This function unloads the Email module.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID SvcEMailUnloadModule()
+{	
+	// unhook event handlers
+	UnhookEvent(hChangedHook);		hChangedHook		= NULL;
+	UnhookEvent(hApplyIconHook);	hApplyIconHook		= NULL;
+	UnhookEvent(hRebuildIconsHook);	hRebuildIconsHook	= NULL;
+}
diff --git a/plugins/UserInfoEx/src/svc_email.h b/plugins/UserInfoEx/src/svc_email.h
new file mode 100644
index 0000000000..5544f247f3
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_email.h
@@ -0,0 +1,39 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_email.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCEMAIL_H_INCLUDED_
+#define _UINFOEX_SVCEMAIL_H_INCLUDED_
+
+VOID SvcEMailRebuildMenu();
+VOID SvcEMailEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB = FALSE);
+VOID SvcEMailOnModulesLoaded();
+VOID SvcEMailLoadModule();
+VOID SvcEMailUnloadModule();
+
+#endif /* _UINFOEX_SVCEMAIL_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_gender.cpp b/plugins/UserInfoEx/src/svc_gender.cpp
new file mode 100644
index 0000000000..433a7664b9
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_gender.cpp
@@ -0,0 +1,284 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_gender.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_contacts.h"
+
+static HANDLE ghExtraIconF			= INVALID_HANDLE_VALUE;
+static HANDLE ghExtraIconM			= INVALID_HANDLE_VALUE;
+static HANDLE ghExtraIconSvc		= INVALID_HANDLE_VALUE;
+
+static HANDLE hChangedHook			= NULL;
+static HANDLE hApplyIconHook		= NULL;
+static HANDLE hRebuildIconsHook		= NULL;
+
+BYTE GenderOf(HANDLE hContact, LPCSTR pszProto)
+{
+	DBVARIANT dbv;
+
+	if (DB::Setting::GetAsIsEx(hContact, USERINFO, pszProto, SET_CONTACT_GENDER, &dbv) == 0)
+	{
+		// gender must be byte and either M or F
+		if (dbv.type == DBVT_BYTE && (dbv.bVal == 'M' || dbv.bVal == 'F'))
+		{
+			return dbv.bVal;	
+		}
+		DB::Variant::Free(&dbv);
+	}
+	return 0;
+}
+
+/**
+ * This function gets the gender of the contact from the database.
+ *
+ * @param	hContact		- handle to contact to read email from
+ *
+ * @retval	F	- contact is female
+ * @retval	M	- contact is male
+ * @retval	0	- contact does not provide its gender
+ **/
+BYTE GenderOf(HANDLE hContact)
+{
+	return GenderOf(hContact, DB::Contact::Proto(hContact));
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+static INT OnCListRebuildIcons(WPARAM wParam, LPARAM lParam)
+{
+	HICON hIcon		= IcoLib_GetIcon(ICO_COMMON_FEMALE);
+	ghExtraIconF	= (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+	Skin_ReleaseIcon(hIcon);
+	hIcon			= IcoLib_GetIcon(ICO_COMMON_MALE);
+	ghExtraIconM	= (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+	Skin_ReleaseIcon(hIcon);
+	return 0;
+}
+
+/**
+ * Notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param	wParam			- handle to the contact whose extra icon is to apply
+ * @param	lParam			- not used
+ **/
+static INT OnCListApplyIcons(HANDLE hContact, LPARAM)
+{
+	if (myGlobals.ExtraIconsServiceExist && (ghExtraIconSvc != INVALID_HANDLE_VALUE))
+	{
+		EXTRAICON ico;
+
+		ZeroMemory(&ico, sizeof(ico));
+		ico.cbSize = sizeof(ico);
+		ico.hContact = hContact;
+		ico.hExtraIcon = ghExtraIconSvc;
+		switch (GenderOf(hContact)) 
+		{
+		case 'M': 
+			ico.icoName = ICO_COMMON_MALE;
+			break;
+		case 'F': 
+			ico.icoName = ICO_COMMON_FEMALE;
+			break;
+		default:
+			ico.icoName = NULL;
+		}
+		CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+	}
+	else
+	{
+		IconExtraColumn iec;
+
+		iec.ColumnType = DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER, DEFVAL_CLIST_EXTRAICON_GENDER);
+		if ((BYTE)iec.ColumnType != -1) 
+		{
+			iec.cbSize = sizeof(IconExtraColumn);
+			switch (GenderOf(hContact)) 
+			{
+			case 'M': 
+				iec.hImage = ghExtraIconM; 
+				break;
+			case 'F': 
+				iec.hImage = ghExtraIconF; 
+				break;
+			default:
+				iec.hImage = INVALID_HANDLE_VALUE;
+			}
+			CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+		}
+	}
+	return 0;
+}
+
+/**
+ * Notification handler for changed contact settings
+ *
+ * @param	wParam			- (HANDLE)hContact
+ * @param	lParam			- (DBCONTACTWRITESETTING*)pdbcws
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+	if (hContact && pdbcws && (pdbcws->value.type <= DBVT_BYTE) && !mir_stricmp(pdbcws->szSetting, SET_CONTACT_GENDER))
+	{
+		OnCListApplyIcons(hContact, 0);
+	}
+	return 0;
+}
+
+/***********************************************************************************************************
+ * public Module Interface functions
+ ***********************************************************************************************************/
+
+/**
+ * Force all icons to be reloaded.
+ *
+ * @param	wParam			- handle to the contact whose extra icon is to apply
+ * @param	lParam			- not used
+ **/
+VOID SvcGenderApplyCListIcons()
+{
+	HANDLE hContact;
+
+	//walk through all the contacts stored in the DB
+	for (hContact = DB::Contact::FindFirst();	hContact != NULL;	hContact = DB::Contact::FindNext(hContact))
+	{
+		OnCListApplyIcons(hContact, 0);
+	}
+}
+
+/**
+ * Enable or disable the replacement of clist extra icons.
+ *
+ * @param	bEnable			- determines whether icons are enabled or not
+ * @param	bUpdateDB		- if true the database setting is updated, too.
+ **/
+VOID SvcGenderEnableExtraIcons(BYTE bColumn, BOOLEAN bUpdateDB) 
+{
+	bool bEnable = (bColumn!=((BYTE)-1));
+	if (myGlobals.HaveCListExtraIcons)
+	{
+		if (bUpdateDB)
+		{
+			if (myGlobals.ExtraIconsServiceExist)
+			{
+				DB::Setting::WriteByte(SET_CLIST_EXTRAICON_GENDER2, bColumn);
+			}
+			else
+			{
+				DB::Setting::WriteByte(SET_CLIST_EXTRAICON_GENDER, bColumn);
+			}
+		}
+
+		if (bEnable)	// Gender checkt or dropdown select
+		{
+			if (myGlobals.ExtraIconsServiceExist)
+			{
+				if (ghExtraIconSvc == INVALID_HANDLE_VALUE)
+				{
+					EXTRAICON_INFO ico;
+					
+					ZeroMemory(&ico, sizeof(ico));
+					ico.cbSize = sizeof(ico);
+					ico.type = EXTRAICON_TYPE_ICOLIB;
+					ico.name = "gender";	//must be the same as the group name in extraicon
+					ico.description="Gender (uinfoex)";
+					ico.descIcon = ICO_COMMON_MALE;
+					ghExtraIconSvc = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+				}
+			}
+			else
+			{
+				if (hRebuildIconsHook == NULL) 
+				{
+					hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, OnCListRebuildIcons);
+					OnCListRebuildIcons(0, 0);
+				}
+			}
+			// hook events
+			if (hChangedHook == NULL) 
+			{
+				hChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+			}
+			if (hApplyIconHook == NULL) 
+			{
+				hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcons);
+			}
+		}
+		else
+		{
+			if (hChangedHook)
+			{
+				UnhookEvent(hChangedHook); 
+				hChangedHook = NULL;
+			}
+			if (hApplyIconHook)
+			{
+				UnhookEvent(hApplyIconHook); 
+				hApplyIconHook = NULL;
+			}
+			if (hRebuildIconsHook)
+			{
+				UnhookEvent(hRebuildIconsHook); 
+				hRebuildIconsHook = NULL;
+			}
+		}
+		SvcGenderApplyCListIcons();
+	}
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcGenderLoadModule()
+{
+	if ( myGlobals.ExtraIconsServiceExist)
+	{
+		SvcGenderEnableExtraIcons(DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER2, 0), FALSE);
+	}
+	else
+	{
+		SvcGenderEnableExtraIcons(DB::Setting::GetByte(SET_CLIST_EXTRAICON_GENDER, DEFVAL_CLIST_EXTRAICON_GENDER), FALSE);
+	}
+}
+
+/**
+ * This function unloads the module.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID SvcGenderUnloadModule()
+{	
+	// unhook event handlers
+	UnhookEvent(hChangedHook);		hChangedHook		= NULL;
+	UnhookEvent(hApplyIconHook);	hApplyIconHook		= NULL;
+	UnhookEvent(hRebuildIconsHook);	hRebuildIconsHook	= NULL;
+}
diff --git a/plugins/UserInfoEx/src/svc_gender.h b/plugins/UserInfoEx/src/svc_gender.h
new file mode 100644
index 0000000000..d11ecdcd66
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_gender.h
@@ -0,0 +1,40 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_gender.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCGENDER_H_INCLUDED_
+#define _UINFOEX_SVCGENDER_H_INCLUDED_
+
+BYTE GenderOf(HANDLE hContact, LPCSTR pszProto);
+BYTE GenderOf(HANDLE hContact);
+
+VOID SvcGenderEnableExtraIcons(BYTE bColumn, BOOLEAN bUpdateDB);
+VOID SvcGenderLoadModule();
+VOID SvcGenderUnloadModule();
+
+#endif /* _UINFOEX_SVCGENDER_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_homepage.cpp b/plugins/UserInfoEx/src/svc_homepage.cpp
new file mode 100644
index 0000000000..77248960ac
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_homepage.cpp
@@ -0,0 +1,337 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_homepage.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+
+static HANDLE ghMenuItem			= NULL;
+static HANDLE ghExtraIconDef		= INVALID_HANDLE_VALUE;
+static HANDLE ghExtraIconSvc		= INVALID_HANDLE_VALUE;
+
+static HANDLE hChangedHook			= NULL;
+static HANDLE hApplyIconHook		= NULL;
+static HANDLE hRebuildIconsHook		= NULL;
+/**
+ * This function reads the homepage address of the contact.
+ *
+ * @param	hContact	- handle to contact to read email from
+ *
+ * @retval	URL to contacts homepage
+ * @retval	NULL if contact provides no homepage
+ **/
+static LPSTR Get(HANDLE hContact)
+{
+	// ignore owner
+	if (hContact != NULL) 
+	{
+		LPCSTR pszProto = DB::Contact::Proto(hContact);
+		
+		if (pszProto != NULL) 
+		{
+			LPCSTR	e[2] = { SET_CONTACT_HOMEPAGE, SET_CONTACT_COMPANY_HOMEPAGE };
+			LPSTR pszHomepage;
+
+			INT i;
+
+			for (i = 0; i < 2; i++)
+			{
+				pszHomepage = DB::Setting::GetAStringEx(hContact, USERINFO, pszProto, e[i]);
+				if (pszHomepage)
+					return pszHomepage;
+			}
+		}
+	}
+	return NULL;
+}
+
+/**
+ * Service function that opens the default browser and displays the homepage.
+ *
+ * @param	wParam		- handle to contact to send an email to
+ * @param	lParam		- not used
+ *
+ * @retval	0 if email was sent
+ * @retval	1 if no email can be sent
+ **/
+static INT_PTR MenuCommand(WPARAM wParam, LPARAM lParam)
+{
+	LPSTR szUrl = Get((HANDLE)wParam);
+
+	if (szUrl) 
+	{
+		CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+		mir_free(szUrl);
+	}
+	else
+	{
+		MessageBox((HWND)lParam, 
+			TranslateT("User has no valid homepage"),
+			TranslateT("View Homepage"), MB_OK);
+	}
+	return 0;
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+static INT OnCListRebuildIcons(WPARAM wParam, LPARAM lParam)
+{
+	HICON hIcon		= IcoLib_GetIcon(ICO_BTN_GOTO);
+	ghExtraIconDef	= (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+	Skin_ReleaseIcon(hIcon);
+	return 0;
+}
+
+/**
+ * Notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param	wParam		- handle to the contact whose extra icon is to apply
+ * @param	lParam		- not used
+ **/
+static INT OnCListApplyIcons(HANDLE hContact, LPARAM)
+{
+	LPSTR val = Get(hContact);
+
+	if (myGlobals.ExtraIconsServiceExist && (ghExtraIconSvc != INVALID_HANDLE_VALUE))
+	{
+		EXTRAICON ico;
+
+		ZeroMemory(&ico, sizeof(ico));
+		ico.cbSize = sizeof(ico);
+		ico.hContact = hContact;
+		ico.hExtraIcon = ghExtraIconSvc;
+		ico.icoName = (val) ? ICO_BTN_GOTO : NULL;
+		CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+	}
+	else
+	{
+		IconExtraColumn iec;
+
+		ZeroMemory(&iec, sizeof(iec));
+		iec.cbSize = sizeof(IconExtraColumn);
+		iec.ColumnType = EXTRA_ICON_WEB;
+		iec.hImage = (val) ? ghExtraIconDef : INVALID_HANDLE_VALUE;
+		CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+	}
+	MIR_FREE(val);
+	return 0;
+}
+
+/**
+ * Notification handler for changed contact settings
+ *
+ * @param	wParam		- (HANDLE)hContact
+ * @param	lParam		- (DBCONTACTWRITESETTING*)pdbcws
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+	if (hContact && pdbcws && pdbcws->szSetting && 
+			((pdbcws->value.type & DBVTF_VARIABLELENGTH) || (pdbcws->value.type == DBVT_DELETED)) &&
+			(!strncmp(pdbcws->szSetting, SET_CONTACT_HOMEPAGE, 8) ||
+			 !strncmp(pdbcws->szSetting, SET_CONTACT_COMPANY_HOMEPAGE, 15)))
+	{
+		OnCListApplyIcons(hContact, 0);
+	}
+	return 0;
+}
+
+/**
+ * This function decides whether to show menuitem for sending emails or not.
+ *
+ * @param	wParam		- handle to contact to send an email to
+ * @param	lParam		- not used
+ *
+ * @return	always 0
+ **/
+static INT OnPreBuildMenu(WPARAM wParam, LPARAM lParam)
+{
+	CLISTMENUITEM mi;
+	LPSTR val;
+	
+	ZeroMemory(&mi, sizeof(mi));
+	mi.cbSize = sizeof(mi);
+	mi.flags = CMIM_FLAGS;
+
+	val = Get((HANDLE)wParam);
+	if (val) 
+	{
+		MIR_FREE(val);
+	}
+	else 
+	{
+		mi.flags = CMIM_FLAGS | CMIF_HIDDEN;
+	}
+	CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)ghMenuItem, (LPARAM)&mi);
+	return 0;
+}
+
+/***********************************************************************************************************
+ * public Module Interface functions
+ ***********************************************************************************************************/
+
+/**
+ * enable or disable menuitem
+ *
+ * @param	not used
+ * @return	nothing
+ **/
+VOID SvcHomepageRebuildMenu()
+{
+	static HANDLE hPrebuildMenuHook = NULL;
+
+	if (!hPrebuildMenuHook)
+		hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildMenu);
+
+	if (!ghMenuItem) {
+		// insert contact menuitem
+		CLISTMENUITEM mi = { 0 };
+		ZeroMemory(&mi, sizeof(mi));
+		mi.cbSize = sizeof(mi);
+		mi.position = -2000010000;
+		mi.hIcon = IcoLib_GetIcon(ICO_BTN_GOTO);
+		mi.pszName = "&Homepage";
+		mi.pszService = MS_USERINFO_HOMEPAGE_OPENURL;
+		ghMenuItem = Menu_AddContactMenuItem(&mi);
+	}
+}
+
+/**
+ * Force all icons to be reloaded.
+ *
+ * @param	wParam		- handle to the contact whose extra icon is to apply
+ * @param	lParam		- not used
+ **/
+VOID SvcHomepageApplyCListIcons()
+{
+	HANDLE hContact;
+
+	//walk through all the contacts stored in the DB
+	for (hContact = DB::Contact::FindFirst();	hContact != NULL;	hContact = DB::Contact::FindNext(hContact))
+	{
+		OnCListApplyIcons(hContact, 0);
+	}
+}
+
+/**
+ * Enable or disable the replacement of clist extra icons.
+ *
+ * @param	bEnable		- determines whether icons are enabled or not
+ * @param	bUpdateDB	- if true the database setting is updated, too.
+ **/
+VOID SvcHomepageEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB) 
+{
+	if (myGlobals.HaveCListExtraIcons)
+	{
+		if (bUpdateDB)
+		{
+			DB::Setting::WriteByte(SET_CLIST_EXTRAICON_HOMEPAGE, bEnable);
+		}
+
+		if (bEnable) 
+		{
+			// hook events
+			if (hChangedHook == NULL) 
+			{
+				hChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+			}
+			if (hApplyIconHook == NULL) 
+			{
+				hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcons);
+			}
+			if (myGlobals.ExtraIconsServiceExist)
+			{
+				if (ghExtraIconSvc == INVALID_HANDLE_VALUE)
+				{
+					EXTRAICON_INFO ico;
+					
+					ZeroMemory(&ico, sizeof(ico));
+					ico.cbSize = sizeof(ico);
+					ico.type = EXTRAICON_TYPE_ICOLIB;
+					ico.name = "homepage";	//must be the same as the group name in extraicon
+					ico.description = "Homepage (uinfoex)";
+					ico.descIcon = ICO_BTN_GOTO;
+					ghExtraIconSvc = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+				}
+			}
+			else if (hRebuildIconsHook == NULL) 
+			{
+				hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, OnCListRebuildIcons);
+				OnCListRebuildIcons(0, 0);
+			}
+		}
+		else 
+		{
+			if (hChangedHook)
+			{
+				UnhookEvent(hChangedHook); 
+				hChangedHook = NULL;
+			}			
+			if (hApplyIconHook)
+			{
+				UnhookEvent(hApplyIconHook); 
+				hApplyIconHook = NULL;
+			}			
+			if (hRebuildIconsHook)
+			{
+				UnhookEvent(hRebuildIconsHook); 
+				hRebuildIconsHook = NULL;
+			}
+		}
+		SvcHomepageApplyCListIcons();
+	}
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ *
+ * @param	not used
+ * @return	nothing
+ **/
+VOID SvcHomepageLoadModule()
+{
+	CreateServiceFunction(MS_USERINFO_HOMEPAGE_OPENURL, MenuCommand);
+	SvcHomepageEnableExtraIcons(
+		myGlobals.ExtraIconsServiceExist ||
+		DB::Setting::GetByte(SET_CLIST_EXTRAICON_HOMEPAGE, DEFVAL_CLIST_EXTRAICON_HOMEPAGE), FALSE);
+}
+
+/**
+ * This function unloads the Email module.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID SvcHomepageUnloadModule()
+{	
+	// unhook event handlers
+	UnhookEvent(hChangedHook);		hChangedHook		= NULL;
+	UnhookEvent(hApplyIconHook);	hApplyIconHook		= NULL;
+	UnhookEvent(hRebuildIconsHook);	hRebuildIconsHook	= NULL;
+}
diff --git a/plugins/UserInfoEx/src/svc_homepage.h b/plugins/UserInfoEx/src/svc_homepage.h
new file mode 100644
index 0000000000..8ca7ce6ae9
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_homepage.h
@@ -0,0 +1,38 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_homepage.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SvcHomepage_H_INCLUDED_
+#define _UINFOEX_SvcHomepage_H_INCLUDED_
+
+VOID SvcHomepageRebuildMenu();
+VOID SvcHomepageEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB = FALSE);
+VOID SvcHomepageLoadModule();
+VOID SvcHomepageUnloadModule();
+
+#endif /* _UINFOEX_SvcHomepage_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_phone.cpp b/plugins/UserInfoEx/src/svc_phone.cpp
new file mode 100644
index 0000000000..fab967c9a1
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_phone.cpp
@@ -0,0 +1,309 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_phone.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_icq.h"
+
+enum EPhoneType 
+{
+	PHONE_NONE,
+	PHONE_NORMAL,
+	PHONE_SMS
+};
+
+static HANDLE ghMenuItem			= NULL;
+static HANDLE ghExtraIconDef[2]		= { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
+static HANDLE ghExtraIconSvc		= INVALID_HANDLE_VALUE;
+
+static HANDLE hChangedHook			= NULL;
+static HANDLE hApplyIconHook		= NULL;
+static HANDLE hRebuildIconsHook		= NULL;
+
+/**
+ * This function reads the contact's phone number from database and returns its type.
+ *
+ * @param	 hContact		- handle to contact to read email from
+ *
+ * @retval	PHONE_SMS:		The phone supports sms, so is a cellular
+ * @retval	PHONE_NORMAL:	The phone is a normal phone
+ * @retval	PHONE_NONE:		The contact does not provide any phone number
+ **/
+static INT_PTR Get(HANDLE hContact)
+{
+	INT_PTR nType = PHONE_NONE;
+
+	// ignore owner
+	if (hContact != NULL) 
+	{
+		LPCSTR pszProto = DB::Contact::Proto(hContact);
+		if (pszProto != NULL) 
+		{
+			LPCSTR	e[2][4] = {
+				{ SET_CONTACT_CELLULAR,			SET_CONTACT_PHONE,			"MyPhone0"			},
+				{ SET_CONTACT_COMPANY_CELLULAR,	SET_CONTACT_COMPANY_PHONE,	"MyCompanyPhone0"	}
+			};
+
+			INT i, j;
+			LPSTR pszPhone;
+
+			for (i = 0; (i < 2) && (nType == PHONE_NONE); i++)
+			{
+				for (j = 0; (j < 3) && (nType == PHONE_NONE); j++)
+				{
+					pszPhone = DB::Setting::GetAStringEx(hContact, USERINFO, pszProto, e[i][j]);
+					if (pszPhone)
+					{
+						nType = (strstr(pszPhone, " SMS")) ? PHONE_SMS : PHONE_NORMAL;
+						MIR_FREE(pszPhone);
+						break;
+					}
+				}
+			}
+		}
+	}
+	return nType;
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+static INT OnCListRebuildIcons(WPARAM wParam, LPARAM lParam)
+{
+	HICON hIcon			= IcoLib_GetIcon(ICO_BTN_PHONE);
+	ghExtraIconDef[0]	= (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+	Skin_ReleaseIcon(hIcon);
+	hIcon				= IcoLib_GetIcon(ICO_BTN_CELLULAR);
+	ghExtraIconDef[1]	= (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIcon, 0);
+	Skin_ReleaseIcon(hIcon);
+	return 0;
+}
+
+/**
+ * Notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param	wParam			- handle to the contact whose extra icon is to apply
+ * @param	lParam			- not used
+ **/
+static INT OnCListApplyIcons(HANDLE hContact, LPARAM)
+{
+	if (!myGlobals.ExtraIconsServiceExist)
+	{
+		IconExtraColumn iec;
+		iec.cbSize = sizeof(IconExtraColumn);
+		iec.ColumnType = EXTRA_ICON_SMS;
+		switch (Get(hContact)) 
+		{
+		case PHONE_NORMAL:
+			{
+				iec.hImage = ghExtraIconDef[0];
+			}
+			break;
+
+		case PHONE_SMS:
+			{
+				iec.hImage = ghExtraIconDef[1];
+			}
+			break;
+
+		default:
+			{
+				iec.hImage = INVALID_HANDLE_VALUE;
+			}
+		}
+		CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+	}
+	else
+	{
+		EXTRAICON ico;
+
+		ZeroMemory(&ico, sizeof(ico));
+		ico.cbSize = sizeof(ico);
+		ico.hContact = hContact;
+		ico.hExtraIcon = ghExtraIconSvc;
+		switch (Get(hContact)) 
+		{
+		case PHONE_NORMAL:
+			{
+				ico.icoName = ICO_BTN_PHONE;
+			}
+			break;
+
+		case PHONE_SMS:
+			{
+				ico.icoName = ICO_BTN_CELLULAR;
+			}
+			break;
+
+		default:
+			{
+				ico.icoName = (char *)0;
+			}
+		}
+		CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+	}
+	return 0;
+}
+
+/**
+ * Notification handler for changed contact settings
+ *
+ * @param	wParam			- (HANDLE)hContact
+ * @param	lParam			- (DBCONTACTWRITESETTING*)pdbcws
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+	if (hContact && pdbcws && pdbcws->szSetting && 
+			((pdbcws->value.type & DBVTF_VARIABLELENGTH) || (pdbcws->value.type == DBVT_DELETED)) &&
+			(!strcmp(pdbcws->szSetting, SET_CONTACT_PHONE) ||
+			 !strcmp(pdbcws->szSetting, SET_CONTACT_CELLULAR) ||
+			 !strcmp(pdbcws->szSetting, SET_CONTACT_COMPANY_PHONE) ||
+			 !strcmp(pdbcws->szSetting, SET_CONTACT_COMPANY_CELLULAR) ||
+			 !strncmp(pdbcws->szSetting, "MyPhone0", 8)))
+	{
+		OnCListApplyIcons(hContact, 0);
+	}
+	return 0;
+}
+
+/***********************************************************************************************************
+ * public Module Interface functions
+ ***********************************************************************************************************/
+
+/**
+ * Force all icons to be reloaded.
+ *
+ * @param	wParam			- handle to the contact whose extra icon is to apply
+ * @param	lParam			- not used
+ **/
+VOID SvcPhoneApplyCListIcons()
+{
+	HANDLE hContact;
+
+	//walk through all the contacts stored in the DB
+	for (hContact = DB::Contact::FindFirst();	hContact != NULL;	hContact = DB::Contact::FindNext(hContact))
+	{
+		OnCListApplyIcons(hContact, 0);
+	}
+}
+
+/**
+ * Enable or disable the replacement of clist extra icons.
+ *
+ * @param	bEnable			- determines whether icons are enabled or not
+ * @param	bUpdateDB		- if true the database setting is updated, too.
+ **/
+VOID SvcPhoneEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB) 
+{
+	if (myGlobals.HaveCListExtraIcons)
+	{
+		if (bUpdateDB)
+		{
+			DB::Setting::WriteByte(SET_CLIST_EXTRAICON_PHONE, bEnable);
+		}
+
+		// force module enabled, if extraicon plugin was found
+		if (bEnable) 
+		{
+			// hook events
+			if (hChangedHook == NULL) 
+			{
+				hChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+			}
+			if (hApplyIconHook == NULL) 
+			{
+				hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcons);
+			}
+			if (myGlobals.ExtraIconsServiceExist)
+			{
+				if (ghExtraIconSvc == INVALID_HANDLE_VALUE)
+				{
+					EXTRAICON_INFO ico;
+					
+					ZeroMemory(&ico, sizeof(ico));
+					ico.cbSize = sizeof(ico);
+					ico.type = EXTRAICON_TYPE_ICOLIB;
+					ico.name = "sms";	//must be the same as the group name in extraicon
+					ico.description = "(uinfoex)";
+					ico.descIcon = ICO_BTN_CELLULAR;
+					ghExtraIconSvc = (HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+				}
+			}
+			else if (hRebuildIconsHook == NULL) 
+			{
+				hRebuildIconsHook = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, (MIRANDAHOOK)OnCListRebuildIcons);
+				OnCListRebuildIcons(0, 0);
+			}
+		}
+		else 
+		{
+			if (hChangedHook)
+			{
+				UnhookEvent(hChangedHook); 
+				hChangedHook = NULL;
+			}			
+			if (hApplyIconHook)
+			{
+				UnhookEvent(hApplyIconHook); 
+				hApplyIconHook = NULL;
+			}			
+			if (hRebuildIconsHook)
+			{
+				UnhookEvent(hRebuildIconsHook); 
+				hRebuildIconsHook = NULL;
+			}
+		}
+		SvcPhoneApplyCListIcons();
+	}
+}
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcPhoneLoadModule()
+{
+	SvcPhoneEnableExtraIcons(
+		myGlobals.ExtraIconsServiceExist || 
+		DB::Setting::GetByte(SET_CLIST_EXTRAICON_PHONE, DEFVAL_CLIST_EXTRAICON_PHONE), FALSE);
+}
+
+/**
+ * This function unloads the Email module.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID SvcPhoneUnloadModule()
+{	
+	// unhook event handlers
+	UnhookEvent(hChangedHook);		hChangedHook		= NULL;
+	UnhookEvent(hApplyIconHook);	hApplyIconHook		= NULL;
+	UnhookEvent(hRebuildIconsHook);	hRebuildIconsHook	= NULL;
+}
diff --git a/plugins/UserInfoEx/src/svc_phone.h b/plugins/UserInfoEx/src/svc_phone.h
new file mode 100644
index 0000000000..20f7c249b5
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_phone.h
@@ -0,0 +1,38 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_phone.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _UINFOEX_SVCPHONE_H_INCLUDED_
+#define _UINFOEX_SVCPHONE_H_INCLUDED_
+
+VOID SvcPhoneApplyCListIcons();
+VOID SvcPhoneEnableExtraIcons(BOOLEAN bEnable, BOOLEAN bUpdateDB = FALSE);
+VOID SvcPhoneLoadModule();
+VOID SvcPhoneUnloadModule();
+
+#endif /* _UINFOEX_SVCPHONE_H_INCLUDED_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_refreshci.cpp b/plugins/UserInfoEx/src/svc_refreshci.cpp
new file mode 100644
index 0000000000..bddc9e60ca
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_refreshci.cpp
@@ -0,0 +1,936 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_refreshci.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "mir_contactqueue.h"
+#include "mir_menuitems.h"
+#include "svc_refreshci.h"
+
+#define HM_PROTOACK	(WM_USER+100)
+
+typedef INT_PTR	(*PUpdCallback) (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, PVOID UserData);
+
+/***********************************************************************************************************
+ * class CUpdProgress
+ ***********************************************************************************************************/
+
+class CUpdProgress
+{
+protected:
+	BOOLEAN			_bBBCode;		// TRUE if text renderer can handle BBCodes
+	BOOLEAN			_bIsCanceled;	// is set to TRUE uppon click on the CANCEL button
+	PUpdCallback	_pFnCallBack;	// a pointer to a callback function, which can be used 
+									// to catch several messages by the caller.
+	PVOID			_pData;			// application defined data
+	HWND			_hWnd;			// window handle of the progress dialog/popup
+
+	/**
+	 * This is the default window procedure, which is called for both progress dialog and popup.
+	 * It handles some common messages and calls the user defined callback function if defined.
+	 *
+	 * @param	pProgress		- This is the pointer to the object of CUpdProgress.
+	 * @param	hWnd			- HWND window handle of the progress dialog
+	 * @param	uMsg			- message sent to the dialog
+	 * @param	wParam			- message specific parameter
+	 * @param	lParam			- message specific parameter
+	 *
+	 * @return	This method returns 0.
+	 **/
+	static INT_PTR CALLBACK DefWndProc(CUpdProgress *pProgress, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
+	{
+		__try
+		{
+			if (PtrIsValid(pProgress))
+			{
+				switch (uMsg)
+				{
+				case UM_POPUPACTION:
+				case WM_COMMAND:
+					{
+						if (wParam == MAKEWORD(IDSKIP, BN_CLICKED))
+						{
+							pProgress->Destroy();
+						}
+						else						
+						if (wParam == MAKEWORD(IDCANCEL, BN_CLICKED))
+						{
+							pProgress->_bIsCanceled = TRUE;
+						}
+					}
+				}
+				if (PtrIsValid(pProgress->_pFnCallBack))
+				{
+					pProgress->_pFnCallBack(hWnd, uMsg, wParam, lParam, pProgress->_pData);
+				}
+			}
+		}
+		__except(GetExceptionCode()==EXCEPTION_ACCESS_VIOLATION ? 
+						EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) 
+		{ // code to handle exception
+			puts("Exception Occurred");
+		}
+		return 0;
+	}
+
+public:
+
+	virtual HWND	Create		(LPCTSTR szTitle, PUpdCallback pFnCallBack) = 0;
+	virtual VOID	Destroy		(VOID) {};
+	virtual VOID	SetTitle	(LPCTSTR szText) = 0;
+	virtual VOID	SetText		(LPCTSTR szText) = 0;
+
+	BOOLEAN IsVisible() const
+	{
+		return _hWnd != NULL;
+	}
+	/**
+	 *
+	 *
+	 **/
+	BOOLEAN IsCanceled() const
+	{
+		return _bIsCanceled;
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	VOID SetTitleParam(LPCTSTR szText, ...)
+	{
+		if (szText)
+		{
+			TCHAR buf[MAXDATASIZE];
+			va_list vl;
+			
+			va_start(vl, szText);
+			if (mir_vsntprintf(buf, SIZEOF(buf), szText, vl) != -1)
+			{
+				SetTitle(buf);	 
+			}
+			va_end(vl);
+		}
+	}
+
+	/**
+	 * This method is used to set the popups or dialogs message text.
+	 * It takes text with parameters as sprintf does. If bbcodes are
+	 * disabled this method automatically deletes them from the text. 
+	 *
+	 * @param	szText			- the text to display. Can contain formats like
+	 *							  sprintf does.
+	 * @param	...				- a list of parameters, which depends on the
+	 *							  format of szText.
+	 *
+	 * @return	nothing
+	 **/
+	VOID SetTextParam(LPCTSTR szText, ...)
+	{
+		if (szText)
+		{
+			INT_PTR cch = mir_tcslen(szText);
+			LPTSTR	fmt = (LPTSTR) mir_alloc((cch + 1) * sizeof(TCHAR));
+			
+			if (fmt)
+			{
+				TCHAR buf[MAXDATASIZE];
+				va_list vl;
+
+				_tcscpy(fmt, szText);
+
+				// delete bbcodes
+				if (!_bBBCode)
+				{
+					LPTSTR s, e;
+
+					for (s = fmt, e = fmt + cch; s[0] != 0; s++)
+					{
+						if (s[0] == '[')
+						{
+							// leading bbcode tag (e.g.: [b], [u], [i])
+							if ((s[1] == 'b' || s[1] == 'u' || s[1] == 'i') && s[2] == ']')
+							{
+								memmove(s, s + 3, (e - s - 2) * sizeof(TCHAR));
+								e -= 3;
+							}
+							// ending bbcode tag (e.g.: [/b], [/u], [/i])
+							else if (s[1] == '/' && (s[2] == 'b' || s[2] == 'u' || s[2] == 'i') && s[3] == ']')
+							{
+								memmove(s, s + 4, (e - s - 3) * sizeof(TCHAR));
+								e -= 4;
+							}
+						}
+					}
+				}
+			
+				va_start(vl, szText);
+				if (mir_vsntprintf(buf, SIZEOF(buf), fmt, vl) != -1)
+				{
+					SetText(buf);	 
+				}
+				va_end(vl);
+				mir_free(fmt);
+			}
+		}
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	CUpdProgress()
+	{
+		_hWnd = NULL;
+		_pFnCallBack = NULL;
+		_pData = NULL;
+		_bIsCanceled = FALSE;
+		_bBBCode = FALSE;
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	CUpdProgress(PVOID data)
+	{
+		_hWnd = NULL;
+		_pFnCallBack = NULL;
+		_pData = data;
+		_bIsCanceled = FALSE;
+		_bBBCode = FALSE;
+	}
+
+	~CUpdProgress()
+	{
+	}
+
+};
+
+/***********************************************************************************************************
+ * class CDlgUpdProgress
+ ***********************************************************************************************************/
+
+class CDlgUpdProgress : public CUpdProgress
+{
+	/**
+	 *
+	 *
+	 **/
+	static INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
+	{
+		switch (uMsg)
+		{
+		case WM_INITDIALOG:
+			{
+				const ICONCTRL idIcon[] = {
+					{ ICO_BTN_UPDATE,		WM_SETICON,		NULL		},
+					{ ICO_BTN_DOWNARROW,	BM_SETIMAGE,	IDSKIP		},
+					{ ICO_BTN_CANCEL,		BM_SETIMAGE,	IDCANCEL	}
+				};
+				IcoLib_SetCtrlIcons(hWnd, idIcon, DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? 2 : 1);
+	
+				SendDlgItemMessage(hWnd, IDCANCEL,	BUTTONTRANSLATE, NULL, NULL);
+				SendDlgItemMessage(hWnd, IDSKIP,	BUTTONTRANSLATE, NULL, NULL);
+				SetUserData(hWnd, lParam);
+				
+				TranslateDialogDefault(hWnd);
+			}
+			return TRUE;
+
+		case WM_CTLCOLORSTATIC:
+			{
+				switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) 
+				{
+					case STATIC_WHITERECT:
+					case IDC_INFO:
+						{
+							SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+							return GetSysColor(COLOR_WINDOW);
+						}
+				}
+			}
+			return FALSE;
+
+		}
+		return CUpdProgress::DefWndProc((CUpdProgress *) GetUserData(hWnd), hWnd, uMsg, wParam, lParam);
+	}
+
+public:
+
+	/**
+	 *
+	 *
+	 **/
+	CDlgUpdProgress(PVOID data)
+		: CUpdProgress(data)
+	{
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	virtual HWND Create(LPCTSTR szTitle, PUpdCallback pFnCallBack)
+	{
+		_pFnCallBack = pFnCallBack;
+		_hWnd = CreateDialogParam(ghInst, 
+							MAKEINTRESOURCE(IDD_REFRESHDETAILS), 
+							0, 
+							(DLGPROC)CDlgUpdProgress::WndProc, 
+							(LPARAM) this);
+		if (_hWnd)
+		{
+			SetTitle(szTitle);
+			ShowWindow(_hWnd, SW_SHOW);
+		}
+		return _hWnd;
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	virtual VOID Destroy()
+	{
+		if (_hWnd)
+		{
+			SetUserData(_hWnd, NULL);
+			EndDialog(_hWnd, IDOK);
+			_hWnd = NULL;
+		}
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	virtual VOID SetTitle(LPCTSTR szText)
+	{
+		SetWindowText(_hWnd, szText);
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	virtual VOID SetText(LPCTSTR szText)
+	{
+		SetDlgItemText(_hWnd, IDC_INFO, szText);
+	}
+
+};
+
+/***********************************************************************************************************
+ * class CPopupUpdProgress
+ ***********************************************************************************************************/
+
+class CPopupUpdProgress : public CUpdProgress
+{
+	LPCTSTR			_szText;
+	POPUPACTION		_popupButtons[2];
+
+	/**
+	 *
+	 *
+	 **/
+	VOID UpdateText()
+	{
+		if (_szText)
+		{
+			INT_PTR cb = mir_tcslen(_szText) + 8;
+			LPTSTR	pb = (LPTSTR) mir_alloc(cb * sizeof(TCHAR));
+
+			if (pb)
+			{
+				mir_tcscpy(pb, _szText);
+
+				SendMessage(_hWnd, UM_CHANGEPOPUP, CPT_TITLET, (LPARAM) pb);
+			}
+		}
+	}
+
+	/**
+	 * This static member is the window procedure, which is used to modify the behaviour of
+	 * a popup dialog, so that it can act as a replacement for the progress dialog.
+	 * The major task of this method is to filter out some messages, who would lead to a crash,
+	 * if passed to the default windows procedure.
+	 *
+	 **/
+	static INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
+	{
+		// Filter out messages, which must not be passed to default windows procedure or even
+		// to the callback function!
+		switch (uMsg)
+		{
+		case UM_INITPOPUP:
+		case UM_CHANGEPOPUP:
+		case UM_FREEPLUGINDATA:
+			break;
+		default:
+			CUpdProgress::DefWndProc((CUpdProgress *) PUGetPluginData(hWnd), hWnd, uMsg, wParam, lParam);
+		}
+		return DefWindowProc(hWnd, uMsg, wParam, lParam);
+	}
+
+public:
+
+	/**
+	 *
+	 *
+	 **/
+	CPopupUpdProgress(PVOID data)
+		: CUpdProgress(data)
+	{
+		_szText = NULL;
+		_bBBCode = DB::Setting::GetByte("PopUp", "UseMText", FALSE);
+
+		_popupButtons[0].cbSize = sizeof(POPUPACTION);
+		_popupButtons[0].flags = PAF_ENABLED;
+		_popupButtons[0].lchIcon = IcoLib_GetIcon(ICO_BTN_DOWNARROW);
+		_popupButtons[0].wParam = MAKEWORD(IDSKIP, BN_CLICKED);
+		_popupButtons[0].lParam = NULL;
+		strcpy(_popupButtons[0].lpzTitle, MODNAME"/Hide");
+
+		// cancel button
+		_popupButtons[1].cbSize = sizeof(POPUPACTION);
+		_popupButtons[1].flags = PAF_ENABLED;
+		_popupButtons[1].lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+		_popupButtons[1].wParam = MAKEWORD(IDCANCEL, BN_CLICKED);
+		_popupButtons[1].lParam = NULL;
+		strcpy(_popupButtons[1].lpzTitle, MODNAME"/Cancel");
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	virtual HWND Create(LPCTSTR szTitle, PUpdCallback pFnCallBack)
+	{
+		POPUPDATAT_V2	pd;
+		
+		ZeroMemory(&pd, sizeof(POPUPDATAT_V2));
+		pd.cbSize = sizeof(POPUPDATAT_V2);
+		pd.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+		pd.iSeconds = -1;
+		pd.PluginData = this;
+		pd.PluginWindowProc = (WNDPROC)CPopupUpdProgress::WndProc;
+		pd.actionCount = SIZEOF(_popupButtons);
+		pd.lpActions = _popupButtons;
+
+		// dummy text
+		_szText = mir_tcsdup(szTitle);
+		mir_tcscpy(pd.lptzContactName, _szText);
+		
+		_tcscpy(pd.lptzText, _T(" "));
+		
+		_pFnCallBack = pFnCallBack;
+		_hWnd = (HWND) CallService(MS_POPUP_ADDPOPUPT, (WPARAM) &pd, APF_RETURN_HWND|APF_NEWDATA);
+		return _hWnd;
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	virtual VOID Destroy()
+	{
+		if (_hWnd)
+		{
+			PUDeletePopUp(_hWnd);
+			_hWnd = NULL;
+		}
+		MIR_FREE(_szText);
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	virtual VOID SetTitle(LPCTSTR szText)
+	{
+		MIR_FREE(_szText);
+		_szText = mir_tcsdup(szText);
+		UpdateText();
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	virtual VOID SetText(LPCTSTR szText)
+	{
+		SendMessage(_hWnd, UM_CHANGEPOPUP, CPT_TEXTT, (LPARAM) mir_tcsdup(szText));
+	}
+};
+
+
+/***********************************************************************************************************
+ * class CContactUpdater
+ ***********************************************************************************************************/
+
+class CContactUpdater : public CContactQueue
+{
+	CUpdProgress*		_pProgress;			// pointer to the progress dialog/popup
+	HANDLE				_hProtoAckEvent;	// handle to protocol ack notification
+	HANDLE				_hContact;			// current contact being refreshed
+	PBYTE				_hContactAcks;		// array of acknoledgements for the current contact to wait for
+	INT_PTR				_nContactAcks;		// number of acknoledgements for the current contact to wait for
+
+	MIRANDA_CPP_PLUGIN_API(CContactUpdater);
+
+	/**
+	 * This is a callback dialog procedure, which is assigned the update progress dialog to
+	 * gain control over certain messages.
+	 *
+	 * @param	hWnd		- HWND window handle of the progress dialog
+	 * @param	uMsg		- message sent to the dialog
+	 * @param	wParam		- message specific parameter
+	 * @param	lParam		- message specific parameter
+	 * @param	u			- This is a parameter assigned by the CUpdProgress' constructur.
+	 *						  In this case it is a pointer to this class's object.
+	 *
+	 * @return	This method returns 0.
+	 **/
+	static INT DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, CContactUpdater* u) 
+	{
+		switch (uMsg) 
+		{
+
+		/**
+		 * User has clicked on the skip or cancel button.
+		 **/
+		case UM_POPUPACTION:
+		case WM_COMMAND: 
+			{
+				if (PtrIsValid(u))
+				{
+					switch (LOWORD(wParam))
+					{
+					case IDCANCEL:
+						{
+							if (HIWORD(wParam) == BN_CLICKED) 
+							{
+								u->Cancel();
+							}
+						}
+					}
+				}
+			}
+		}
+		return 0;
+	}
+
+	/**
+	 * This method handles ack broadcasts from the protocols to determine,
+	 * whether a contact's information set's update is complete to continue
+	 * with the next faster. By default the queue is configured to wait
+	 * about 15s between contacts. If the protocol sends a complete ack,
+	 * the time is shortend to 4s.
+	 *
+	 * @param	wParam		- not used
+	 * @param	ack			- pointer to an ACKDATA structure containing all 
+	 *						  data for the acknoledgement.
+	 *
+	 * @return	nothing
+	 **/
+	INT __cdecl OnProtoAck(WPARAM wParam, ACKDATA *ack)
+	{
+		if (ack && ack->cbSize == sizeof(ACKDATA) && ack->hContact == _hContact && ack->type == ACKTYPE_GETINFO)
+		{
+			if (ack->hProcess || ack->lParam) 
+			{
+				if (!_hContactAcks)
+				{
+					_nContactAcks = (INT_PTR)ack->hProcess;
+					_hContactAcks = (PBYTE)mir_calloc(sizeof(BYTE) * (INT_PTR)ack->hProcess);
+				}
+				if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED)
+				{
+					_hContactAcks[ack->lParam] = 1;
+				}
+
+				for (INT i = 0; i < _nContactAcks; i++)
+				{
+					if (_hContactAcks[i] == 0)
+					{
+						return 0;
+					}
+				}
+			}
+			// don't wait the full time, but continue immitiatly
+			ContinueWithNext();
+		}
+		return 0;
+	}
+
+	/**
+	 * This method is called just before the worker thread is going to suspend,
+	 * if the queue is empty.
+	 *
+	 * @param	none
+	 *
+	 * @return	nothing
+	 **/
+	virtual VOID OnEmpty() 
+	{
+		// This was the last contact, so destroy the progress window.
+		if (_hProtoAckEvent)
+		{
+			UnhookEvent(_hProtoAckEvent);
+			_hProtoAckEvent = NULL;
+		}
+
+		// free up last ackresult array
+		MIR_FREE(_hContactAcks);
+		_nContactAcks = 0;
+		_hContact = NULL;
+
+		// close progress bar
+		if (_pProgress)
+		{
+			_pProgress->Destroy();
+
+			delete _pProgress;
+			_pProgress = NULL;
+		}
+
+		// reset menu
+		if (hMenuItemRefresh)
+		{
+			CLISTMENUITEM clmi;
+
+			clmi.cbSize = sizeof(CLISTMENUITEM);
+			clmi.flags = CMIM_NAME|CMIM_ICON;
+			clmi.pszName = LPGEN("Refresh Contact Details");
+			clmi.hIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+			CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuItemRefresh, (LPARAM)&clmi);
+		}
+	}
+
+	/**
+	 * This virtual method is called by the derived CContactQueue class,
+	 * if an action is requested for a queue item.
+	 *
+	 * @param	hContact	- the handle of the contact an action is requested for
+	 * @param	param		- not used here
+	 *
+	 * @return	nothing
+	 **/
+	virtual VOID Callback(HANDLE hContact, PVOID param)
+	{
+		LPSTR	pszProto	= DB::Contact::Proto(hContact);
+
+		if (pszProto && pszProto[0])
+		{
+			MIR_FREE(_hContactAcks);
+			_nContactAcks = 0;
+			_hContact = hContact;
+
+			if (!_hProtoAckEvent)
+			{
+				_hProtoAckEvent = (HANDLE) ThisHookEvent(ME_PROTO_ACK, (EVENTHOOK) &CContactUpdater::OnProtoAck);
+			}
+
+			if (_pProgress)
+			{
+				_pProgress->SetTextParam(TranslateT("[b]%s (%S)...[/b]\n%d Contacts remaning"),
+					DB::Contact::DisplayName(_hContact), pszProto, Size());
+			}
+			if (IsProtoOnline(pszProto))
+			{
+				INT i;
+				for (i = 0; i < 3 && CallContactService(hContact, PSS_GETINFO, 0, 0); i++)
+				{
+					Sleep(3000);
+				}
+			}
+		}
+	}
+
+public:
+
+	/**
+	 * This is the default constructor
+	 *
+	 **/
+	CContactUpdater() : CContactQueue()
+	{
+		_hContactAcks	= NULL;
+		_nContactAcks	= 0;
+		_hContact		= NULL;
+		_pProgress		= NULL;
+		_hProtoAckEvent	= NULL;
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	~CContactUpdater()
+	{
+		RemoveAll();
+		OnEmpty();
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	BOOL QueueAddRefreshContact(HANDLE hContact, INT iWait)
+	{
+		LPSTR pszProto = DB::Contact::Proto(hContact);
+
+		if ((mir_strcmp(pszProto, "Weather")!=0) && 
+				(mir_strcmp(pszProto, "MetaContacts")!=0) && 
+				IsProtoOnline(pszProto))
+		{
+			return Add(iWait, hContact);
+		}
+		return 0;
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	VOID RefreshAll()
+	{
+		HANDLE		hContact;
+		INT				iWait;
+
+		for (hContact = DB::Contact::FindFirst(),	iWait = 100;
+				 hContact != NULL;
+				 hContact = DB::Contact::FindNext(hContact))
+		{
+			if (QueueAddRefreshContact(hContact, iWait))
+			{
+				iWait += 5000;
+			}
+		}
+		if (Size() && !_pProgress)
+		{
+			if (ServiceExists(MS_POPUP_CHANGETEXTT) && DB::Setting::GetByte("PopupProgress", FALSE))
+			{
+				_pProgress = new CPopupUpdProgress(this);
+			}
+			else
+			{
+				_pProgress = new CDlgUpdProgress(this);
+			}
+
+			_pProgress->Create(TranslateT("Refresh Contact Details"), (PUpdCallback) CContactUpdater::DlgProc);
+			_pProgress->SetText(TranslateT("Preparing..."));
+		}
+
+		// if there are contacts in the queue, change the main menu item to indicate it is meant for canceling.
+		if (hMenuItemRefresh && Size() > 0)
+		{
+			CLISTMENUITEM clmi;
+
+			clmi.cbSize = sizeof(CLISTMENUITEM);
+			clmi.flags = CMIM_NAME|CMIM_ICON;
+			clmi.pszName = LPGEN("Abort Refreshing Contact Details");
+			clmi.hIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+			CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuItemRefresh, (LPARAM) &clmi);
+		}
+	}
+
+	/**
+	 *
+	 *
+	 **/
+	VOID Cancel()
+	{
+		RemoveAll();
+		ContinueWithNext();
+	}
+
+};
+
+static CContactUpdater	*ContactUpdater = NULL;
+
+/***********************************************************************************************************
+ * common helper functions
+ ***********************************************************************************************************/
+
+/**
+ * This function checks, whether at least one protocol is online!
+ *
+ * @param	none
+ *
+ * @retval	TRUE		- At least one protocol is online.
+ * @retval	FALSE		- All protocols are offline.
+ **/
+static BOOL IsMirandaOnline()
+{
+	PROTOACCOUNT **pAcc;
+	INT i, nAccCount;
+	BOOL bIsOnline = FALSE;
+
+	if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc)))
+	{
+		for (i = 0; (i < nAccCount) && !bIsOnline; i++) 
+		{
+			bIsOnline |= (IsProtoAccountEnabled(pAcc[i]) && IsProtoOnline(pAcc[i]->szModuleName));
+		}
+	}
+	return bIsOnline;
+}
+
+/***********************************************************************************************************
+ * services
+ ***********************************************************************************************************/
+
+/**
+ * This is the service function being called by MS_USERINFO_REFRESH.
+ * It adds each contact, whose protocol is online, to the queue of contacts to refresh.
+ * The queue is running a separate thread, which is responsible for requesting the contact information
+ * one after another with a certain time to wait in between.
+ *
+ * @param	wParam		- not used
+ * @param	lParam		- not used
+ *
+ * @return	This service function always returns 0.
+ **/
+static INT_PTR RefreshService(WPARAM wParam, LPARAM lParam)
+{
+	try
+	{
+		if (IsMirandaOnline())
+		{
+			if (!ContactUpdater)
+			{
+				ContactUpdater = new CContactUpdater();
+			}
+
+			if (ContactUpdater->Size() == 0)
+			{
+				ContactUpdater->RefreshAll();
+			}
+			else if (IDYES == MsgBox(NULL, MB_YESNO|MB_ICON_QUESTION, LPGENT("Refresh Contact Details"), NULL, 
+				LPGENT("Do you want to cancel the current refresh procedure?")))
+			{
+				ContactUpdater->Cancel();
+			}
+		}
+		else
+		{
+			MsgErr(NULL, LPGENT("Miranda must be online for refreshing contact information!"));
+		}
+	}
+	catch(...)
+	{
+		MsgErr(NULL, LPGENT("The function caused an exception!"));
+	}
+	return 0;
+}
+
+/***********************************************************************************************************
+ * events
+ ***********************************************************************************************************/
+
+/**
+ *
+ *
+ **/
+static INT OnContactAdded(WPARAM wParam, LPARAM lParam)
+{
+	try
+	{
+		DWORD dwStmp;
+
+		dwStmp = DB::Setting::GetDWord((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME, 0);
+		if (!dwStmp)
+		{
+			MTime mt;
+			
+			mt.GetLocalTime();
+			mt.DBWriteStamp((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME);
+
+			// create updater, if not yet exists
+			if (!ContactUpdater)
+			{
+				ContactUpdater = new CContactUpdater();
+			}
+			
+			// add to the end of the queue
+			ContactUpdater->AddIfDontHave(
+				(ContactUpdater->Size() > 0) 
+					? max(ContactUpdater->Get(ContactUpdater->Size() - 1)->check_time + 15000, 4000) 
+					: 4000,
+					(HANDLE)wParam);
+		}
+	}
+	catch(...)
+	{
+		MsgErr(NULL, LPGENT("The function caused an exception!"));
+	}
+	return 0;
+}
+
+/**
+ * Miranda is going to shutdown soon, so any panding contact information refresh is to be terminated
+ * and the queue object must be deleted. Further refresh requests will be ignored.
+ *
+ * @param	wParam		- not used
+ * @param	lParam		- not used
+ *
+ * @return	This function always returns 0.
+ **/
+static INT OnPreShutdown(WPARAM, LPARAM)
+{
+	if(ContactUpdater) {
+		delete ContactUpdater;
+		ContactUpdater = 0;
+	}
+	//MIR_DELETE(ContactUpdater);
+	return 0;
+}
+
+/***********************************************************************************************************
+ * initialization
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcRefreshContactInfoLoadModule(VOID)
+{
+	CreateServiceFunction(MS_USERINFO_REFRESH, RefreshService);
+	HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreShutdown);
+	HookEvent(ME_DB_CONTACT_ADDED, OnContactAdded);
+
+	HOTKEYDESC hk = { 0 };
+	hk.cbSize = sizeof(HOTKEYDESC);
+	hk.pszSection = MODNAME;
+	hk.pszName = "RefreshContactDetails";
+	hk.pszDescription = LPGEN("Refresh Contact Details");
+	hk.pszService = MS_USERINFO_REFRESH;
+	Hotkey_Register(&hk);
+}
diff --git a/plugins/UserInfoEx/src/svc_refreshci.h b/plugins/UserInfoEx/src/svc_refreshci.h
new file mode 100644
index 0000000000..4f9c551385
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_refreshci.h
@@ -0,0 +1,36 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_refreshci.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+#ifndef _SVC_REFRESH_USER_DETAILS_H_
+#define _SVC_REFRESH_USER_DETAILS_H_
+
+VOID SvcRefreshContactInfoLoadModule(VOID);
+
+#endif /* _SVC_REFRESH_USER_DETAILS_H_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_reminder.cpp b/plugins/UserInfoEx/src/svc_reminder.cpp
new file mode 100644
index 0000000000..f91ba47f33
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_reminder.cpp
@@ -0,0 +1,1216 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_reminder.cpp $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+
+/**
+ * System Includes:
+ **/
+#include "commonheaders.h"
+#include "m_skin.h"
+#include "m_clui.h"
+
+#include "svc_Gender.h"
+#include "svc_Reminder.h"
+#include "dlg_anniversarylist.h"
+
+/**
+ * The CEvent structure describes the next anniversary to remind of.
+ **/
+struct CEvent
+{
+	enum EType { NONE, BIRTHDAY, ANNIVERSARY };
+
+	EType	_eType;
+	WORD	_wDaysLeft;
+
+	CEvent();
+	CEvent(EType eType, WORD wDaysLeft);
+
+	BOOLEAN operator << (const CEvent& e);
+};
+
+typedef struct _REMINDEROPTIONS
+{
+	WORD	wDaysEarlier;
+	BYTE	bPopups;
+	BYTE	bCListExtraIcon;
+	BYTE	bFlashCList;
+	BYTE	bCheckVisibleOnly;
+	BYTE	RemindState;
+	CEvent	evt;
+}
+REMINDEROPTIONS, *LPREMINDEROPTIONS;
+
+static HANDLE ExtraIcon = INVALID_HANDLE_VALUE;
+
+
+static HANDLE	ghCListIA = NULL;
+static HANDLE	ghCListIR = NULL;
+static HANDLE	ghSettingsChanged = NULL;
+
+static UINT_PTR	ghRemindTimer = 0;
+static UINT_PTR	ghRemindDateChangeTimer = 0;
+
+HANDLE ghCListAnnivIcons[11];
+HANDLE ghCListBirthdayIcons[11];
+
+static REMINDEROPTIONS	gRemindOpts;
+
+static VOID UpdateTimer(BOOLEAN bStartup);
+
+
+/***********************************************************************************************************
+ * struct CEvent
+ ***********************************************************************************************************/
+
+/**
+ * This is the default constructor.
+ *
+ * @param	none
+ *
+ * @return nothing
+ **/
+CEvent::CEvent()
+{
+	_wDaysLeft = 0xFFFF;
+	_eType = NONE;
+}
+
+/**
+ * This is the default constructor.
+ *
+ * @param	eType			- initial type
+ * @param	wDaysLeft		- initial days to event
+ *
+ * @return	nothing
+ **/
+CEvent::CEvent(EType eType, WORD wDaysLeft)
+{
+	_wDaysLeft = wDaysLeft;
+	_eType = eType;
+}
+
+/**
+ * This operator dups the attributes of the given CEvent object if
+ * the event comes up earlier then the one of the object.
+ *
+ * @param	evt				- the reference to the event object whose attributes to assign.
+ *
+ * @retval	TRUE			- The values of @e evt have been assigned.
+ * @retval	FALSE			- The values are not assigned.
+ **/
+BOOLEAN CEvent::operator << (const CEvent& evt)
+{
+	if (_wDaysLeft > evt._wDaysLeft)
+	{
+		_wDaysLeft = evt._wDaysLeft;
+		_eType = evt._eType;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/***********************************************************************************************************
+ * notification functions
+ ***********************************************************************************************************/
+
+/**
+ * This function returns the icon for the given anniversary,
+ * which is the given number of days in advance.
+ *
+ * @param	evt				- structure specifying the next anniversary
+ *
+ * @return	The function returns icolib's icon if found or NULL otherwise.
+ **/
+static HICON GetAnnivIcon(const CEvent &evt)
+{
+	HICON hIcon = NULL;
+
+	CHAR szIcon[MAXSETTING];
+
+	switch (evt._eType)
+	{
+		case CEvent::BIRTHDAY:
+			{
+				if (evt._wDaysLeft > 9)
+				{
+					hIcon = IcoLib_GetIcon(ICO_RMD_DTBX);
+				}
+				else
+				{
+					mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dtb%u", evt._wDaysLeft);
+					hIcon = IcoLib_GetIcon(szIcon);
+				}
+			}
+			break;
+
+		case CEvent::ANNIVERSARY:
+			{
+				if (evt._wDaysLeft > 9)
+				{
+					hIcon = IcoLib_GetIcon(ICO_RMD_DTAX);
+				}
+				else
+				{
+					mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dta%u", evt._wDaysLeft);
+					hIcon = IcoLib_GetIcon(szIcon);
+				}
+			}
+	}
+	return hIcon;
+}
+
+/**
+ * This function adds the icon for the given anniversary, which is the given number of days
+ * in advance to the contact list's imagelist.
+ *
+ * @param	evt				- structure specifying the next anniversary
+ *
+ * @return	The function returns the clist's extra icon handle if found and successfully added.
+ **/
+static HANDLE AddCListExtraIcon(const CEvent &evt)
+{
+	HANDLE hClistIcon;
+	HICON hIco = GetAnnivIcon(evt);
+	if (hIco)
+	{
+		hClistIcon = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIco, 0);
+		if (hClistIcon == (HANDLE)CALLSERVICE_NOTFOUND)
+			hClistIcon = INVALID_HANDLE_VALUE;
+
+		Skin_ReleaseIcon(hIco);
+	}
+	else hClistIcon = INVALID_HANDLE_VALUE;
+
+	return hClistIcon;
+}
+
+/**
+ * This function returns the clist extra icon handle for the given anniversary.
+ *
+ * @param	evt				- structure specifying the next anniversary
+ *
+ * @return	The function returns the clist extra icon handle for the given anniversary.
+ **/
+static HANDLE GetCListExtraIcon(const CEvent &evt)
+{
+	if (gRemindOpts.bCListExtraIcon)
+	{
+		WORD wIndex = evt._wDaysLeft;
+
+		switch (evt._eType)
+		{
+		case CEvent::BIRTHDAY:
+			{
+				if (wIndex >= SIZEOF(ghCListBirthdayIcons))
+				{
+					wIndex = SIZEOF(ghCListBirthdayIcons) - 1;
+				}
+				// add the icon to clists imagelist if required
+				if (ghCListBirthdayIcons[wIndex] == INVALID_HANDLE_VALUE)
+				{
+					ghCListBirthdayIcons[wIndex] = AddCListExtraIcon(evt);
+				}
+			}
+			return ghCListBirthdayIcons[wIndex];
+
+		case CEvent::ANNIVERSARY:
+			{
+				if (wIndex >= SIZEOF(ghCListAnnivIcons))
+				{
+					wIndex = SIZEOF(ghCListAnnivIcons) - 1;
+				}
+				// add the icon to clists imagelist if required
+				if (ghCListAnnivIcons[wIndex] == INVALID_HANDLE_VALUE)
+				{
+					ghCListAnnivIcons[wIndex] = AddCListExtraIcon(evt);
+				}
+			}
+			return ghCListAnnivIcons[wIndex];
+		}
+	}
+	return INVALID_HANDLE_VALUE;
+}
+
+/**
+ * Displays an clist extra icon according to the kind of anniversary
+ * and the days in advance.
+ *
+ * @param	evt				- structure specifying the next anniversary
+ *
+ * @return	nothing
+ **/
+static VOID NotifyWithExtraIcon(HANDLE hContact, const CEvent &evt)
+{
+	if (myGlobals.HaveCListExtraIcons && gRemindOpts.bCListExtraIcon)
+	{
+		if (!myGlobals.ExtraIconsServiceExist)
+		{
+			IconExtraColumn iec;
+
+			iec.cbSize = sizeof(IconExtraColumn);
+			iec.ColumnType = gRemindOpts.bCListExtraIcon;
+			iec.hImage = GetCListExtraIcon(evt);
+			CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec);
+		}
+		else
+		{
+			CHAR szIcon[MAXSETTING];
+			EXTRAICON ico;
+
+			ico.cbSize=sizeof(ico);
+			ico.hContact=hContact;
+			ico.hExtraIcon=ExtraIcon;
+			switch (evt._eType)
+			{
+			case CEvent::BIRTHDAY:
+				{
+					if (evt._wDaysLeft > 9)
+					{
+						ico.icoName=ICO_RMD_DTAX;
+					}
+					else
+					{
+						mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dtb%u", evt._wDaysLeft);
+						ico.icoName=szIcon;
+					}
+					break;
+				}
+			case CEvent::ANNIVERSARY:
+				{
+					if (evt._wDaysLeft > 9)
+					{
+						ico.icoName=ICO_RMD_DTAX;
+					}
+					else
+					{
+						mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dta%u", evt._wDaysLeft);
+						ico.icoName=szIcon;
+					}
+					break;
+				}
+			default:
+				ico.icoName=(char *)0;
+			}
+			CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0);
+		}
+	}
+}
+
+/**
+ * Message procedure for popup messages
+ *
+ * @param	hWnd			- handle to the popupwindow
+ * @param	uMsg			- message to handle
+ * @param	wParam			- message specific parameter
+ * @param	lParam			- message specific parameter
+ *
+ * @return	message specific
+ **/
+static INT_PTR CALLBACK PopupWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg)
+	{
+	case WM_COMMAND:
+		{
+			if (HIWORD(wParam) == STN_CLICKED)
+			{
+				PUDeletePopUp(hWnd);
+				return TRUE;
+			}
+			break;
+		}
+
+	case WM_CONTEXTMENU:
+		{
+			PUDeletePopUp(hWnd);
+			return TRUE;
+		}
+	}
+	return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+/**
+ * Displays a popup
+ *
+ * @param	hContact		- contact to display popup for
+ * @param	eventType		- indicates which popup settings to apply
+ * @param	DaysToAnniv		- days left until anniversary occures
+ * @param	pszDesc			- this is the headline
+ * @param	szMsg			- message to display
+ *
+ * @return	return value of the popup service
+ **/
+static INT NotifyWithPopup(HANDLE hContact, CEvent::EType eventType, INT DaysToAnniv, LPCTSTR pszDesc, LPCTSTR pszMsg)
+{
+	if (gRemindOpts.bPopups)
+	{
+		POPUPDATAT_V2 ppd;
+
+		ZeroMemory(&ppd, sizeof(POPUPDATAT_V2));
+		ppd.PluginWindowProc = (WNDPROC)PopupWindowProc;
+		ppd.iSeconds = (INT)DB::Setting::GetByte(SET_POPUP_DELAY, 0);
+
+		if (hContact)
+		{
+			ppd.lchContact = hContact;
+			mir_sntprintf(ppd.lptzContactName, SIZEOF(ppd.lptzContactName),
+				_T("%s - %s"), TranslateTS(pszDesc), DB::Contact::DisplayName(hContact));
+		}
+		else
+		{
+			mir_tcsncpy(ppd.lptzContactName, TranslateT("Reminder"), SIZEOF(ppd.lptzContactName));
+		}
+		mir_tcsncpy(ppd.lptzText, pszMsg, MAX_SECONDLINE);
+
+		ppd.lchIcon = GetAnnivIcon(CEvent(eventType, DaysToAnniv));
+
+		switch (eventType)
+		{
+		case CEvent::BIRTHDAY:
+			switch (DB::Setting::GetByte(SET_POPUP_BIRTHDAY_COLORTYPE, POPUP_COLOR_CUSTOM))
+			{
+			case POPUP_COLOR_WINDOWS:
+				ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+				ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+				break;
+
+			case POPUP_COLOR_CUSTOM:
+				ppd.colorBack = DB::Setting::GetDWord(SET_POPUP_BIRTHDAY_COLOR_BACK, RGB(192,180,30));
+				ppd.colorText = DB::Setting::GetDWord(SET_POPUP_BIRTHDAY_COLOR_TEXT, 0);
+				break;
+			}
+			break;
+
+		case CEvent::ANNIVERSARY:
+			switch (DB::Setting::GetByte(SET_POPUP_ANNIVERSARY_COLORTYPE, POPUP_COLOR_CUSTOM))
+			{
+			case POPUP_COLOR_WINDOWS:
+				ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+				ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+				break;
+
+			case POPUP_COLOR_CUSTOM:
+				ppd.colorBack = DB::Setting::GetDWord(SET_POPUP_ANNIVERSARY_COLOR_BACK, RGB(90, 190, 130));
+				ppd.colorText = DB::Setting::GetDWord(SET_POPUP_ANNIVERSARY_COLOR_TEXT, 0);
+				break;
+			}
+		}
+		return PUAddPopUpT(&ppd);
+	}
+	return 1;
+}
+
+/**
+ * Flash contact list's contact icon.
+ *
+ * @param	hContact		- contact whose icon to flash
+ * @param	evt				- structure specifying the next anniversary
+ *
+ * @return	nothing
+ **/
+static VOID NotifyFlashCListIcon(HANDLE hContact, const CEvent &evt)
+{
+	if (gRemindOpts.bFlashCList && evt._wDaysLeft == 0)
+	{
+		CLISTEVENT cle ={0};
+		TCHAR szMsg[MAX_PATH];
+
+		cle.cbSize = sizeof(CLISTEVENT);
+		cle.hContact = hContact;
+		cle.flags = CLEF_URGENT|CLEF_TCHAR;
+		cle.hDbEvent = NULL;
+
+		switch (evt._eType) {
+			case CEvent::BIRTHDAY:
+				{
+					mir_sntprintf(szMsg, SIZEOF(szMsg),
+						TranslateT("%s has %s today."),
+						DB::Contact::DisplayName(hContact),
+						TranslateT("Birthday"));
+					cle.hIcon = IcoLib_GetIcon(ICO_COMMON_BIRTHDAY);
+				}
+				break;
+
+			case CEvent::ANNIVERSARY:
+				{
+					mir_sntprintf(szMsg, SIZEOF(szMsg),
+						TranslateT("%s has %s today."),
+						DB::Contact::DisplayName(hContact),
+						TranslateT("an anniversary"));
+					cle.hIcon = IcoLib_GetIcon(ICO_COMMON_ANNIVERSARY);
+				}
+				break;
+
+			default:
+				szMsg[0] = NULL;
+		}
+		cle.ptszTooltip = szMsg;
+
+		// pszService = NULL get error (crash),
+		// pszService = "dummy" get 'service not fount' and continue;
+		cle.pszService = "dummy";
+		cle.lParam = NULL;
+
+		CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle);
+	}
+}
+
+/**
+ * Play a sound for the nearest upcoming anniversary
+ *
+ * @param	evt				- structure specifying the next anniversary
+ *
+ * @retval	0 if sound was played
+ * @retval	1 otherwise
+ **/
+static BYTE NotifyWithSound(const CEvent &evt)
+{
+	if (evt._wDaysLeft <= min(DB::Setting::GetByte(SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET), gRemindOpts.wDaysEarlier))
+	{
+		switch (evt._eType)
+		{
+			case CEvent::BIRTHDAY:
+				SkinPlaySound(evt._wDaysLeft == 0 ? SOUND_BIRTHDAY_TODAY : SOUND_BIRTHDAY_SOON);
+				return 0;
+
+			case CEvent::ANNIVERSARY:
+				SkinPlaySound(SOUND_ANNIVERSARY);
+				return 0;
+		}
+	}
+	return 1;
+}
+
+/***********************************************************************************************************
+ * "check for anniversary" functions
+ ***********************************************************************************************************/
+
+static LPCTSTR ContactGender(HANDLE hContact)
+{
+	switch (GenderOf(hContact))
+	{
+		case 'M': return TranslateT("He");
+		case 'F': return TranslateT("She");
+	}
+	return TranslateT("He/She");
+}
+
+static BOOLEAN CheckAnniversaries(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify)
+{
+	INT numAnniversaries = 0;
+	INT Diff;
+	MAnnivDate mta;
+	INT i;
+	TCHAR szAnniv[MAX_PATH];
+	TCHAR strMsg[MAX_SECONDLINE];
+	BOOLEAN bOverflow = FALSE;
+	WORD wDaysEarlier;
+
+	if ((gRemindOpts.RemindState == REMIND_ANNIV) || (gRemindOpts.RemindState == REMIND_ALL))
+	{
+		for (i = 0; i < ANID_LAST && !mta.DBGetAnniversaryDate(hContact, i); i++)
+		{
+			mta.DBGetReminderOpts(hContact);
+
+			if (mta.RemindOption() != BST_UNCHECKED)
+			{
+				wDaysEarlier = (mta.RemindOption() == BST_CHECKED) ? mta.RemindOffset() : -1;
+				if (wDaysEarlier == (WORD)-1)
+				{
+					wDaysEarlier = gRemindOpts.wDaysEarlier;
+				}
+
+				Diff = mta.CompareDays(Now);
+				if ((Diff >= 0) && (Diff <= wDaysEarlier))
+				{
+					if (evt._wDaysLeft > Diff)
+					{
+						evt._wDaysLeft = Diff;
+						evt._eType = CEvent::ANNIVERSARY;
+					}
+					numAnniversaries++;
+
+					// create displayed text for popup
+					if (bNotify && !bOverflow)
+					{
+						// first anniversary found
+						if (numAnniversaries == 1)
+						{
+							mir_sntprintf(szAnniv, MAX_PATH,
+								TranslateT("%s has the following anniversaries:\0"),
+								ContactGender(hContact));
+							mir_tcsncpy(strMsg, szAnniv, mir_tcslen(szAnniv));
+						}
+						switch (Diff)
+						{
+							case 0:
+								{
+									mir_sntprintf(szAnniv, MAX_PATH,
+										TranslateT("%d. %s today\0"),
+										mta.Age(), mta.Description());
+								}
+								break;
+
+							case 1:
+								{
+									mir_sntprintf(szAnniv, MAX_PATH,
+										TranslateT("%d. %s tomorrow\0"),
+										mta.Age() + 1, mta.Description());
+								}
+								break;
+
+							default:
+								{
+									mir_sntprintf(szAnniv, MAX_PATH,
+										TranslateT("%d. %s in %d days\0"),
+										mta.Age() + 1, mta.Description(), Diff);
+								}
+						}
+						if (mir_tcslen(szAnniv) >= MAX_SECONDLINE - mir_tcslen(strMsg))
+						{
+							if (strMsg)
+								mir_tcsncat(strMsg, _T("\n...\0"), SIZEOF(strMsg));
+							else
+								mir_tcsncpy(strMsg, _T("\n...\0"), mir_tcslen(_T("\n...\0")));
+							bOverflow = TRUE;
+						}
+						else
+						{
+							if (strMsg)
+								mir_tcsncat(strMsg, _T("\n- \0"), SIZEOF(strMsg));
+							else
+								mir_tcsncpy(strMsg, _T("\n- \0"), mir_tcslen(_T("\n- \0")));
+							mir_tcsncat(strMsg, szAnniv, SIZEOF(strMsg));
+						}
+					}
+				}
+			}
+		}
+	}
+	// show one popup for all anniversaries
+	if (numAnniversaries != 0 && bNotify)
+	{
+		NotifyWithPopup(hContact, CEvent::ANNIVERSARY, Diff, LPGENT("Anniversaries"), strMsg);
+	}
+	return numAnniversaries != 0;
+}
+
+/**
+ * This function checks, whether a contact has a birthday and it is within the period of time to remind of or not.
+ *
+ * @param	hContact		- the contact to check
+ * @param	Now				- current time
+ * @param	evt				- the reference to a structure, which retrieves the resulting DTB
+ * @param	bNotify			- if TRUE, a popup will be displayed for a contact having birthday within the next few days.
+ * @param	LastAnswer		- this parameter is used for the automatic backup function
+ *
+ * @retval	TRUE			- contact has a birthday to remind of
+ * @retval	FALSE			- contact has no birthday or it is not within the desired period of time.
+ **/
+static BOOLEAN CheckBirthday(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify, PWORD LastAnwer)
+{
+	BOOLEAN result = FALSE;
+
+	if (gRemindOpts.RemindState == REMIND_BIRTH || gRemindOpts.RemindState == REMIND_ALL)
+	{
+		MAnnivDate mtb;
+
+		if (!mtb.DBGetBirthDate(hContact))
+		{
+			INT Diff;
+			WORD wDaysEarlier;
+
+			mtb.DBGetReminderOpts(hContact);
+
+			// make backup of each protocol based birthday
+			if (DB::Setting::GetByte(SET_REMIND_SECUREBIRTHDAY, TRUE))
+			{
+				mtb.BackupBirthday(hContact, NULL, 0, LastAnwer);
+			}
+
+			if (mtb.RemindOption() != BST_UNCHECKED)
+			{
+				wDaysEarlier = (mtb.RemindOption() == BST_CHECKED) ? mtb.RemindOffset() : -1;
+				if (wDaysEarlier == (WORD)-1)
+				{
+					wDaysEarlier = gRemindOpts.wDaysEarlier;
+				}
+
+				Diff = mtb.CompareDays(Now);
+				if ((Diff >= 0) && (Diff <= wDaysEarlier))
+				{
+					if (evt._wDaysLeft > Diff)
+					{
+						evt._wDaysLeft = Diff;
+						evt._eType = CEvent::BIRTHDAY;
+					}
+
+					if (bNotify)
+					{
+						TCHAR szMsg[MAXDATASIZE];
+						WORD cchMsg = 0;
+
+						switch (Diff)
+						{
+							case 0:
+								{
+									cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg),
+										TranslateT("%s has birthday today."),
+										DB::Contact::DisplayName(hContact));
+								}
+								break;
+
+							case 1:
+								{
+									cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg),
+										TranslateT("%s has birthday tomorrow."),
+										DB::Contact::DisplayName(hContact));
+								}
+								break;
+
+							default:
+								{
+									cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg),
+										TranslateT("%s has birthday in %d days."),
+										DB::Contact::DisplayName(hContact), Diff);
+								}
+						}
+						mir_sntprintf(szMsg + cchMsg, SIZEOF(szMsg) - cchMsg,
+							TranslateT("\n%s becomes %d years old."),
+							ContactGender(hContact), mtb.Age(&Now) + (Diff > 0));
+
+						NotifyWithPopup(hContact, CEvent::BIRTHDAY, Diff, mtb.Description(), szMsg);
+					}
+					result = TRUE;
+				}
+			}
+		}
+	}
+	return result;
+}
+
+/**
+ * This function checks one contact. It is mainly used for clist extra icon rebuild notification handler.
+ *
+ * @param	hContact		- the contact to check
+ * @param	Now				- current time
+ * @param	evt				- the reference to a structure, which retrieves the resulting DTB
+ * @param	bNotify			- if TRUE, a popup will be displayed for a contact having birthday within the next few days.
+ * @param	LastAnswer		- this parameter is used for the automatic backup function
+ *
+ * @return	nothing
+ **/
+static VOID CheckContact(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify, PWORD LastAnwer = 0)
+{
+	// ignore meta subcontacts here as their birthday information are collected explicitly
+	if (hContact &&
+			(!gRemindOpts.bCheckVisibleOnly || !DB::Setting::GetByte(hContact, MOD_CLIST, "Hidden", FALSE)) &&
+			(!DB::MetaContact::IsSub(hContact)))
+	{
+		CEvent ca;
+
+		if (CheckBirthday(hContact, Now, ca, bNotify, LastAnwer) ||
+				CheckAnniversaries(hContact, Now, ca, bNotify))
+		{
+			evt << ca;
+
+			if (bNotify)
+			{
+				NotifyFlashCListIcon(hContact, ca);
+			}
+		}
+		NotifyWithExtraIcon(hContact, ca);
+	}
+}
+
+/**
+ * This function checks all contacts.
+ *
+ * @param	notify			- notification type
+ *
+ * @return	nothing
+ **/
+VOID SvcReminderCheckAll(const ENotify notify)
+{
+	if (gRemindOpts.RemindState != REMIND_OFF)
+	{
+		HANDLE hContact;
+		CEvent evt;
+		MTime now;
+		WORD a1 = 0;
+
+		now.GetLocalTime();
+
+		//walk through all the contacts stored in the DB
+		for (hContact = DB::Contact::FindFirst();
+				 hContact != NULL;
+				 hContact = DB::Contact::FindNext(hContact))
+		{
+			CheckContact(hContact, now, evt, notify != NOTIFY_CLIST, &a1);
+		}
+
+		if (notify != NOTIFY_CLIST)
+		{
+			// play sound for the next anniversary
+			NotifyWithSound(evt);
+
+			// popup anniversary list
+			if (DB::Setting::GetByte(SET_ANNIVLIST_POPUP, FALSE))
+			{
+				DlgAnniversaryListShow(0, 0);
+			}
+
+			if (evt._wDaysLeft > gRemindOpts.wDaysEarlier && notify == NOTIFY_NOANNIV)
+			{
+				NotifyWithPopup(NULL, CEvent::NONE, 0, NULL, TranslateT("No anniversaries to remind of"));
+			}
+		}
+		UpdateTimer(FALSE);
+	}
+}
+
+/***********************************************************************************************************
+ * Event Handler functions
+ ***********************************************************************************************************/
+
+/**
+ * This is the notification handler to tell reminder to reload required icons.
+ * The reminder only loads icons to clist, which are really required at the moment.
+ * This should help to save a bit memory.
+ *
+ * @param:	wParam			- not used
+ * @param:	lParam			- not used
+ *
+ * @return	This function must return 0 in order to continue in the notification chain.
+ **/
+static INT OnCListRebuildIcons(WPARAM, LPARAM)
+{
+	UINT i;
+
+	for (i = 0; i < SIZEOF(ghCListAnnivIcons); i++)
+	{
+		ghCListAnnivIcons[i] = INVALID_HANDLE_VALUE;
+	}
+	for (i = 0; i < SIZEOF(ghCListBirthdayIcons); i++)
+	{
+		ghCListBirthdayIcons[i] = INVALID_HANDLE_VALUE;
+	}
+	return 0;
+}
+
+/**
+ * This function is the notification handler for clist extra icons to be applied for a contact.
+ *
+ * @param	hContact		- handle to the contact whose extra icon is to apply
+ * @param	lParam			- not used
+ *
+ * @return	This function must return 0 in order to continue in the notification chain.
+ **/
+INT OnCListApplyIcon(HANDLE hContact, LPARAM)
+{
+	if (gRemindOpts.RemindState != REMIND_OFF)
+	{
+		CEvent evt;
+		MTime now;
+
+		now.GetLocalTime();
+		CheckContact(hContact, now, evt, FALSE);
+	}
+	return 0;
+}
+
+/**
+ * This is a notification handler for changed contact settings.
+ * If any anniversary setting has changed for a meta sub contact,
+ * the parental meta contact is rescanned.
+ *
+ * @param	hContact		- handle of the contect the notification was fired for
+ * @param	pdbcws			- pointer to a DBCONTACTWRITESETTING structure
+ *
+ * @return	This function must return 0 in order to continue in the notification chain.
+ **/
+static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws)
+{
+	if (hContact &&										// valid contact not owner!
+			ghCListIA &&								// extraicons active
+			pdbcws && pdbcws->szSetting &&				// setting structure valid
+			(pdbcws->value.type < DBVT_DWORD) &&		// anniversary datatype
+			(gRemindOpts.RemindState != REMIND_OFF) &&	// reminder active
+			(!strncmp(pdbcws->szSetting, "Birth", 5) ||
+			 !strncmp(pdbcws->szSetting, "Anniv", 5) ||
+			 !strncmp(pdbcws->szSetting, "DOB", 3)))
+	{
+		HANDLE hMeta = DB::MetaContact::GetMeta(hContact);
+		WORD LastAnswer = IDNONE;
+		CEvent evt;
+		MTime now;
+
+		// check metacontact instead of subcontact
+		if (hMeta)
+		{
+			hContact = hMeta;
+		}
+		now.GetLocalTime();
+		if (!strcmp(pdbcws->szModule, SvcReminderGetMyBirthdayModule()))
+		{
+			CheckContact(hContact, now, evt, FALSE, &LastAnswer);
+		}
+		else
+		{
+			CheckContact(hContact, now, evt, FALSE, 0);
+		}
+	}
+	return 0;
+}
+
+#define TBB_IDBTN		"CheckAnniv"
+#define TBB_ICONAME	TOOLBARBUTTON_ICONIDPREFIX TBB_IDBTN TOOLBARBUTTON_ICONIDPRIMARYSUFFIX
+
+/**
+ * This function is called by the ME_TTB_MODULELOADED event.
+ * It adds a set of buttons to the TopToolbar plugin.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+
+VOID SvcReminderOnTopToolBarLoaded()
+{
+	TTBButton ttb = {0};
+	ttb.cbSize = sizeof(ttb);
+
+	ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP;
+	ttb.pszService = MS_USERINFO_REMINDER_CHECK;
+	ttb.name = ttb.pszTooltipUp = LPGEN("Check anniversaries");
+	ttb.hIconHandleUp = Skin_GetIconHandle(ICO_COMMON_BIRTHDAY);
+	TopToolbar_AddButton(&ttb);
+}
+
+
+/***********************************************************************************************************
+ * services
+ ***********************************************************************************************************/
+
+/**
+ * This is the service function for MS_USERINFO_REMINDER_CHECK.
+ *
+ * @param:	wParam			- not used
+ * @param:	lParam			- not used
+ *
+ * @return	0
+ **/
+static INT_PTR CheckService(WPARAM, LPARAM)
+{
+	if (gRemindOpts.RemindState != REMIND_OFF)
+	{
+		SvcReminderCheckAll(NOTIFY_NOANNIV);
+	}
+	return 0;
+}
+
+/**
+ * This is the service function for MS_USERINFO_REMINDER_AGGRASIVEBACKUP.
+ *
+ * @param	hContact		- handle to single contact or NULL to backup all
+ * @param	lParam			- if 1, the messagebox will not be displayed
+ *
+ * return:	0
+ **/
+static INT_PTR BackupBirthdayService(WPARAM wParam, LPARAM lParam)
+{
+	HANDLE hContact	= (HANDLE)wParam;
+	MAnnivDate mdb;
+
+	if (hContact)
+	{
+		if (!mdb.DBGetBirthDate(hContact))
+		{
+			mdb.BackupBirthday(hContact, NULL, TRUE);
+		}
+	}
+	else
+	{
+		WORD a1 = 0;
+
+		//walk through all the contacts stored in the DB
+		for (hContact = DB::Contact::FindFirst();
+				 hContact != NULL;
+				 hContact = DB::Contact::FindNext(hContact))
+		{
+			if (!DB::MetaContact::IsSub(hContact) && !mdb.DBGetBirthDate(hContact))
+			{
+				mdb.BackupBirthday(hContact, NULL, TRUE, &a1);
+			}
+		}
+	}
+
+	if (lParam != TRUE)
+	{
+		MSGBOX mBox;
+
+		mBox.cbSize = sizeof(MSGBOX);
+		mBox.hParent = NULL;
+		mBox.hiLogo = IcoLib_GetIcon(ICO_COMMON_BIRTHDAY);
+		mBox.uType = MB_ICON_INFO;
+		mBox.ptszTitle = TranslateT("Update custom birthday");
+		mBox.ptszMsg = TranslateT("Backing up and syncing all birthdays complete!");
+		MsgBoxService(NULL, (LPARAM)&mBox);
+	}
+	return 0;
+}
+
+/**
+ * This function returns a constant pointer to the module the date should be saved to
+ *
+ * @param	none
+ *
+ * @return	module to write birthday information to, MOD_MBIRTHDAY by default
+ **/
+LPCSTR SvcReminderGetMyBirthdayModule()
+{
+	return ((DB::Setting::GetByte(SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE) == 1) ? USERINFO : MOD_MBIRTHDAY);
+}
+
+
+/***********************************************************************************************************
+ * timer stuff
+ ***********************************************************************************************************/
+
+/**
+ * Timer procedure, called if date changed. This updates clist icons.
+ *
+ * @param	hwnd			- not used
+ * @param	uMsg			- not used
+ * @param	idEvent			- not used
+ * @param	dwTime			- not used
+ * @return	nothing
+ **/
+static VOID CALLBACK TimerProc_DateChanged(HWND, UINT, UINT_PTR, DWORD)
+{
+	static MTime last;
+	MTime now;
+
+	now.GetLocalTime();
+	if (now.Day() > last.Day() || now.Month() > last.Month() || now.Year() > last.Year()) {
+		SvcReminderCheckAll(NOTIFY_CLIST);
+		last = now;
+	}
+}
+
+/**
+ * Timer procedure, called again and again if the notification interval ellapsed
+ *
+ * @param	hwnd			- not used
+ * @param	uMsg			- not used
+ * @param	idEvent			- not used
+ * @param	dwTime			- not used
+ *
+ * @return	nothing
+ **/
+static VOID CALLBACK TimerProc_Check(HWND, UINT, UINT_PTR, DWORD)
+{
+	SvcReminderCheckAll(NOTIFY_POPUP);
+}
+
+/**
+ * Load timers or update them.
+ *
+ * @param	bStartup		- is only TRUE if module is loaded to indicate startup process
+ *
+ * @return	nothing
+ **/
+static VOID UpdateTimer(BOOLEAN bStartup)
+{
+	LONG	wNotifyInterval =	60 * 60 * (LONG)DB::Setting::GetWord(MODNAME, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL);
+	MTime	now, last;
+
+	now.GetTimeUTC();
+
+	if (bStartup) {
+		last.DBGetStamp(NULL, MODNAME, SET_REMIND_LASTCHECK);
+
+		// if last check occured at least one day before just do it on startup again
+		if (now.Year() > last.Year() ||	now.Month() > last.Month() ||	now.Day() > last.Day() || DB::Setting::GetByte(SET_REMIND_CHECKON_STARTUP, FALSE))
+			wNotifyInterval = 5;
+		else
+			wNotifyInterval -= now.Compare(last);
+
+		ghRemindDateChangeTimer = SetTimer(0, 0, 1000 * 60 * 5, (TIMERPROC)TimerProc_DateChanged);
+	}
+	else {
+		now.DBWriteStamp(NULL, MODNAME, SET_REMIND_LASTCHECK);
+	}
+	// wait at least 5 seconds before checking at startup, to give miranda a better chance to load faster
+	KillTimer(0, ghRemindTimer);
+	ghRemindTimer = SetTimer(0, 0, 1000 * wNotifyInterval, TimerProc_Check);
+}
+
+/***********************************************************************************************************
+ * module loading & unloading
+ ***********************************************************************************************************/
+
+VOID SvcReminderEnable(BOOLEAN bEnable)
+{
+	if (bEnable)	// Reminder is on
+	{
+		if (myGlobals.ExtraIconsServiceExist && (ExtraIcon == INVALID_HANDLE_VALUE))
+		{
+			EXTRAICON_INFO ico = {0};
+			ico.type = EXTRAICON_TYPE_ICOLIB;
+			ico.cbSize=sizeof(ico);
+			ico.name="Reminder";
+			ico.description="Reminder (uinfoex)";
+			ico.descIcon=ICO_COMMON_ANNIVERSARY;
+			ExtraIcon=(HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0);
+			ZeroMemory(&ico,sizeof(ico));
+		}
+		// init hooks
+		if (!ghCListIR)
+		{
+			ghCListIR = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, (MIRANDAHOOK)OnCListRebuildIcons);
+		}
+
+		if (!ghCListIA)
+		{
+			ghCListIA = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcon);
+		}
+		if (!ghSettingsChanged && !myGlobals.UseDbxTree)
+		{
+			ghSettingsChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged);
+		}
+
+		// reinit reminder options
+		gRemindOpts.RemindState	= DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED);
+		gRemindOpts.wDaysEarlier = DB::Setting::GetWord(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET);
+		gRemindOpts.bCListExtraIcon = DB::Setting::GetByte(SET_REMIND_EXTRAICON, 1);
+		gRemindOpts.bCheckVisibleOnly = DB::Setting::GetByte(SET_REMIND_CHECKVISIBLE, DEFVAL_REMIND_CHECKVISIBLE);
+		gRemindOpts.bFlashCList = DB::Setting::GetByte(SET_REMIND_FLASHICON, FALSE);
+		gRemindOpts.bPopups = ServiceExists(MS_POPUP_ADDPOPUPT) && DB::Setting::GetByte(SET_POPUP_ENABLED, DEFVAL_POPUP_ENABLED);
+
+		// init the timer
+		UpdateTimer(TRUE);
+	}
+	else	// Reminder is off
+	{
+		HANDLE hContact;
+
+		for (hContact = DB::Contact::FindFirst();
+				 hContact != NULL;
+				 hContact = DB::Contact::FindNext(hContact))
+		{
+			NotifyWithExtraIcon(hContact, CEvent());
+		}
+		gRemindOpts.RemindState	= REMIND_OFF;
+		SvcReminderUnloadModule();
+	}
+}
+
+/**
+ * This function is called by Miranda just after loading all system modules.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID SvcReminderOnModulesLoaded(VOID)
+{
+	// init clist extra icon structure
+	OnCListRebuildIcons(0, 0);
+
+	SvcReminderEnable(DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED) != REMIND_OFF);
+}
+
+/**
+ * This function initially loads all required stuff for reminder.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID SvcReminderLoadModule(VOID)
+{
+	// init sounds
+	SKINSOUNDDESCEX ssd = { 0 };
+	ssd.cbSize = sizeof(ssd);
+	ssd.pszSection = MODNAME;
+
+	ssd.pszName = SOUND_BIRTHDAY_TODAY;
+	ssd.pszDescription = LPGEN("Birthday reminder");
+	ssd.pszDefaultFile = "Sounds\\BirthDay.wav";
+	Skin_AddSound(&ssd);
+
+	ssd.pszName = SOUND_BIRTHDAY_SOON;
+	ssd.pszDescription = LPGEN("Birthday reminder: it's coming");
+	ssd.pszDefaultFile = "Sounds\\BirthDayComing.wav";
+	Skin_AddSound(&ssd);
+
+	ssd.pszName = SOUND_ANNIVERSARY;
+	ssd.pszDescription = LPGEN("Anniversary Reminder");
+	ssd.pszDefaultFile = "Sounds\\Reminder.wav";
+	Skin_AddSound(&ssd);
+
+	// create service functions
+	CreateServiceFunction(MS_USERINFO_REMINDER_CHECK, CheckService);
+	CreateServiceFunction(MS_USERINFO_REMINDER_AGGRASIVEBACKUP, BackupBirthdayService);
+
+	// register hotkey
+	HOTKEYDESC hk = { 0 };
+	hk.cbSize = sizeof(HOTKEYDESC);
+	hk.pszSection = MODNAME;
+	hk.pszName = "ReminderCheck";
+	hk.pszDescription = LPGEN("Check anniversaries");
+	hk.pszService = MS_USERINFO_REMINDER_CHECK;
+	Hotkey_Register(&hk);
+}
+
+/**
+ * This function unloads the reminder module.
+ *
+ * @param	none
+ *
+ * @return	nothing
+ **/
+VOID SvcReminderUnloadModule(VOID)
+{
+	// kill timers
+	KillTimer(0, ghRemindTimer);
+	ghRemindTimer = 0;
+	KillTimer(0, ghRemindDateChangeTimer);
+	ghRemindDateChangeTimer = 0;
+
+	// unhook event handlers
+	UnhookEvent(ghCListIR);
+	ghCListIR = 0;
+	UnhookEvent(ghCListIA);
+	ghCListIA = 0;
+	UnhookEvent(ghSettingsChanged);
+	ghSettingsChanged = 0;
+}
diff --git a/plugins/UserInfoEx/src/svc_reminder.h b/plugins/UserInfoEx/src/svc_reminder.h
new file mode 100644
index 0000000000..210250b9f3
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_reminder.h
@@ -0,0 +1,117 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_reminder.h $
+Revision       : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (Ср, 08 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _SVCREMINDER_H_
+#define _SVCREMINDER_H_
+
+#define POPUP_TYPE_BIRTHDAY		1
+#define POPUP_TYPE_ANNIVERSARY	2
+
+// for PopupDelayType
+#define POPUP_DELAY_DEFAULT		3
+#define POPUP_DELAY_CUSTOM		4
+#define POPUP_DELAY_PERMANENT	5
+
+// for PopupColorType
+#define POPUP_COLOR_DEFAULT		6
+#define POPUP_COLOR_WINDOWS		7
+#define POPUP_COLOR_CUSTOM		8
+
+#define SOUND_BIRTHDAY_TODAY				"Birthday"
+#define SOUND_BIRTHDAY_SOON					"BirthdayComing"
+#define SOUND_ANNIVERSARY					"Anniversary"
+
+// databbase settings
+#define SET_REMIND_LASTCHECK				"RemindLastCheck"
+#define SET_REMIND_ENABLED					"RemindEnabled"
+#define SET_REMIND_OFFSET					"RemindOffset"
+#define SET_REMIND_CHECKVISIBLE				"RemindCheckVisible"
+#define SET_REMIND_NOTIFYINTERVAL			"RemindNotifyInterval"
+#define SET_REMIND_FLASHICON				"RemindFlashIcon"
+#define SET_REMIND_EXTRAICON				"RemindExtraIcon"
+#define SET_REMIND_BIRTHMODULE				"RemindBirthModule"
+#define SET_REMIND_MENUENABLED				"RemindMenuEnabled"
+#define SET_REMIND_BIRTHDAY_ENABLED			"RemindBirthday"
+#define SET_REMIND_BIRTHDAY_OFFSET			"RemindBirthdayOffset"
+#define SET_REMIND_CHECKON_STARTUP			"RemindStartupCheck"
+#define SET_REMIND_SECUREBIRTHDAY			"RemindSecureBirthday"
+#define SET_REMIND_BIRTHDAY_IGNORED			"RemindSecureIgnored"
+#define SET_REMIND_SOUNDOFFSET				"RemindSoundOffset"
+#define SET_POPUP_ENABLED					"PopupEnabled"
+#define SET_POPUP_BIRTHDAY_COLORTYPE		"PopupBirthClrType"
+#define SET_POPUP_BIRTHDAY_COLOR_TEXT		"PopupBirthClrBirthText"
+#define SET_POPUP_BIRTHDAY_COLOR_BACK		"PopupBirthClrBirthBack"
+#define SET_POPUP_ANNIVERSARY_COLORTYPE		"PopupAnnivClrType"
+#define SET_POPUP_ANNIVERSARY_COLOR_TEXT	"PopupAnnivClrText"
+#define SET_POPUP_ANNIVERSARY_COLOR_BACK	"PopupAnnivClrBack"
+#define SET_POPUP_DELAY						"PopupDelay"
+
+// default values
+#define DEFVAL_REMIND_ENABLED				REMIND_ALL
+#define DEFVAL_REMIND_MENUENABLED			1
+#define DEFVAL_REMIND_OFFSET				9
+#define DEFVAL_REMIND_SOUNDOFFSET			3
+#define DEFVAL_REMIND_NOTIFYINTERVAL		12
+#define DEFVAL_REMIND_BIRTHMODULE			1
+#define DEFVAL_POPUP_ENABLED				1
+#define DEFVAL_REMIND_CHECKVISIBLE			0
+#define HM_OPENMSG							(WM_USER+1)
+
+/**
+ * typedefs:
+ **/
+enum EEnabled 
+{
+	REMIND_OFF,
+	REMIND_BIRTH,
+	REMIND_ANNIV,
+	REMIND_ALL,
+};
+
+enum ENotify 
+{
+	NOTIFY_CLIST,		// notify with clist extra icon only
+	NOTIFY_POPUP,		// notify with popup and clist extra icon
+	NOTIFY_NOANNIV		// notify of no anniversary was found
+};
+
+/**
+ * Global functions:
+ **/
+VOID	SvcReminderCheckAll(const ENotify notify);
+LPCSTR	SvcReminderGetMyBirthdayModule(VOID);
+
+VOID	SvcReminderOnTopToolBarLoaded(VOID);
+VOID	SvcReminderOnModulesLoaded(VOID);
+
+VOID	SvcReminderEnable(BOOLEAN bEnable);
+VOID	SvcReminderLoadModule(VOID);
+VOID	SvcReminderUnloadModule(VOID);
+
+#endif /* _SVCREMINDER_H_ */
diff --git a/plugins/UserInfoEx/src/svc_timezone.cpp b/plugins/UserInfoEx/src/svc_timezone.cpp
new file mode 100644
index 0000000000..d9d0b4688c
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_timezone.cpp
@@ -0,0 +1,83 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: $
+Revision       : $Revision: $
+Last change on : $Date: $
+Last change by : $Author: $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_icq.h"
+#include "svc_timezone.h"
+
+/***********************************************************************************************************
+ * services
+ ***********************************************************************************************************/
+
+/**
+ * This service function provides a TIME_ZONE_INFORMATION structure
+ * for the desired contact, in case the contact's timezone can be determined.
+.* parsed to new core tzi interface if present.
+ * 
+ * @param	wParam			- HANDLE of the contact, to retrieve timezone information from.
+ * @param	lParam			- pointer to a TIME_ZONE_INFORMATION to fill.
+ *
+ * @retval	0 - success
+ * @retval	1 - failure
+ **/
+INT_PTR GetContactTimeZoneInformation(WPARAM wParam,LPARAM lParam)
+{
+	//use new core tz interface
+	LPTIME_ZONE_INFORMATION pTimeZoneInformation = (LPTIME_ZONE_INFORMATION)lParam;
+	(*pTimeZoneInformation) = *tmi.getTzi(tmi.createByContact((HANDLE)wParam, 0));
+	return (pTimeZoneInformation == NULL);
+}
+
+/**
+ * This function returns the contact's local time.
+ *
+ * @param	wParam			- HANDLE of the contact, to retrieve timezone information from.
+ * @param	lParam			- pointer to a systemtime structure
+ *	
+ * @return	TRUE or FALSE
+ **/
+INT_PTR GetContactLocalTime(WPARAM wParam, LPARAM lParam)
+{
+	//use new core tz interface
+	LPSYSTEMTIME pSystemTime = (LPSYSTEMTIME)lParam;
+	return (INT_PTR)tmi.getTimeZoneTimeByContact((HANDLE)wParam, pSystemTime);
+}
+
+/***********************************************************************************************************
+ * initialization
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcTimezoneLoadModule()
+{
+	CreateServiceFunction(MS_USERINFO_TIMEZONEINFO, GetContactTimeZoneInformation);
+	CreateServiceFunction(MS_USERINFO_LOCALTIME, GetContactLocalTime);
+}
diff --git a/plugins/UserInfoEx/src/svc_timezone.h b/plugins/UserInfoEx/src/svc_timezone.h
new file mode 100644
index 0000000000..1c2dee943d
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_timezone.h
@@ -0,0 +1,51 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_timezone.h $
+Revision       : $Revision: 191 $
+Last change on : $Date: 2010-09-20 11:52:01 +0200 (Mo, 20. Sep 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _SVC_TIMEZONE_H_
+#define _SVC_TIMEZONE_H_
+
+/**
+ * This structure is used by GetTimeZoneInformationByIndex to retrieve
+ * timezone information from windows' registry
+ **/
+typedef struct _REG_TZI_FORMAT
+{
+	LONG Bias;
+	LONG StandardBias;
+	LONG DaylightBias;
+	SYSTEMTIME StandardDate;
+	SYSTEMTIME DaylightDate;
+} REG_TZI_FORMAT, *PREG_TZI_FORMAT;
+
+INT_PTR		GetContactTimeZoneInformation(WPARAM wParam, LPARAM lParam);
+INT_PTR		GetContactLocalTime(WPARAM wParam, LPARAM lParam);
+
+VOID		SvcTimezoneLoadModule();
+
+#endif /* _SVC_TIMEZONE_H_ */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/svc_timezone_old.cpp b/plugins/UserInfoEx/src/svc_timezone_old.cpp
new file mode 100644
index 0000000000..f602f68f5d
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_timezone_old.cpp
@@ -0,0 +1,645 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_timezone_old.cpp $
+Revision       : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "m_icq.h"
+#include "svc_timezone_old.h"
+
+#define TZREG		"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"
+#define TZREG_9X	"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones"
+
+/**************************************************************************************************
+ * struct CTimeZone
+ **************************************************************************************************/
+
+/**
+ * This is the default constructure, which resets
+ * all attributes to NULL.
+ **/
+CTimeZone::CTimeZone()
+{
+	ZeroMemory(this, sizeof(*this));
+}
+
+/**
+ * The default construcor's task ist to clear out
+ * all pieces of the used memory.
+ **/
+CTimeZone::~CTimeZone()
+{
+	MIR_FREE(ptszName);
+	MIR_FREE(ptszDisplay);
+}
+
+/**
+ * This method can be used to basically convert a Windows
+ * timezone to the format, known by miranda.
+ *
+ * @warning		This operation does not work vice versa in
+ *				all cases, as there are sometimes more then
+ *				one Windows timezones with the same Bias.
+ **/
+BYTE CTimeZone::ToMirandaTimezone() const
+{
+	return (BYTE) (Bias / 30);
+}
+
+/**
+ * This operator translates the content of this object to
+ * a TIME_ZONE_INFORMATION structure as it is required by 
+ * several windows functions.
+ **/
+CTimeZone::operator TIME_ZONE_INFORMATION() const
+{
+	TIME_ZONE_INFORMATION tzi;
+
+	tzi.Bias = Bias;
+	tzi.DaylightBias = DaylightBias;
+	tzi.StandardBias = StandardBias;
+
+	memcpy(&tzi.DaylightDate, &DaylightDate, sizeof(DaylightDate));
+	memcpy(&tzi.StandardDate, &StandardDate, sizeof(DaylightDate));
+	return tzi;
+}		
+
+/***********************************************************************************************************
+ * class CTzBias
+ ***********************************************************************************************************/
+
+class CTzBias : public LIST<CTimeZone>
+{
+	static INT sortFunc(const CTimeZone *tz1, const CTimeZone *tz2)
+	{
+		INT result = tz2->Bias - tz1->Bias;
+		// DO NOT USE mir_tcsicmp here as it does only return TRUE or FALSE!!!
+		// lstrcmpi takes care of umlauts e.g. �,�,....
+		return (result || !tz1->ptszDisplay || !tz2->ptszDisplay) ? result : lstrcmpi(tz1->ptszDisplay, tz2->ptszDisplay);
+	}
+public:
+	CTzBias() : LIST<CTimeZone>(50, (FTSortFunc) CTzBias::sortFunc)
+	{
+	}
+
+	~CTzBias()
+	{
+		// delete the list, items delete by CTzMgr
+		this->destroy();
+	}
+};
+/***********************************************************************************************************
+ * class CTzMgr
+ ***********************************************************************************************************/
+
+/**
+ * This class is a deriviant of miranda's SortedList and holds all timezones
+ * known by Windows. By default there is no API to list timezones, so we
+ * need to get the information directly from the registry. In order to avoid
+ * heavy reading operations from registry, this class has the task to cache
+ * all required information for much faster access.
+ **/
+class CTzMgr : public LIST<CTimeZone>
+{
+	CTzBias _bias;
+
+	static INT sortFunc(const CTimeZone *tz1, const CTimeZone *tz2)
+	{
+		// DO NOT USE mir_tcsicmp here as it does only return TRUE or FALSE!!!
+		return _tcsicmp(tz1->ptszName, tz2->ptszName);
+	}
+
+	/**
+	 * This method clears the TzTzMgr's data.
+	 **/		
+	VOID destroy()
+	{
+		INT i;
+
+		// delete data
+		for (i = 0 ; i < count; i++)
+		{
+			delete (*this)[i];
+		}
+		// delete the list
+		LIST<CTimeZone>::destroy();
+		// delete the _bias list   ????
+		//_bias.destroy();
+
+	}
+
+public:
+
+	const CTzBias& Bias;
+
+	CTzMgr() 
+		:LIST<CTimeZone>(50, (FTSortFunc) CTzMgr::sortFunc),
+		_bias(), Bias(_bias)
+	{
+	}
+
+	/**
+	 * This is the default destructor of the class.
+	 *
+	 * @param	 none
+	 *
+	 * @return	nothing
+	 **/
+	~CTzMgr()
+	{
+		destroy();
+	}
+
+	/**
+	 * This method loads all information about timezones from windows' registry.
+	 **/
+	INT Init()
+	{
+		INT			result;
+		HKEY		hKeyRoot,
+					hKeyTz;
+		DWORD		i,
+					cbData;
+		TCHAR		szName[MAX_PATH],
+					szDisplay[MAX_PATH];
+		CTimeZone	*pTimeZone;
+
+		result = RegOpenKey(HKEY_LOCAL_MACHINE, _T(TZREG), &hKeyRoot);
+		if (result != ERROR_SUCCESS)
+		{
+			result = RegOpenKey(HKEY_LOCAL_MACHINE, _T(TZREG_9X), &hKeyRoot);
+		}
+		if (result == ERROR_SUCCESS)
+		{
+			// clear out old list
+			this->destroy(); _bias.destroy();
+			for (i = 0; ERROR_SUCCESS == RegEnumKey(hKeyRoot, i, szName, SIZEOF(szName)); i++)
+			{
+				result = RegOpenKey(hKeyRoot, szName, &hKeyTz);
+				if (result == ERROR_SUCCESS)
+				{
+					pTimeZone = new CTimeZone();
+					if (pTimeZone)
+					{
+						cbData = sizeof(szDisplay);
+						result |= RegQueryValueEx(hKeyTz, _T("Display"), 0, 0, (LPBYTE)szDisplay, &cbData);
+
+						cbData = sizeof(REG_TZI_FORMAT);
+						result |= RegQueryValueEx(hKeyTz, _T("TZI"), 0, 0, (LPBYTE)pTimeZone, &cbData);
+
+						cbData = sizeof(DWORD);
+						if (RegQueryValueEx(hKeyTz, _T("Index"), 0, 0, (LPBYTE)(UINT_PTR)pTimeZone->dwIndex, &cbData) != ERROR_SUCCESS)
+						{
+							pTimeZone->dwIndex = TZINDEX_UNSPECIFIED;
+						}
+						if (result == ERROR_SUCCESS)
+						{
+							pTimeZone->ptszName = mir_tcsdup(szName);
+							pTimeZone->ptszDisplay = mir_tcsdup(szDisplay);
+							result = (insert(pTimeZone) == ERROR_SUCCESS);
+						}
+						if (result != ERROR_SUCCESS)
+						{
+							delete pTimeZone;
+						}
+						else
+						{
+							_bias.insert(pTimeZone);
+						}
+					}
+					RegCloseKey(hKeyTz);
+				}
+			}
+			RegCloseKey(hKeyRoot);
+		}
+		return result;
+	}
+
+	/**
+	 * This method is used to find a certain list entry according to
+	 * a key, providing information about the entry to look for.
+	 * 
+	 * @param	result		- Pointer to a pointer, retrieving the CTimeZone
+	 *						  object, matching the criteria provided by key
+	 * @param	key			- Pointer to a CTimeZone structure, providing
+	 *						  information about the item to look for.
+	 *						  The Bias member and/or pszDisplay member must
+	 *						  be valid.
+	 * @retval	-1			: item not found
+	 * @retval 0...count	: index of the found item
+	 **/
+	INT find(CTimeZone** pTimezone, CTimeZone* pKey) const
+	{ 
+		INT nItemIndex = -1;
+		
+		if (pKey && pKey->ptszName)
+		{
+			nItemIndex = getIndex(pKey);
+			if (pTimezone)
+			{
+				*pTimezone = (nItemIndex == -1) ? NULL : items[nItemIndex];
+			}
+		}
+		return nItemIndex;
+	}
+
+	INT find(CTimeZone** pTimezone, LPTSTR ptszName) const
+	{ 
+		CTimeZone key;
+		INT nItemIndex;
+
+		key.ptszName = ptszName;
+		nItemIndex = find(pTimezone, &key);
+		key.ptszName = NULL; // prevents ptszName from being deleted by the destructor.
+		return nItemIndex;
+	}
+
+	/**
+	 * This method is used to find a certain list entry according to
+	 * a given dwTzIndex, providing information about the entry to look for.
+	 * 
+	 * @param	result		- Pointer to a pointer, retrieving the CTimeZone
+	 *						  object, matching the criteria provided by key
+	 * @param	dwTzIndex	- Timezone index as read from Windows Registry
+	 * @retval	-1			: item not found
+	 * @retval	0...count	: index of the found item
+	 **/	 
+	INT find(CTimeZone** result, DWORD dwTzIndex) const
+	{ 
+		INT nItemIndex = -1;
+		CTimeZone *ptz = NULL;
+		
+		if (dwTzIndex != TZINDEX_UNSPECIFIED)
+		{
+			for (nItemIndex = 0; nItemIndex < count; nItemIndex++)
+			{
+				ptz = items[nItemIndex];
+				if (ptz && (ptz->dwIndex == dwTzIndex))
+					break;
+			}
+		}
+		if (result)
+		{
+			*result = ptz;
+		}
+		return ((nItemIndex == count) ? -1 : nItemIndex);
+	}
+
+};
+// global timezone TzMgr object
+static CTzMgr TzMgr;
+
+/***********************************************************************************************************
+ * public Service functions
+ ***********************************************************************************************************/
+
+/**
+ * This method trys to find some default windows timezone idices for a given
+ * miranda timezone.
+ *
+ * @param	MirTz			- this is a miranda timezone with values between -24 and 24.
+ *
+ * @return	This method returns a @TZ_MAP struct of a windows timezone, which is maps
+ *			the @MirTz value,name or {-1,NULL} if no windows timezone index exists.
+ **/
+static TZ_MAP MirTZ2WinTZ(const CHAR MirTz)
+{
+	/**
+	 * This is an item of an array of timezones, which are known by both Miranda-IM
+	 * and Windows. It is used to map an ICQ timezone against a Windows timezone
+	 * for retrieving information about daylight saving time and more.
+	 **/
+	static const TZ_MAP TzMap[] = {
+		{ 0,	_T("Dateline Standard Time")},			// GMT-12:00 Eniwetok; Kwajalein
+		{-1,	_T("")},								// GMT-11:30
+		{ 1,	_T("Samoa Standard Time")},				// GMT-11:00 Midway Island; Samoa
+		{-1,	_T("")},								// GMT-10:30
+		{ 2,	_T("Hawaiian Standard Time")},			// GMT-10:00 Hawaii
+		{-1,	_T("")},								// GMT-9:30
+		{ 3,	_T("Alaskan Standard Time")},			// GMT-9:00 Alaska
+		{-1,	_T("")},								// GMT-8:30
+		{ 4,	_T("Pacific Standard Time")},			// GMT-8:00 Pacific Time; Tijuana
+		{-1,	_T("")},								// GMT-7:30
+		{15,	_T("US Mountain Standard Time")},		// GMT-7:00 Arizona; Mountain Time
+		{-1,	_T("")},								// GMT-6:30
+		{33,	_T("Central America Standard Time")},	// GMT-6:00 Central Time; Central America; Saskatchewan
+		{-1,	_T("")},								// GMT-5:30
+		{45,	_T("SA Pacific Standard Time")},		// GMT-5:00 Eastern Time; Bogota; Lima; Quito
+		{-1,	_T("")},								// GMT-4:30
+		{56,	_T("Pacific SA Standard Time")},		// GMT-4:00 Atlantic Time; Santiago; Caracas; La Paz
+		{60,	_T("Newfoundland Standard Time")},		// GMT-3:30 Newfoundland
+		{70,	_T("SA Eastern Standard Time")},		// GMT-3:00 Greenland; Buenos Aires; Georgetown
+		{-1,	_T("")},								// GMT-2:30
+		{75,	_T("Mid-Atlantic Standard Time")},		// GMT-2:00 Mid-Atlantic
+		{-1,	_T("")},								// GMT-1:30
+		{80,	_T("Azores Standard Time")},			// GMT-1:00 Cape Verde Islands; Azores
+		{-1,	_T("")},								// GMT-0:30
+		{85,	_T("GMT Standard Time")},				// GMT+0:00 London; Dublin; Edinburgh; Lisbon; Casablanca
+		{-1,	_T("")},								// GMT+0:30
+		{105,	_T("Romance Standard Time")},			// GMT+1:00 Central European Time; West Central Africa; Warsaw
+		{-1,	_T("")},								// GMT+1:30
+		{140,	_T("South Africa Standard Time")},		// GMT+2:00 Jerusalem; Helsinki; Harare; Cairo; Bucharest; Athens
+		{-1,	_T("")},								// GMT+2:30
+		{145,	_T("Russian Standard Time")},			// GMT+3:00 Moscow; St. Petersburg; Nairobi; Kuwait; Baghdad
+		{160,	_T("Iran Standard Time")},				// GMT+3:30 Tehran
+		{165,	_T("Arabian Standard Time")},			// GMT+4:00 Baku; Tbilisi; Yerevan; Abu Dhabi; Muscat
+		{175,	_T("Afghanistan Standard Time")},		// GMT+4:30 Kabul
+		{185,	_T("West Asia Standard Time")},			// GMT+5:00 Calcutta; Chennai; Mumbai; New Delhi; Ekaterinburg
+		{200,	_T("Sri Lanka Standard Time")},			// GMT+5:30 Sri Jayawardenepura
+		{201,	_T("N. Central Asia Standard Time")},	// GMT+6:00 Astana; Dhaka; Almaty; Novosibirsk
+		{203,	_T("Myanmar Standard Time")},			// GMT+6:30 Rangoon
+		{207,	_T("North Asia Standard Time")},		// GMT+7:00 Bankok; Hanoi; Jakarta; Krasnoyarsk
+		{-1,	_T("")},								// GMT+7:30
+		{210,	_T("China Standard Time")},				// GMT+8:00 Perth; Taipei; Singapore; Hong Kong; Beijing
+		{-1,	_T("")},								// GMT+8:30
+		{235,	_T("Tokyo Standard Time")},				// GMT+9:00 Tokyo; Osaka; Seoul; Sapporo; Yakutsk
+		{245,	_T("AUS Central Standard Time")},		// GMT+9:30 Darwin; Adelaide
+		{270,	_T("Vladivostok Standard Time")},		// GMT+10:00 East Australia; Guam; Vladivostok
+		{-1,	_T("")},								// GMT+10:30
+		{280,	_T("Central Pacific Standard Time")},	// GMT+11:00 Magadan; Solomon Is.; New Caledonia
+		{-1,	_T("")},								// GMT+11:30
+		{290,	_T("New Zealand Standard Time")},		// GMT+12:00 Auckland; Wellington; Fiji; Kamchatka; Marshall Is.
+		{-1,	_T("")}
+	};
+	return (MirTz >= -24 && MirTz <= 24) ? TzMap[24 - MirTz] : TzMap[49] ;
+}
+
+/**
+ * This function reads out the Timezone, associated with the given contact
+ *
+ * @param	hContact		- HANDLE of the contact to retrieve the timezone for.
+ * @param	pszProto		- contact's protocol 
+ *
+ * @retval	NULL			- No timezone exists.
+ * @retval	CTimeZone*		- Pointer to the timezone.
+ **/
+CTimeZone* GetContactTimeZone(HANDLE hContact, LPCSTR pszProto)
+{
+	LPTSTR ptszName;
+	CTimeZone* pTimeZone = NULL;
+
+	// read windows timezone from database (include meta subcontacts)
+	ptszName = DB::Setting::GetTStringEx(hContact, USERINFO, pszProto, SET_CONTACT_TIMEZONENAME);
+	if (!ptszName || FAILED(TzMgr.find(&pTimeZone, ptszName)))
+	{
+		DBVARIANT dbv;
+		TZ_MAP MirTZ;
+
+		// try to get miranda's timezone index value
+		if (!myGlobals.TzIndexExist || DB::Setting::GetAsIsEx(hContact, USERINFO, pszProto, SET_CONTACT_TIMEZONEINDEX, &dbv) || FAILED(TzMgr.find(&pTimeZone,dbv.dVal)))
+		{
+			// maybe a failure lets us read a string, so clear it out
+			DB::Variant::Free(&dbv);
+
+			// try to get miranda's timezone value
+			if (DB::Setting::GetAsIsEx(hContact, USERINFO, pszProto, SET_CONTACT_TIMEZONE, &dbv) || (dbv.type != DBVT_BYTE))
+			{
+				// maybe a failure lets us read a string, so clear it out
+				DB::Variant::Free(&dbv);
+			}
+			else 
+			{
+				MirTZ = MirTZ2WinTZ(dbv.cVal);
+				if (*MirTZ.Name != 0)
+				{
+					TzMgr.find(&pTimeZone, MirTZ.Name);
+				}
+			}
+		}
+	}
+	MIR_FREE(ptszName);
+	return pTimeZone;
+}
+
+/**
+ *
+ *
+ **/
+CTimeZone* GetContactTimeZone(HANDLE hContact)
+{
+	return GetContactTimeZone(hContact, DB::Contact::Proto(hContact));
+}
+
+/**
+ * This method trys to find the contact's windows timezone.
+ *
+ * @warning	Make sure you convert @e dwIndex to CHAR if the function returns 1 in order to get
+ *			the correct miranda timezone!
+ *
+ * @param	hContact		- the HANDLE of the contact to read timezone information for
+ * @param	szProto			- contact's protocol
+ * @param	pTimeZone		- Pointer to the pointer of a CTimeZone structure, 
+ *							  which retrieves information about contact's timezone.
+ *
+ * @retval	CTRLF_... flag	- The index for a windows timezone was found for the contact.
+ * @retval	0				- There is no index, but if the contact's 'timezone' setting is valid,
+ *							  @e dwIndex retrieved its value. If not, dwIndex is -100 (unspecified).
+ **/
+WORD GetContactTimeZoneCtrl(HANDLE hContact, LPCSTR pszProto, CTimeZone** pTimeZone)
+{
+	WORD flags;
+	DBVARIANT dbv;
+	CTimeZone* pTz = NULL;
+
+	// try to read windows' timezone name from database
+	flags = DB::Setting::GetCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_TIMEZONENAME, &dbv, DBVT_TCHAR);
+	if (flags == 0 || FAILED(TzMgr.find(&pTz, dbv.ptszVal)))
+	{
+		DB::Variant::Free(&dbv);
+
+		// try to get miranda's timezone index value
+		if (myGlobals.TzIndexExist)
+		{
+			flags = DB::Setting::GetAsIsCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_TIMEZONEINDEX, &dbv);
+			if (flags && FAILED(TzMgr.find(&pTz, dbv.dVal)))
+			{
+				flags = 0;
+			}
+		}
+		if (flags == 0)
+		{
+			// try to get miranda's timezone value
+			flags = DB::Setting::GetAsIsCtrl(hContact, USERINFO, USERINFO, pszProto, SET_CONTACT_TIMEZONE, &dbv);
+			if (flags != 0)
+			{
+				TZ_MAP MirTZ;
+				MirTZ = MirTZ2WinTZ(dbv.cVal);
+				if ((*MirTZ.Name == 0) || FAILED(TzMgr.find(&pTz, MirTZ.Name)))
+				{
+					flags = 0;
+				}
+			}
+		}
+	}
+	if (pTimeZone && flags != 0)
+	{
+		*pTimeZone = pTz;
+	}
+	DB::Variant::Free(&dbv);
+	return flags;
+}
+
+/**
+ * This function returns the display name for the contact's timezone
+ *
+ * @param	hContact		- handle of the contact
+ *
+ * @return	String containing display name.
+ **/
+LPCTSTR GetContactTimeZoneDisplayName(HANDLE hContact)
+{
+	CTimeZone *pTimeZone;
+	
+	pTimeZone = GetContactTimeZone(hContact);
+	return (pTimeZone) ? pTimeZone->ptszDisplay : NULL;
+}
+
+/**
+ *
+ *
+ **/
+INT_PTR EnumTimeZones(PEnumNamesProc enumProc, LPARAM lParam)
+{
+	INT_PTR i, c, r = 0;
+	CTimeZone *pTz;
+
+	for (i = 0, c = TzMgr.Bias.getCount(); i < c; i++)
+	{
+		pTz = TzMgr.Bias[i];
+		if (pTz)
+		{
+			r = enumProc(pTz, i, lParam);
+			if (r) break;
+		}
+	}
+	return r;
+}
+
+/**
+ *
+ *
+ **/
+static BOOL SvcTimezoneSyncWithWindowsProc(LPCSTR pszProto, INT bias)
+{
+	INT tz = (INT) ((CHAR)DB::Setting::GetByte(pszProto, SET_CONTACT_TIMEZONE, (BYTE)-100));
+	if (tz * 30 != bias)
+	{
+		DB::Setting::WriteByte(pszProto, SET_CONTACT_TIMEZONE, (BYTE)(bias / 30));
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/**
+ *
+ *
+ **/
+VOID SvcTimezoneSyncWithWindows()
+{
+	PROTOACCOUNT **pAcc;
+	INT i, nAccCount;
+	TIME_ZONE_INFORMATION tzi;
+
+	ZeroMemory(&tzi, sizeof(tzi));
+	GetTimeZoneInformation(&tzi);
+
+	if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc)))
+	{
+		for (i = 0; i < nAccCount; i++) 
+		{
+			// update local timezone as database setting
+			if (IsProtoAccountEnabled(pAcc[i]) && SvcTimezoneSyncWithWindowsProc(pAcc[i]->szModuleName, tzi.Bias))
+			{
+				// update my contact information on icq server
+				CallProtoService(pAcc[i]->szModuleName, PS_CHANGEINFOEX, CIXT_LOCATION, NULL);
+			}
+		}
+	}
+}
+
+/***********************************************************************************************************
+ * services use old UIEX timezone
+ ***********************************************************************************************************/
+
+/**
+ * This service function provides a TIME_ZONE_INFORMATION structure
+ * for the desired contact, in case the contact's timezone can be determined.
+ * 
+ * @param	wParam			- HANDLE of the contact, to retrieve timezone information from.
+ * @param	lParam			- pointer to a TIME_ZONE_INFORMATION to fill.
+ *
+ * @retval	0 - success
+ * @retval	1 - failure
+ **/
+INT_PTR GetContactTimeZoneInformation_old(WPARAM wParam,LPARAM lParam)
+{
+	CTimeZone *pTimeZone;
+	TIME_ZONE_INFORMATION* pTimeZoneInformation = (TIME_ZONE_INFORMATION*)lParam;
+	
+	pTimeZone = GetContactTimeZone((HANDLE)wParam);
+	if (pTimeZone && pTimeZoneInformation)
+	{
+		(*pTimeZoneInformation) = *pTimeZone;
+	}
+	return (pTimeZone == NULL) || (pTimeZoneInformation == NULL);
+}
+
+/**
+ * This function returns the contact's local time.
+ *
+ * @param	wParam			- HANDLE of the contact, to retrieve timezone information from.
+ * @param	lParam			- pointer to a systemtime structure
+ *	
+ * @return	TRUE or FALSE
+ **/
+INT_PTR GetContactLocalTime_old(WPARAM wParam, LPARAM lParam)
+{
+	MTime	now;
+	LPSYSTEMTIME pSystemTime = (LPSYSTEMTIME)lParam;
+
+	now.GetLocalTime((HANDLE)wParam);
+	*pSystemTime = now.SystemTime();
+	return 0;
+}
+
+/***********************************************************************************************************
+ * initialization
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcTimezoneLoadModule_old()
+{
+	TzMgr.Init();
+	CreateServiceFunction(MS_USERINFO_TIMEZONEINFO, GetContactTimeZoneInformation);
+	CreateServiceFunction(MS_USERINFO_LOCALTIME, GetContactLocalTime);
+	if (DB::Setting::GetByte(SET_OPT_AUTOTIMEZONE, TRUE))
+	{
+		SvcTimezoneSyncWithWindows();
+	}
+}
diff --git a/plugins/UserInfoEx/src/svc_timezone_old.h b/plugins/UserInfoEx/src/svc_timezone_old.h
new file mode 100644
index 0000000000..9704584e1a
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_timezone_old.h
@@ -0,0 +1,102 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+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.
+
+===============================================================================
+
+File name      : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_timezone_old.h $
+Revision       : $Revision: 194 $
+Last change on : $Date: 2010-09-20 15:57:18 +0400 (Пн, 20 сен 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#ifndef _SVC_TIMEZONE_H_OLD
+#define _SVC_TIMEZONE_H_OLD
+
+#include "svc_timezone.h"
+#define TZINDEX_UNSPECIFIED	 -100
+
+/**
+ * This structure is used by GetTimeZoneInformationByIndex to retrieve
+ * timezone information from windows' registry
+ **/
+struct TZ_MAP 
+{
+	DWORD	Index;
+	LPTSTR Name;
+};
+
+/**
+ * This structure is an element of the CTzManager.
+ * It holds information about a timezone, which are required
+ * to display the correct time of a contact which is not
+ * in the same timezone as the owner contact.
+ **/
+struct CTimeZone : public REG_TZI_FORMAT
+{
+	LPTSTR	ptszName;
+	LPTSTR	ptszDisplay;
+	DWORD	dwIndex;			//old, only supportet in win9x
+
+	/**
+	 * This is the default constructure, which resets
+	 * all attributes to NULL.
+	 **/
+	CTimeZone();
+
+	/**
+	 * The default construcor's task ist to clear out
+	 * all pieces of the used memory.
+	 **/
+	~CTimeZone();
+
+	/**
+	 * This method can be used to basically convert a Windows
+	 * timezone to the format, known by miranda.
+	 *
+	 * @warning		This operation does not work vice versa in
+	 *				all cases, as there are sometimes more then
+	 *				one Windows timezones with the same Bias.
+	 **/
+	BYTE ToMirandaTimezone() const;
+
+	/**
+	 * This operator translates the content of this object to
+	 * a TIME_ZONE_INFORMATION structure as it is required by 
+	 * several windows functions.
+	 **/
+	operator TIME_ZONE_INFORMATION() const;
+};
+
+typedef INT_PTR (*PEnumNamesProc)(CTimeZone* pTimeZone, INT index, LPARAM lParam);
+
+CTimeZone*	GetContactTimeZone(HANDLE hContact);
+CTimeZone*	GetContactTimeZone(HANDLE hContact, LPCSTR pszProto);
+WORD		GetContactTimeZoneCtrl(HANDLE hContact, LPCSTR pszProto, CTimeZone** pTimeZone);
+LPCTSTR		GetContactTimeZoneDisplayName(HANDLE hContact);
+INT_PTR		EnumTimeZones(PEnumNamesProc enumProc, LPARAM lParam);
+
+INT_PTR		GetContactTimeZoneInformation_old(WPARAM wParam, LPARAM lParam);
+INT_PTR		GetContactLocalTime_old(WPARAM wParam, LPARAM lParam);
+
+VOID		SvcTimezoneSyncWithWindows();
+VOID		SvcTimezoneLoadModule_old();
+
+#endif /* _SVC_TIMEZONE_H_OLD */
\ No newline at end of file
diff --git a/plugins/UserInfoEx/src/version.h b/plugins/UserInfoEx/src/version.h
new file mode 100644
index 0000000000..8ee84378bf
--- /dev/null
+++ b/plugins/UserInfoEx/src/version.h
@@ -0,0 +1,51 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 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.
+
+*/
+
+#define __MAJOR_VERSION   0
+#define __MINOR_VERSION   8
+#define __RELEASE_NUM     4	// due to beta builders
+#define __BUILD_NUM       2	// due to beta builders
+
+#define __STRINGIFY_(x) #x
+#define __STRINGIFY(x)  __STRINGIFY_(x)
+
+#define __FILEVERSION_STRING      __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM
+#define __FILEVERSION_STRING_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM
+
+#define __VERSION_STRING  __STRINGIFY(__FILEVERSION_STRING_DOTS)
+#define __VERSION_DWORD   PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM)
+
+#define __SHORT_DESC  "Extended UserInfo module for Miranda-IM. Provides interface to edit all contact information."
+#define __DESC        "Gives extended ability to edit information about your contacts locally. "\
+                      "It does not matter what information your contact gives about himself. "\
+                      "If you know more you can add more."
+#define __AUTHOR      "DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol"
+#define __AUTHOREMAIL "deathaxe@web.de"
+#define __COPYRIGHT   "� 2006-2009 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol"
+#define __AUTHORWEB   "http://nightly.miranda.im/" __STRINGIFY(__UPDATER_DOWNLOAD_ID)
+
+#define __UPDATER_DOWNLOAD_ID 2537
+#define __PLUGIN_DISPLAY_NAME	"UserInfoEx"
+#define __PLUGIN_FILENAME		"uinfoex.dll"
+ 
\ No newline at end of file
-- 
cgit v1.2.3