summaryrefslogtreecommitdiff
path: root/plugins/ShlExt
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/ShlExt')
-rw-r--r--plugins/ShlExt/clean.bat1
-rw-r--r--plugins/ShlExt/docs/HowToBuild.txt22
-rw-r--r--plugins/ShlExt/docs/shlext release notes.txt344
-rw-r--r--plugins/ShlExt/inc/README.txt92
-rw-r--r--plugins/ShlExt/inc/m_addcontact.inc54
-rw-r--r--plugins/ShlExt/inc/m_api.pas75
-rw-r--r--plugins/ShlExt/inc/m_awaymsg.inc40
-rw-r--r--plugins/ShlExt/inc/m_clc.inc284
-rw-r--r--plugins/ShlExt/inc/m_clist.inc641
-rw-r--r--plugins/ShlExt/inc/m_clui.inc215
-rw-r--r--plugins/ShlExt/inc/m_contacts.inc90
-rw-r--r--plugins/ShlExt/inc/m_database.inc654
-rw-r--r--plugins/ShlExt/inc/m_email.inc39
-rw-r--r--plugins/ShlExt/inc/m_file.inc66
-rw-r--r--plugins/ShlExt/inc/m_findadd.inc38
-rw-r--r--plugins/ShlExt/inc/m_globaldefs.pas99
-rw-r--r--plugins/ShlExt/inc/m_globaldefs.ppubin0 -> 8913 bytes
-rw-r--r--plugins/ShlExt/inc/m_helpers.inc613
-rw-r--r--plugins/ShlExt/inc/m_history.inc37
-rw-r--r--plugins/ShlExt/inc/m_icq.inc191
-rw-r--r--plugins/ShlExt/inc/m_ignore.inc74
-rw-r--r--plugins/ShlExt/inc/m_langpack.inc82
-rw-r--r--plugins/ShlExt/inc/m_message.inc57
-rw-r--r--plugins/ShlExt/inc/m_netlib.inc713
-rw-r--r--plugins/ShlExt/inc/m_options.inc109
-rw-r--r--plugins/ShlExt/inc/m_plugins.inc70
-rw-r--r--plugins/ShlExt/inc/m_popup.inc222
-rw-r--r--plugins/ShlExt/inc/m_protocols.inc180
-rw-r--r--plugins/ShlExt/inc/m_protomod.inc105
-rw-r--r--plugins/ShlExt/inc/m_protosvc.inc753
-rw-r--r--plugins/ShlExt/inc/m_skin.inc120
-rw-r--r--plugins/ShlExt/inc/m_system.inc170
-rw-r--r--plugins/ShlExt/inc/m_url.inc39
-rw-r--r--plugins/ShlExt/inc/m_userinfo.inc84
-rw-r--r--plugins/ShlExt/inc/m_utils.inc279
-rw-r--r--plugins/ShlExt/inc/m_v8.inc62
-rw-r--r--plugins/ShlExt/inc/newpluginapi.inc94
-rw-r--r--plugins/ShlExt/inc/statusmodes.inc54
-rw-r--r--plugins/ShlExt/inc/testdll.dpr60
-rw-r--r--plugins/ShlExt/make.bat9
-rw-r--r--plugins/ShlExt/resource.h13
-rw-r--r--plugins/ShlExt/shlc.inc168
-rw-r--r--plugins/ShlExt/shlcom.pas2383
-rw-r--r--plugins/ShlExt/shldlgs.rc93
-rw-r--r--plugins/ShlExt/shldlgs.resbin0 -> 2616 bytes
-rw-r--r--plugins/ShlExt/shlext.dpr379
-rw-r--r--plugins/ShlExt/shlicons.pas162
-rw-r--r--plugins/ShlExt/shlipc.pas369
48 files changed, 10498 insertions, 0 deletions
diff --git a/plugins/ShlExt/clean.bat b/plugins/ShlExt/clean.bat
new file mode 100644
index 0000000000..575eed729c
--- /dev/null
+++ b/plugins/ShlExt/clean.bat
@@ -0,0 +1 @@
+del *.o *.ppu *.dll *.a fpc-res.res *.or \ No newline at end of file
diff --git a/plugins/ShlExt/docs/HowToBuild.txt b/plugins/ShlExt/docs/HowToBuild.txt
new file mode 100644
index 0000000000..53b6738616
--- /dev/null
+++ b/plugins/ShlExt/docs/HowToBuild.txt
@@ -0,0 +1,22 @@
+shlext 2.0.0.9
+
+Info
+=======================
+
+This source code is based on shlext 1.0.6.6 with minor changes so that it works
+with FreePascal 2.2.2.
+
+The included headers (inc dir) are from Miranda 0.3.3.1 SDK and so if you want newer APIs
+then get the API headers from the latest SVN tree.
+
+Note: I have included v0.8.xx API changes for GUIDs within a new file (m_v8.inc)
+
+
+How to build
+=======================
+
+Make sure you have installed the FreePascal compiler ( http://freepascal.org )
+the latest version is 2.2.2 at the time of writing.
+
+Run "make.bat" in this directory, this contains all the command line switches
+for the newer version should produce shlext.dll
diff --git a/plugins/ShlExt/docs/shlext release notes.txt b/plugins/ShlExt/docs/shlext release notes.txt
new file mode 100644
index 0000000000..0e58edf9ff
--- /dev/null
+++ b/plugins/ShlExt/docs/shlext release notes.txt
@@ -0,0 +1,344 @@
+shlext 2.0.1.2
+
+Contents:
+
+ Introduction ``What is shlext?``
+ Why so long?
+ What you need
+ New features
+ Features
+ Quirks
+ Important changes
+ But Miranda has drag'n'drop!
+ Installation
+ Upgrading/Removing
+ Translation
+ License
+ Contact/Bug reporting
+ Credits
+
+
+
+ ---Introduction ``What is shlext?``
+
+ shlext is a Miranda and Explorer shell plugin, it allows you to use your
+ contact list under any file/directory from Windows.
+
+ This means that you can right click on a file/folder, see "Miranda" and then
+ see your entire contact list! this is a feature that ICQ has built in.
+
+ shlext is better of course.
+
+ ---Why so long?
+
+ A few people contacted me aeons ago about implementing a better file scanner
+ so that they could recreate directories whilst sending, etc, I said I would do
+ this as soon as I had time, that was several months ago.
+
+ I had made several changes/bugfixes when I had time, because I'm a Miranda
+ dev too, I don't usually have lots of time for this plugin, however lately I needed
+ shlext to run again, since I was sending lots of docs/logs around with Miranda.
+
+ So I fixed several things and improved lots of other stuff so that other users
+ could use shlext again (the XP bug was really annoying as soon as I got XP myself ;)
+
+
+ ---What you need
+
+ (2008) You will need 0.7.xx or 0.8.x -- older versions will not work.
+
+ shlext should work on all Window Explorer versions that support it,
+ certain features will not work on older Explorers, i.e. icons, but you will
+ still be able to use the main function of shlext, selection 'n' transfer.
+
+ ---New Features (2.0.1.2)
+
+ * shlext is now compiled with Free Pascal 2.2.4
+
+ * shlext now works with Windows Vista:
+
+ 1. shlext cannot automatically register itself with Windows Explorer due to permissions issues in Vista,
+ therefore you will be UAC prompted if shlext detects you are running Vista and that shlext isn't registered
+ with Explorer.
+
+ This is almost automatic, and you just have to press "OK".
+
+ 2. The entire menu drawing was overhauled and now looks much better, new APIs are used so that Vista draws the menus
+ (with theme) but the status icons are still present.
+
+ * added UAC button for "Remove" from the options dialog.
+
+ * Removed GetMenuItemInfo() debug message box.
+
+ * Note: Miranda is a 32bit application, 64bit editions of Windows require a 64bit extension DLL, this is not possible at present.
+
+ ---New features (2.0.0.9+)
+
+ * shlext is now compiled with Free Pascal 2.2.2 which is a newer compiler with better
+ optimisations so shlext should be faster. (2002 v.s. 2008)
+
+ * shlext now works with Miranda 0.8.x UUID typing system and 0.8.xx plugin loading APIs,
+ 0.7.xx still works too however.
+
+ * shlext now keeps track of recently used contacts and builds a "MRU" menu for quick
+ access within the menu system. This cannot be disabled, if you hate this feature,
+ please stick pins into a voodoo doll named "Christian", that is all.
+
+ * The menu strings "Recently" and "Clear entries" are translate()able but MRU is not.
+
+ ---New features (1.0.6.6+)
+
+ * shlext will now use all your icons per protocol, **not** just the first iconset
+ it finds, it will also use everything properly (because it doesn't do the icon
+ extraction, it just asks Miranda [don't ask why it didn't do this before :P])
+
+ * shlext will now use a Translate()'d version of "Miranda" so that each menu
+ shown for a profile can be given a custom user string
+
+ * reimplemented file/folder selection, finally! a work-as-expected version, it will
+ scan and add all files and folders you give it, producing a file list in the background
+ (scanning your drive) and then send the list to Miranda to send to your selected contact.
+
+ * Added option for disabling status icons in menus, which means that you can use shlext
+ with shell variants/file managers that invoke the shlext interface, such as FAR, but
+ don't need/use the icons.
+
+ * Added option about hiding offline users from the context menu, if this option is off
+ it will fall back onto syncing with your contact list's "hide offline users"
+
+ * Added proper thread safety because Miranda 0.3 now has it.
+
+ * Completely reimplemented group parsing, which means that all the old group bugs
+ can be expected to be gone, note that shlext will now even create menus for
+ subgroups of the same name, e.g. "Miranda\Miranda".
+
+ * shlext will now not show a menu for a running Miranda fails the following checks:
+
+ * not running shlext (duh)
+ * no non-offline contacts (or you have the setting 'hide offline users')
+ * and so on
+
+ * shlext will now also completely ignore contacts on protocols who have no file transfer support
+
+
+ ---Features
+
+ shlext can:
+
+ * allow you to refer to your entire contact list from a file/folder context
+ menu, this includes multiple profiles! if you have Miranda running
+ different profiles, you'll see all your profiles as menu items as long
+ as you're running shlext as a Miranda plugin in that profile.
+
+ * Group ability, see a faithful menu rendition of your group hierarchy.
+ This means you can go something like File->My Profile->Work->Friends->Dude...
+
+ This feature can also be turned off, or enabled in sync with your contact
+ list option to "Disable groups", this is a per profile setting, i.e. setting
+ disable groups on one profile won't affect other profiles running shlext.
+
+ * Multi protocol aware, shlext can send to anyone on your contact list
+ not just ICQ!
+
+ * Each contact will be shown next to their status icon, as selected in your
+ profile(s) which means that you'll easily feel at home with the icons,
+ because they will be used as how they are set in each profile.
+
+ * lots of files, shlext will now, if given a directory/folder go into that
+ folder and scan for files and sub directories/folders til it's added
+ everything.
+
+ This means if you send c:\foobar, it will search c:\foobar\*.* for more
+ files to add, it will also add c:\foobar as a directory space to send.
+ Which means that if the other side hasn't got a 'foobar' directory, it will
+ be created! (Note: recreating directory trees depends on the protocol being used to send)
+
+
+ --Quirks
+
+ * shlext displays all your users by default, if your contact list is set to
+ NOT show offline users, then shlext will not show them.
+
+ * shlext doesn't use all your group settings, it will not ad here to
+ "hide offline users in here", however if a group has got offline users
+ it won't show them (per setting option!)
+
+
+
+ ---Important changes
+
+ Older versions of shlext did not go into folders more than one level, i.e.
+ if you added c:\foobar it would scan for c:\foobar\*.* and add all the files
+ but not go into each directory\folder deeper than that!
+
+ shlext also now does background selection scanning, which means when you select
+ a group of files/folders/directories, it will let you get on with chating
+ until it's made a file list which you can send to the person you've selected.
+
+ shlext will NOT send any file/folder/directory that is marked "hidden"
+
+ Also, sometimes you will see "n files, 1 directory" when you say select something
+ e.g. c:\foobar, this is because shlext now also includes the top level directory so that the
+ remote side will know to create it, as well as sub directories.
+
+
+
+ ---But Miranda has drag'n'drop!
+
+ Yeah, that's okay when you can reach Miranda, but I have multiple profiles and
+ the "hide after NN seconds" option enabled, also I have groups!
+
+ Miranda doesn't auto expand a group when someone is online unless you do that
+ yourself, which means drag 'n' drop has failed. Also, when you've selected a
+ large amount of files, Miranda will *freeze* completely whilst
+ it 1) scans all those files, 2) builds a copy of the given send list
+
+ Whilst shlext only freezes Miranda for the latter, and that is seldom a "complete freeze".
+
+ And of course, shlext uses Miranda 0.3's advanced threading services, which means
+ if you've asked shlext to build a massive send list, you can still exit Miranda safely
+ which you can't with drag 'n' drop!
+
+
+
+ ---Installation
+
+ If you've never installed shlext before, all you have to do is install it like
+ any other Miranda plugin, i.e. copy it to your plugins directory.
+
+ That's it! you should goto Miranda->Options->Plugins->Shell Context Menus
+ to see if you'd like to set any of the options, however shlext works straight
+ out of the box and you don't really need to set anything up after that.
+
+ If you want to use shlext with multiple profiles, you don't have to do any
+ special setting up either, just make sure that shlext is running with each Miranda
+ you want shlext to show a menu contact list for.
+
+ Make sure ALL copies of shlext.dll are the same, i.e. 1.0.6.6, if they're not
+ then shlext will fail (this doesn't mean 'crash').
+
+
+ ---Upgrading/Removing
+
+ Upgrading shlext has always been a pain for users (and me!) this is because
+ shlext.dll runs in Windows and in Miranda (at the same time).
+
+ So when you've shutdown Miranda, shlext.dll maybe kept in memory by Windows
+ to make things worse, clicking any file/folder will result in shlext.dll being
+ reloaded, so if you do shlext.dll->Delete, Windows will ask shlext.dll if
+ it wants to show any menus, nevermind the fact delete was selected!
+
+ This happens also if you just press 'delete' whilst shlext.dll is selected.
+
+ However! All is not lost, this is what you do:
+
+ * goto M->Options->Plugins and disable shlext.dll as a Miranda plugin
+ * goto M->Options->Plugins->Shell context menus and click 'Remove'.
+ * Shutdown Miranda IM
+
+ Advanced users only: ----------------------------------------------------
+
+ * Do all the above and then open a console window (Command prompt, etc)
+ * Make sure all applications have been shutdown
+ * Goto the directory where Miranda is, e.g. c:\, cd Miranda
+ * Goto Start->Shutdown, let the dialog come up and hold CTRL+ALT+SHIFT
+ and press cancel.
+
+ This will shutdown Explorer but not Windows, you can now do: del shlext.dll
+
+ * now run Explorer.exe usually in C:\Windows, shlext.dll will be removed.
+
+ ----------------------------------------------------------------------------
+
+ The remove button will ask Windows not to load it anymore, by removing
+ all shlext registry entries, the button will also remove any settings from your
+ profile settings database that it may of made.
+
+ You should now be able to delete shlext.dll! however if you still are unable
+ to, you may need to log out (if you're using XP/2000/NT) if you're using
+ 9x then you may have to restart Windows (pain I know, sorry!)
+
+ You should now be free of old shlext copies and you can refer to "Installation"
+ above.
+
+ If you were using shlext.dll with multiple profiles, the remove shlext
+ from each profile as stated above and then copy the newer shlext.dll to
+ your plugins folder.
+
+ ---Translation
+
+ I haven't been nice about translation strings in the past, but you
+ can pretty much translate everything shlext uses a string, even
+ "Miranda" which is shown in the menu.
+
+ Note that some strings can't be translated, this is because some parts
+ of the plugin run within Explorer and that doesn't have access to Miranda's
+ langpacks, the "Miranda" string that appears in menus is a special exception
+
+ ;
+ ; Translate()'able strings for shlext/2.0.0.9
+ ;
+
+ ;"Miranda" limited to 63characters! (exceed and it's chopped)
+ ;[Miranda]
+ ;[Problem, registration missing/deleted.]
+ ;[Successfully created shell registration.]
+ ;[Not Approved]
+ ;[Approved]
+ ;[Are you sure? this will remove all the settings stored in your database and all registry entries created for shlext to work with Explorer]
+ ;[Disable/Remove shlext]
+ ;[Shell context menus]
+
+ ; new in 2.0.0.9, both these strings cannot be longer than 63 chracters
+ ;[Clear entries]
+ ;[Recently]
+
+ ;IDD_SHLOPTS
+ ;[Menus]
+ ;[Display contacts in their assigned groups (if any)]
+ ;[Only if/when the contact list is using them]
+ ;[Display hidden, ignored or temporary contacts]
+ ;[Shell Status]
+ ;[Do not display the profile name in use]
+ ;[Contact Status]
+ ;[Show contacts that you have set privacy rules for]
+ ;[Remove]
+ ;[Do not show status icons in menus]
+ ;[Do not show contacts that are offline, even if my contact list does]
+
+
+
+
+ ---License
+
+ Like Miranda, shlext is released under the GPL, you may find the full
+ FreePascal source-code on the CVS in plugins module 'shlext'
+
+ You will need at least FreePascal/2.2.2, GNU make (if you want to use the makefile)
+
+ Follow the CVS links from http://sf.net/projects/miranda-icq/
+
+ Note: All the tools used to build shlext are also under the GPL!
+
+
+ ---Contact/Bug reporting
+
+ In the past shlext hasn't been as stable as it could be, but this was mainly
+ due to the problems of 0.2.0.0 and early 0.3.0.0 Miranda builds, I've taken
+ care to make sure things are stable as can be.
+
+ If you have any problems/crashes, please contact me at: egodust at users.sf.net.
+
+ Please include the following information: Windows version, service packs installed,
+ build version of Explorer, Miranda version, shlext version, a list of plugins
+ that you think maybe involved in crashes, steps to reproduce errors and so on.
+
+ Note that shlext has been blamed for several bugs that were not shlext's fault,
+ for example the file xfer cancel bug was in ICQ and Miranda but not shlext ;)
+
+
+ ---Credits
+
+ Tig-crash\d - Thanks for beta testing every version before this one ;)
+ Erik?, DD Of Borg - Thanks for beta testing 0.0.2.2/1.0.6.6 -- ideas and suggestions
+ as well what to exactly steal from ShellFileSend, heh.. \ No newline at end of file
diff --git a/plugins/ShlExt/inc/README.txt b/plugins/ShlExt/inc/README.txt
new file mode 100644
index 0000000000..4c0ab5f0cf
--- /dev/null
+++ b/plugins/ShlExt/inc/README.txt
@@ -0,0 +1,92 @@
+
+ - Miranda Module API for Borland Delphi, FreePascal -
+
+ These include files allow you to write modules to extend Miranda
+ Older versions of these files
+ limited support for FPC, versions & compilers are :
+
+ Borland Delphi 2.0 thru 6.0
+ FreePascal 1.0.4, 1.0.6
+
+ You can now create modules for Miranda (v0.1.2.2) and use
+ new stuff like Netlib! though you can still write for
+ the current stable release (v0.1.2.1) but you'll have to
+ be aware of version dependant things.
+
+ Worry not though, every service/event is marked with a version
+ code if it's not present in older Miranda versions.
+
+ Be warned, this is a brand new porting though it has borrowed
+ from older ports (see CVS, oh this is viewCVS? mmm, cheese.)
+ Things are presented in a more Delphi esque than a C esque manner
+ so if you feel confused refer to the C header.
+
+ A word of warning, don't try to compile /delphiplugins examples
+ with these include files and expect it to work,
+
+
+ Include files use the {$include } syntax and will never work
+ as units.
+
+ -- FPC support? --
+
+ FPC is now properly supported, but you may need to use -SD -S2
+ command line switches (for Delphi, BP7 mode) remember to use -Fi
+ and -Fu to give the path to these files or use {$UNITPATH} and {$INCLUDEPATH}
+
+ These include files don't any FPC stuff like macros
+ and inlined functions.
+
+ -- Things to be aware of --
+
+ This version is not yet directly supported, if you want to learn
+ the API look at the CVS tree for documentation on plugins, as well
+ as guidelines and examples of the general structure of Miranda.
+
+ This is my cop out for now, I'll try to write a more general 'guide' later
+ on.
+
+ -
+
+ Miranda uses a manifest to allow COMCTRL v6 to be loaded on XP,
+ This causes problems with image lists with Delphi (there are work arounds)
+ see borland.com for the article.
+
+ You may want to refuse to load on XP or try to use Miranda's API to work with
+ imagelists and load images from resource as bitmaps (ugh)
+
+ - lstrcat, lstrcpy
+
+ I've used the Windows API calls to these C functions over Delphi's RTL
+ because SysUtils.pas just adds a bloat.
+
+ - *If* you use SysUtils.pas
+
+ Delphi loads OLE for variant support, it maybe advisable to unload
+ the DLL as soon as you start, this maybe a problem though, since Miranda
+ also uses OLE for extended image support, it doesn't however keep
+ it loaded all the time.
+
+ There should be no problem in just decrementing the reference count
+ to the DLL and it'll unload if you were the only reference.
+
+ if however you're using variants in your code, blergh.
+
+ -- How you get it to work --
+
+ see testdll.dpr, it won't do much but it'll show a pretty description in the
+ options dialog (oh impressive!)
+
+ To bring in new files, just use {$include that_file_you_want.inc}
+ If other include files are needed by the include file you bring in,
+ it'll try to include it itself.
+
+ Of course you need to add the path to where the .inc files are to the project's
+ search path, or if you compile via the command line, use -U and -I
+ -U is needed because m_globaldefs.pas is a unit.
+
+ Each header file is marked with "UNITDEP" which will tell you which units
+ it requires.
+
+ All files that require the PLUGINLINK structure require m_globaldefs.pas
+ (this is all the C header files that use such macros!)
diff --git a/plugins/ShlExt/inc/m_addcontact.inc b/plugins/ShlExt/inc/m_addcontact.inc
new file mode 100644
index 0000000000..2e74ad1279
--- /dev/null
+++ b/plugins/ShlExt/inc/m_addcontact.inc
@@ -0,0 +1,54 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_ADDCONTACT}
+{$DEFINE M_ADDCONTACT}
+
+const
+
+ HANDLE_SEARCHRESULT = 0;
+ HANDLE_EVENT = 1;
+ HANDLE_CONTACT = 2;
+
+type
+
+ PADDCONTACTSTRUCT = ^TADDCONTACTSTRUCT;
+ TADDCONTACTSTRUCT = record
+ handleType: Integer;
+ handle: THandle; // HDBEVENT, HCONTACT, SearchResult
+ szProto: PChar; // used by search result only
+ psr: Pointer; // @PROTOSEARCHRESULT
+ end;
+
+const
+
+ {
+ wParam : (HWND) Parent window of the dialog that will be presented
+ lParam : Pointer to an initialised TADDCONTACTSTRUCT
+ Affect : Open's the add contact dialog
+ Version: 0.1.2.2+
+ }
+ MS_ADDCONTACT_SHOW = 'AddContact/Show';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_api.pas b/plugins/ShlExt/inc/m_api.pas
new file mode 100644
index 0000000000..78fda24976
--- /dev/null
+++ b/plugins/ShlExt/inc/m_api.pas
@@ -0,0 +1,75 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFDEF FPC}
+ {$PACKRECORDS C}
+ {$MODE Delphi}
+{$ENDIF}
+
+unit m_api;
+
+interface
+
+uses
+
+ m_globaldefs, windows;
+
+ {$include m_plugins.inc}
+ {$include m_system.inc}
+ {$include m_database.inc}
+ {$include m_findadd.inc}
+ {$include m_awaymsg.inc}
+ {$include m_email.inc}
+ {$include m_history.inc}
+ {$include m_message.inc}
+ {$include m_url.inc}
+ {$include newpluginapi.inc}
+ {$include m_clui.inc}
+ {$include m_ignore.inc}
+ {$include m_skin.inc}
+ {$include m_file.inc}
+ {$include m_netlib.inc}
+ {$include m_langpack.inc}
+ {$include m_clist.inc}
+ {$include m_clc.inc}
+ {$include m_userinfo.inc}
+ {$include m_protosvc.inc}
+ {$include m_options.inc}
+ {$include m_icq.inc}
+ {$include m_protocols.inc}
+ {$include m_protomod.inc}
+ {$include m_utils.inc}
+ {$include m_addcontact.inc}
+ {$include statusmodes.inc}
+ {$include m_contacts.inc}
+ {$define M_API_UNIT}
+ {$include m_helpers.inc}
+
+implementation
+
+ {$undef M_API_UNIT}
+ {$include m_helpers.inc}
+
+end.
+
diff --git a/plugins/ShlExt/inc/m_awaymsg.inc b/plugins/ShlExt/inc/m_awaymsg.inc
new file mode 100644
index 0000000000..2ad330f9de
--- /dev/null
+++ b/plugins/ShlExt/inc/m_awaymsg.inc
@@ -0,0 +1,40 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_AWAYMSG}
+{$DEFINE M_AWAYMSG}
+
+const
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Show the away/na/etc message for a contact
+ Returns: 0 on success, non zero on failure, see notes
+ notes : returns without waiting for the message to be shown.
+ version: v0.1.0.1+
+ }
+ MS_AWAYMSG_SHOWAWAYMSG = 'SRAway/GetMessage';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_clc.inc b/plugins/ShlExt/inc/m_clc.inc
new file mode 100644
index 0000000000..d55ecc7438
--- /dev/null
+++ b/plugins/ShlExt/inc/m_clc.inc
@@ -0,0 +1,284 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_CLC}
+{$DEFINE M_CLC}
+
+const
+
+ CLISTCONTROL_CLASS = 'CListControl';
+
+ // styles
+
+ CLS_MANUALUPDATE = $0001; // todo
+ CLS_SHOWHIDDEN = $0002;
+ CLS_HIDEOFFLINE = $0004; // hides all offline users
+ CLS_CHECKBOXES = $0008;
+ CLS_MULTICOLUMN = $0010; // not true multi-column, just for ignore/vis options
+ CLS_HIDEEMPTYGROUPS = $0020; // note: this flag will be spontaneously removed if the 'new subgroup' menu item is clicked, for obvious reasons
+ CLS_USEGROUPS = $0040;
+ CLS_NOHIDEOFFLINE = $0080; // overrides CLS_HIDEOFFLINE and the per-group hideoffline setting
+ CLS_GREYALTERNATE = $0100; // make every other line slightly grey
+ CLS_GROUPCHECKBOXES = $0200; // put checkboxes on groups too (managed by CLC)
+
+ CLS_EX_DISABLEDRAGDROP = $00000001;
+ CLS_EX_EDITLABELS = $00000002;
+ CLS_EX_SHOWSELALWAYS = $00000004;
+ CLS_EX_TRACKSELECT = $00000008;
+ CLS_EX_SHOWGROUPCOUNTS = $00000010;
+ CLS_EX_DIVIDERONOFF = $00000020;
+ CLS_EX_HIDECOUNTSWHENEMPTY = $00000040;
+ CLS_EX_NOTRANSLUCENTSEL = $00000080;
+ CLS_EX_LINEWITHGROUPS = $00000100;
+ CLS_EX_QUICKSEARCHVISONLY = $00000200;
+ CLS_EX_SORTGROUPSALPHA = $00000400;
+ CLS_EX_NOSMOOTHSCROLLING = $00000800;
+
+ CLM_FIRST = $1000; // this is the same as LVM_FIRST
+ CLM_LAST = $1100;
+
+// messages, compare with equivalent TVM_* in the WINAPI
+
+ CLM_ADDCONTACT = (CLM_FIRST+0); // wParam=hContact
+ CLM_ADDGROUP = (CLM_FIRST+1); // wParam=hGroup
+ CLM_AUTOREBUILD = (CLM_FIRST+2);
+ CLM_DELETEITEM = (CLM_FIRST+3); // wParam=hItem
+ CLM_EDITLABEL = (CLM_FIRST+4); // wParam=hItem
+ CLM_ENDEDITLABELNOW = (CLM_FIRST+5); // wParam=cancel, 0 to save
+ CLM_ENSUREVISIBLE = (CLM_FIRST+6); // wParam=hItem, lParam=partialOk
+
+ CLE_TOGGLE = -1;
+ CLE_COLLAPSE = 0;
+ CLE_EXPAND = 1;
+ CLE_INVALID = $FFFF;
+
+ CLM_EXPAND = (CLM_FIRST+7); // wParam=hItem, lParam=CLE_
+ CLM_FINDCONTACT = (CLM_FIRST+8); // wParam=hContact, returns an hItem
+ CLM_FINDGROUP = (CLM_FIRST+9); // wParam=hGroup, returns an hItem
+ CLM_GETBKCOLOR = (CLM_FIRST+10); // returns a COLORREF
+ CLM_GETCHECKMARK = (CLM_FIRST+11); // wParam=hItem, returns 1 or 0
+ CLM_GETCOUNT = (CLM_FIRST+12); // returns the total number of items
+
+ CLM_GETEDITCONTROL = (CLM_FIRST+13); // returns the HWND, or NULL
+ CLM_GETEXPAND = (CLM_FIRST+14); // wParam=hItem, returns a CLE_, CLE_INVALID if not a group
+ CLM_GETEXTRACOLUMNS = (CLM_FIRST+15); // returns number of extra columns
+ CLM_GETEXTRAIMAGE = (CLM_FIRST+16); // wParam=hItem, lParam=MAKELPARAM(iColumn (0 based),0), returns iImage or $FF
+ CLM_GETEXTRAIMAGELIST = (CLM_FIRST+17); // returns HIMAGELIST
+ CLM_GETFONT = (CLM_FIRST+18); // wParam=fontId, see clm_setfont. returns hFont.
+ CLM_GETINDENT = (CLM_FIRST+19); // wParam=new group indent
+ CLM_GETISEARCHSTRING = (CLM_FIRST+20); // lParam=(char*)pszStr, max 120 bytes, returns number of chars in string
+ CLM_GETITEMTEXT = (CLM_FIRST+21); // wParam=hItem, lParam=(char*)pszStr, max 120 bytes
+ CLM_GETSCROLLTIME = (CLM_FIRST+22); // returns time in ms
+ CLM_GETSELECTION = (CLM_FIRST+23); // returns hItem
+
+ CLCHT_ABOVE = $0001; // above client area
+ CLCHT_BELOW = $0002; // below client area
+ CLCHT_TOLEFT = $0004; // left of client area
+ CLCHT_TORIGHT = $0008; // right of client area
+ CLCHT_NOWHERE = $0010; // in client area, not on an item
+ CLCHT_ONITEMICON = $0020;
+ CLCHT_ONITEMCHECK = $0040;
+ CLCHT_ONITEMLABEL = $0080;
+ CLCHT_ONITEMINDENT = $0100; // to the left of an item icon
+ CLCHT_ONITEMEXTRA = $0200; // on an extra icon, HIBYTE(HIWORD()) says which
+ CLCHT_ONITEM = $03E0;
+ CLCHT_INLEFTMARGIN = $0400;
+ CLCHT_BELOWITEMS = $0800; // in client area but below last item
+
+ CLM_HITTEST = (CLM_FIRST+25); // lParam=MAKELPARAM(x,y) (relative to control), wParam=(PDWORD)&hitTest (see encoding of HitTest() in clc.h, can be NULL) returns hItem or NULL
+ CLM_SELECTITEM = (CLM_FIRST+26); // wParam=hItem
+
+ CLB_TOPLEFT = 0;
+ CLB_STRETCHV = 1;
+ CLB_STRETCHH = 2; // and tile vertically
+ CLB_STRETCH = 3;
+
+ CLBM_TYPE = $00FF;
+ CLBF_TILEH = $1000;
+ CLBF_TILEV = $2000;
+ CLBF_PROPORTIONAL = $4000;
+ CLBF_SCROLL = $8000;
+
+ CLM_SETBKBITMAP = (CLM_FIRST+27); // wParam=mode, lParam=hBitmap (don't delete it), NULL for none
+ CLM_SETBKCOLOR = (CLM_FIRST+28); // wParam=a COLORREF, default is GetSysColor(COLOR_3DFACE)
+ CLM_SETCHECKMARK = (CLM_FIRST+29); // wParam=hItem, lParam=1 or 0
+ CLM_SETEXTRACOLUMNS = (CLM_FIRST+30); // wParam=number of extra columns (zero to MAXEXTRACOLUMNS from clc.h, currently 16)
+ CLM_SETEXTRAIMAGE = (CLM_FIRST+31); // wParam=hItem, lParam=MAKELPARAM(iColumn (0 based),iImage). iImage=$FF is a blank
+ CLM_SETEXTRAIMAGELIST = (CLM_FIRST+32); // lParam=HIMAGELIST
+
+ FONTID_CONTACTS = 0;
+ FONTID_INVIS = 1;
+ FONTID_OFFLINE = 2;
+ FONTID_NOTONLIST = 3;
+ FONTID_GROUPS = 4;
+ FONTID_GROUPCOUNTS = 5;
+ FONTID_DIVIDERS = 6;
+ FONTID_OFFINVIS = 7;
+ FONTID_MAX = 7;
+
+ CLM_SETFONT = (CLM_FIRST+33); // wParam=hFont, lParam=MAKELPARAM(fRedraw,fontId)
+ CLM_SETINDENT = (CLM_FIRST+34); // wParam=new indent, default is 3 pixels
+ CLM_SETITEMTEXT = (CLM_FIRST+35); // wParam=hItem, lParam=(char*)pszNewText
+ CLM_SETSCROLLTIME = (CLM_FIRST+36); // wParam=time in ms, default 200
+ CLM_SETHIDEEMPTYGROUPS = (CLM_FIRST+38); // wParam=TRUE/FALSE
+
+ GREYF_UNFOCUS = $80000000;
+ MODEF_OFFLINE = $40000000;
+
+ // and use the PF2_ #defines from m_protosvc.inc
+ CLM_SETGREYOUTFLAGS = (CLM_FIRST+39); // wParam=new flags
+ CLM_GETHIDEOFFLINEROOT = (CLM_FIRST+40); // returns TRUE/FALSE
+ CLM_SETHIDEOFFLINEROOT = (CLM_FIRST+41); // wParam=TRUE/FALSE
+ CLM_SETUSEGROUPS = (CLM_FIRST+42); // wParam=TRUE/FALSE
+ CLM_SETOFFLINEMODES = (CLM_FIRST+43); // for 'hide offline', wParam=PF2_ flags and MODEF_OFFLINE
+ CLM_GETEXSTYLE = (CLM_FIRST+44); // returns CLS_EX_ flags
+ CLM_SETEXSTYLE = (CLM_FIRST+45); // wParam=CLS_EX_ flags
+ CLM_GETLEFTMARGIN = (CLM_FIRST+46); // returns count of pixels
+ CLM_SETLEFTMARGIN = (CLM_FIRST+47); // wParam=pixels
+ // the order of info items is never changed, so make sure you add them in the
+ // order you want them to remain
+ CLM_ADDINFOITEM = (CLM_FIRST+48); // lParam=&TCLCINFOITEM, returns hItem
+ CLM_GETITEMTYPE = (CLM_FIRST+49); // wParam=hItem, returns a CLCIT_
+ CLM_GETNEXTITEM = (CLM_FIRST+50); // wParam=flag, lParam=hItem, returns an hItem
+ CLM_GETTEXTCOLOR = (CLM_FIRST+51); // wParam=FONTID_, returns COLORREF
+ CLM_SETTEXTCOLOR = (CLM_FIRST+52); // wParam=FONTID_, lParam=COLORREF
+
+ CLCIIF_BELOWGROUPS = 1; // put it between groups and contacts, default is at top
+ CLCIIF_BELOWCONTACTS = 2; // put it at the bottom
+ CLCIIF_CHECKBOX = $40; // give this item a check box
+ CLCIIF_GROUPFONT = $80; // draw the item using FONTID_GROUPS
+
+ CLCIT_INVALID = -1;
+ CLCIT_GROUP = 0;
+ CLCIT_CONTACT = 1;
+ CLCIT_DIVIDER = 2;
+ CLCIT_INFO = 3;
+
+ CLGN_ROOT = 0;
+ CLGN_CHILD = 1;
+ CLGN_PARENT = 2;
+ CLGN_NEXT = 3;
+ CLGN_PREVIOUS = 4;
+ CLGN_NEXTCONTACT = 5;
+ CLGN_PREVIOUSCONTACT = 6;
+ CLGN_NEXTGROUP = 7;
+ CLGN_PREVIOUSGROUP = 8;
+
+ CLNF_ISGROUP = 1;
+ CLNF_ISINFO = 2;
+
+ CLN_FIRST = (0-100);
+ CLN_EXPANDED = (CLN_FIRST-0); // hItem=hGroup, action=CLE_*
+ CLN_LISTREBUILT = (CLN_FIRST-1);
+ CLN_ITEMCHECKED = (CLN_FIRST-2); // todo // hItem,action,flags valid
+ CLN_DRAGGING = (CLN_FIRST-3); // hItem,pt,flags valid. only sent when cursor outside window, return nonzero if processed
+ CLN_DROPPED = (CLN_FIRST-4); // hItem,pt,flags valid. only sent when cursor outside window, return nonzero if processed
+ CLN_LISTSIZECHANGE = (CLN_FIRST-5); // pt.y valid. the vertical height of the visible items in the list has changed.
+ CLN_OPTIONSCHANGED = (CLN_FIRST-6); // nothing valid. If you set some extended options they have been overwritten and should be re-set
+ CLN_DRAGSTOP = (CLN_FIRST-7); // hItem,flags valid. sent when cursor goes back in to the window having been outside, return nonzero if processed
+ CLN_NEWCONTACT = (CLN_FIRST-8); // hItem,flags valid. sent when a new contact is added without a full list rebuild
+ CLN_CONTACTMOVED = (CLN_FIRST-9); // hItem,flags valid. sent when contact is moved without a full list rebuild
+ CLN_CHECKCHANGED = (CLN_FIRST-10); // hItem,flags valid. sent when any check mark is changed, but only for one change if there are many
+
+type
+
+ PCLCINFOITEM = ^TCLCINFOITEM;
+ TCLCINFOITEM = record
+ cbSize: int;
+ pszText: PChar;
+ hParentGroup: THandle;
+ flags: DWORD;
+ hIcon: THandle; // todo
+ end;
+
+ PNMCLISTCONTROL = ^TNMCLISTCONTROL;
+ TNMCLISTCONTROL = record
+ hdr: TNMHDR; // depends on Windows.pas
+ hItem: THandle;
+ action: int;
+ iColumn: int; // -1 if not on an extra column
+ flags: DWORD;
+ pt: TPoint; // depends on Windows.pas
+ end;
+
+ PCLCINFOTIP = ^TCLCINFOTIP;
+ TCLCINFOTIP = record
+ cbSize: int;
+ isTreeFocused: int; // so the plugin can provide an option
+ isGroup: int; // 0 if it's contact, 1 if it's a group
+ hItem: THandle; // handle to group or contact
+ ptCursor: TPoint;
+ rcItem: TRect;
+ end;
+
+const
+
+ {
+ wParam : 0
+ lParam : Pointer to a TCLCINFOTIP structure
+ Affect : An InfoTip for an item should be shown now, see notes
+ Returns: [non zero] if you process this, because it makes no sense
+ for more than one module to process this.
+ Notes : It's upto the module where to put the InfoTip, Normally
+ it's a few pixels below and to the right of the cursor.
+ -
+ This event is called after the mouse ehas been stationary over
+ a contact for (by default) 200ms
+ }
+ ME_CLC_SHOWINFOTIP = 'CLC/ShowInfoTip';
+
+ {
+ wParam : 0
+ lParam : Pointer to an initialised TCLCINFOTIP
+ Affect : It's time to destroy an infotip, see notes
+ Notes : Only cbSize, isGroup, hItem are set
+ notes : This is sent when the mouse moves off a contact when ME_CLC_SHOWINFOTIP
+ has previously been called.
+ -
+ If you don't want this behaviour, you should have grabbed the mouse
+ capture yourself --
+ }
+ ME_CLC_HIDEINFOTIP = 'CLC/HideInfoTip';
+
+ {
+ wParam : new_time
+ lParam : 0
+ Affect : Set a new hover time before the info tip hooks are called, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : The value of this setting is applid to all current CLC windows
+ and saved to b applied to all future windows, it is persistent.
+ -
+ Time is in milliseconds, default is 750ms
+ }
+ MS_CLC_SETINFOTIPHOVERTIME = 'CLC/SetInfoTipHoverTime';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : get the hover time before the infotip hooks are called
+ returns: the hover time in MS
+ }
+ MS_CLC_GETINFOTIPHOVERTIME = 'CLC/GetInfoTipHoverTime';
+
+{$ENDIF} \ No newline at end of file
diff --git a/plugins/ShlExt/inc/m_clist.inc b/plugins/ShlExt/inc/m_clist.inc
new file mode 100644
index 0000000000..58f59fac14
--- /dev/null
+++ b/plugins/ShlExt/inc/m_clist.inc
@@ -0,0 +1,641 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_CLIST}
+{$DEFINE M_CLIST}
+
+{$ifndef STATUSMODES}
+ {$include statusmodes.inc}
+{$endif}
+
+const
+
+ // for MS_CLIST_GETSTATUSMODEDESCRIPTION
+
+ GSMDF_PREFIXONLINE = 1; // prefix "Online :" for online submodes, e.g. 'away'
+
+ // for MS_CLIST_ADDMAINMENUITEM
+
+ CMIF_GRAYED = 1;
+ CMIF_CHECKED = 2;
+ CMIF_HIDDEN = 4; // only works on contact menus
+ CMIF_NOTOFFLINE = 8; // item won't appear for contacts that are offline
+ CMIF_NOTONLINE = 16; // " online
+ CMIF_NOTONLIST = 32; // item won't appear on standard contacts
+ CMIF_NOTOFFLIST = 64; // item won't appear on contacts that have the 'NotOnList' setting
+
+ // for MS_CLIST_MODIFYMENUITEM
+
+ CMIM_NAME = $80000000;
+ CMIM_FLAGS = $40000000;
+ CMIM_ICON = $20000000;
+ CMIM_HOTKEY = $10000000;
+ CMIM_ALL = $F0000000;
+
+ // for MS_CLIST_GETCONTACTDISPLAYNAME
+
+ // will never return the user's custom name, even if that's the one to be displayed
+ GCDNF_NOMYHANDLE = 1;
+
+ // for MS_CLIST_ADDEVENT
+
+ //flashes the icon even if the user is occupied, and puts the event
+ // at the top of the queue
+ CLEF_URGENT = 1;
+ { icon will not flash forever, only a few times, e.g. online alert }
+ CLEF_ONLYAFEW = 2;
+
+ // for MS_CLIST_GETICONSIMAGELIST
+
+ IMAGE_GROUPOPEN = 11;
+ IMAGE_GROUPSHUT = 12;
+
+ // for MS_CLIST_MENUPROCESSCOMMAND
+
+ MPCF_CONTACTMENU = 1; // test commands from a contact menu
+ MPCF_MAINMENU = 2; // test commands from the main menu
+
+ // for MS_CLIST_GROUPGETNAME/2
+
+ GROUPF_EXPANDED = $04;
+ GROUPF_HIDEOFFLINE = $08;
+
+ //
+
+ SETTING_TOOLWINDOW_DEFAULT = 1;
+ SETTING_SHOWMAINMENU_DEFAULT = 1;
+ SETTING_SHOWCAPTION_DEFAULT = 1;
+ SETTING_CLIENTDRAG_DEFAULT = 0;
+ SETTING_ONTOP_DEFAULT = 1;
+ SETTING_MIN2TRAY_DEFAULT = 1;
+ SETTING_TRAY1CLICK_DEFAULT = 0;
+ SETTING_HIDEOFFLINE_DEFAULT = 0;
+ SETTING_HIDEEMPTYGROUPS_DEFAULT = 0;
+ SETTING_USEGROUPS_DEFAULT = 1;
+ SETTING_SORTBYSTATUS_DEFAULT = 0;
+ SETTING_TRANSPARENT_DEFAULT = 0;
+ SETTING_ALPHA_DEFAULT = 200;
+ SETTING_AUTOALPHA_DEFAULT = 150;
+ SETTING_CONFIRMDELETE_DEFAULT = 1;
+ SETTING_AUTOHIDE_DEFAULT = 0;
+ SETTING_HIDETIME_DEFAULT = 30;
+ SETTING_CYCLETIME_DEFAULT = 4;
+ SETTING_ALWAYSSTATUS_DEFAULT = 0;
+ SETTING_ALWAYSMULTI_DEFAULT = 0;
+ SETTING_TRAYICON_SINGLE = 0;
+ SETTING_TRAYICON_CYCLE = 1;
+ SETTING_TRAYICON_MULTI = 2;
+ SETTING_TRAYICON_DEFAULT = SETTING_TRAYICON_SINGLE;
+ SETTING_STATE_HIDDEN = 0;
+ SETTING_STATE_MINIMIZED = 1;
+ SETTING_STATE_NORMAL = 2;
+
+type
+
+ PCLISTMENUITEM = ^TCLISTMENUITEM;
+ TCLISTMENUITEM = record
+ cbSize: int; // size in bytes of this structure
+ pszName: PChar; // text of the menu item
+ flags: DWORD;
+ position: int; // approx position on the menu, lower numbers go nearer the top
+ hIcon: THandle; // icon to put by the item, if this was *not* loaded from
+ // a resource, you can delete it straight after the call
+ pszService: PChar; // name of the service to call when the service is clicked
+ pszPopupName: PChar;// name of the popup menu that this item is on, if this
+ // is NULL the iteem is on the root of the menu
+ popupPosition: int; // position of the popup menu on the root menu, ignored
+ // if pszPopupName is NULL(0) or if the popup menu already exists
+ hotKey: DWORD; // keyboard accelerator, same as lParam of WM_HOTKEY, 0 for none
+ pszContactOwner: PChar; // contact menus only, the protocol module that owns
+ // the contacts to which this to which this menu item
+ // applies, NULL(0) if it applies to all contacts.
+ // if it applies to multiple but not all protocols
+ // add multiple menu items or use ME_CLIST_PREBUILDCONTACTMENU
+ end;
+
+ PCLISTDOUBLECLICKACTION = ^TCLISTDOUBLECLICKACTION;
+ TCLISTDOUBLECLICKACTION = record
+ cbSize: int;
+ pszContactOwner: PChar; // name of the protocol owning the contact or NULL(0) for all
+ flags: DWORD; // CMIF_NOT flags above
+ pszService: PChar; // service to call on double click, is called with wParam=hContact, lParam=0
+ end;
+
+ PCLISTEVENT = ^TCLISTEVENT;
+ TCLISTEVENT = record
+ cbSize: int; // size in bytes
+ hContact: THandle; // handle to the contact to put the icon by
+ hIcon: THandle; // icon to flash!
+ flags: DWORD;
+ hDBEvent: THandle; // caller defined, but should be unique for hContact
+ lParam: LPARAM;
+ pszService: PChar; // name of service to call on activation
+ pszTooltip: PChar; // short description of the event to display as a tooltip on the systray
+ end;
+
+const
+
+ {
+ wParam : new_status
+ lParam : 0
+ Affect : Sent when the user acks to change their status, see notes
+ Notes : Also sent due to a MS_CLIST_SETSTATUSMODE
+ }
+ ME_CLIST_STATUSMODECHANGE = 'CList/StatusModeChange';
+
+ {
+ wParam : new_status
+ lParam : 0
+ Affect : Force a change of status mode, see statusmodes.inc
+ }
+ MS_CLIST_SETSTATUSMODE = 'CList/SetStatusMode';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Get the current status mode, see notes
+ Notes : This is the status, as set by the user, not any protocol specific status
+ all protocol modules will attempt to conform to this setting at ALL times.
+ }
+ MS_CLIST_GETSTATUSMODE = 'CList/GetStatusMode';
+
+ {
+ wParam : status_mode
+ lParam : flags
+ Affect : Get a textual description of the given status mode
+ Returns: pointer to a static buffer of the description of the given status mode
+ or NULL(0) if the mode was unknown.
+ Version: v0.1.0.1+
+ }
+ MS_CLIST_GETSTATUSMODEDESCRIPTION = 'CList/GetStatusModeDescription';
+
+ {
+ wParam : 0
+ lParam : Pointer to a initalised TCLISTMENUITEM structure
+ Affect : Add a new menu item to the main menu, see notes
+ Returns: A handle to the new MENU item or NULL(0) on failure
+ Notes : The given TCLISTMENUITEM.pszService in is called when the item
+ get clicked with :
+ -
+ wParam = 0, lParam = hwndContactList
+ }
+ MS_CLIST_ADDMAINMENUITEM = 'CList/AddMainMenuItem';
+
+ {
+ wParam : 0
+ lParam : Pointer to a initalised TCLISTMENUITEM structure
+ Affect : Add a new item to the user contact menus, see notes
+ Notes : exactly the same as MS_CLIST_ADDMAINMENUITEM except when an item
+ is selected, the service gets called with wParam=hContact,
+ pszContactOwner is obeyed.
+ -
+ Popup menus are not supported, pszPopupName and popupPosition
+ are ignored. If CTRL is held down when right clicking the menu
+ position numbers will be displayed in brackets afterr the menu item
+ text, this only works in debug builds!
+ }
+ MS_CLIST_ADDCONTACTMENUITEM = 'CList/AddContactMenuItem';
+
+ {
+ wParam : HMENUITEM
+ lParam : Pointer to a initalised TCLISTMENUITEM
+ Affect : Modify an existing menu item, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : hMenuItem will have been returned by MS_CLIST_ADD[MAIN]MENUITEM
+ TCLISTMENUITEM.flags should contain CMIM_* constants (see above)
+ to mark which fields should be updated, if it's not present, they
+ can't be updated -- if flags do not exist for a field it can not
+ be updated.
+ Version: v0.1.0.1+
+ }
+ MS_CLIST_MODIFYMENUITEM = 'CList/ModifyMenuItem';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : the context menu for a contact is about to be built, see notes
+ Notes : modules should use this to change menu items that are specific
+ to the contact that has them
+ Version: v0.1.0.1+
+ }
+ ME_CLIST_PREBUILDCONTACTMENU = 'CList/PreBuildContactMenu';
+
+ {
+ wParam : 0
+ lParam : Pointer to a initalised TCLISTDOUBLECLICKACTION structure
+ Affect : Sets the service to call when a contact is double-clicked, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : in case of conflicts, the first module to have registered
+ will get the double click, no others will, this service
+ will return success even for duplicates
+ -
+ This service was dropped from development during 0.3.0.0, it is no
+ longer supported, see ME_CLIST_DOUBLECLICKED
+ Version: 0.1.2.2+, 0.2.0+ ONLY (not 3.0a)
+ }
+ MS_CLIST_SETDOUBLECLICKACTION = 'CList/SetDoubleClickAction';
+
+ {
+ wParam : HCONTACT
+ lParam : <none>
+ Affect : Register with this event to be notified of a double click on the CList
+ against a HCONTACT, you will not be notified if there is a pending CList event
+ that the double click clears, (i.e. flashing icon is presented to be clicked)
+ Version: 0.3.0.0
+ }
+ ME_CLIST_DOUBLECLICKED = 'CList/DoubleClicked';
+
+ {
+ wParam : HCONTACT
+ lParam : flags
+ Affect : Gets the string that the contact list will use to represent a contact
+ Returns: Always a pointer
+ Notes : Returns a pointer to the name, will always succeed, even if it needs
+ to return "(Unknown Contact)"
+ -
+ this pointer is a statically allocated buffer which will
+ be overwritten on every call to this service, callers should make
+ sure that they copy the information before they call it again
+ Version: v0.1.2.0+, 0.2.0+ ONLY (0.3a supports the contacts module)
+ }
+ MS_CLIST_GETCONTACTDISPLAYNAME = 'CList/GetContactDisplayName';
+
+ {
+ wParam : 0
+ lParam : Pointer to a TCLISTEVENT
+ Affect : Add's an event to the list
+ Notes : The service will flash TCLISTEVENT.hIcon, next to the
+ contact, TCLISTEVENT.hContact
+ -
+ pszService is called is called wParam=hwndContactList,
+ lParam=pointer to a TCLISTEVENT.
+ -
+ the TCLISTEVENT data is invalidated after this service returns
+ so copy anything from it if required.
+ -
+ TCLISTEVENT.pszService will also be called if the user
+ double clicks on the icon, at which point it will be removed
+ from the contact lists queue automatically.
+ -
+ TCLISTEVENT.hContact and TCLISTEVENT.hDBEvent should be unique.
+ }
+ MS_CLIST_ADDEVENT = 'CList/AddEvent';
+
+ {
+ wParam : HCONTACT
+ lParam : HDBEVENT
+ Affect : Remove an event from the contact list queue
+ Returns: 0 on success, [non zero] on failure
+ }
+ MS_CLIST_REMOVEEVENT = 'Clist/RemoveEvent';
+
+ {
+ wParam : HCONTACT
+ lParam : iEvent
+ Affect : Get the details of an event in the queue, see notes
+ Returns: A CLISTEVENT* or NULL(0) on failure
+ Notes : Returns the iEvent'1st/2nd/3rd/nth elemented queried,
+ e.g. iEvent=0 will get the event that will be returned if the
+ user double clicks on that HCONTACT
+ -
+ Use HCONTACT=NULL, iEvent=0 for example to get the event
+ the user will get if they double click on the tray.
+ Version: v0.1.2.1+
+ }
+ MS_CLIST_GETEVENT = 'CList/GetEvent';
+
+ {
+ wParam : ControlID
+ lParam : Pointer to MEASUREITEMSTRUCT struct
+ Affect : Process a WM_MEASUREITEM message for user context menus, see notes
+ Notes : just because wParam, lParam is defined here, only pass them
+ opaquely to this service, as is.
+ -
+ This is just to draw icons, if it is not called, the icons
+ will not be drawn
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_MENUMEASUREITEM = 'CList/MenuMeasureItem';
+
+ {
+ wParam :
+ lParam :
+ Affect : Process a WM_DRAWITEM message for user context menus,
+ wParam, lParam should be passed from such message handler.
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_MENUDRAWITEM = 'CList/MenuDrawItem';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Built the context menu for a specific contact
+ Returns: A HMENU handle identifying the menu, thhis should be DestroyMenu()ed
+ when done.
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_MENUBUILDCONTACT = 'CList/MenuBuildContact';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Get the image list handle with all the useful icons in it
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_GETICONSIMAGELIST = 'CList/GetIconsImageList';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Get the icon that should be associated with a contact
+ Returns: an index into the contact list imagelist, if the icon
+ is a flashing icon, this service won't return information about it
+ see below
+ Version: v0.1.2.0+
+ }
+ MS_CLIST_GETCONTACTICON = 'CList/GetContactIcon';
+
+ {
+ wParam : HCONTACT
+ lParam : ICON_ID
+ Affect : The icon of a contact in the contact list has changed,
+ ICON_ID is an index to what image has changed
+ Version: v0.1.2.1+
+ }
+ ME_CLIST_CONTACTICONCHANGED = 'CList/ContactIconChanged';
+
+ // ideally only used by a CLIST UI module
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Get the handle to Miranda's main menu
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_MENUGETMAIN = 'CList/MenuGetMain';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Get a handle to Miranda's status menu
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_MENUGETSTATUS = 'CList/MenuGetStatus';
+
+ {
+ wParam : MAKEWPARAM(LOWORD(wParam of WM_COMMAND),flags)
+ lParam : HCONTACT
+ Affect : Process a mennu selection from a menu, see notes
+ Returns: True if it processed the command, False otherwise
+ notes : hContact is the currently selected contact, it is not used
+ if this is a main menu command, if this is NULL then the command
+ is a contact menu one, the command is ignored
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_MENUPROCESSCOMMAND = 'CList/MenuProcessCommand';
+
+ {
+ wParam : virtual key code
+ lParam : MPCF_* flags
+ Affect : Process a menu hotkey, see notes
+ Returns: True if it processed the command, False otherwise
+ Notes : this should be called in WM_KEYDOWN
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_MENUPROCESSHOTKEY = 'CList/MenuProcessHotkey';
+
+ {
+ wParam : Pointer to a MSG structurer
+ lParam : Pointer to an LRESULT
+ Affect : Process all the messages required for docking, see notes
+ Returns: True if the message should NOT be processed anymore, False otherwise
+ Notes : only msg.hwnd, msg.message, msg.wParam and msg.lParam are used
+ your WndProc should return the lResult if AND only IF, TRUE is returned
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_DOCKINGPROCESSMESSAGE = 'CList/DockingProcessMessage';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Determines wheter the contact list docked
+ Returns: pnon zero] if the contact list is docked, or 0 if it's not
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_DOCKINGISDOCKED = 'CList/DockingIsDocked';
+
+ {
+ wParam : Pointer to a TMSG
+ lParam : Pointer to an LRESULT
+ Affect : Process all the messages required for the tray icon, see notes
+ Returns: TRUE if the message should not be processed anymore, False otherwise
+ Notes : Only msg.hwnd, msg.message, msg.wparam and msg.lParam are used
+ your WndProc should return LRESULT if and ONLY if TRUE is returned
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_TRAYICONPROCESSMESSAGE = 'CList/TrayIconProcessMessage';
+
+ {
+ wParam : Pointer to TMSG
+ lParam : Pointer to an LRESULT
+ Affect : Process all the messages required for hotkeys, see notes
+ Returns: True if the message should not be processed anymore or False otherwise
+ Notes : only msg.hwnd, msg.message, msg.wParam, msg.lParam are used
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_HOTKEYSPROCESSMESSAGE = 'CList/HotkeysProcessMessage';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Toggles the show/hide status of the contact list
+ Returns: 0 on success, [non zero] on failure
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_SHOWHIDE = 'CList/ShowHide';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : temporarily disable the autohide feature, see notes
+ Notes : this service will restart the auto hide timer, so if you need
+ to keep the window visible you'll have to bee getting user input
+ or calling this service each time
+ Version: v0.1.2.1+
+ }
+ MS_CLIST_PAUSEAUTOHIDE = 'CList/PauseAutoHide';
+
+ {
+ wParam : HPARENTGROUP
+ lParam : 0
+ Affect : Create a new group and calls CLUI to display it, see notes
+ Returns: A handle to the new group.
+ Notes : If HPARENTGROUP is NULL(0) it will create a group at the root.
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_GROUPCREATE = 'CList/GroupCreate';
+
+ {
+ wParam : HGROUP
+ lParam : 0
+ Affect : Delete a group and call CLUI to display the change
+ Returns: 0 on success, [non zero] on failure
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_GROUPDELETE = 'CList/GroupDelete';
+
+ {
+ wParam : HGROUP
+ lParam : newState
+ Affect : Change the expanded state flag for a group internally, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : if newState is non zero then the group is expanded, 0 it's collapsed
+ CLUI IS *NOT* called when the change is made.
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_GROUPSETEXPANDED = 'CList/GroupSetExpanded';
+
+ {
+ wParam : HGROUP
+ lParam : MAKELPARAM(flags, flagsMask)
+ Affect : Change the flag for a group, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : only if flags given in flagsmask are altered,
+ CLUI is called on change to GROUPF_HIDEOFFLINE
+ Version: v0.1.2.1+
+ }
+ MS_CLIST_GROUPSETFLAGS = 'CList/GroupSetFlags';
+
+ {
+ wParam : HGROUP
+ lParam : Pointer to a integer to be filled with expanded state
+ Affect : get the name of a group, see notes
+ Returns: a static buffer pointing to the name of the group
+ returns NULL(0) if HGROUP is invalid.
+ Notes : the returned buffer is only valid til the next call
+ to this service, lParam can be NULL(0) if you don't
+ want to know if the group is expanded
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_GROUPGETNAME = 'CList/GroupGetName';
+
+ {
+ wParam : HGROUP
+ lParam : Pointer to flags
+ Affect : Get the name of the group, see notes
+ Returns: A static buffer pointing to the name of the group
+ returns NULL(0) if HGROUP is invalid
+ Note : this buffer is only valid til the next call to this service
+ flags can be NULL(0), otherwise it'll return GROUPF_* constants
+ Version: v0.1.2.1+
+ }
+ MS_CLIST_GROUPGETNAME2 = 'CList/GroupGetName2';
+
+ {
+ wParam : HGROUP
+ lParam : HBEFOREGROUP
+ Affect : Move a group directly before another group
+ Returns: the new handle of the group on success, NULL(0) on failure
+ Notes : the order is represented by the order in which MS_CLUI_GROUPADDED
+ is called, however UI's are free to ignore this order and sort
+ if they wish.
+ Version: v0.1.2.1+
+ }
+ MS_CLIST_GROUPMOVEBEFORE = 'CList/GroupMoveBefore';
+
+ {
+ wParam : HGROUP
+ lParam : Pointer to a null terminated string containing the new name
+ Affect : Rename a group internally, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : this will fail if the group name is a duplicate of an existing
+ a name, CLUI is not called when this change is made.
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_GROUPRENAME = 'CList/GroupRename';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Build a menu of the group tree, see notes
+ Returns: Handle to the menu, NULL(0) on failure
+ Notes : NULL be returned if the user doesn't have any groups
+ the dwItemData of every menu item is the handle to that group.
+ Menu item ID's are assigned starting at 100 in no particular order
+ Version: v0.1.2.1+
+ }
+ MS_CLIST_GROUPBUILDMENU = 'CList/GroupBuildMenu';
+
+ {
+ wParam : newValue
+ lParam : 0
+ Affect : Changes the 'hide offline contacts' flag and calls CLUI, see notes
+ Returns: 0 success, [non zero] on failure
+ Notes : newValue is 0 to show all contacts, 1 to show only online contacts
+ -1 to toggle the value
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_SETHIDEOFFLINE = 'CList/SetHideOffline';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Do the message processing associated with the double clicking a contact
+ Returns: 0 on success, [non zero] on failure
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_CONTACTDOUBLECLICKED = 'CList/ContactDoubleClicked';
+
+ {
+ wParam : HCONTACT
+ lParam : Pointer to an array of pchar's containing files/dirs
+ Affect : Do the processing when some files are droppeed on a contact, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : the array is terminated when a NULL(0) entry is found
+ Version: v0.1.2.1+
+ }
+ MS_CLIST_CONTACTFILESDROPPED = 'CList/ContactFilesDropped';
+
+ {
+ wParam : HCONTACT
+ lParam : HGROUP
+ Affect : Change the group a contact belongs to, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : use hGroup=NULL(0) to remove any group association with the contact
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_CONTACTCHANGEGROUP = 'CList/ContactChangeGroup';
+
+ {
+ wParam : HCONTACT_1
+ lParam : HCONTACT_2
+ Affect : Determine the ordering of two given contacts
+ Returns: 0 if hContact1 is the same as hContact2
+ 1 if hContact1 should be displayed before hContact2
+ -1 if hContact1 should be displayed after hCotnact2
+ Version: v0.1.1.0+
+ }
+ MS_CLIST_CONTACTSCOMPARE = 'CList/ContactsCompare';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_clui.inc b/plugins/ShlExt/inc/m_clui.inc
new file mode 100644
index 0000000000..09f2b999a3
--- /dev/null
+++ b/plugins/ShlExt/inc/m_clui.inc
@@ -0,0 +1,215 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_CLUI}
+{$DEFINE M_CLUI}
+
+ {<</
+ this header was created for use for v0.1.1.0, most of it's UI related
+ stuff and you probably don't need to call it, see m_clist.inc instead.
+ -- There are some functions that were implemented in v0.1.2.0 though
+ />>}
+
+const
+
+ {
+ wParam : 0
+ lParam : 0
+ Affects: Returns a window handle for the contact list window, see notes
+ Returns: ""
+ Notes : This call has a very specific purpose internally Miranda
+ and shouldn't be used gratuitously, in almost all cases
+ there's another call to do whatever it is that you're
+ trying to do.
+ }
+ MS_CLUI_GETHWND = 'CLUI/GetHwnd';
+
+ {
+ wParam : new status
+ lParam : null terminated string to a protocol ID
+ Affects: Change the protocol specific status indicators, see notes!
+ Returns: 0 on success, [non zero] on failure
+ Notes : protocol modules don't want to call this, they want
+ clist/protocolstatuschanged instead
+ }
+ MS_CLUI_PROTOCOLSTATUSCHANGED = 'CLUI/ProtocolStatusChanged';
+
+ {
+ wParam : Handle to a group
+ lParam : 1 or 0
+ Affect : A new group was created, add it to the list, see notes
+ Notes : lParam is set to 1 or 0 if the user just created
+ the group or not.
+ -
+ this is also called when the contact list is being rebuilt,
+ new groups are always created with the name 'New group'
+ }
+ MS_CLUI_GROUPADDED = 'CLUI/GroupCreated';
+
+ {
+ wParam : HCONTACT
+ lParam : ICON_ID
+ Affect : Change the icon for a contact, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : ICON_ID is an offset in the imagelist, see clist/geticonsimagelist
+ }
+ MS_CLUI_CONTACTSETICON = 'CLUI/ContactSetIcon';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Remove a contact from the list, see notes
+ Returns: 0 on success, [non zereo] on failure
+ Notes : this contact is NOT actually being deleted, since if
+ a contact goes offline while 'hide offline' option is sset,
+ this service will be called then ALSO
+ }
+ MS_CLUI_CONTACTDELETED = 'CLUI/ContactDeleted';
+
+ {
+ wParam : HCONTACT
+ lParam : ICON_ID
+ Affect : Add a contact to the list, see note
+ returns: 0 on success, [non zero] on failure
+ Notes : the caller processes the 'hide offline' setting, so the callee
+ should not do further processing based on the value of this setting
+ -
+ WARNING: this will be called to re-add a contact when they come
+ online if 'hide offline' is on, but it cannot determine if
+ the contact is already on the list, so you may get requests to
+ add a contact when it is already on the list, which you should ignore.
+ -
+ You'll also get this whenever an event is added for a contact,
+ since if the contact was offline, it needs to be shown to
+ display the mesage, even if 'hide offlines' is on.
+ -
+ you should not resort the list on this call, a seperate resort
+ request will be sent.
+ -
+ ICON_ID is an offset in the image list, see clist/geticonsimagelist
+
+ }
+ MS_CLUI_CONTACTADDED = 'CLUI/ContactAdded';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Reename a contact in the lists, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : You should not re-sort the list on this call, a separate resort
+ request will be sent, you can get the new name from clist/getcontactdisplayname
+ }
+ MS_CLUI_CONTACTRENAMED = 'CLUI/ContactRenamed';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Start a rebuild of the contact list, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : this is the cue to clear the existing content of the list
+ expect to get a series of :
+
+ clui/groupadded
+ clui/contactadded
+ clui/resortlist
+ }
+ MS_CLUI_LISTBEGINREBUILD = 'CLUI/ListBeginRebuild';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : End a rebuild of the contact list, see notes
+ Returns: 0 on success, [non zero] on error
+ Notes : if you dissplayed an hourglass in beginbuild, set it back
+ here, you do not need to explicitly sort the list
+ }
+ MS_CLUI_LISTENDREBUILD = 'CLUI/ListEndRebuild';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Sort the contact list now, see notes
+ Returns: 0 success, [non zero] on failure
+ Notes : Sorts are buffered so you won't get this message lots of times
+ if the lists needs to be resorted many times rapidly
+ }
+ MS_CLUI_SORTLIST = 'CLUI/SortList';
+
+ {
+ wParam : CLUICAPS_*
+ lParam : 0
+ Affect : Gets a load of capabilites for the loaded CLUI, see notes
+ Returns: the requested value, 0 of wParam is unknown --
+ if this service is not implemented it is assumed all return
+ values will be 0.
+ Version: v0.1.2.1+
+ }
+
+ { can only provide this flag to return the following set of caps, the strings
+ show the database setting/type to store the list option, changing the value
+ does not reflect what the change is, i.e. ontop can only be affected with
+ a call to SetWindowPos() }
+ CLUICAPS_FLAGS1 = 0;
+ { empty groups aren't shown, 'CList/HideEmptyGroups' (byte) [changes make the list reload] }
+ CLUIF_HIDEEMPTYGROUPS = 1;
+ { groups can be disabled, lists can be merged into one seamlessly, (byte) 'CList/UseGroups' }
+ CLUIF_DISABLEGROUPS = 2;
+ { list can be displayed 'on top' of all other windows, 4 (byte) 'CList/OnTop' }
+ CLUIF_HASONTOPOPTION = 4;
+ { can disappear after a while of inactive use,
+ (byte) 'CList/AutoHide' (word) 'CList/HideTime' }
+ CLUIF_HASAUTOHIDEOPTION = 8;
+
+ MS_CLUI_GETCAPS = 'CLUI/GetCaps';
+
+ {
+ wParam : HCONTACT
+ lParam : MAKELPARAM(screenX, screenY)
+ Affect : A contact is being dragged outside the main window
+ Return : return [non zero] to show the drag cursor as "accepting" the drag
+ or zero to show the circle/slash 'not allowed'
+ Version: v0.1.2.0+
+ }
+ ME_CLUI_CONTACTDRAGGING = 'CLUI/ContactDragging';
+
+ {
+ wParam : HCONTACT
+ lParam : MAKELPARAM(screenX, screenY)
+ Affect : a contact has just been dropped outside the main window, see notes
+ Notes : return non zero to stop other hooks processing this event.
+ Version: v0.1.2.0+
+ }
+ ME_CLUI_CONTACTDROPPED = 'CLUI/ContactDropped';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : A contact that *was* being dragged outside the main window
+ has gone back to the main window
+ Return : always return 0
+ Version: v0.1.2.1+
+ }
+ ME_CLUI_CONTACTDRAGSTOP = 'CLUI/ContactDragStop';
+
+{$ENDIF} \ No newline at end of file
diff --git a/plugins/ShlExt/inc/m_contacts.inc b/plugins/ShlExt/inc/m_contacts.inc
new file mode 100644
index 0000000000..7ba6f68bbb
--- /dev/null
+++ b/plugins/ShlExt/inc/m_contacts.inc
@@ -0,0 +1,90 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+type
+
+ PCONTACTINFO = ^TCONTACTINFO;
+ TCONTACTINFO = record
+ cbSize: int;
+ dwFlag: Byte;
+ hContact: THandle;
+ szProto: PChar;
+ type_: Byte;
+ retval: record (* in C this is a nameless union *)
+ case longint of
+ 0: (bVal: Byte);
+ 1: (wVal: WORD);
+ 2: (dVal: DWORD);
+ 3: (pszVal: PChar);
+ 4: (cchVal: Word);
+ end;
+ end;
+
+const
+
+// CNF_* Types of information you can retreive by setting the dwFlag in CONTACTINFO
+
+ CNF_FIRSTNAME = 1; // returns first name (string)
+ CNF_LASTNAME = 2; // returns last name (string)
+ CNF_NICK = 3; // returns nick name (string)
+ CNF_CUSTOMNICK = 4; // returns custom nick name, clist name (string)
+ CNF_EMAIL = 5; // returns email (string)
+ CNF_CITY = 6; // returns city (string)
+ CNF_STATE = 7; // returns state (string)
+ CNF_COUNTRY = 8; // returns country (string)
+ CNF_PHONE = 9; // returns phone (string)
+ CNF_HOMEPAGE = 10; // returns homepage (string)
+ CNF_ABOUT = 11; // returns about info (string)
+ CNF_GENDER = 12; // returns gender (byte,'M','F' character)
+ CNF_AGE = 13; // returns age (byte, 0==unspecified)
+ CNF_FIRSTLAST = 14; // returns first name + last name (string)
+ CNF_UNIQUEID = 15; // returns uniqueid, protocol username (must check type for type of return)
+
+// Special types
+// Return the custom name using the name order setting
+// IMPORTANT: When using CNF_DISPLAY you MUST free the string returned
+// You must **NOT** do this from your version of free() you have to use Miranda's free()
+// you can get a function pointer to Miranda's free() via MS_SYSTEM_GET_MMI, see m_system.h
+ CNF_DISPLAY = 16;
+// Same as CNF_DISPLAY except the custom handle is not used
+// IMPORTANT: When using CNF_DISPLAYNC you MUST free the string returned
+// You must **NOT** do this from your version of free() you have to use Miranda's free()
+// you can get a function pointer to Miranda's free() via MS_SYSTEM_GET_MMI, see m_system.h
+ CNF_DISPLAYNC = 17;
+
+// If MS_CONTACT_GETCONTACTINFO returns 0 (valid), then one of the following
+// types is setting telling you what type of info you received
+ CNFT_BYTE = 1;
+ CNFT_WORD = 2;
+ CNFT_DWORD = 3;
+ CNFT_ASCIIZ = 4;
+
+ {
+ wParam : not used
+ lParam : Pointer to an initialised TCONTACTINFO structure
+ affects: Get contact information
+ returns: Zero on success, non zero on failure.
+ notes : If successful, the type is set and the result is put into the associated member of TCONTACTINFO
+ }
+ MS_CONTACT_GETCONTACTINFO = 'Miranda/Contact/GetContactInfo';
diff --git a/plugins/ShlExt/inc/m_database.inc b/plugins/ShlExt/inc/m_database.inc
new file mode 100644
index 0000000000..f2b26508df
--- /dev/null
+++ b/plugins/ShlExt/inc/m_database.inc
@@ -0,0 +1,654 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_DATABASE}
+{$DEFINE M_DATABASE}
+
+const
+
+ DBVT_DELETED = 0; // setting got deleted, no values are valid
+ DBVT_BYTE = 1; // bVal, cVal are valid
+ DBVT_WORD = 2; // wVal, sVal are valid
+ DBVT_DWORD = 4; // dVal, lVal are valid
+ DBVT_ASCIIZ = 255; // pszVal is valid
+ DBVT_BLOB = 254; // cpbVal and pbVal are valid
+ DBVTF_VARIABLELENGTH = $80; // ?
+
+type
+
+ HCONTACT = Integer;
+ HDBEVENT = Integer;
+
+ PDBVARIANT = ^TDBVARIANT;
+ TDBVARIANT = record
+ type_: Byte;
+ case LongInt of
+ 0: (bVal: Byte);
+ 1: (cVal: Char);
+ 2: (wVal: Word);
+ 3: (sVal: SmallInt);
+ 4: (dVal: LongInt);
+ 5: (lVal: Integer);
+ 6: (
+ pszVal: PChar;
+ cchVal: Word;
+ );
+ 7: (
+ cpbVal: Word;
+ pbVal: PByte;
+ );
+ end;
+
+const
+
+ {
+ wParam : size of the buffer to be filled
+ lParam : pointer to the buffer to be filled
+ affect : Get's the name of the current profile being used by the database
+ module -- this is the same as the filename of the profile without
+ the .ext
+ return : 0 on success, non zero on failure
+ }
+ MS_DB_GETPROFILENAME = 'DB/GetProfileName';
+
+ {
+ wParam : size of buffer pointed to by lParam
+ lParam : pointer to a buffer to be filled
+ affect : Fill a buffer with the current profile path being used, this does not include the trailing backslash.
+ return : 0 on success, non zero on failure
+ version: 0.3a only
+ }
+ MS_DB_GETPROFILEPATH = 'DB/GetProfilePath';
+
+type
+
+ PDBCONTACTGETSETTING = ^TDBCONTACTGETSETTING;
+ TDBCONTACTGETSETTING = record
+ { name of the module that wrote the setting to get }
+ szModule: PChar;
+ { the name of the setting to get }
+ szSetting: PChar;
+ { pointer to DBVARIANT to receive the value -- must be allocated for GETSETTINGSTATIC
+ calls thou }
+ pValue: PDBVARIANT;
+ end;
+
+ PDBCONTACTWRITESETTING = ^TDBCONTACTWRITESETTING;
+ TDBCONTACTWRITESETTING = record
+ { module sig to write this setting under }
+ szModule: PChar;
+ { setting name to write }
+ szSetting: PChar;
+ { variant containing value to set }
+ value: TDBVARIANT;
+ end;
+
+const
+
+ {
+ wParam : Handle of a contact to get the setting for (see notes)
+ lParam : pointer to a TDBCONTACTGETSETTING structure to be filled with setting
+ this structure also has to be initalised (see notes)
+ affect : Queries the database module for a setting from a contact.
+ returns: 0 on success, non zero on failure (contact not found, setting doesn't exist)
+ notes : TDBCONTACTGETSETTING must be filled with the module name that created
+ /wrote the setting you want to get (e.g. your module name)
+ and the actual setting to read with TDBCONTACTGETSETTING.szModule and
+ TDBCONTACTGETSETTING.szSetting -- TDBCONTACTGETSETTING.pValue is
+ a pointer to a TDBVARIANT with the returned setting, this maybe nil
+ and MUST be freed after you're done with it with FreeVariant()
+
+ There are helper functions for reading/writing/deleting common types to and
+ from the database -- see DBGetContactSetting<type>
+
+ the contact handle (hContact) can be returned by FindContact/AddContact
+ }
+ MS_DB_CONTACT_GETSETTING = 'DB/Contact/GetSetting';
+
+ {
+ wParam : Handle for a contact to query a setting for
+ lParam : Pointer to a TDBCONTACTGETSETTING structure
+ affects: This service is almost the same as the one above, but it does
+ not return a dynamic copy (with malloc()) -- the caller
+ must do this for datatypes which require it, e.g. a string.
+
+ This means the TDBCONTACTGETSETTING.pValue *has* to exist and be
+ allocated by the caller (doesn't have to be allocated from the heap)
+ the DBVARIANT structure has to be initalised with the type wanted
+ and enough buffer space around to return the info, do not
+ expect this service to be as fast as the one above.
+
+ returns: 0 on success, non zero on failure.
+ }
+ MS_DB_CONTACT_GETSETTINGSTATIC = 'DB/Contact/GetSettingStatic';
+
+ {
+ wParam : 0
+ lParam : Pointer to a TDBVARIANT structure
+ affect : Free's the passed DBVARIANT's dynamic memory (if any) see notes
+ returns: 0 on success, non zero on failure
+ notes : use the helper function FreeVariant()
+ }
+ MS_DB_CONTACT_FREEVARIANT = 'DB/Contact/FreeVariant';
+
+ {
+ wParam : Handle to contact to write setting for
+ lParam : Pointer to TDBCONTACTWRITESETTING which must be initalised
+ affects: writes a setting under a contact -- TDBCONTACTWRITESETTING structure
+ must contain the module name writing -- the setting name, and the value
+ to write (which is NOT a pointer) .szModule, .szSetting, .Value, see notes
+ returns: 0 on success, non zero on failure
+ notes : this service triggers 'DB/Contact/SettingChanged' before it returns
+ as always, there is a helper function to use this service.
+ }
+ MS_DB_CONTACT_WRITESETTING = 'DB/Contact/WriteSetting';
+
+ {
+ wParam : hContact under which the setting should be deleted
+ lParam : Pointer to a TDBCONTACTGETSETTING structure
+ affects: Deletes the given setting for a contact, the TDBCONTACTGETSETTING.pValue
+ field is ignored -- only .szModule and .szSetting are needed, see notes
+ returns: 0 on success, non zero on failure
+ notes : triggers 'DB/Contact/SettingChanged' BEFORE it deletes the given
+ setting, when the service returns the TDBVARIANT structure .type_ is set
+ to 0 and no fields are valid, there is a helper function for this
+ service, see below.
+ }
+ MS_DB_CONTACT_DELETESETTING = 'DB/Contact/DeleteSetting';
+
+ {
+ wParam : Handle of a contact to enum settings for
+ lParam : Pointer to a TDBCONTACTENUMSETTINGS structure, must be initalised
+ affect : Enumerates all settings for a given contact under a module,
+ TDBCONTACTENUMSETTINGS must be filled with the function pointer to call
+ the TDBCONTACTENUMSETTINGS.lParam value to pass to it each time,
+ as well as the .szModule under which the contact is valid
+ returns: returns the value of the last call to the enum function, or -1
+ if no settings could be enumerated
+ notes : the szSetting argument passed to the enumeration function is only
+ valid for the duration of that enumeration call,
+ it must be allocated dynamically if it is required after that call frame
+ has returned.
+ Also, deleting settings as they are enumerated has unpredictable results!
+ but writing a new value for a setting is okay.
+ it is unclear how you stop the enumeration once it is started, maybe
+ possible to return -1 to stop it.
+ vesion : only valid for 0.1.0.1+
+ }
+
+type
+
+ TDBSETTINGENUMPROC = function(const szSetting: PChar; lParam: LPARAM): int; cdecl;
+
+ PDBCONTACTENUMSETTINGS = ^TDBCONTACTENUMSETTINGS;
+ TDBCONTACTENUMSETTINGS = record
+ { function pointer to call to start the enum via MS_DB_CONTACT_ENUMSETTINGS }
+ pfnEnumProc: TDBSETTINGENUMPROC;
+ { passed to the above function }
+ lParam: LPARAM;
+ { name of the module to get settings for }
+ szModule: PChar;
+ { not used by us }
+ ofsSettings: DWORD;
+ end;
+
+const
+
+ MS_DB_CONTACT_ENUMSETTINGS = 'DB/Contact/EnumSettings';
+
+ {
+ wParam : 0
+ lParam : 0
+ affect : none
+ returns: Returns the number of contacts in the database for the loaded profile
+ not including the profile user, see notes.
+ notes : the contacts in the database can be read with FindFirst/FindNext
+ }
+ MS_DB_CONTACT_GETCOUNT = 'DB/Contact/GetCount';
+
+ {
+ wParam : 0
+ lParam : 0
+ returns: Returns a handle to the first contact in the database,
+ this handle does not need to be closed, if there are no users
+ NULL(0) is returned.
+ }
+ MS_DB_CONTACT_FINDFIRST = 'DB/Contact/FindFirst';
+
+ {
+ wParam : Contact handle
+ lParam : 0
+ returns: Returns a handle to the next contact after the given contact in
+ wParam, this handle does not neeed to be closed -- may return NULL(0)
+ if the given contact in wParam was the last in the database, or the
+ given contact was invalid
+ }
+ MS_DB_CONTACT_FINDNEXT = 'DB/Contact/FindNext';
+
+ {
+ wParam : Handle of a contact to delete
+ lParam : 0
+ affect : the user by the given handle is deleted from the database, see notes
+ returns: Returns 0 on success or nonzero if the handle was invalid
+ notes : this triggers DB/Contact/Deleted BEFORE it actually deletes the contact
+ all events are also deleted -- other modules may end up with invalid
+ handles because of this, which they should be prepared for.
+ }
+ MS_DB_CONTACT_DELETE = 'DB/Contact/Delete';
+
+ {
+ wParam : 0
+ lParam : 0
+ affects: creates a new contact in the database, they have no settings,
+ settings must be added with MS_DB_CONTACT_WRITESETTING or
+ database helper functions for writing, see notes
+ returns: A handle to a new contact or NULL(0) on failure.
+ notes : triggers the ME_DB_CONTACT_ADDED event just before the service returns
+ }
+ MS_DB_CONTACT_ADD = 'DB/Contact/Add';
+
+
+ {
+ wParam : (HANDLE) hContact
+ lParam : 0
+ affects: Checks the given handle within the database for valid information, for
+ a proper internal header.
+ returns: Returns 1 if the contact handle is valid, 0 if it is not
+ notes : Due to the nature of multiple threading a contact handle can be deleted
+ soon after this service has returned a handle as valid, however it will never point
+ to another contact.
+ }
+ MS_DB_CONTACT_IS = 'DB/Contact/Is';
+
+
+ {
+ wParam : contact handle for events count is needed
+ lParam : 0
+ service: Gets the number of events in the chain belonging to a contact
+ in the databasee.
+ returns: the numbef of events owned by hContact or -1 if hContact
+ is invalid, they can be found with the event/find* servicees
+ }
+ MS_DB_EVENT_GETCOUNT = 'DB/Event/GetCount';
+
+ {
+ wParam : contact handle to add an event for
+ lParam : Pointer to TDBEVENTINFO initialised with data
+ affect : Add's an event to the contact's event list, the TDBEVENTINFO
+ structure should be filled with the event of message -- see notes
+ returns: a handle to a DB event (HDBEVENT), or NULL on error
+ notes : Triggers DB/Event/Added event just before it returns,
+ Events are sorted chronologically as they are entered,
+ so you cannot guarantee that the new hEvent is the last event in the chain,
+ however if a new event is added that has a timestamp less than
+ 90 seconds *before* the event that should be after it,
+ it will be added afterwards, to allow for protocols that only
+ store times to the nearest minute, and slight delays in transports.
+ There are a few predefined eventTypes below for easier compatibility, but
+ modules are free to define their own, beginning at 2000
+ DBEVENTINFO.timestamp is in GMT, as returned by time()
+ }
+
+ DBEF_FIRST = 1; // internally only, do not use
+ DBEF_SENT = 2; // if set, the event was sent by the user, otherwise it was received
+ DBEF_READ = 4; // event has been read by the user -- only needed for history
+
+ EVENTTYPE_MESSAGE = 0;
+ EVENTTYPE_URL = 1;
+ EVENTTYPE_CONTACTS = 2; // v0.1.2.2+
+ EVENTTYPE_ADDED = 1000; // v0.1.1.0+: these used to be module-
+ EVENTTYPE_AUTHREQUEST = 1001; // specific codes, hence the module-
+ EVENTTYPE_FILE = 1002; // specific limit has been raised to 2000
+
+type
+
+ PDBEVENTINFO = ^TDBEVENTINFO;
+ TDBEVENTINFO = record
+ { size of the structure }
+ cbSize: int;
+ { module that 'owns' this event and controls the data format }
+ szModule: PChar;
+ { timestamp in UNIX time }
+ timestamp: DWORD;
+ { the DBEF_* flags above }
+ flags: DWORD;
+ { event type, such as message, can be module defined }
+ eventType: WORD;
+ { size in bytes of pBlob^ }
+ cbBlob: DWORD;
+ { pointer to buffer containing the module defined event data }
+ pBlob: PByte;
+ end;
+
+const
+
+ MS_DB_EVENT_ADD = 'DB/Event/Add';
+
+
+
+ {
+ wParam : Handle to the contact
+ lParam : HDBEVENT handle to delete
+ affects: Removes a single event from the database for the given contact
+ returns: 0 on success, nonzero on failure
+ notes : Triggers DB/Event/Deleted just before the event *is* deleted
+ }
+ MS_DB_EVENT_DELETE = 'DB/Event/Delete';
+
+ {
+ wParam : Handle to DB event
+ lParam : 0
+ returns: Returns the space in bytes requried to store the blob in HDBEVENT
+ given by HDBEVENT(wParam) -- or -1 on error
+ }
+ MS_DB_EVENT_GETBLOBSIZE = 'DB/Event/GetBlobSize';
+
+ {
+ wParam : Handle to a DB event
+ lParam : Pointer to a TDBEVENTINFO structure which must be initialised
+ affects: Returns all the information about an DB event handle to a TDBEVENTINFO
+ structure which must be initalised, DBEI.cbSize, DBEI.pBlob and DBEI.cbSize
+ before calling this service, the size can be assertained with
+ GetBlobSize() service, see notes
+ returns: Returns 0 on success, non zero on failure
+ notes : The correct value dbe.cbBlob can be got using db/event/getblobsize
+ If successful, all the fields of dbe are filled. dbe.cbBlob is set to the
+ actual number of bytes retrieved and put in dbe.pBlob
+ If dbe.cbBlob is too small, dbe.pBlob is filled up to the size of dbe.cbBlob
+ and then dbe.cbBlob is set to the required size of data to go in dbe.pBlob
+ On return, dbe.szModule is a pointer to the database module's
+ own internal list of modules. Look but don't touch.
+ }
+ MS_DB_EVENT_GET = 'DB/Event/Get';
+
+ {
+ wParam : HCONTACT
+ lParam : HDBEVENT
+ affect : Changes the flag for an event to mark it as read
+ Returns: Returns the entire flag DWORD for the event after the change, or -1
+ if HDBEVENT is invalid, see notes
+ notes : This iss one of the database write operations that does not trigger
+ an event, modules should not save flagss states for any length of time.
+ }
+ MS_DB_EVENT_MARKREAD = 'DB/Event/MarkRead';
+
+ {
+ wParam : HDBEVENT
+ lParam : 0
+ Affect : Returns a handle to a contact that owns the HDBEVENT,
+ see notes
+ Returns: Returns a handle if successful or HDBEEVENT(-1) on failure
+ notes : This service is very slow, only use wheen you have no other choice
+ at all.
+ }
+ MS_DB_EVENT_GETCONTACT = 'DB/Event/GetContact';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Retrieves a handlee to the first event in the chain
+ for a HCONTACT
+ returns: Returns a handle, or NULL(0) if HCONTACT is invalid or has
+ no events, events in a chain are sorted chronologically automatically
+ }
+ MS_DB_EVENT_FINDFIRST = 'DB/Event/FindFirst';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Retrieves a handle to the first unreead event in a chain for a HCONTACT
+ see notes
+ Returns: Returns a HDBEVENT handle or NULL(0) if the HCONTACT is invalid
+ or all it's events have beeen read.
+ Notes : Events in a chain are sorted chronologically automatically,
+ but this does not necessarily mean that all events after
+ the first unread are unread too.
+ They should be checked individually with event/findnext and event/get
+ This service is designed for startup, reloading all the events that remained
+ unread from last time
+ }
+ MS_DB_EVENT_FINDFIRSTUNREAD = 'DB/Event/FindFirstUnread';
+
+ {
+ wParam : HCONTACT
+ lParam : 0;
+ Affects: Retrieves a handle to the lasts event in the chain for a HCONTACT
+ Returns: Returns a handle or NULL(0) if HCONTACT is invalid or has no events
+ }
+ MS_DB_EVENT_FINDLAST = 'DB/Event/FindLast';
+
+ {
+ wParam : HDBEVENT
+ lParam : 0
+ Affects: Retrieves a handle to the next event in a chain after HDBEVENT
+ Returns: A handle to the next DB event or NULL(0) if HDBEVENT is invalid
+ or the last event in the chain.
+ }
+ MS_DB_EVENT_FINDNEXT = 'DB/Event/FindNext';
+
+ {
+ wParam : HDBEVENT
+ lParam : 0
+ Affects: Retrieves a handle to the previous event in a chain before HDBEVENT
+ Returns: A handle to the previous HDBEVENT or NULL(0) if HDBEVENT is invalid
+ or is the first event in the chain
+ }
+ MS_DB_EVENT_FINDPREV = 'DB/Event/FindPrev';
+
+
+
+ {
+ wParam : size in bytes of string buffer (including null term)
+ lParam : pointer to string buffer
+ Affect : Scrambles the string buffer in place using a strange encryption algorithm,
+ see notes
+ Returns: Always returns 0
+ notes : this service may be changed at a later date such that it increasess
+ the length of the string
+ }
+ MS_DB_CRYPT_ENCODESTRING = 'DB/Crypt/EncodeString';
+
+ {
+ wParam : size in bytes of string buffer, including null term
+ lParam : pointer to string buffer
+ Affect : Descrambles pszString in-place using the strange encryption algorithm,
+ see notes.
+ Return : Always returns 0
+ notes : Reverses the operation done by MS_DB_CRYPT_ENCODINGSTRING
+ }
+ MS_DB_CRYPT_DECODESTRING = 'DB/Crypt/DecodeString';
+
+
+
+ {
+ wParam : timestamp (DWORD)
+ lParam : 0
+ Affect : Converts a GMT timestap into local time
+ Returns: Returns the converted value, see notes
+ Notes : Timestamps have a zereo at midnight 1/1/1970 GMT, this service
+ converts such a value to be based at midnight 1/1/1970 local time.
+ This service does not use a simple conversion based on the current offset
+ between GMT and local. Rather, it figures out whether daylight savings time
+ would have been in place at the time of the stamp and gives the local time as
+ it would have been at the time and date the stamp contains.
+ }
+ MS_DB_TIME_TIMESTAMPTOLOCAL = 'DB/Time/TimestampToLocal';
+
+ {
+ wParam : timestamp (DWORD)
+ lParam : pointer to initalised DBTIMETOSTRING structure
+ Affect : Converts a GMT timestamp to a customisable local time string
+ see notes
+ Returns: Always returns 0
+ notes : The string is formatted according to thhe current user's locale
+ language and preference --
+
+ .szFormat can have the following special chars :
+ t time without seconds, e.g. hh:mm
+ s time with seconds, e.g. hh:mm:ss
+ m time without minutes e.g. hh
+ d short date, e.g. dd/mm/yyyy
+ D long date, e.g. d mmmm yyyy
+
+ all other characters are copied as is.
+ }
+
+type
+
+ PDBTIMETOSTRING = ^TDBTIMETOSTRING;
+ TDBTIMETOSTRING = record
+ { format string, see above }
+ szFormat: PChar;
+ { pointer to dest buffer to store the result }
+ szDest: PChar;
+ { size of the buffer }
+ cbDest: int;
+ end;
+
+const
+
+ MS_DB_TIME_TIMESTAMPTOSTRING = 'DB/Time/TimestampToString';
+
+
+
+ {
+ wParam : newSetting (BOOLEAN)
+ lParam : 0
+ Affect : Miranda's database is normally protected against corruption by
+ aggressively flushing data to the disk on writes, if you're doing
+ alot of writes e.g. an import plugin, it can sometimes be desirable
+ to switch this feature off to speed up the process, if you do switch
+ it off, you must remember that crashes are far more likely to be
+ catastrophic, so switch it back on at the earliest possible opportunity.
+ if you're doing a lot of setting writes, the flush is already delayed
+ so you need not use this service for that purpose, see notes.
+ Returns: Always returns 0 (successful)
+ notes : This is set to true initally
+ }
+ MS_DB_SETSAFETYMODE = 'DB/SetSafetyMode';
+
+ {
+ wParam : (caller defined data) will be passed to lParam of the call back
+ lParam : function pointer to TDBMODULEENUMPROC
+ Affects: Enumerates the names of all modules that have stored or
+ requested information from the database,
+ the modules are returned in no real order --
+ Writing to the database while module names are being enumerated will cause
+ unpredictable results in the enumeration, but the write will work.
+
+ the enumeration will stop if the callback returns a non zero value.
+
+ Returns: the last return value from the enumeration call back.
+ Notes : This service is only useful for debugging or EnumSettings
+ version: The service registered to enumerate all modules that have touched
+ the database module uses wParam as the lParam cookie value and the lParam
+ value given here is the function pointer -- this is not safe
+ to use before v0.1.2.1 because I don't know if this was done in v0.1.2.1-
+
+ prior to v0.1.2.1 you can not pass a value to the enumeration because
+ of a bug -- which is fixed, but hey :) -- [sam]
+ }
+type
+ TDBMODULEENUMPROC = function(const szModule: PChar; ofsModuleName: DWORD; lParam: LPARAM): int; cdecl;
+const
+ MS_DB_MODULES_ENUM = 'DB/Modules/Enum';
+
+
+
+ {
+ wParam : HCONTACT
+ lParam : HDBCONTACT
+ Affect : Called when a new event has been added to the event chain
+ for a contact, HCONTACT contains the contact who added the event,
+ HDBCONTACT a handle to what was added.
+ see notes
+ notes : since events are sorted chronologically, you can not guarantee
+ that HDBEVEnT is in any particular position in the chain.
+
+ }
+ ME_DB_EVENT_ADDED = 'DB/Event/Added';
+
+ {
+ wParam : HANDLE (hContact)
+ lParam : @DBEVENTINFO
+ Affects: Hook is fired before any DBEVENTS are created within the database for
+ a contact (or a user, if hContact is NULL(0)) - It allows a module to
+ query/change DBEVENTINFO before it is created, see notes.
+ Returns: Hook should return 1 to stop event being added (will stop other hooks seeing the event too)
+ Or 0 to continue processing (passing the data on as well)
+ Notes : This hook is fired for all event types, and the BLOBS that the eventypes mark
+ Maybe changed, therefore be careful about using BLOB formats.
+ Because the memory pointing within the DBEVENTINFO CAN NOT BE OWNED or free()'d
+ it is recommended that the hook only be used to stop events.
+ Version: 0.3.3a+ (2003/12/03)
+ }
+ ME_DB_EVENT_FILTER_ADD = 'DB/Event/FilterAdd';
+
+ {
+ wParam : HCONTACT
+ lParam : HDBEVENT
+ Affect : Called when an event is about to be deleted from the event chain
+ for a contact, see notes
+ notes : Returning non zero from your hook will NOT stop the deletion,
+ but it will as usual stop other hooks being called
+ }
+ ME_DB_EVENT_DELETED = 'DB/Event/Deleted';
+
+
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Called when a new contact has been added to the database,
+ HCONTACT contains a handle to the new contact.
+ }
+ ME_DB_CONTACT_ADDED = 'DB/Contact/Added';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Called when a contact is about to be deleted
+ Returns: Returning nonzero from your hook will not stop the deletion
+ but it will stop the other hooks from being called
+ }
+ ME_DB_CONTACT_DELETED = 'DB/Contact/Deleted';
+
+ {
+ wParam : HCONTACT
+ lParam : Pointer to a TDBCONTACTWRITESETTING
+ Affect : Calleed when a contact has one of it's settings changed
+ hContact is a valid handle to the contact that has changed,
+ see notes.
+ notes : this event will be triggered many times rapidly when alot of values
+ are set.
+ Modules that hook this should be aware of this fact and quickly
+ return if they are not interested in the value that has changed.
+ Careful not to get into infinite loops with this event,
+
+ The TDBCONTACTWRITESETTING pointer is the same one as the
+ original service all, so don't change any of it's fields
+ }
+ ME_DB_CONTACT_SETTINGCHANGED = 'DB/Contact/SettingChanged';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_email.inc b/plugins/ShlExt/inc/m_email.inc
new file mode 100644
index 0000000000..93312c7e59
--- /dev/null
+++ b/plugins/ShlExt/inc/m_email.inc
@@ -0,0 +1,39 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+{$IFNDEF M_EMAIL}
+{$DEFINE M_EMAIL}
+
+const
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affects: Send an e-mail to the specified contact, see notes
+ Returns: Returns 0 on success or nonzero on failure
+ Notes : If an error occurs the service displays a message box
+ with the error text -- use this service to alter this
+ }
+ MS_EMAIL_SENDEMAIL = 'SREMail/SendCommand';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_file.inc b/plugins/ShlExt/inc/m_file.inc
new file mode 100644
index 0000000000..dd14286455
--- /dev/null
+++ b/plugins/ShlExt/inc/m_file.inc
@@ -0,0 +1,66 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_FILE}
+{$DEFINE M_FILE}
+
+const
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affects: Brings up the send file dialog for a contact, see notes
+ Returns: 0 on success [non zero] on failure
+ Notes : Returns immediately without waiting for the send
+ }
+ MS_FILE_SENDFILE = 'SRFile/SendCommand';
+
+ {
+ wParam : HCONTACT
+ lParam : pointer to an array of PChar's the first nil item
+ terminates the list -- see notes
+ Affects: Brings up the send file dialog with specifieed files already chosen
+ the user is not prevented from editing the list --
+ Returns: 0 on success [non zero] on failure -- returns immediately without
+ waiting for the send to finish
+ Notes : both directories and files can be given
+ Version: v0.1.2.1+
+ }
+ MS_FILE_SENDSPECIFICFILES = 'SRFile/SendSpecificFiles';
+
+ {
+ wParam : HCONTACT
+ lParam : Pointer to a buffer
+ Affects: returns the received files folder for a contact, the buffer
+ should be at least MAX_PATH long (defined with WinAPI),
+ the returned path may not exist -- see notes
+ Returns: Returns 0 on success [non zero] on failure
+ notes : If HCONTACT is NULL(0) the path returned is the path
+ without the postfix contact name.
+ Version: v0.1.2.2+
+ }
+ MS_FILE_GETRECEIVEDFILESFOLDER = 'SRFile/GetReceivedFilesFolder';
+
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_findadd.inc b/plugins/ShlExt/inc/m_findadd.inc
new file mode 100644
index 0000000000..61b0d066b1
--- /dev/null
+++ b/plugins/ShlExt/inc/m_findadd.inc
@@ -0,0 +1,38 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+{$IFNDEF M_FINDADD}
+{$DEFINE M_FINDADD}
+
+const
+
+ {
+ wParam : 0
+ lParam : 0
+ Affects: Openss the find/add users dialog box, or gives it focus if it's
+ already open.
+ Returns: Always returns 0
+ }
+ MS_FINDADDFINDADD = 'FindAdd/FindAddCommand';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_globaldefs.pas b/plugins/ShlExt/inc/m_globaldefs.pas
new file mode 100644
index 0000000000..614719a68c
--- /dev/null
+++ b/plugins/ShlExt/inc/m_globaldefs.pas
@@ -0,0 +1,99 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFDEF FPC}
+ {$PACKRECORDS C}
+ {$MODE Delphi}
+{$ENDIF}
+
+unit m_globaldefs;
+
+interface
+
+uses
+
+{$ifdef FPC}
+ strings;
+{$else}
+ Windows;
+{$endif}
+
+type
+
+ PByte = ^Byte;
+ int = Integer;
+ pint = ^int;
+ WPARAM = Integer;
+ LPARAM = Integer;
+ DWORD = Integer;
+ THandle = Integer;
+
+ // strcpy()
+
+ {$ifdef FPC}
+ TStrCpy = function(Dst, Src: PChar): PChar;
+ {$else}
+ TStrCpy = function(Dst, Src: PChar): PChar; stdcall;
+ {$endif}
+
+ // strcat()
+
+ {$ifdef FPC}
+ TStrCat = function(Dst, Src: PChar): PChar;
+ {$else}
+ TStrCat = function(Dst, Src: PChar): PChar; stdcall;
+ {$endif}
+
+const
+
+ {$ifdef FPC}
+ strcpy: TStrCpy = strings.strcopy;
+ {$else}
+ strcpy: TStrCpy = lstrcpy;
+ {$endif}
+
+ {$ifdef FPC}
+ strcat: TStrCat = strings.strcat;
+ {$else}
+ strcat: TStrCat = lstrcat;
+ {$endif}
+
+ {$include newpluginapi.inc}
+ {$include m_v8.inc}
+
+var
+ { this is now a pointer to a record of function pointers to match the C API,
+ and to break old code and annoy you. }
+
+ PLUGINLINK: PPLUGINLINK;
+
+ { has to be returned via MirandaPluginInfo and has to be statically allocated,
+ this means only one module can return info, you shouldn't be merging them anyway! }
+
+ PLUGININFO: TPLUGININFO;
+ PLUGININFOEX: TPLUGININFOEX;
+
+implementation
+
+end.
diff --git a/plugins/ShlExt/inc/m_globaldefs.ppu b/plugins/ShlExt/inc/m_globaldefs.ppu
new file mode 100644
index 0000000000..dff0527ff7
--- /dev/null
+++ b/plugins/ShlExt/inc/m_globaldefs.ppu
Binary files differ
diff --git a/plugins/ShlExt/inc/m_helpers.inc b/plugins/ShlExt/inc/m_helpers.inc
new file mode 100644
index 0000000000..ef8edeb088
--- /dev/null
+++ b/plugins/ShlExt/inc/m_helpers.inc
@@ -0,0 +1,613 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$ifdef M_API_UNIT}
+
+ function PLUGIN_MAKE_VERSION(a,b,c,d: Cardinal): int;
+ function PLUGIN_CMP_VERSION(verA: LongInt; verB: LongInt): int;
+
+{$else}
+
+ function PLUGIN_MAKE_VERSION(a,b,c,d: Cardinal): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := (a shl 24) or (b shl 16) or (c shl 8) or d;
+ end;
+
+ function PLUGIN_CMP_VERSION(verA: LongInt; verB: LongInt): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := 0;
+ { could be used to compare for severity of age for positive values, if a<b
+ results are minus values, 0 for equal, positive if a is newer }
+ Inc(Result, (verA and $FF) - (verB and $FF));
+ Inc(Result, (verA and $FF00) - (verB and $FF00));
+ Inc(Result, (verA and $FF0000) - (verB and $FF0000));
+ Inc(Result, (verA and $FF000000) - (verB and $FF000000));
+ end;
+
+{$endif}
+
+{$ifdef M_SYSTEM}
+ {$ifdef M_API_UNIT}
+
+ function CallService(const szService: PChar; wParam: WPARAM; lParam: LPARAM): int;
+
+ function HookEvent(const szHook: PChar; hook_proc: TMIRANDAHOOK): int;
+
+ function CreateServiceFunction(const szName: PChar; const MirandaService: TMIRANDASERVICE): int;
+
+ {$else}
+
+ function CallService(const szService: PChar; wParam: WPARAM; lParam: lParam): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := PluginLink^.CallService(szService, wParam, lParam);
+ end;
+
+ function HookEvent(const szHook: PChar; hook_proc: TMIRANDAHOOK): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := PluginLink^.HookEvent(szHook, @hook_proc);
+ end;
+
+ function CreateServiceFunction(const szName: PChar; const MirandaService: TMIRANDASERVICE): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := PluginLink^.CreateServiceFunction(szName, @MirandaService);
+ end;
+
+ {$endif}
+
+{$endif}
+
+{$ifdef M_DATABASE}
+
+ {$ifdef M_API_UNIT}
+
+ function DBGetContactSettingByte(hContact: THandle;
+ const szModule: PChar; const szSetting: PChar; errorValue: Integer): Integer;
+
+ function DBGetContactSettingWord(hContact: THandle;
+ const szModule: PChar; const szSetting: PChar; errorValue: Integer): Integer;
+
+ function DBGetContactSettingDword(hContact: THandle;
+ const szModule: PChar; const szSetting: PChar; errorValue: Integer): Integer;
+
+ function DBGetContactSetting(hContact: THandle;
+ const szModule: PChar; const szSetting: PChar; dbv: PDBVARIANT): Integer;
+
+ function DBFreeVariant(dbv: PDBVARIANT): Integer;
+
+ function DBDeleteContactSetting(hContact: THandle; const szModule: PChar; const szSetting: PChar): Integer;
+
+ function DBWriteContactSettingByte(hContact: THandle; const szModule: PChar; const szSetting: PChar; val: Byte): Integer;
+
+ function DBWriteContactSettingWord(hContact: THandle; const szModule: PChar; const szSetting: PChar; val: Word): Integer;
+
+ function DBWriteContactSettingDWord(hContact: THandle; const szModule: PChar; const szSetting: PChar; val: LongInt): Integer;
+
+ function DBWriteContactSettingString(hContact: THandle; const szModule: PChar; const szSetting: PChar; const val: PChar): Integer;
+
+ {$else}
+
+ function DBGetContactSettingByte(hContact: THandle;
+ const szModule: PChar; const szSetting: PChar; errorValue: Integer): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ dbv: TDBVARIANT;
+ cgs: TDBCONTACTGETSETTING;
+ begin
+
+ cgs.szModule := szModule;
+ cgs.szSetting := szSetting;
+ cgs.pValue := @dbv;
+
+ If PluginLink^.CallService(MS_DB_CONTACT_GETSETTING, hContact, lParam(@cgs)) <> 0 then
+ Result := ErrorValue
+ else
+ Result := dbv.bVal;
+ end;
+
+ function DBGetContactSettingWord(hContact: THandle;
+ const szModule: PChar; const szSetting: PChar; errorValue: Integer): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ dbv: TDBVARIANT;
+ cgs: TDBCONTACTGETSETTING;
+ begin
+ cgs.szModule := szModule;
+ cgs.szSetting := szSetting;
+ cgs.pValue := @dbv;
+ If PluginLink^.CallService(MS_DB_CONTACT_GETSETTING, hContact, lParam(@cgs)) <> 0 then
+ Result := ErrorValue
+ else
+ Result := dbv.wVal;
+ end;
+
+ function DBGetContactSettingDword(hContact: THandle;
+ const szModule: PChar; const szSetting: PChar; errorValue: Integer): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ dbv: TDBVARIANT;
+ cgs: TDBCONTACTGETSETTING;
+ begin
+ cgs.szModule := szModule;
+ cgs.szSetting := szSetting;
+ cgs.pValue := @dbv;
+ If PluginLink^.CallService(MS_DB_CONTACT_GETSETTING, hContact, lParam(@cgs)) <> 0 then
+ Result := ErrorValue
+ else
+ Result := dbv.dVal;
+ end;
+
+ function DBGetContactSetting(hContact: THandle;
+ const szModule: PChar; const szSetting: PChar; dbv: PDBVARIANT): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ cgs: TDBCONTACTGETSETTING;
+ begin
+ cgs.szModule := szModule;
+ cgs.szSetting := szSetting;
+ cgs.pValue := dbv;
+ Result := PluginLink^.CallService(MS_DB_CONTACT_GETSETTING, hContact, lParam(@cgs));
+ end;
+
+ function DBFreeVariant(dbv: PDBVARIANT): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := PluginLink^.CallService(MS_DB_CONTACT_FREEVARIANT, 0, lParam(dbv));
+ end;
+
+ function DBDeleteContactSetting(hContact: THandle; const szModule: PChar; const szSetting: PChar): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ cgs: TDBCONTACTGETSETTING;
+ begin
+ cgs.szModule := szModule;
+ cgs.szSetting := szSetting;
+ Result := PluginLink^.CallService(MS_DB_CONTACT_DELETESETTING, hContact, lParam(@cgs));
+ end;
+
+ function DBWriteContactSettingByte(hContact: THandle; const szModule: PChar; const szSetting: PChar; val: Byte): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ cws: TDBCONTACTWRITESETTING;
+ begin
+ cws.szModule := szModule;
+ cws.szSetting := szSetting;
+ cws.value.type_ := DBVT_BYTE;
+ cws.value.bVal := Val;
+ Result := PluginLink^.CallService(MS_DB_CONTACT_WRITESETTING, hContact, lParam(@cws));
+ end;
+
+ function DBWriteContactSettingWord(hContact: THandle; const szModule: PChar; const szSetting: PChar; val: Word): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ cws: TDBCONTACTWRITESETTING;
+ begin
+ cws.szModule := szModule;
+ cws.szSetting := szSetting;
+ cws.value.type_ := DBVT_WORD;
+ cws.value.wVal := Val;
+ Result := PluginLink^.CallService(MS_DB_CONTACT_WRITESETTING, hContact, lParam(@cws));
+ end;
+
+ function DBWriteContactSettingDWord(hContact: THandle; const szModule: PChar; const szSetting: PChar; val: LongInt): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ cws: TDBCONTACTWRITESETTING;
+ begin
+ cws.szModule := szModule;
+ cws.szSetting := szSetting;
+ cws.value.type_ := DBVT_DWORD;
+ cws.value.dVal := Val;
+ Result := PluginLink^.CallService(MS_DB_CONTACT_WRITESETTING, hContact, lParam(@cws));
+ end;
+
+ function DBWriteContactSettingString(hContact: THandle; const szModule: PChar; const szSetting: PChar; const val: PChar): Integer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ cws: TDBCONTACTWRITESETTING;
+ begin
+ cws.szModule := szModule;
+ cws.szSetting := szSetting;
+ cws.value.type_ := DBVT_ASCIIZ;
+ cws.value.pszVal := Val;
+ Result := PluginLink^.CallService(MS_DB_CONTACT_WRITESETTING, hContact, lParam(@cws));
+ end;
+
+ {$endif}
+
+{$endif}
+
+{$ifdef M_NETLIB}
+
+ {$ifdef M_API_UNIT}
+
+ function Netlib_CloseHandle(Handle: THandle): int;
+
+ function Netlib_GetBase64DecodedBufferSize(const cchEncoded: int): int;
+
+ function Netlib_GetBase64EncodedBufferSize(const cbDecoded: int): int;
+
+ function Netlib_Send(hConn: THandle; const buf: PChar; len: int; flags: int): int;
+
+ function Netlib_Recv(hConn: THandle; const buf: PChar; len: int; flags: int): int;
+
+ procedure Netlib_Log(hNetLib: THandle; const sz: PChar);
+
+ {$else}
+
+ function Netlib_CloseHandle(Handle: THandle): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := PluginLink^.CallService(MS_NETLIB_CLOSEHANDLE, Handle, 0);
+ end;
+
+ function Netlib_GetBase64DecodedBufferSize(const cchEncoded: int): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := (cchEncoded shr 2) * 3;
+ end;
+
+ function Netlib_GetBase64EncodedBufferSize(const cbDecoded: int): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := (cbDecoded * 4+11) div 12*4+1;
+ end;
+
+ function Netlib_Send(hConn: THandle; const buf: PChar; len: int; flags: int): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ nlb: TNETLIBBUFFER;
+ begin
+ nlb.buf := buf;
+ nlb.len := len;
+ nlb.flags := flags;
+ Result := PluginLink^.CallService(MS_NETLIB_SEND, wParam(hConn), lParam(@nlb));
+ end;
+
+ function Netlib_Recv(hConn: THandle; const buf: PChar; len: int; flags: int): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ nlb: TNETLIBBUFFER;
+ begin
+ nlb.buf := buf;
+ nlb.len := len;
+ nlb.flags := flags;
+ Result := PluginLink^.CallService(MS_NETLIB_RECV, wParam(hConn), lParam(@nlb));
+ end;
+
+ procedure Netlib_Log(hNetLib: THandle; const sz: PChar);
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ PluginLink^.CallService(MS_NETLIB_LOG, hNetLib, lParam(sz));
+ end;
+
+ {$endif}
+
+{$endif}
+
+{$ifdef M_UTILS}
+
+ {$ifdef M_API_UNIT}
+
+ function WindowList_Add(hList: THandle; hWnd: HWND; hContact: THandle): int;
+
+ function WindowList_Remove(hList: THandle; hWnd: THandle): int;
+
+ function WindowList_Find(hList: THandle; hContact: THandle): int;
+
+ function WindowList_Broadcast(hList: THandle; message: int; wParam: WPARAM; lParam: LPARAM): int;
+
+ function Utils_SaveWindowPosition(hWnd: THandle; hContact: THandle; const szModule, szNamePrefix: PChar): int;
+
+ function Utils_RestoreWindowPosition(hWnd: THandle; hContact: THandle; Flags: int; const szModule, szNamePrefix: PChar): int;
+
+ {$else}
+
+ function WindowList_Add(hList: THandle; hWnd: hWnd; hContact: THandle): int;
+ var
+ wle: TWINDOWLISTENTRY;
+ begin
+ wle.hList := hList;
+ wle.hWnd := hWnd;
+ wle.hContact := hContact;
+ Result := PluginLink^.CallService(MS_UTILS_ADDTOWINDOWLIST, 0, lParam(@wle));
+ end;
+
+ function WindowList_Remove(hList: THandle; hWnd: THandle): int;
+ begin
+ Result := PluginLink^.CallService(MS_UTILS_REMOVEFROMWINDOWLIST, hList, hWnd);
+ end;
+
+ function WindowList_Find(hList: THandle; hContact: THandle): int;
+ begin
+ Result := PluginLink^.CallService(MS_UTILS_FINDWINDOWINLIST, hList, hContact);
+ end;
+
+ function WindowList_Broadcast(hList: THandle; message: int; wParam: WPARAM; lParam: LPARAM): int;
+ var
+ msg: TMSG;
+ begin
+ msg.message := message;
+ msg.wParam := wParam;
+ msg.lParam := lParam;
+ Result := PluginLink^.CallService(MS_UTILS_BROADCASTTOWINDOWLIST, hList, Integer(@Msg));
+ end;
+
+ function Utils_SaveWindowPosition(hWnd: THandle; hContact: THandle; const szModule, szNamePrefix: PChar): int;
+ var
+ swp: TSAVEWINDOWPOS;
+ begin
+ swp.hWnd := hWnd;
+ swp.hContact := hContact;
+ swp.szModule := szModule;
+ swp.szNamePrefix := szNamePrefix;
+ Result := PluginLink^.CallService(MS_UTILS_SAVEWINDOWPOSITION, 0, lParam(@swp));
+ end;
+
+ function Utils_RestoreWindowPosition(hWnd: THandle; hContact: THandle; Flags: int; const szModule, szNamePrefix: PChar): int;
+ var
+ swp: TSAVEWINDOWPOS;
+ begin
+ swp.hWnd := hWnd;
+ swp.hContact := hContact;
+ swp.szModule := szModule;
+ swp.szNamePrefix := szNamePrefix;
+ Result := PluginLink^.CallService(MS_UTILS_RESTOREWINDOWPOSITION, Flags, lParam(@swp));
+ end;
+
+ {$endif}
+
+{$endif}
+
+{$ifdef M_LANGPACK}
+
+ {$ifdef M_API_UNIT}
+
+ function Translate(sz: PChar): PChar;
+
+ function TranslateString(sz: string): string;
+
+ function TranslateDialogDefault(hwndDlg: THandle): int;
+
+ {$else}
+
+ function Translate(sz: PChar): PChar;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ { the return value maybe NULL(0) -- it's upto the caller to know if the allocated
+ string has to be removed from the DLL heap, this has little to do with Miranda,
+ but if a dynamic string is passed and a return string is used -- the dynamic
+ string is lost -- be careful, lazy? use TranslateString (note it's slower) }
+ Result := PChar(PluginLink^.CallService(MS_LANGPACK_TRANSLATESTRING, 0, lParam(sz)));
+ end;
+
+ function TranslateString(sz: string): string;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := string(PChar( PluginLink^.CallService(MS_LANGPACK_TRANSLATESTRING, 0, lParam(sz))));
+ end;
+
+ function TranslateDialogDefault(hwndDlg: THandle): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ lptd: TLANGPACKTRANSLATEDIALOG;
+ begin
+ lptd.cbSize := sizeof(lptd);
+ lptd.flags := 0;
+ lptd.hwndDlg := hwndDlg;
+ lptd.ignoreControls := nil;
+ Result := PluginLink^.CallService(MS_LANGPACK_TRANSLATEDIALOG, 0, lParam(@lptd));
+ end;
+
+ {$endif}
+
+{$endif}
+
+{$ifdef M_PROTOCOLS}
+ {$ifdef M_API_UNIT}
+
+ function CallContactService(hContact: THandle; const szProtoService: PChar; wParam: WPARAM; lParam: LPARAM): int;
+
+ function CallProtoService(const szModule, szService: PChar; wParam: WPARAM; lParam: LPARAM): int;
+
+ {$else}
+
+ function CallContactService(hContact: THandle; const szProtoService: PChar; wParam: WPARAM; lParam: LPARAM): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ css: TCCSDATA;
+ begin
+ css.hContact := hContact;
+ css.szProtoService := szProtoService;
+ css.wParam := wParam;
+ css.lParam := lParam;
+ Result := PluginLink^.CallService(MS_PROTO_CALLCONTACTSERVICE, 0, Integer(@css));
+ end;
+
+ function CallProtoService(const szModule, szService: PChar; wParam: WPARAM; lParam: LPARAM): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ szStr: array[0..MAXMODULELABELLENGTH] of Char;
+ begin
+ strcpy(szStr, szModule);
+ strcat(szStr, szService);
+ Result := PluginLink^.CallService(szStr, wParam, lParam);
+ end;
+
+ {$endif}
+{$endif}
+
+{$ifdef M_PROTOMOD}
+ {$ifdef M_API_UNIT}
+
+ function ProtoBroadcastAck(const szModule: PChar; hContact: THandle; type_: int; result_: int; hProcess: THandle; lParam: LPARAM): int;
+
+ function CreateProtoServiceFunction(const szModule, szService: PChar; serviceProc: TMIRANDASERVICE): int;
+
+ {$else}
+
+ function ProtoBroadcastAck(const szModule: PChar; hContact: THandle; type_: int; result_: int; hProcess: THandle; lParam: LPARAM): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ ack: TACKDATA;
+ begin
+ ack.cbSize := sizeof(TACKDATA);
+ ack.szModule := szModule;
+ ack.hContact := hContact;
+ ack.type_ := type_;
+ ack.result_ := result_;
+ ack.hProcess := hProcess;
+ ack.lParam := lParam;
+ Result := PluginLink^.CallService(MS_PROTO_BROADCASTACK, 0, Integer(@ack));
+ end;
+
+ function CreateProtoServiceFunction(const szModule, szService: PChar; serviceProc: TMIRANDASERVICE): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ szStr: array[0..MAXMODULELABELLENGTH] of Char;
+ begin
+ strcpy(szStr, szModule);
+ strcat(szStr, szService);
+ Result := PluginLink^.CreateServiceFunction(szStr, @serviceProc);
+ end;
+
+ {$endif}
+
+{$endif}
+
+{$ifdef M_SKIN}
+
+ {$ifdef M_API_UNIT}
+
+ function LoadSkinnedIcon(id: int): THandle;
+
+ function LoadSkinnedProtoIcon(const szProto: PChar; status: int): THandle;
+
+ function SkinAddNewSound(const name, description, defaultFile: PChar): int;
+
+ function SkinPlaySound (const name: PChar): int;
+
+ {$else}
+
+ function LoadSkinnedIcon(id: int): THandle;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := PluginLink^.CallService(MS_SKIN_LOADICON, id, 0);
+ end;
+
+ function LoadSkinnedProtoIcon(const szProto: PChar; status: int): THandle;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := PluginLink^.CallService(MS_SKIN_LOADPROTOICON, wParam(szProto), status);
+ end;
+
+ function SkinAddNewSound(const name, description, defaultFile: PChar): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ ssd: TSKINSOUNDDESC;
+ begin
+ ssd.cbSize := sizeof(ssd);
+ ssd.pszName := name;
+ ssd.pszDescription := description;
+ ssd.pszDefaultFile := defaultFile;
+ Result := PluginLink^.CallService(MS_SKIN_ADDNEWSOUND, 0, lParam(@ssd));
+ end;
+
+ function SkinPlaySound (const name: PChar): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := PluginLink^.CallService(MS_SKIN_PLAYSOUND, 0, lParam(name));
+ end;
+
+ {$endif}
+
+{$endif}
diff --git a/plugins/ShlExt/inc/m_history.inc b/plugins/ShlExt/inc/m_history.inc
new file mode 100644
index 0000000000..325afad909
--- /dev/null
+++ b/plugins/ShlExt/inc/m_history.inc
@@ -0,0 +1,37 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+{$IFNDEF M_HISTORY}
+{$DEFINE M_HISTORY}
+
+const
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affects: Show's the history dialog box for a contact, see notes
+ Notes : HCONTACT can be NULL(0) to show system messages
+ }
+ MS_HISTORY_SHOWCONTACTHISTORY = 'History/ShowContactHistory';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_icq.inc b/plugins/ShlExt/inc/m_icq.inc
new file mode 100644
index 0000000000..d359eae928
--- /dev/null
+++ b/plugins/ShlExt/inc/m_icq.inc
@@ -0,0 +1,191 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_ICQ}
+{$DEFINE M_ICQ}
+
+const
+
+ // extra database event type
+ ICQEVENTTYPE_WEBPAGER = 2003;
+
+ // extra flags for PSS_MESSAGE
+ PIMF_ROUTE_DEFAULT = 0;
+ PIMF_ROUTE_DIRECT = $10000;
+ PIMF_ROUTE_THRUSERVER = $20000;
+ PIMF_ROUTE_BESTWAY = $30000;
+ PIMF_ROUTE_MASK = $30000;
+
+ // for SMS
+
+ ICQACKTYPE_SMS = 1001;
+ ICQEVENTTYPE_SMS = 2001; // database event type
+
+ // for e-mail express
+
+ {
+ BLOB:
+ text: ASCIIZ usually in the form "Subject: %s\r\n%s"
+ from-name: ASCIIZ
+ from-e-mail: ASCIIZ
+ }
+
+ ICQEVENTTYPE_EMAILEXPRESS = 2002;
+
+ // for server side lists, used internally only
+
+ // hProcess=dwSequence, lParam=server's error code, 0 for success
+ ICQACKTYPE_SERVERCLIST = 1003;
+
+{$ifndef m_protosvc}
+ {$include m_protosvc.inc}
+{$endif}
+
+type
+
+ PICQSEARCHRESULT = ^TICQSEARCHRESULT;
+ TICQSEARCHRESULT = record
+ hdr: TPROTOSEARCHRESULT;
+ uin: DWORD;
+ auth: Byte;
+ end;
+
+ PICQDETAILSSEARCH = ^TICQDETAILSSEARCH;
+ TICQDETAILSSEARCH = record
+ nick: PChar;
+ firstName: PChar;
+ lastNamee: PChar;
+ end;
+
+const
+
+ {
+ wParam : 0
+ lParam : null terminated string containing e-mail to search
+ affects: Start a search for all ICQ users by e-mail -- see notes
+ returns: Returnss a handle to the search on success, NULL(0) on failure
+ notes : uses the same scheme as PSS_BASICSEARCH,
+ *DEPRECATED* in favour of PS_SEARCHBYEMAIL
+ }
+ MS_ICQ_SEARCHBYEMAIL = 'ICQ/SearchByEmail';
+
+ {
+ wParam : 0
+ lParam : POinter to a TICQDETAILSSEARCH structure
+ Affect : Start a search of all ICQ users by details, see notes
+ Returns: A handle to the search on success, NULL(0) on failure
+ Notes : Results are returned in the same scheme as in PSS_BASICSEARCH,
+ Not recommended, use PS_SEARCHBYNAME
+ }
+ MS_ICQ_SEARCHBYDETAILS = 'ICQ/SearchByDetails';
+
+ {
+ wParam : Pointer to a null terminated string containing phone number
+ lParam : Pointer to a null terminated string containing the message
+ Affect : Send an SMS via the ICQ network, See notes
+ Returns: Handle to the send on success, NULL(0) on failure
+ Notes : the phone number should be the full number with internation code
+ and prefixed by + e.g. +44<numba>
+ }
+ MS_ICQ_SENDSMS = 'ICQ/SendSMS';
+
+ {
+ wParam : level
+ lParam : null terminated string containing logging message
+ Affect : a logging message was sent from ICQLib
+ }
+ ME_ICQ_LOG = 'ICQ/Log';
+
+{$ENDIF}
+
+ {$ifdef __}
+//Changing user info:
+//See documentation of PS_CHANGEINFO
+//The changing user info stuff built into the protocol is purposely extremely
+//thin, to the extent that your data is passed as-is to the server without
+//verification. Don't mess up.
+//Everything is byte-aligned
+//WORD: 2 bytes, little-endian (that's x86 order)
+//DWORD: 4 bytes, little-endian
+//LNTS: a WORD containing the length of the string, followed by the string
+// itself. No zero terminator.
+#define ICQCHANGEINFO_MAIN 0xEA03
+/* pInfoData points to:
+ WORD datalen
+ LNTS nick
+ LNTS first
+ LNTS last
+ LNTS email
+ LNTS city
+ LNTS state
+ LNTS phone
+ LNTS fax
+ LNTS street
+ LNTS cellular (if SMS-able string contains an ending ' SMS')
+ LNTS zip
+ WORD country
+ BYTE gmt
+ BYTE unknown, usually 0
+*/
+#define ICQCHANGEINFO_MORE 0xFD03
+/* pInfoData points to:
+ WORD datalen
+ BYTE age
+ BYTE 0
+ BYTE sex
+ LNTS homepage
+ WORD birth-year
+ BYTE birth-month
+ BYTE birth-day
+ BYTE lang1
+ BYTE lang2
+ BYTE lang3
+*/
+#define ICQCHANGEINFO_ABOUT 0x0604
+/* pInfoData points to:
+ WORD datalen
+ LNTS about
+*/
+#define ICQCHANGEINFO_WORK 0xF303
+/* pInfoData points to:
+ WORD datalen
+ LNTS city
+ LNTS state
+ DWORD 0
+ LNTS street
+ LNTS zip
+ WORD country
+ LNTS company-name
+ LNTS company-dept
+ LNTS company-position
+ WORD 0
+ LNTS company-web
+*/
+#define ICQCHANGEINFO_PASSWORD 0x2E04
+/* pInfoData points to:
+ WORD datalen
+ LNTS newpassword
+*/
+ {$endif}
+
diff --git a/plugins/ShlExt/inc/m_ignore.inc b/plugins/ShlExt/inc/m_ignore.inc
new file mode 100644
index 0000000000..e4c9e3c062
--- /dev/null
+++ b/plugins/ShlExt/inc/m_ignore.inc
@@ -0,0 +1,74 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+{$IFNDEF M_IGNORE}
+{$DEFINE M_IGNORE}
+
+ { this module only provides UI and storage for blocking only, protocol modules
+ are responsible for implementing the block }
+
+const
+
+ IGNOREEVENT_ALL = LPARAM(-1);
+ IGNOREEVENT_MESSAGE = 1;
+ IGNOREEVENT_URL = 2;
+ IGNOREEVENT_FILE = 3;
+ IGNOREEVENT_USERONLINE = 4;
+ IGNOREEVENT_AUTHORIZATION=5;
+ IGNOREEVENT_YOUWEREADDED=6; // 0.3.3a+
+
+ {
+ wParam : HCONTACT
+ lParam : IGNOREEVENT_*
+ Affects: Determines if a message type to a contact should be ignored, see notes
+ Returns: 0 if the message type MUST be shown [non zero] if it MUST be ignored
+ Notes : HCONTACT can be NULL(0) to see what to do with a contact
+ that isn't on the list (or is unknown in some way)
+ don't use the IGNOREEVENT_ALL type!
+ Version: v0.1.0.1+
+ }
+ MS_IGNORE_ISIGNORED = 'Ignore/IsIgnored';
+
+ {
+ wParam : HCONTACT
+ lParam : IGNOREEVENT_* constant
+ Affects: Ignore future messages from a contact, see notes
+ Returns: 0 on success, [nonzero] on failure
+ Notes : wParam: NULL(0) can be used to see if an unknown contact should be ignored
+ or not - you can't SET unknown contact's ignore types, this is to stop
+ a plugin allowing certain functions (I guess)
+ Version: v0.1.0.1+
+ }
+ MS_IGNORE_IGNORE = 'Ignore/Ignore';
+
+ {
+ wParam : HCONTACT
+ lParam : IGNOREEVENT_*
+ Affects: Receive future messages from a contact -- of the given type, see notes
+ Returns: 0 on success, non zero on failure
+ Notes : Use NULL(0) for HCONTACT to retrieve the setting for an unknown contact
+ Version: v0.1.0.1+
+ }
+ MS_IGNORE_UNIGNORE = 'Ignore/Unignore';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_langpack.inc b/plugins/ShlExt/inc/m_langpack.inc
new file mode 100644
index 0000000000..498f844997
--- /dev/null
+++ b/plugins/ShlExt/inc/m_langpack.inc
@@ -0,0 +1,82 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_LANGPACK}
+{$DEFINE M_LANGPACK}
+
+const
+
+ {
+ wParam : 0
+ lParam : pointer to a null terminated string
+ Affects: Returns a pointer to a localised string, if there is no known
+ translation it will return lParam, the return value does *not*
+ have to be freed in anyway (if successful) -- see notes
+ Returns: a pointer to a null terminated string
+ Notes : No check is done to see if Miranda has the required version
+ Version: v0.1.1.0+
+ }
+ MS_LANGPACK_TRANSLATESTRING = 'LangPack/TranslateString';
+
+ {
+ wParam : 0
+ lParam : Pointer to a LANGPACKTRANSLATEDIALOG initialised structure, see notes
+ Affects: Translates a dialog into the user's local language
+ Returns: 0 on successs [non zero] on failure
+ Notes : this service only knows about the following window classes/elements:
+ Window titles, STATIC, EDIT, Hyperlink, BUTTON.
+ Version: v0.1.1.0+
+ }
+
+type
+
+ PLANGPACKTRANSLATEDIALOG = ^TLANGPACKTRANSLATEDIALOG;
+ TLANGPACKTRANSLATEDIALOG = record
+ cbSize: int;
+ flags: DWORD;
+ hwndDlg: THandle;
+ ignoreControls: ^Integer; // pointer to an array of integers? mebbe?
+ end;
+
+const
+
+ { translate all edit controls, by default non-read-only edit controls are not }
+ LPTDF_NOIGNOREEDIT = 1;
+ { don't translate the title of the dialog }
+ LPTDF_NOTITLE = 2;
+
+ MS_LANGPACK_TRANSLATEDIALOG = 'LangPack/TranslateDialog';
+
+ {
+ wParam : HMENU handle (WinAPI handle to a menu)
+ lParam : 0
+ Affects: Translates a menu into the user's local language -- see notes
+ Returns: 0 on success [non zero] on failure
+ Notes : This doesn't work with owner draw menus that store their
+ captions in a structure known by the owner -- something to be aware of ;)
+ version: v0.1.1.0+
+ }
+ MS_LANGPACK_TRANSLATEMENU = 'LangPack/TranslateMenu';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_message.inc b/plugins/ShlExt/inc/m_message.inc
new file mode 100644
index 0000000000..5cee0040b3
--- /dev/null
+++ b/plugins/ShlExt/inc/m_message.inc
@@ -0,0 +1,57 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_MESSAGE}
+{$DEFINE M_MESSAGE}
+
+const
+
+ {
+ wParam : HCONTACT
+ lParam : Pointer to a null terminated string
+ Affects: brings up the send message dialog for a contact, see notes
+ Returns: 0 on success, non zero on failure
+ Notes : returns immediately, just after the send dialog is shown,
+ the lParam is entered into the editbox of the window,
+ but it's not sent.
+ Version: v0.1.2.0+ only supports a string, prior NULL(0) is expected
+ this service was defined as 'SRMsg/LaunchMessageWindow'
+ use both if compatibility use both, the correct one will work,
+ but don't rely on the message to be displayed
+
+ }
+ MS_MSG_SENDMESSAGE = 'SRMsg/SendCommand';
+ MS_MSG_SENDMESSAGE_OLD = 'SRMsg/LaunchMessageWindow';
+
+ {
+ wParam : 0
+ lParam : Pointer to a null termed string
+ Affects: displays the send message dialog with the 'multiple' option open
+ and no contacts selected
+ Returns: Returns 0 on success, nonzero on failure
+ Version: only present after v0.1.2.1+
+ }
+ MS_MSG_FORWARDMESSAGE = 'SRMsg/ForwardMessage';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_netlib.inc b/plugins/ShlExt/inc/m_netlib.inc
new file mode 100644
index 0000000000..11334c9897
--- /dev/null
+++ b/plugins/ShlExt/inc/m_netlib.inc
@@ -0,0 +1,713 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_NETLIB}
+{$DEFINE M_NETLIB}
+
+{>>/
+
+ NetLib :
+
+ Instead of you writing all the code for working with sockets and supporting
+ app level protocols such as SOCKS5, it's all done for you.
+
+ NetLib takes care of all that and you can even register a special abstract
+ nexus, e.g. ICQ direct, the user can configure all this from the options dialog
+ and you don't have to bother with any of it.
+
+ NetLib wraps up any Winsock calls but you can still get the socket handle
+ from your netlib handle and do stuff.
+
+ It gives all modules an abstract way of dealing with transport -- mainly sockets
+ and proxies, Now the but..
+
+ It's new (mmmm) thus unsupported by any older version of Miranda, and if you
+ want to be lazy and not write any "wrapper" mini netlib then you'll have
+ the kudos of "only works with nightly build version of Miranda" :)
+
+/<<}
+
+ {$ifndef M_SYSTEM}
+ {$include m_system.inc}
+ {$endif}
+
+const
+
+ // for TNETLIBUSER.flags
+
+ { bind incoming ports }
+ NUF_INCOMING = $01;
+ { makes outgoing plain connections }
+ NUF_OUTGOING = $02;
+ { can use HTTP gateway for plain sockets. ???HttpGateway* are valid,
+ enables the HTTP proxy option, displayed in options }
+ NUF_HTTPGATEWAY = $04;
+ { don't show this as an entry for custom settings to be defined for,
+ TNETLIB.szDescriptiveName is ignored }
+ NUF_NOOPTIONS = $08;
+ { some connections are made for HTTP communication,
+ enables the HTTP proxy option, displayed in options }
+ NUF_HTTPCONNS = $10;
+ { Disables the HTTPS proxy option in options, Use this if all communication
+ is HTTP }
+ NUF_NOHTTPSOPTION = $20;
+
+ // for TNETLIBUSERSETTINGS.proxyType
+
+ { SOCKS4 -- No DNS or multi addressing mode (proxy side) -- optional username can
+ be given, no password }
+ PROXYTYPE_SOCKS4 = 1;
+ { SOCKS5 -- DNS names can be given as addresses to connect to, optional
+ plain text username/password scheme (which may cause failure due to denied access)
+ IP address maybe returned for DNS addresses -- thus server side DNS }
+ PROXYTYPE_SOCKS5 = 2;
+ PROXYTYPE_HTTP = 3;
+ PROXYTYPE_HTTPS = 4;
+
+ // for TNETLIBOPENCONNECTION.flags
+
+ { this connection will be useed for HTTP communications,
+ if configured for an HTTP(S) proxy the connection is opened as if there
+ was no proxy }
+
+ NLOCF_HTTP = $0001;
+
+ // for TNETLIBHTTPPROXYINFO.flags
+
+ { append sequence numbers to GET requests }
+ NLHPIF_USEGETSEQUENCE = $0001;
+ { append sequence numbers to POST requests }
+ NLHPIF_USEPOSTSEQUENCE = $0002;
+ { GET and POST use the same sequence }
+ NLHPIF_GETPOSTSAMESEQUENCE = $0004;
+
+ // for TNETLIBHTTPREQUEST.flags, .requestType
+
+ { used by MS_NETLIB_RECVHTTPHEADERS returned structure }
+
+ REQUEST_RESPONSE = 0;
+ REQUEST_GET = 1;
+ REQUEST_POST = 2;
+ REQUEST_CONNECT = 3;
+
+ { auto generate a 'host' header from .szUrl }
+ NLHRF_GENERATEHOST = $00000001;
+ { remove any host and/or protocol portion of szUrl before sending it }
+ NLHRF_REMOVEHOST = $00000002;
+ { removes host and/or protocol from szUrl unless the connection was
+ opened through an HTTP or HTTPS proxy. }
+ NLHRF_SMARTREMOVEHOST = $00000004;
+ { if the connection was opened through an HTTP or HTTPS proxy then
+ send a Proxy-Authorization header if required. }
+ NLHRF_SMARTAUTHHEADER = $00000008;
+ { never dump this to the log }
+ NLHRF_NODUMP = $00010000;
+ { don't dump http headers (only useful for POSTs and MS_NETLIB_HTTPTRANSACTION }
+ NLHRF_NODUMPHEADERS = $00020000;
+ { this transaction is a proxy communication. For dump filtering only. }
+ NLHRF_DUMPPROXY = $00040000;
+ { dump posted and reply data as text. Headers are always dumped as text. }
+ NLHRF_DUMPASTEXT = $00080000;
+
+ // for TNETLIBBUFFER.flags
+
+ { don't wrap outgoing packet using TNETLIBUSER.pfnHttpGatewayWrapSend }
+ MSG_NOHTTPGATEWAYWRAP = $010000;
+ { don't dump this packet to the log }
+ MSG_NODUMP = $020000;
+ { this iss proxy communication, for dump filtering only }
+ MSG_DUMPPROXY = $040000;
+ { don't dump as hex, it's text }
+ MSG_DUMPASTEXT = $080000;
+ { send as raw, bybpass HTTP proxy stuff }
+ MSG_RAW = $100000;
+
+
+ // all record types structures are declared in their own block because the C header
+ // file used forward declaration (to get typed parameters for certain function pointers)
+ // This sort of define-type-pointer-before-type can only be done in the same type block
+ // in D2 (don't know about later versions)
+
+type
+
+ { forward typed pointers to records }
+
+ PNETLIBOPENCONNECTION = ^TNETLIBOPENCONNECTION;
+ PNETLIBHTTPREQUEST = ^TNETLIBHTTPREQUEST;
+
+ { This function pointer is to the CRT realloc() used by Miranda -- it allows reallocation of memory passed
+ to us (not that we could EVER share the same CRT) but to allow DLLs in general to reallocate memory }
+ TNetlibRealloc = function(Mem: Pointer; size_t: int): Pointer; cdecl;
+ TNetlibHTTPGatewayInitProc = function(hConn: THandle; nloc: PNETLIBOPENCONNECTION; nlhr: PNETLIBHTTPREQUEST): int; cdecl;
+ TNetlibHTTPGatewayBeginProc = function(hConn: THandle; nloc: PNETLIBOPENCONNECTION): int; cdecl;
+ TNetlibHTTPGatewayWrapSendProc = function(hConn: THandle; buf: PByte; len: int; flags: int; pfnNetLibSend: TMIRANDASERVICE): int; cdecl;
+ TNetlibHTTPGatewayUnwrapRecvProc = function(nlhr: PNETLIBHTTPREQUEST; buf: PByte; len: int; outBufLen: pInt; NetlibRealloc: TNetlibRealloc): PByte; cdecl;
+
+ PNETLIBUSER = ^TNETLIBUSER;
+ TNETLIBUSER = record
+ cbSize: int;
+ { used for DB settings and log, 'NL' stuff }
+ szSettingsModule: PChar;
+ { shows a descriptive name for which different proxy settings can be defined }
+ szDescriptiveName: PChar;
+ { see NUF_* constants above }
+ flags: DWORD;
+ szHttpGatewayHello: PChar;
+ { can be NULL(0) to send no User-Agent: also used by HTTPS proxies }
+ szHttpGatewayUserAgent: PChar;
+ pfnHttpGatewayInit: TNetlibHTTPGatewayInitProc;
+ { can be NULL(0) if no begin is required }
+ pfnHttpGatewayBegin: TNetlibHTTPGatewayBeginProc;
+ { can be NULL(0) if no wrapping is required }
+ pfnHttpGatewayWrapSend: TNetlibHTTPGatewayWrapSendProc;
+ { can be NULL(0) " " }
+ pfnHttpGatewayUnwrapRecv: TNetlibHTTPGatewayUnwrapRecvProc;
+ { only if NUF_INCOMING, will be used for validation of user input }
+ minIncomingPorts: int;
+ end;
+
+ PNETLIBUSERSETTINGS = ^TNETLIBUSERSETTINGS;
+ TNETLIBUSERSETTINGS = record
+ { filled before calling }
+ cbSize: int;
+ { 1 or 0 }
+ useProxy: int;
+ { PROXYTYPE_* constant, see above }
+ proxyType: int;
+ { can be NULL(0) }
+ szProxyServer: PChar;
+ { in host byte order }
+ wProxyPort: int;
+ { 1 or 0, always 0 for SOCKS4 (doesn't have auth) }
+ useProxyAuth: int;
+ { can be NULL(0), always used by SOCKS4 }
+ szProxyAuthUser: PChar;
+ { can be NULL(0) }
+ szProxyAuthPassword: PChar;
+ { 1 or 0, only used by HTTP, HTTPS }
+ useProxyAuthNtlm: int;
+ { 1 or 0 }
+ dnsThroughProxy: int;
+ { 1 or 0 }
+ specifyIncomingPorts: int;
+ { can be NULL(0), form '1024-1050,1060-1070,2000' }
+ szIncomingPorts: PChar;
+ end;
+
+ TNetlibNewConnectionProc = procedure(hNewConnection: THandle; dwRemoveIP: DWORD); cdecl;
+
+ PNETLIBBIND = ^TNETLIBBIND;
+ TNETLIBBIND = record
+ cbSize: int;
+ { function to call when there's a new connection, dwRemoteIP is in host byte
+ order -- the handle is to the new connection }
+ pfnNewConnection: TNetlibNewConnectionProc;
+ { set on return, host byte order }
+ dwInternalIP: DWORD;
+ { set on return, host byte order }
+ wPort: WORD;
+ end;
+
+ { Pointered type is above }
+ TNETLIBOPENCONNECTION = record
+ cbSize: int;
+ szHost: PChar; // can be an IP in string form
+ wPort: Word;
+ flags: DWORD; // see NLOCF_* flags
+ end;
+
+ PNETLIBHTTPPROXYINFO = ^TNETLIBHTTPPROXYINFO;
+ TNETLIBHTTPPROXYINFO = record
+ cbSize: int;
+ { see NLHPIF_* above }
+ flags: DWORD;
+ szHttpPostUrl: PChar;
+ szHttpGetUrl: PChar;
+ firstGetSequence: int;
+ firstPostSequence: int;
+ end;
+
+ PNETLIBBASE64 = ^TNETLIBBASE64;
+ TNETLIBBASE64 = record
+ pszEncoded: PChar;
+ cchEncoded: int;
+ pbDecoded: PByte;
+ cbDecoded: int;
+ end;
+
+ PNETLIBHTTPHEADER = ^TNETLIBHTTPHEADER;
+ TNETLIBHTTPHEADER = record
+ szName: PChar;
+ szValue: PChar;
+ end;
+
+ { PNETLIBHTTPREQUEST = ^TNETLIBHTTPREQUEST, defined above because this is
+ forward referenced from there }
+ TNETLIBHTTPREQUEST = record
+ cbSize: int;
+ requestType: int; // REQUEST_* constant
+ flags: DWORD;
+ szUrl: PChar;
+ { doesn't contain Content-Length, it'll be added automatically }
+ headers: PNETLIBHTTPHEADER; // pointer to an array of em?
+ headersCount: int; // yes they do
+ pData: PChar; // data to be sent on POST request
+ dataLength: int; // must be 0 for REQUEST_GET/REQUEST_CONNECT
+ resultCode: int;
+ szResultDescr: PChar;
+ end;
+
+ PNETLIBBUFFER = ^TNETLIBBUFFER;
+ TNETLIBBUFFER = record
+ buf: PChar;
+ len: int;
+ { see MSG_* constants above }
+ flags: int;
+ end;
+
+ PNETLIBSELECT = ^TNETLIBSELECT;
+ TNETLIBSELECT = record
+ cbSize: int;
+ dwTimeout: DWORD; // in milliseconds, INFINITE is acceptable
+ hReadConns: array[0..64+1] of THandle;
+ hWriteConns: array[0..64+1] of THandle;
+ hExceptConns: array[0..64+1] of THandle;
+ end;
+
+ PNETLIBPACKETRECVER = ^TNETLIBPACKETRECVER;
+ TNETLIBPACKETRECVER = record
+ cbSize: int;
+ { infinite is allowed -- initialise before use }
+ dwTimeout: DWORD;
+ { this many bytes are removed from the start of the buffer,
+ set to 0 on return -- initialise before use }
+ bytesUsed: int;
+ { equal the returnd value by service, unless the return value is 0 (connection closed) }
+ bytesAvailable: int;
+ { same as the parameter given to MS_NETLIB_CREATEPACKETRECVER: wParam }
+ bufferSize: int;
+ { contains the read data }
+ buffer: PByte;
+ end;
+
+const
+
+ {
+ wParam : 0
+ lParam : Pointer to an initalised TNETLIBUSER structure
+ Affects: Initialises the netlib for a set of connections, see notes
+ Returns: Returns a handle for future netlib calls, NULL on failure.
+ Notes : Netlib is loaded AFTER all plugins, thus a call to this service
+ in Load() will fail, hook ME_SYSTEM_MODULESLOADED and call it
+ from there.
+ -
+ Netlib will save settings under .szSettings module, all settings
+ (being?) begin with 'NL'.
+ -
+ Defacto settings are the same as <All connections> combobox entry option
+ as seen in Miranda->Options->Network
+ Version: v0.1.2.2+
+ Errors : ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY, ERROR_DUP_NAME
+ }
+ MS_NETLIB_REGISTERUSER = 'Netlib/RegisterUser';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to a initalised TNETLIBUSERSETTINGS structure
+ Affects: Gets the user configured settings for a Netlib user, see notes
+ Returns: [non zero] on SUCCESS, NULL(0) on failure
+ Notes : .cbSize must be filled with sizeof() before calling --
+ the returned null terminated strings (in the structure) are valid
+ as long as HANDLE remains open or proxy options are changed
+ again, do not rely on them being around forever.
+ Version: v0.1.2.2+
+ Errors : ERROR_INVALID_PARAMETER
+ }
+ MS_NETLIB_GETUSERSETTINGS = 'Netlib/GetUserSettings';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to a initalised NETLIBUSERSETTINGS structure
+ Affect : Changes the configurable settings for a Netlib user -- see notes
+ Returns: [non zero] on success, NULL(0) on failure
+ Notes : This service is only really useful for people that specify NUF_NOOPTIONS
+ when registering and want to create their own options.
+ Settings will be stored even if the option to enable it, is it not enabled,
+ e.g. useProxyAuth is 0, szProxyAuthPassword will still be saved
+ Errors : ERROR_INVALID_PARAMETER
+ }
+ MS_NETLIB_SETUSERSETTINGS = 'Netlib/SetUserSettings';
+
+ {
+ wParam : HANDLE / SOCKET
+ lParam : 0
+ Affects: Closes a handle, see notes
+ Returns: Returns [non zero] on success, NULL(0) on failure
+ Notes : All netlib handles should be closed once they're finished with,
+ If a SOCKET type is passed instead of netlib handle type, it is closed
+ Errors : ERROR_INVALID_PARAMETER
+ }
+ MS_NETLIB_CLOSEHANDLE = 'Netlib/CloseHandle';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to a initialised TNETLIBBIND
+ Affects: Open a port and wait for connections on it -- see notes
+ Returns: Returns a handle on success, NULL(0) on failure
+ Notes : this function does the equivalent of socket(), bind(), getsockname(),
+ listen(), accept() -- internally this function creates a new thread
+ which waits around in accept() for new connections.
+ When one is received, TNETLIBBIND.pfnNewConnection is called,
+ from the context of the NEW thread and then it
+ returns to waiting for connections.
+ -
+ Close the returned handle to end the thread and close the port.
+ -
+ Errors : ERROR_INVALID_PARAMETER, any returned by socket(), bind(), listen()
+ getsockname()
+ }
+ MS_NETLIB_BINDPORT = 'Netlib/BindPort';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to an initalised TNETLIBOPENCONNECTION structure
+ Affects: Opens a connection -- see notes
+ Returns: Returns a Handle to a new connection on success, NULL(0) on failure
+ Notes : internally this service is the equivalent of socket(), gethostbyname(),
+ connect()
+ -
+ If NLOCF_HTTP is set and HANDLE is configured for HTTP(S) proxy
+ then this function will connect() to that proxy server ONLY,
+ without performing any initialisation conversation.
+ -
+ If HANDLE is configured for an HTTP proxy and does not support
+ HTTP gateways and you try to open a connection without NLOCF_HTTP
+ then this service will first attempt to open an HTTPS connection,
+ if that fails, it will try a direct connection, if *that* fails
+ then it will return failure with the error
+ from connect() during the connection attempt
+ Errors : ERROR_INVALID_PARAMETER, any returned by socket(), gethostbyname(),
+ connect(), MS_NETLIB_SEND, MS_NETLIB_RECV, select()
+ -
+ ERROR_TIMEOUT (during proxy communication)
+ ERROR_BAD_FORMAT (very invalid proxy reply)
+ ERROR_ACCESS_DENIED (by proxy)
+ ERROR_CONNECTION_UNAVAIL (socks proxy can't connect to identd)
+ ERROR_INVALID_ACCESS (proxy refused identd auth)
+ ERROR_INVALID_DATA (proxy returned invalid code)
+ ERROR_INVALID_ID_AUTHORITY (proxy requires use of auth method that's not supported)
+ ERROR_GEN_FAILURE (socks5/https general failure)
+ ERROR_CALL_NOT_IMPLEMENTED (socks5 command not supported)
+ ERROR_INVALID_ADDRESS (socks5 address type not supported)
+ -
+ HTTP: anything from TNETLIBUSER.pfnHttpGatewayInit, TNETLIBUSER.pfnHttpGatewayBegin,
+ MS_NETLIB_SENDHTTPREQUEST or MS_NETLIB_RECVHTTPHEADERS
+ }
+ MS_NETLIB_OPENCONNECTION = 'Netlib/OpenConnection';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to an initialised NETLIBHTTPPROXYINFO structure
+ Affects: Sets the required information for an HTTP proxy connection -- see notes
+ Returns: [non zero] on success, NULL(0) on failure
+ Notes : This service is designed to be called from
+ within TNETLIBUSER.pfnHttpGatewayInit (see notes in C header under
+ MS_NETLIB_REGISTERUSER)
+ Errors : ERROR_INVALID_PARAMETER
+ }
+ MS_NETLIB_SETHTTPPROXYINFO = 'Netlib/SetHttpProxyInfo';
+
+ {
+ wParam : HANDLE
+ lParam : 0
+ Affects: Get's the SOCKET associated with a handle -- see notes
+ Returns: the SOCKET on success, INVALID_SOCKET on failure
+ Notes : The Netlib handle passed to this service should only be passed
+ if they were returned with MS_NETLIB_OPENCONNECTION or MS_NETLIB_BINDPORT
+ -
+ Be careful how you use this socket because you might be connected via an
+ HTTP proxy, in which case calling send/recv() will break things
+ -
+ Errors : ERROR_INVALID_PARAMETER
+ }
+ MS_NETLIB_GETSOCKET = 'Netlib/GetSocket';
+
+ {
+ wParam : 0
+ lParam : Pointer to a null terminated string
+ Affects: URL-encodes a string for x-www-form-urlencoded (and other uses) -- see notes
+ Returns: A pointer to a null terminated string, NULL(0) on failure
+ Notes : The returned string must be freed after it's no longer needed,
+ to do this Miranda's process heap must be used (under the WINAPI), e.g.
+ HeapFree(GetProcessHeap(), 0, the_returned_string)
+ Errors : ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY
+ }
+ MS_NETLIB_URLENCODE = 'Netlib/UrlEncode';
+
+ {
+ wParam : 0
+ lParam : Pointer to a TNETLIBBASE64 initialised structure
+ Affects: Decodes a Base64 null terminated string, see notes
+ Returns: [non zero] on success, NULL(0) on failure
+ Notes : TNETLIBBASE64.pszEncoded and cchEncoded must contain a pointer to
+ a buffer to use as input, and it's length, the length
+ should not include space taken for null termination --
+ -
+ Output is placed in ..pbDecoded and ..cbDecoded for buffer and
+ length of buffer -- the maxiumum output for a given input can
+ be worked out with Netlib_GetBase64DecodedBufferSize() function
+ see below.
+ -
+ For more information on Base64 see rfc-1421.
+ Errors : ERROR_INVALID_PARAMETER, ERROR_INVALID_DATA, ERROR_BUFFER_OVERFLOW
+ }
+ MS_NETLIB_BASE64DECODE = 'Netlib/Base64Decode';
+
+ {
+ wParam : 0
+ lParam : Pointer to an initialised TNETLIBBASE64 structure
+ Affect : Base64 encode a string, see notes
+ Returns: [non zero] on success, NULL(0) on failure
+ Notes : TNETLIBBASE64.pbDecode and TNETLIBBASE64.cbDecoded contain
+ the input buffer and it's length --
+ TNETLIBBASE64.pszEncoded and TNETLIBBASE64.cchEncoded contain the
+ buffer in which to put the output and it's length.
+ -
+ The maximum output size for a given input can be worked
+ out with the function Netlib_GetBase64EncodedBufferSize() below
+ .pszEncoded is null terminated, on return TNETLIBBASE64.cchEncoded
+ is set to the actual length excluding 0.
+ Errors : ERROR_INVALID_PARAMETER, ERROR_BUFFER_OVERFLOW
+ }
+ MS_NETLIB_BASE64ENCODE = 'Netlib/Base64Encode';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to a initialised TNETLIBHTTPREQUEST structure
+ Affect : Send an HTTP request over a connection, see notes
+ Returns: The number of bytes on success, SOCKET_ERROR on failure
+ Notes : HANDLE must of been returned by MS_NETLIB_OPENCONNECTION,,
+ If you use NLHRF_SMARTAUTHHEADER and NTLM auth is in use then
+ full NTLM auth transcation occurs, comprising sending the
+ domain, getting the challenge, sending the response.
+ NETLIBHTTPREQUEST.resultCode and NETLIBHTTPREQUEST.szResultDescr are
+ ignored by this service.
+ Errors : ERROR_INVALID_PARAMETER, MS_NETLIB_SEND (return codes)
+ }
+ MS_NETLIB_SENDHTTPREQUEST = 'Netlib/SendHttpRequest';
+
+ {
+ wParam : HANDLE
+ lParam : 0
+ Affect : Receive HTTP headers, see notes
+ Returns: A pointer to a TNETLIBHTTPREQUEST structure on success, NULL(0) on failure
+ Notes : The returned pointer must be freed after it's done with
+ use MS_NETLIB_FREEHTTPREQUESTSTRUCT.
+ -
+ HANDLE must be returned by MS_NETLIB_OPENCONNECTION
+ -
+ Return^.pData=NIL and Return^.dataLength=0 always
+ -
+ The returned data should be retrieved using MS_NETLIB_RECV once
+ the headers have been parsed.
+ If headers haven't finished within 60 seconds the function returns
+ NULL(0) and ERROR_TIMEOUT
+ Errors : ERROR_INVALID_PARAMETER, any MS_NETLIB_RECV or select()
+ ERROR_HANDLE_EOF (connection closed bfore headers complete)
+ ERROR_TIMEOUT (headers still not complete after 60 seconds)
+ ERROR_BAD_FORMAT (invalid character or line ending in headers, or first line is blank)
+ ERROR_BUFFER_OVERFLOW (each header line must be less than 4096 chars long)
+ ERROR_INVALID_DATA (first header line is malformed ("http/[01].[0-9] [0-9]+ .*", or no colon in subsequent line)
+
+ }
+ MS_NETLIB_RECVHTTPHEADERS = 'Netlib/RecvHttpHeaders';
+
+ {
+ wParam : 0
+ lParam : Pointer returned by MS_NETLIB_RECVHTTPHEADERS to free
+ Affect : Free the memory used by a TNETLIBHTTPREQUEST structure, see notes
+ Returns: [non zero] on success, NULL(0) on failure
+ Notes : This service should only be used with memory pointers returned
+ by either MS_NETLIB_RECVHTTPHEADERS or MS_NETLIB_HTTPTRANSACTION!.
+ Errors : ERROR_INVALID_PARAMETER
+
+ }
+ MS_NETLIB_FREEHTTPREQUESTSTRUCT = 'Netlib/FreeHttpRequestStruct';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to a TNETLIBHTTPREQUEST structure
+ Affect : Carry out an entire HTTP transaction, see notes
+ Returns: another pointer to a TNETLIBHTTPREQUEST structure or NULL(0)
+ on failure
+ Notes : The returned pointer must be freed at some point
+ with MS_NETLIB_FREEHTTPREQUESTSTRUCT,
+ -
+ TNETLIBHTTPREQUEST.szUrl should have a full HTTP URL, if it
+ does not start with http://, that will be assumed, but do not
+ take this assumption to stay assumed (heh..) in the future
+ -
+ this service equivalent of open(), sendhttp(), getheaders()
+ netlib_recv(), netlib_closehandle()
+ -
+ TNETLIBHTTPREQUEST.headers will be added to with the following
+ headers if they're not already present :
+ "Host" (even if it is requested in .flags)
+ "User-Agent" (in form : 'Miranda/d.d.d.d <(status of release)>')
+ "Content-Length" (for POSTs only, set to TNETLIBHTTPREQUEST.dataLength)
+
+ If you don't want to send any of these headers --
+ set TNETLIBHTTPREQUEST.headers to NULL(0)
+ -
+ In the returned pointer, pData[dataLen] is always 0 for 'safety'
+ also : headers, headersCount, pData, dataLength, resultCode and
+ szResultDescr are all valid
+ -
+ Also take care not to assume that a returned pointer means that
+ at the HTTP level it all worked out -- refer to the resultCode for
+ 2xx before doing anything else
+ -
+ Errors : ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY
+ Errors returned by the aforementioned internally used functions
+ }
+ MS_NETLIB_HTTPTRANSACTION = 'Netlib/HttpTransaction';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to an initialised TNETLIBBUFFER structure
+ Affect : Send data over an open connection see notes
+ Returns: The number of bytes sent on success, SOCKET_ERROR on failure
+ Notes : see Netlib_Send() helper function
+ Errors : ERROR_INVALID_PARAMETER,
+ anything from socket(), connect()
+ send(), TNETLIBUSER.pfnHttpGatewayWrapSend(),
+ (HTTP proxy): ERROR_GEN_FAILURE (http result code wasn't 2xx)
+ MS_NETLIB_SENDHTTPREQUEST, MS_NETLIB_RECVHTTPHEADERS
+ }
+ MS_NETLIB_SEND = 'Netlib/Send';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to an initialised TNETLIBBUFFER structure
+ Affect : Receive data over a connection, see notes
+ Returns: The number of bytes read on success, SOCKET_ERROR on failure
+ Notes :
+ This service uses some of the same flags as MS_NETLIB_SEND :
+ MSG_PEEK,
+ MSG_NODUMP,
+ MSG_DUMPPROXY,
+ MSG_NOHTTPGATEWAYWRAP,
+ MSG_DUMPASTEXT,
+ MSG_RAW
+ -
+ On using MSG_NOHTTPGATEWAYWRAP: Because packets through an HTTP proxy are
+ batched and cached and stuff, using this flag is not a guarantee that it
+ will be obeyed, and if it is it may even be propogated to future calls
+ even if you don't specify it then. Because of this, the flag should be
+ considered an all-or-nothing thing: either use it for the entire duration
+ of a connection, or not at all.
+ Errors : ERROR_INVALID_PARAMETER, anything from recv()
+ (HTTP proxy):
+ ERROR_GEN_FAILURE (http result code wasn't 2xx)
+ ERROR_INVALID_DATA (no Content-Length header in reply)
+ ERROR_NOT_ENOUGH_MEMORY (Content-Length very large)
+ ERROR_HANDLE_EOF (connection closed before Content-Length bytes recved)
+ anything from select(),
+ MS_NETLIB_RECVHTTPHEADERS, nlu.pfnHttpGatewayUnwrapRecv, socket(),
+ connect(), MS_NETLIB_SENDHTTPREQUEST
+
+ }
+ MS_NETLIB_RECV = 'Netlib/Recv';
+
+ {
+ wParam : 0
+ lParam : Pointer to an initialised TNETLIBSELECT structure
+ Affect : Determine the status of one or more connections, see notes
+ Returns: The numbe of ready connections, SOCKET_ERROR on failure
+ Notes : All handles passed to this service must have been returned
+ either by MS_NETLIB_OPENCONNECTION or MS_NETLIB_BINDPORT,
+ the last handle in each list must be followed by either NULL
+ or INVALID_HANDLE_VALUE.
+ Errors : ERROR_INVALID_HANDLE, ERROR_INVALID_DATA, anything from select()
+ }
+ MS_NETLIB_SELECT = 'Netlib/Select';
+
+ {
+ wParam : HANDLE
+ lParam : maxPacketSize
+ Affect : Create a packet receiver, see notes
+ Returns: A handle on success, NULL(0) on failure
+ Notes : The packet receiver implements the common situation where
+ you have a variable length of packets coming thru over a connection
+ and you want them split up in order to handle them.
+ -
+ The major limiation is, that the buffer is created in memory,
+ so you can't have arbitrarily large packets
+ Errors : ERROR_INVALID_PARAMETER, ERROR_OUTOFMEMORY
+ }
+ MS_NETLIB_CREATEPACKETRECVER = 'Netlib/CreatePacketRecver';
+
+ {
+ wParam : Handle returned by MS_NETLIB_CREATEPACKETRECVER
+ lParam : Pointer to an initialised TNETLIBPACKETRECVER
+ Returns: The total number of bytes available in the buffer, NULL(0)
+ if the connection was closed or SOCKET_ERROR.
+ -
+ If TNETLIBPACKETRECVER.bytesUsed is set to zero and the
+ buffer is already full up to the maxPacketSize, it is assumed
+ that a too large packet has been received, All data in
+ the buffer is discarded and receiving has started anew.
+ -
+ This will probably cause alignment problem so if you think
+ that tis iss likely to happen, then you should deal with it
+ yourself.
+ -
+ Closing the packet receiver will not close the associated
+ connection but will discard any bytes still in the buffer,
+ so if you intend to carry on reading from that connection,
+ make sure you have processed the buffer first.
+ -
+ This service is equivalent of memmove() to remove
+ the first bytesUsed from the buffer, select(), if dwTimeOut
+ is not INFINITE, then MS_NETLIB_RECV
+ Errors : ERROR_INVALID_PARAMETER, ERROR_TIMEOUT, anything from select(),
+ MS_NETLIB_RECV
+ }
+ MS_NETLIB_GETMOREPACKETS = 'Netlib/GetMorePackets';
+
+ {
+ wParam : HANDLE
+ lParam : Pointer to null terminated string to uh, log.
+ Affect : Add a message to the log (if it's running) see notes
+ Returns: non zeror on success, NULL(0) on failure
+ Notes : Don't include \r\n or #13#10 it's not needed,
+ -
+ Doesn't support formatting like the given C code for
+ Netlib_Logf, just use FmtStr() and then call this service
+ if you want that.
+ Errors : ERROR_INVALID_PARAMETER
+ }
+ MS_NETLIB_LOG = 'Netlib/Log';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_options.inc b/plugins/ShlExt/inc/m_options.inc
new file mode 100644
index 0000000000..6a011fecdc
--- /dev/null
+++ b/plugins/ShlExt/inc/m_options.inc
@@ -0,0 +1,109 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_OPTIONS}
+{$DEFINE M_OPTIONS}
+
+const
+
+ {
+ wParam : addinfo
+ lParam : 0
+ Affects: The user opened the options dialog, see notes
+ Notes : Modules should do whatever initalisation they need and call
+ MS_OPT_ADDPAGE with the wParam -- MS_OPT_ADDPAGE
+ can be called one or more times
+ if more than one page wants to be displayed.
+ }
+ ME_OPT_INITIALISE = 'Opt/Initialise';
+
+ {
+ wParam : wParam from ME_OPT_INITIALISE
+ lParam : Pointer to an initialised TOPTIONSDIALOGPAGE
+ Affects: Adds a page to the options dialog, see notes
+ Notes : Strings in the structure can be released as soon as the
+ service returns -- but icons must be kept around, this iss
+ not a problem if you're loading theem from a resource.
+ -
+ This service should only be called within the ME_OPT_INITIALISE
+ event hook.
+ -
+ Pages in the options dialog operate just like pages in property
+ sheets, See the WinAPI documentation for details on how they operate.
+ Version: Prior to v0.1.2.1 the options dialog would resize
+ to fit the largest page, but since then it's a fixed size
+ The largest page that fits neatly is 314x240 DLU's
+ -
+ Some of OPTIONSDIALOGPAGE's fields are version dependant.
+ }
+ MS_OPT_ADDPAGE = 'Opt/AddPage';
+
+ { defacto size }
+
+ OPTIONSDIALOGPAGE_V0100_SIZE = $18;
+ OPTIONSDIALOGPAGE_V0120_SIZE = $28;
+
+ { page is only shown when in 'simple' mode }
+ ODPF_SIMPLEONLY = 1;
+ { page is only shown when in 'expert' mode }
+ ODPF_EXPERTONLY = 2;
+ { give group box titles a bold font }
+ ODPF_BOLDGROUPS = 4;
+
+type
+
+ POPTIONSDIALOGPAGE = ^TOPTIONSDIALOGPAGE;
+ TOPTIONSDIALOGPAGE = record
+ cbSize: int;
+ position: int; // position number, lower numbers are top most
+ pszTitle: PChar;
+ pfnDlgProc: Pointer; // DLGPROC prototype
+ pszTemplate: PChar;
+ hInstance: THandle;
+ hIcon: THandle; // v0.1.0.1+
+ pszGroup: PChar; // v0.1.0.1+
+ groupPosition: int; // v0.1.0.1+
+ hGroupIcon: THandle; // v0.1.0.1+
+ flags: DWORD; // v0.1.2.1+
+ { if in simple mode the dialog will be cut off AFTER this control ID, 0
+ for disable }
+ nIDBottomSimpleControl: int; // v0.1.2.1+
+ { if in simple mode the dialog will cut off AFTER this control ID, 0 to disable }
+ nIDRightSimpleControl: int; // v0.1.2.1+
+ { these controls will be hidden in simple mode, pointer to an array of ID's
+ must remain valid for the duration of the dialog }
+ expertOnlyControls: ^int;
+ nExpertOnlyControls: int; // v0.1.2.1+
+ end;
+
+const
+
+ { sent to pages via WM_NOTIFY when the expert checkbox is clicked, lParam = new state }
+ PSN_EXPERTCHANGED = 2;
+ { returns true/false }
+ PSM_ISEXPERT = ($0400 + 101);
+ { returns HFONT used for group box titles }
+ PSM_GETBOLDFONT = ($0400 + 102);
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_plugins.inc b/plugins/ShlExt/inc/m_plugins.inc
new file mode 100644
index 0000000000..72d5ff69a3
--- /dev/null
+++ b/plugins/ShlExt/inc/m_plugins.inc
@@ -0,0 +1,70 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_PLUGINS}
+{$DEFINE M_PLUGINS}
+
+const
+
+ DEFMOD_PROTOCOLICQ = 1; // removed from v0.3.0.0 alpha
+ DEFMOD_PROTOCOLMSN = 2; // removed from v0.1.2.0+
+ DEFMOD_UIFINDADD = 3;
+ DEFMOD_UIUSERINFO = 4;
+ DEFMOD_SRMESSAGE = 5;
+ DEFMOD_SRURL = 6;
+ DEFMOD_SREMAIL = 7;
+ DEFMOD_SRAUTH = 8;
+ DEFMOD_SRFILE = 9;
+ DEFMOD_UIHELP = 10;
+ DEFMOD_UIHISTORY = 11;
+ DEFMOD_RNDCHECKUPD = 12;
+ DEFMOD_RNDICQIMPORT = 13; // not built in to v0.1.0.1+
+ DEFMOD_RNDAUTOAWAY = 14;
+ DEFMOD_RNDUSERONLINE = 15;
+ DEFMOD_RNDCRYPT = 16; // v0.1.0.1-v0.1.2.0
+ DEFMOD_SRAWAY = 17; // v0.1.0.1+
+ DEFMOD_RNDIGNORE = 18; // v0.1.0.1+
+ DEFMOD_UIVISIBILITY = 19; // v0.1.1.0+, options page only
+ DEFMOD_UICLUI = 20; // v0.1.1.0+
+ DEFMOD_UIPLUGINOPTS = 21; // v0.1.2.1+
+ DEFMOD_PROTOCOLNETLIB = 22; // v0.1.2.2+
+
+ DEFMOD_HIGHEST = 22;
+
+
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Gets an array of modules that the plugins report they want to replace
+ Returns: Returns a pointer to an array of ints, with elements 1 or 0,
+ indexed by the DEFMOD_* constants, 1 is to mark that the default
+ module shouldn't be loaded, see notes
+ Notes : this is primarily for use by the core's module initialiser,
+ but could also be used by modules that are doing
+ naughty things that are very feature-dependent.
+ }
+ MS_PLUGINS_GETDISABLEDEFAULTARRAY = 'Plugins/GetDisableDefaultArray';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_popup.inc b/plugins/ShlExt/inc/m_popup.inc
new file mode 100644
index 0000000000..f8d2ea9df9
--- /dev/null
+++ b/plugins/ShlExt/inc/m_popup.inc
@@ -0,0 +1,222 @@
+(*
+===============================================================================
+ PopUp plugin
+Plugin Name: PopUp
+Plugin author: hrk, Luca Santarelli, hrk@users.sourceforge.net
+This file has been created by egodust, Sam, egodust@users.sourceforge.net
+===============================================================================
+
+The purpose of this plugin is to give developers a common "platform/interface" to show PopUps. It is born from the source code of NewStatusNotify, another plugin I've made.
+
+Remember that users *must* have this plugin enabled, or they won't get any popup. Write this in the requirements, do whatever you wish ;-)... but tell them!
+===============================================================================
+
+-- To use this file you need Windows.pas, m_globaldefs.pas (get it from the CVS under the 'inc' module)
+-- To include this in the source, use {$include m_popup.h}
+
+*)
+
+{$ifndef M_POPUP_H}
+{$define M_POPUP_H}
+
+{$ifdef FPC}
+ {$PACKRECORDS C}
+ {$MODE Delphi}
+{$endif}
+
+const
+
+ MAX_CONTACTNAME = 2048;
+ MAX_SECONDLINE = 2048;
+
+ SM_WARNING = $01; //Triangle icon.
+ SM_NOTIFY = $02; //Exclamation mark icon.
+
+type
+
+ // for info on what this stuff is, see m_popup.h
+
+ PPOPUPDATA = ^TPOPUPDATA;
+ TPOPUPDATA = record
+ lchContact: HCONTACT;
+ lchIcon: THandle;
+ lpszContactName: array[0..MAX_CONTACTNAME-1] of Char;
+ lpszText: array[0..MAX_SECONDLINE-1] of Char;
+ colorBack: COLORREF;
+ colorForeText: COLORREF;
+ PluginWindowProc: Pointer; // must be a window procedure using stdcall
+ PluginData: Pointer;
+ end;
+
+type
+
+ // for info on what this stuff is, see m_popup.h
+
+ PPOPUPDATAEX = ^TPOPUPDATAEX;
+ TPOPUPDATAEX = record
+ lchContact: HCONTACT;
+ lchIcon: THandle;
+ lpszContactName: array[0..MAX_CONTACTNAME-1] of Char;
+ lpszText: array[0..MAX_SECONDLINE-1] of Char;
+ colorBack: COLORREF;
+ colorForeText: COLORREF;
+ PluginWindowProc: Pointer; // must be a window procedure using stdcall
+ PluginData: Pointer;
+ iSeconds: int; //Custom delay time in seconds. -1 means "forever", 0 means "default time".
+ cZero: array[0..15] of Char; //16 unused bytes which may come useful in the future.
+ end;
+
+const
+
+(*
+ Creates, adds and shows a popup, given a (valid) POPUPDATA structure pointer.
+ wParam = (WPARAM)(*POPUPDATA)PopUpDataAddress
+ lParam = 0
+ Returns: > 0 on success, 0 if creation went bad, -1 if the PopUpData contained unacceptable values.
+ NOTE: it returns -1 if the PopUpData was not valid, if there were already too many popups, if the module was disabled.
+ Otherwise, it can return anything else...
+*)
+
+ MS_POPUP_ADDPOPUP = 'PopUp/AddPopUp';
+
+(*
+ The same, but with a POPUPDATAEX structure pointer.
+ wParam = (WPARAM)(*POPUPDATAEX)PopUpDataExAddress
+ lParam = 0
+*)
+
+ MS_POPUP_ADDPOPUPEX = 'PopUp/AddPopUpEx';
+
+(*
+ Returns the handle to the contact associated to the specified PopUpWindow.
+ You will probably need to know this handle inside your WNDPROC. Exampole: you want to open the MessageWindow. :-)
+ Call MS_POPUP_GETCONTACT on the hWnd you were given in the WNDPROC.
+ wParam = (WPARAM)(HWND)hPopUpWindow
+ lParam = 0;
+ Returns: the HANDLE of the contact. Can return NULL, meaning it's the main contact. -1 means failure.
+*)
+
+ MS_POPUP_GETCONTACT = 'PopUp/GetContact';
+
+(*
+ wParam = hPopUpWindow
+ lParam = PluginDataAddress;
+ Returns: the address of the PLUGINDATA structure. Can return NULL, meaning nothing was given. -1 means failure.
+ IMPORTANT NOTE: it doesn't seem to work if you do:
+ CallService(..., (LPARAM)aPointerToAStruct);
+ and then use that struct.
+ Do this, instead:
+ aPointerToStruct = CallService(..., (LPARAM)aPointerToAStruct);
+ and it will work. Just look at the example I've written above (PopUpDlgProc).
+*)
+ MS_POPUP_GETPLUGINDATA = 'PopUp/GetPluginData';
+
+(*
+ wParam = 0
+ lParam = 0
+ Returns: 0 if the user has chosen not to have the second line, 1 if he choose to have the second line.
+*)
+ MS_POPUP_ISSECONDLINESHOWN = 'PopUp/IsSecondLineShown';
+
+(*
+ UM_FREEPLUGINDATA
+ wParam = lParam = 0. Process this message if you have allocated your own memory. (i.e.: POPUPDATA.PluginData != NULL)
+*)
+ UM_FREEPLUGINDATA = ((*WM_USER*)$400 + $200);
+
+(*
+ UM_DESTROYPOPUP
+ wParam = lParam = 0. Send this message when you want to destroy the popup, or use the function below.
+*)
+ UM_DESTROYPOPUP = ((*WM_USER*)$400 + $201);
+
+(*
+ UM_INITPOPUP
+ wParam = (WPARAM)(HWND)hPopUpWindow (but this is useless, since I'll directly send it to your hPopUpWindow
+ lParam = 0.
+ This message is sent to the PopUp when its creation has been finished, so POPUPDATA (and thus your PluginData) is reachable.
+ Catch it if you needed to catch WM_CREATE or WM_INITDIALOG, which you'll never ever get in your entire popup-life.
+ Return value: if you process this message, return 0. If you don't process it, return 0. Do whatever you like ;-)
+*)
+ UM_INITPOPUP = ($400(*WM_USER*) + $202);
+
+(*
+ wParam = hPopUpWindow
+ lParam = lpzNewText
+ returns: > 0 for success, -1 for failure, 0 if the failure is due to second line not being shown. (but you could call PUIsSecondLineShown() before changing the text...)
+ Changes the text displayed in the second line of the popup.
+*)
+ MS_POPUP_CHANGETEXT = 'PopUp/Changetext';
+
+(*
+ This is mainly for developers.
+ Shows a warning message in a PopUp. It's useful if you need a "MessageBox" like function, but you don't want a modal window (which will interfere with a DialogProcedure. MessageBox steals focus and control, this one not.
+ wParam = lpzMessage
+ lParam = 0; Returns: 0 if the popup was shown, -1 in case of failure.
+*)
+ MS_POPUP_SHOWMESSAGE = 'PopUp/ShowMessage';
+
+
+ (* helper functions, will be inlined on FPC if you have the swithces enabled *)
+
+ function PUAddPopup(ppdp: PPOPUPDATA): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := CallService(MS_POPUP_ADDPOPUP, WPARAM(ppdp), 0);
+ end;
+
+ function PUGetContact(hPopUpWindow: THandle): THandle;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := CallService(MS_POPUP_GETCONTACT, WPARAM(hPopUpWindow), 0);
+ end;
+
+ function PUGetPluginData(hPopUpWindow: THandle): Pointer;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ var
+ dummy: pointer;
+ begin
+ dummy := nil;
+ Int(Result) := CallService(MS_POPUP_GETPLUGINDATA, WPARAM(hPopUpWindow), LPARAM(dummy));
+ end;
+
+ function PUIsSecondLineShown: BOOL;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Int(Result) := CallService(MS_POPUP_ISSECONDLINESHOWN, 0, 0);
+ end;
+
+ function PUDeletePopUp(hWndPopUp: THandle): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := SendMessage(hWndPopUp, UM_DESTROYPOPUP, 0, 0);
+ end;
+
+ function PUChangeText(hWndPopUp: THandle; lpzNewText: PChar): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := CallService(MS_POPUP_CHANGETEXT, WPARAM(hWndPopUp), LPARAM(lpzNewText));
+ end;
+
+ function PUShowMessage(lpzText: PChar; kind: Byte): int;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := CallService(MS_POPUP_SHOWMESSAGE, WPARAM(lpzText), LPARAM(kind));
+ end;
+
+{$endif}
+
diff --git a/plugins/ShlExt/inc/m_protocols.inc b/plugins/ShlExt/inc/m_protocols.inc
new file mode 100644
index 0000000000..90bd12366b
--- /dev/null
+++ b/plugins/ShlExt/inc/m_protocols.inc
@@ -0,0 +1,180 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_PROTOCOLS}
+{$DEFINE M_PROTOCOLS}
+
+const
+
+ ACKTYPE_MESSAGE = 0;
+ ACKTYPE_URL = 1;
+ ACKTYPE_FILE = 2;
+ ACKTYPE_CHAT = 3;
+ ACKTYPE_AWAYMSG = 4;
+ ACKTYPE_AUTHREQ = 5;
+ ACKTYPE_ADDED = 6;
+ ACKTYPE_GETINFO = 7;
+ ACKTYPE_SETINFO = 8;
+ ACKTYPE_LOGIN = 9;
+ ACKTYPE_SEARCH = 10;
+ ACKTYPE_NEWUSER = 11;
+ ACKTYPE_STATUS = 12;
+ ACKTYPE_CONTACTS = 13; //send/recv of contacts
+
+ ACKRESULT_SUCCESS = 0;
+ ACKRESULT_FAILED = 1;
+ //'in progress' result codes:
+ ACKRESULT_CONNECTING = 100;
+ ACKRESULT_CONNECTED = 101;
+ ACKRESULT_INITIALISING = 102;
+ ACKRESULT_SENTREQUEST = 103; // waiting for reply...
+ ACKRESULT_DATA = 104; // blob of file data sent/recved, or search result
+ ACKRESULT_NEXTFILE = 105; // file transfer went to next file
+ ACKRESULT_FILERESUME = 106; // a file is about to be received, see PS_FILERESUME
+ ACKRESULT_DENIED = 107; // a file send has been denied (0.3a + only)
+
+ // for PROTOCOLDESCRIPTOR.type
+
+ PROTOTYPE_PROTOCOL = 1000;
+ PROTOTYPE_ENCRYPTION = 2000;
+ PROTOTYPE_FILTER = 3000;
+ PROTOTYPE_TRANSLATION = 4000;
+ PROTOTYPE_OTHER = 10000;//avoid using this if at all possible
+
+type
+
+ PCCSDATA = ^TCCSDATA;
+ TCCSDATA = record
+ hContact: THandle;
+ szProtoService: PChar; // a PS_* constant
+ wParam: WPARAM;
+ lParam: LPARAM;
+ end;
+
+ PACKDATA = ^TACKDATA;
+ TACKDATA = record
+ cbSize: int;
+ szModule: PChar; // the name of the protocol module which initiated this ack
+ hContact: THandle;
+ type_: int; // an ACKTYPE_* constant
+ result_: int; // an ACKRESULT_* constant
+ hProcess: THandle; // caller defined seq, I mean process code
+ lParam: LPARAM; // caller defined data
+ end;
+
+ // when type=ACKTYPE_FILE and (result=ACKRESULT_DATA or result=ACKRESULT_FILERESUME)
+
+ PPROTOFILETRANSFERSTATUS = ^TPROTOFILETRANSFERSTATUS;
+ TPROTOFILETRANSFERSTATUS = record
+ cbSize: int;
+ hContact: THandle;
+ sending: int; // true if sending, false if receiving
+ files: PChar; // pointer to an array of pchar's
+ totalFiles: int;
+ currentFileNumber: int;
+ totalBytes: LongInt;
+ totalProgress: LongInt;
+ workingDir: PChar;
+ currentFile: PChar;
+ currentFileSize: LongInt;
+ currentFileProgress: LongInt;
+ currentFileTime: LongInt; // UNIX time
+ end;
+
+ // for registering a protocol, enumeration
+
+ PPROTOCOLDESCRIPTOR = ^TPROTOCOLDESCRIPTOR;
+ TPROTOCOLDESCRIPTOR = record
+ cbSize: int;
+ szName: PChar; // unique name of module
+ type_: int; // a PROTOTYPE_* constant
+ end;
+
+const
+
+ {
+ wParam : 0
+ lParam : Pointer to an initalised CSSDATA structure
+ Affect : Send a general request thru the protocol chain for a contact
+ Return : the return value documented in the PS_* def (m_protosvc.inc)
+ }
+ MS_PROTO_CALLCONTACTSERVICE = 'Proto/CallContactService';
+
+ {
+ wParam : 0
+ lParam : Pointer to an initalised TACKDATA structure
+ Affect : a general 'ack', see notes
+ Notes : Just because defs are here doesn't mean they will be sent
+ read the docs for the function you are calling to see what
+ replies you will get.
+ }
+ ME_PROTO_ACK = 'Proto/Ack';
+
+ {
+ wParam : pointer to an int to store number of protocols
+ lParam : Pointer to an an array of PPROTOCOLDESCRIPTOR pointers
+ Affect : Enumerate the currently running protocols, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : Neither wParam/lParam maybe NULL(0), the list returned by
+ this service is the protocol modules currently installed
+ and running, it is not a complete list of protocols that have
+ ever been installed.
+ -
+ A protocol module does not have to be a protocol running thru
+ the internet, it can be a vast number of things
+ }
+ MS_PROTO_ENUMPROTOCOLS = 'Proto/EnumProtocols';
+
+ {
+ wParam : 0
+ lParam : Pointer to null terminated string containing protocol name
+ Affect : Determines if a protocol is running or not.
+ Returns: A pointer to the PPROTOCOLDESCRIPTOR if the protocol is loaded
+ or NULL(0) if it isn't
+ }
+ MS_PROTO_ISPROTOCOLLOADED = 'Proto/IsProtocolLoaded';
+
+ {
+ wParam : HCONTACT
+ lParam : Pointer to a null terminated string containing a name
+ Affect : Determine whether the given contact has the given protocol
+ in it's chain.
+ Returns : 0 if the protocol isn't in the chain, [non zero] if it is
+ }
+ MS_PROTO_ISPROTOONCONTACT = 'Proto/IsProtoOnContact';
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affect : Gets the network-level protocol associated with a contact
+ Returns: a PChar pointing to the ASCIIZ name of the protocol or NULL(0)
+ if the contact has no protocol, There's no need to dispsose
+ the returned string.
+ -
+ This is the name of the module that actually accesses the network
+ for that contact.
+ }
+ MS_PROTO_GETCONTACTBASEPROTO = 'Proto/GetContactBaseProto';
+
+{$ENDIF} \ No newline at end of file
diff --git a/plugins/ShlExt/inc/m_protomod.inc b/plugins/ShlExt/inc/m_protomod.inc
new file mode 100644
index 0000000000..8bf245316c
--- /dev/null
+++ b/plugins/ShlExt/inc/m_protomod.inc
@@ -0,0 +1,105 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_PROTOMOD}
+{$DEFINE M_PROTOMOD}
+
+ {$ifndef M_PROTOCOLS}
+ {$include m_protocols.inc}
+ {$endif}
+
+const
+
+ {
+ wParam : 0
+ lParam : Pointer to a initalised TPROTOCOLDESCRIPTOR structure
+ Affect : Register a protocol module, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : This service MUST be called from your module's Load() function
+ TPROTOCOLDESCRIPTOR.type can be a value other than PROTOTYPE_*
+ which are used to provide a more precise positioning information
+ for the contact protocol lists.
+ -
+ Relative values to the constants can be given, but this MUST NOT
+ be done for PROTOTYPE_PROTOCOL.
+ }
+ MS_PROTO_REGISTERMODULE = 'Proto/RegisterModule';
+
+ {
+ wParam : HCONTACT
+ lParam : protocol_name_string
+ Affect : Add the given protocol module to the chain for a contact, see notes
+ Returns: 0 success, [non zero] on failure
+ Notes : The module is added to the correct positioning according to it's
+ registered type.
+ }
+ MS_PROTO_ADDTOCONTACT = 'Proto/AddToContact';
+
+ {
+ wParam : HCONTACT
+ lParam : protocol_name_string
+ Affect : Remove the given protocol name from the chain for the given contact
+ Returns: 0 on success, [non zero] on failure
+ }
+ MS_PROTO_REMOVEFROMCONTACT = 'Proto/RemoveFromContact';
+
+ { see m_globaldefs.pas for CreateProtoServiceFunction }
+
+ {
+ wParam : wParam [arg]
+ lParam : lParam [arg]
+ Affect : Call the next service in the chain for the send operation, see notes
+ Return : Return value should be returned by CallService(MS_PROTO_CHAINSEND,wParam,lParam)
+ Notes : wParam MUST remain untouched, lParam is a pointer to a CSSDATA structure
+ and can be modified or copid if needed.
+ wParam and lParam should be the values passed to your service,
+ typically your service should return ASAP.
+ }
+ MS_PROTO_CHAINSEND = 'Proto/ChainSend';
+
+ {
+ wParam : wParam [arg]
+ lParam : lParam [arg]
+ Affect : Call the next service in the chain in this receive operation, see notes
+ Return : Return value should be returned by CallService(MS_PROTO_CHAINRECV,wParam,lParam)
+ Notes : wParam MUST remain untouched, lParam is a pointer to a CSSDATA structure
+ and can be modified or copied if needed.
+ wParam and lParam should be the values passed to your service,
+ typically your service should return ASAP.
+ -
+ MS_PROTO_CHAINRECV is thread safe since 0.1.2.0 -- calls
+ are translated to the main thread and passed from there.
+ }
+ MS_PROTO_CHAINRECV = 'Proto/ChainRecv';
+
+ {
+ wParam : 0
+ lParam : Pointer to an initalised ACKDATA
+ Affect : Broadcast a ME_PROTO_ACK event, see notes
+ Returns: The return value of the NotifyEventHooks() call
+ Notes : ME_PROTO_ACK is completely thread safe since 01.2.0
+ see notes in core/modules.h under NotifyEventHooks()
+ }
+ MS_PROTO_BROADCASTACK = 'Proto/BroadcastAck';
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_protosvc.inc b/plugins/ShlExt/inc/m_protosvc.inc
new file mode 100644
index 0000000000..9fde8268bb
--- /dev/null
+++ b/plugins/ShlExt/inc/m_protosvc.inc
@@ -0,0 +1,753 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_PROTOSVC}
+{$DEFINE M_PROTOSVC}
+
+{<</
+ none of these services should be used on there own (i.e. using CallService(), etc)
+ hence the PS_ prefix, instead use the services exposed in m_protocols.inc
+
+ these should be called with CallProtoService which prefixes the protocol module
+ name before calling.
+ -
+ Deleting contacts from protocols that store the contact list on the server:
+ If a contact is deleted while the protocol is online, it is expected that the
+ protocol will have hooked me_db_contact_deleted and take the appropriate
+ action by itself.
+ If a contact is deleted while the protocol is offline, the contact list will
+ display a message to the user about the problem, and set the byte setting
+ "CList"/"Delete" to 1. Each time such a protocol changes status from offline
+ or connecting to online the contact list will check for contacts with this
+ flag set and delete them at that time. Your hook for me_db_contact_deleted
+ will pick this up and everything will be good.
+/>>}
+
+const
+
+ PFLAGNUM_1 = $1;
+ PF1_IMSEND = $00000001; // supports IM sending
+ PF1_IMRECV = $00000002; // supports IM receiving
+ PF1_IM = (PF1_IMSEND or PF1_IMRECV);
+ PF1_URLSEND = $00000004; // supports separate URL sending
+ PF1_URLRECV = $00000008; // supports separate URL receiving
+ PF1_URL = (PF1_URLSEND or PF1_URLRECV);
+ PF1_FILESEND = $00000010; // supports file sending
+ PF1_FILERECV = $00000020; // supports file receiving
+ PF1_FILE = (PF1_FILESEND or PF1_FILERECV);
+ PF1_MODEMSGSEND = $00000040; // supports broadcasting away messages
+ PF1_MODEMSGRECV = $00000080; // supports reading others' away messages
+ PF1_MODEMSG = (PF1_MODEMSGSEND or PF1_MODEMSGRECV);
+ PF1_SERVERCLIST = $00000100; // contact lists are stored on the server, not locally. See notes below
+ PF1_AUTHREQ = $00000200; // will get authorisation requests for some or all contacts
+ PF1_ADDED = $00000400; // will get 'you were added' notifications
+ PF1_VISLIST = $00000800; // has an invisible list
+ PF1_INVISLIST = $00001000; // has a visible list for when in invisible mode
+ PF1_INDIVSTATUS = $00002000; // supports setting different status modes to each contact
+ PF1_EXTENSIBLE = $00004000; // the protocol is extensible and supports plugin-defined messages
+ PF1_PEER2PEER = $00008000; // supports direct (not server mediated) communication between clients
+ PF1_NEWUSER = $00010000; // supports creation of new user IDs
+ PF1_CHAT = $00020000; // has a realtime chat capability
+ PF1_INDIVMODEMSG = $00040000; // supports replying to a mode message request with different text depending on the contact requesting
+ PF1_BASICSEARCH = $00080000; // supports a basic user searching facility
+ PF1_EXTSEARCH = $00100000; // supports one or more protocol-specific extended search schemes
+ PF1_CANRENAMEFILE = $00200000; // supports renaming of incoming files as they are transferred
+ PF1_FILERESUME = $00400000; // can resume broken file transfers, see PS_FILERESUME below
+ PF1_ADDSEARCHRES = $00800000; // can add search results to the contact list
+ PF1_CONTACTSEND = $01000000; // can send contacts to other users
+ PF1_CONTACTRECV = $02000000; // can receive contacts from other users
+ PF1_CONTACT = (PF1_CONTACTSEND or PF1_CONTACTRECV);
+ PF1_CHANGEINFO = $04000000; // can change our user information stored on server
+ PF1_SEARCHBYEMAIL = $08000000; // supports a search by e-mail feature
+ PF1_USERIDISEMAIL = $10000000; // set if the uniquely identifying field of the network is the e-mail address
+ PF1_SEARCHBYNAME = $20000000; // supports searching by nick/first/last names
+ PF1_EXTSEARCHUI = $40000000; // has a dialog box to allow searching all the possible fields
+ PF1_NUMERICUSERID = $80000000; // the unique user IDs for this protocol are numeric
+
+ PFLAGNUM_2 = 2; // the status modes that the protocol supports
+ PF2_ONLINE = $00000001; // an unadorned online mode
+ PF2_INVISIBLE = $00000002;
+ PF2_SHORTAWAY = $00000004; // Away on ICQ, BRB on MSN
+ PF2_LONGAWAY = $00000008; // NA on ICQ, Away on MSN
+ PF2_LIGHTDND = $00000010; // Occupied on ICQ, Busy on MSN
+ PF2_HEAVYDND = $00000020; // DND on ICQ
+ PF2_FREECHAT = $00000040;
+ PF2_OUTTOLUNCH = $00000080;
+ PF2_ONTHEPHONE = $00000100;
+
+ PFLAGNUM_3 = 3; //the status modes that the protocol supports
+ //away-style messages for. Uses the PF2_ flags.
+ PFLAG_UNIQUEIDTEXT = 100; //returns a static buffer of text describing the unique field by which this protocol identifies users (already translated), or NULL
+
+ PFLAG_MAXCONTACTSPERPACKET = 200; //v0.1.2.2+: returns the maximum number of contacts which can be sent in a single PSS_CONTACTS.
+
+ PFLAGNUM_4 = 4; // v0.3+: flag asking a protocol plugin how auths are handled
+ PF4_FORCEAUTH = $00000001; // protocol has to send auth's for things to work
+ PF4_FORCEADDED = $00000002; // protocol has to tell people that they were added (otherwise things don't work)
+ PF4_NOCUSTOMAUTH = $00000004; // protocol can't send a custom message while asking others for auth
+
+ PFLAG_UNIQUEIDSETTING = 300; // v0.3+: returns the DB setting name (e.g. szProto=ICQ, szSetting=UIN) that has the ID which makes this user unique on that system (0.3a ONLY), the string is statically allocated so no need to free()
+
+ // for PS_SETSTATUS
+
+ LOGINERR_WRONGPASSWORD = 1;
+ LOGINERR_NONETWORK = 2;
+ LOGINERR_PROXYFAILURE = 3;
+ LOGINERR_BADUSERID = 4;
+ LOGINERR_NOSERVER = 5;
+ LOGINERR_TIMEOUT = 6;
+ LOGINERR_WRONGPROTOCOL = 7;
+
+ // flag for PS_ADDTOLIST
+
+ PALF_TEMPORARY = 1; // add the contact temporarily and invisibly, just to get user info or something
+
+ // flags for PS_GETINFO
+
+ SGIF_MINIMAL = 1; // get only the most basic information. This should
+ // contain at least a Nick and e-mail.
+
+ // for PSR_MESSAGE
+
+ PREF_CREATEREAD = 1; // create the database event with the 'read' flag set
+
+ // for PS_FILERESUME
+
+ FILERESUME_OVERWRITE= 1;
+ FILERESUME_RESUME = 2;
+ FILERESUME_RENAME = 3;
+ FILERESUME_SKIP = 4;
+
+type
+
+ PPROTOSEARCHRESULT = ^TPROTOSEARCHRESULT;
+ TPROTOSEARCHRESULT = record
+ cbSize: int;
+ nick: PChar;
+ firstName: PChar;
+ lastName: PChar;
+ email: PChar;
+ reserved: array [0..15] of Byte;
+ // Protocols may extend this structure with extra members at will and supply
+ // a larger cbSize to reflect the new information, but they must not change
+ // any elements above this comment
+ // The 'reserved' field is part of the basic structure, not space to
+ // overwrite with protocol-specific information.
+ // If modules do this, they should take steps to ensure that information
+ // they put there will be retained by anyone trying to save this structure.
+ end;
+
+ PPROTOSEARCHBYNAME = ^TPROTOSEARCHBYNAME;
+ TPROTOSEARCHBYNAME = record
+ pszNick: PChar;
+ pszFirstName: PChar;
+ pszLastName: PChar;
+ end;
+
+ PPROTORECVEVENT = ^TPROTORECVEVENT;
+ TPROTORECVEVENT = record
+ flags: DWORD;
+ timestamp: DWORD;
+ szMessage: PChar;
+ lParam: LPARAM;
+ end;
+
+ PPROTORECVFILE = ^TPROTORECVFILE;
+ TPROTORECVFILE = record
+ flags: DWORD;
+ timestamp: DWORD; // unix time
+ szDescription: PChar;
+ pFiles: PChar; // pointer to an array of pchar's
+ lParam: LPARAM;
+ end;
+
+ PPROTOFILERESUME = ^TPROTOFILERESUME;
+ TPROTOFILERESUME = record
+ action: int; // FILERESUME_* flag
+ szFilename: PChar; // full path, only valid if action=FILERESUME_RENAME
+ end;
+
+const
+
+ {
+ wParam : PFLAGNUM_* (see above)
+ lParam : 0
+ Affects: Returns a bitfield for settings corresponding to flag number, see notes
+ Returns: a bitfield of supported features -- or 0 if flag_num is not supported
+ Notes : this checks what sort of things are actively supported by a protocol
+ module
+ }
+ PS_GETCAPS = '/GetCaps';
+
+ {
+ wParam : cchName
+ lParam : Pointer to a buffer to fill with human-readable name
+ Affect : Get a human-readable name for the protocol, see notes
+ Result : 0 on success, [non zero] on failure
+ Notes : Should be translated before being returned, cchName
+ has the size of the buffer, example strings: "ICQ", "AIM"
+ }
+ PS_GETNAME = '/GetName';
+
+ {
+ wParam : whichIcon
+ lParam : 0
+ Affect : Loads one of the protocol-sspecific icons
+ Returns: the HICON or NULL on failure, the returned icon
+ must be DestroyIcon()ed, the UI should overlay
+ the online icon with further UI-specified icon to
+ repressent the exact status mode.
+ }
+ PLI_PROTOCOL = $1; // An icon representing the protocol (eg the multicoloured flower for ICQ)
+ PLI_ONLINE = $2; // Online state icon for that protocol (eg green flower for ICQ)
+ PLI_OFFLINE = $3; // Offline state icon for that protocol (eg red flower for ICQ)
+ PLIF_LARGE = $0; // Or with one of the above to get the large (32x32 by default) icon
+ PLIF_SMALL = $10000; // Or with one of the above to get the small (16x16 by default) icon
+
+ PS_LOADICON = '/LoadIcon';
+
+ {
+ wParam : status_mode
+ lParam : Pointer to a null terminated string containing message
+ Affect : Sets the status mode specific message for the user, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : This service is not available unless PF1_MODEMSGSEND is set,
+ and PF1_INDIVMODEMSG is *not* set.
+ If PF1_INDIVMODEMSG is set, then see PSS_AWAYMSSG for details
+ of operations of away messages.
+ -
+ Protocol modules smust support lParam=NULL, it may eithere mean
+ to use an empty message or (preferably) not to reply at all to
+ any requests.
+ }
+ PS_SETAWAYMSG = '/SetAwayMsg';
+
+ {
+ wParam : newMode from statusmodes.inc
+ lParam : 0
+ Affect : Change the protocol's status mode, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : Will send an ack with :
+ type=ACKTYPE_SUCCESS, result=ACKRESULT_SUCCESS, hProcess=previousMode, lParam=newMode
+ -
+ when the change completes. This ack is sent for all changes, not
+ just ones caused by calling this function.
+ -
+ NewMode can be ID_STATUS_CONNECTING<=newMode<ID_STATUS_CONNECTING+
+ MAX_CONNECT_RETRIES to signify that it's connecting and it's the nth retry.
+ -
+ Protocols are initially always in offline mode, if a protocol
+ doesn't support a specific status mode, it should pick the closest
+ ones that it does support, and change to that.
+
+ If a protocol has to switch from offline mode to online (or a substate
+ of online, like away) then it should report any errors in the
+ form of an additional ack :
+
+ type=ACKTYPE_LOGIN, result=ACKRESULT_FAILURE, hProcess=NULL, lParam=LOGINERR_*
+
+ SetStatus() is called when a protocol module is first loaded
+ with newMode=ID_STATUS_ONLINE.
+ -
+ Protocols can define their own LOGINERR_* starting at $1000, see
+ LOGINERR_* above
+ }
+ PS_SETSTATUS = '/SetStatus';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Get the status mode that a protocol is currently in, see notes
+ Returns: The current status mode
+ Notes : Only protocol modules need to implement this, non network level
+ protocol modules do not need to (but if you register as a protocol
+ you need to, Miranda will GPF otherwise)
+ }
+ PS_GETSTATUS = '/GetStatus';
+
+ {
+ wParam : HDBEVENT
+ lParam : 0
+ Affect : allow 'somebody' to add the user to their contact list, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : Auth request come in the form of an event added to the database
+ for the NULL(0) user, the form is:
+ -
+ protocolSpecific: DWORD;
+ nick, firstname, lastName, e-mail, requestReason: ASCIIZ;
+ -
+ HDBEVENT musts be the handle of such an event, one or more
+ fields may be empty if the protocol doesn't support them
+ }
+ PS_AUTHALLOW = '/Authorize';
+
+ {
+ wParam : HDBEVENT
+ lParam : Pointer to a null terminated string containing the reason, see notes
+ Affect : Deny an authorisation request
+ Returns: 0 on success, [non zero] on failure
+ Notes : Protocol modules must be able to cope with lParam=NULL(0)
+ }
+ PS_AUTHDENY = '/AuthDeny';
+
+ {
+ wParam : 0
+ lParam : Pointer to a null terminated string containing an ID to search for
+ Affect : Send a basic search request, see notes
+ Returns: A handle to the search request or NULL(0) on failure
+ Notes : All protocols identify users uniquely by a single field
+ this service will search by that field.
+ -
+ All search replies (even protocol-spec extended searches)
+ are replied by a series of ack's,-
+ -
+ Result acks are a series of:
+ type=ACKTYPE_SEARCH, result=ACKRESULT_DATA, lParam=Pointer to a TPROTOSEARCHRESULT structure
+ -
+ ending ack:
+ type=ACKTYPE_SEARCH, result=ACKRESULT_SUCCESS, lParam=0
+ -
+ The pointers in the structure are not guaranteed to be
+ valid after the ack is complete.
+ -
+ The structure to reply with search results can be extended
+ per protocol basis (see below)
+
+ }
+ PS_BASICSEARCH = '/BasicSearch';
+
+ {
+ wParam : 0
+ lParam : Pointer to a NULL terminated string containing the e-mail to search for
+ Affect : Search for user(s) by e-mail address, see notes
+ Returns: A HANDLE to the search, or NULL(0) on failure
+ Notes : Results are returned as for PS_BASICSEARCH, this service
+ is only available if the PF1_USERIDISEMAIL flag is set for caps --
+ -
+ This service with the above service should be mapped to the same
+ function if the aforementioned flag is set.
+ Version: v0.1.2.1+
+ }
+ PS_SEARCHBYEMAIL = '/SearchByEmail';
+
+ {
+ wParam : 0
+ lParam : Pointer to a TPROTOSEARCHBYNAME structure
+ Affect : Search for users by name, see notes
+ Returns: Handle to the search, NULL(0) on failure
+ Notes : this service is only available, if PF1_SEARCHBYNAME capability is set.
+ Results are returned in the same manner as PS_BASICSEEARCH
+ Version: v0.1.2.1+
+ }
+ PS_SEARCHBYNAME = '/SearchByName';
+
+ {
+ wParam : 0
+ lParam : Handle to window owner
+ Affect : Create the advanced search dialog box, see notes
+ Returns: A window handle, or NULL(0) on failure
+ Notes : this service is only available if PF1_EXTSEARCHUI capability is
+ set, advanced search is very protocol-spec'd so it is left to
+ the protocol itself to supply a dialog containing the options,
+ this dialog should not have a titlebar and contain only search
+ fields. the rest of the UI is supplied by Miranda.
+ -
+ The dialog should be created with CreateDialog() or it's kin
+ and still be hidden when this function returns,
+ -
+ The dialog will be destroyed when the find/add dialog is closed
+ Version: v0.1.2.1+
+ }
+ PS_CREATEADVSEARCHUI= '/CreateAdvSearchUI';
+
+ {
+ wParam : 0
+ lParam : Handle to advanced search window handle
+ Affect : Search using the advanced search dialog, see notes
+ Returns: A handle or NULL(0) on failure
+ Notes : Results are returned in the same manner as PS_BASICSEARCH,
+ this service is only available if PF1_EXTSEARCHUI capability is set
+ Version: v0.1.2.1+
+ }
+ PS_SEARCHBYADVANCED = '/SearchByAdvanced';
+
+ {
+ wParam : flags
+ lParam : Pointer to a TPROTOSEARCHRESULT structure
+ Affect : Adds a search result to the contact list, see notes
+ Returns: A handle to the new contact (HCONTACT) or NULL(0) on failure
+ Notes : The pointer MUST be a result returned by a search function
+ since there maybe extra protocol-spec data required by the protocol.
+ -
+ the protocol module should not allow duplicate contains to be added,
+ but if such a request *is* received it should return a HCONTACT
+ to the original user,
+ -
+ If flags is PALF_TEMPORARY set, the contact should be added
+ temorarily and invisiblely, just to get the user info (??)
+ -
+ }
+ PS_ADDTOLIST = '/AddToList';
+
+ {
+ wParam : MAKEWPARAM(flags, iContact)
+ lParam : HDBEVENT
+ Affects: Add a contact to the contact list given an auth/added/contacts events, see notes
+ Returns: A HCONTACT or NULL(0) on failure
+ Notes : HDBEVENT must be either EVENTTYPE_AUTHREQ or EVENTTYPE_ADDED
+ flags are the same as PS_ADDTOLIST,
+ -
+ iContacts is only used for contacts vents, it is 0-based index
+ of the contacts in the event to add, there's no way to add two or more
+ contacts at once, you should just call this as many times as needed.
+ }
+ PS_ADDTOLISTBYEVENT = '/AddToListByEvent';
+
+ {
+ wParam : InfoType
+ lParam : Pointer to InfoData
+ Affect : Changes user details as stored on the server, see notes
+ Returns: A Handle to the change request or NULL(0) on failure
+ Notes : the details stored on the server are very protocol spec'd
+ so this service just supplies an outline for protocols to use.
+ See protocol-specific documentation for what infoTypes are available
+ and what InfoData should be for each infoTypes.
+ -
+ Sends an ack type=ACKTYPE_SETINFO, result=ACKRESULT_SUCCESS/FAILURE, lParam=0
+ -
+ This description just leaves me cold.
+ Version: v0.1.2.0+
+ }
+ PS_CHANGEINFO = '/ChangeInfo';
+
+ {
+ wParam : HFILETRANSFER
+ lParam : Pointer to a initalised TPROTOFILERESUME
+ Affect : Informs the protocol of the user's chosen resume behaviour, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : If the protocol supports file resume (caps: PF1_FILERESUME) then before
+ each file receive begins it will broadcast an ack with :
+
+ type=ACKTYPE_FILE, result=ACKRESULT_RESUME, hProcess=hFileTransfer,
+ lParam = TPROTOFILETRANSFERSTATUS.
+
+ If the UI processes this ack it must return a [non zero] valuee from it's
+ hook, it all the hooks complete without returning [non zero] then the
+ protocol will assume that no resume UI was available and will continue
+ to receive the file with a default behaviour (default: overwrite)
+ -
+ If a hook does return [non zero] then that UI MUST call this service,
+ PS_FILERESUME at some point.
+ When the protocol module receives this call it will proceed wit the
+ file recieve usingg the given information.
+ -
+ Having sasid that, PS_FILERESUME MUST be called, it is also
+ acceptable to completely abort the transfer instead, i.e. the file
+ exists locally and the user doesn't want to overwrite or resume or
+ reget.
+ Version: v0.1.2.2+
+ }
+ PS_FILERESUME = '/FileResume';
+
+ // these should be called with CallContactService()
+
+ {<</
+ !IMPORTANT!
+ wParam, lParam data expected declarations should be treated with
+ one level of indirection, where it says (CCSDATA: Yes)
+ should be :
+
+ What you *actually* get in the service:
+
+ wParam = 0
+ lParam = pCCSDATA
+
+ CCSDATA contains the ..wParam, ..lParam, hContact data declared with each service,
+ so the wParam, lParam passed does not contain the data itself, but lParam
+ contains a pointer to a structure which contains the data.
+
+ />>}
+
+ {
+ CCSDATA: Yes
+ wParam : flags
+ Param : 0
+
+ Affect : Updates a contact's details from the server, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes :
+
+ flags which may have SGIF_MINIMAL set to only get
+ "basic" information, such as nickname, email address.
+
+ PCCSDATA(lParam)^.hContact has the HCONTACT handle to get user
+ information for.
+
+ Will update all the information in the database and then
+ send acks with :
+
+ type=ACKTYPE_GETINFO, result=ACKRESULT_SUCCESS, hProcess=nReplies, lParam=thisReply
+ -
+ Since some protocol do not allow the module to tell when it has
+ got all the information so it can send a final ack, one
+ ack will be sent after each chunk of data has been received,
+ -
+ nReplies contains the number of distinct acks
+ that will be sent to get all the information, 'thisReply'
+ is the zero based index of this ack.
+ When thisReply=0 the minimal information has just been received,
+ all other numbering is arbitrary.
+
+ }
+ PSS_GETINFO = '/GetInfo';
+
+ {
+ CCSDATA: Yes
+ wParam : flags
+ lParam : Pointer to a null terminated string
+ Affect : Send an instant message
+ Returns: an hProcess corresponding to an ACK which will be sent after
+ the hProcess.
+ Notes: type=ACKTYPE_MESSAGE, result=ACKRESULT_SUCCESS/FAILURE, lParam = 0
+ -
+ here's the deal, you must return a 'seq' from this service
+ which you have to ack when the message actually get's sent,
+ or send a fake ack sometime soon if you can't find out if the message
+ was successfully received with the protocol that you're using.
+ -
+ this event is NOT added to the database automatically.
+ }
+ PSS_MESSAGE = '/SendMsg';
+
+ {
+ CCSDATA: Yes
+ wParam : flags
+ lParam : null terminated string to the URL, see notes
+ Affect : Send a URL message, see notes
+ Returns: A hProcess which will be ack'd later
+ Notes : lParam may contain TWO strings, the first for URL, the second for
+ description, in the format :
+ <url>#0<desc>#0 or <url>#0#0
+ Will send an ack for hProcess when the URL actually gets sent
+ type=ACKTYPE_URL, result=ACKRESULT_SUCCESS/FAILURE, lParam=0
+ -
+ protocol modules are free to define flags starting at $10000
+ -
+ The event will *not* be added to the database automatically
+ }
+ PSS_URL = '/SendUrl';
+
+ {
+ CCSDATA: Yes
+ wParam : MAKEWPARAM(flags)
+ lParam : Pointer to hContactsList
+ Affect : Send a set of contacts, see notes
+ Returns: A hProcess which will be ack, NULL(0) on failure
+ Notes : hContactsList is an array of nContacts handles to contacts,
+ if this array includes one or more contains that can not be transferred
+ using this protocol the function will fail.
+ -
+ Will send an ack when the contacts actually get sent:
+
+ type=ACKTYPE_CONTACTS, result=ACKRESULT_SUCCESS/FAILURE, lParam=0
+ -
+ No flags have ben defined yet,
+ -
+ The event will *not* be added to the database automatically
+ }
+ PSS_CONTACTS = '/SendContacts';
+
+ {
+ CCSDATA: Yes
+ wParam : 0
+ lParam : 0
+ Affect : Send a request to retrieve HCONTACT's mode message, see notes
+ Returns: a hProcess which will be ack'd later, NULL(0) on failure
+ Notes : the reply will come in a form of an ack :
+
+ type=ACKTYPE_AWAYMSG, result=ACKRESULT_SUCCESS/FAILURE,
+ lParam=pointer to a null terminated string the containing message
+ }
+ PSS_GETAWAYMSG = '/GetAwayMsg';
+
+ {
+ CCSDATA: Yes
+ wParam : hProcess
+ lParam : pointer to a buffer to fill with away message to reply with
+ Affect : Sends an away message reply to a user, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : This service must only be called is caps has PF1_MODEMSGSEND set
+ as well as PF1_INDIVMODEMSG otherwise PS_SETAWAYMESSAGE should
+ be used.
+ -
+ Reply will be sent in the form of an ack :
+
+ type=ACKTYPE_AWAYMSG, result=ACKRESULT_SENTREQUEST, lParam=0
+ }
+ PSS_AWAYMSG = '/SendAwayMsg';
+
+ {
+ CCSDATA: Yes
+ wParam : status_mode
+ lParam : Pointer to a TPROTORECVEVENT structure
+ Affect : An away message reply has been received
+ }
+ PSR_AWAYMSG = '/RecvAwayMsg';
+
+ {
+ CCSDATA: Yes
+ wParam : status_mode
+ lParam : 0
+ Affect : Set the status mode the user will appear in to a user, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : If status_mode = 0 then revert to normal state for the user,
+ ID_STATUS_ONLINE is possible if PF1_VISLIST
+ ID_STATUS_ONLINE is possible if PF1_INDIVSTATUS
+ }
+ PSS_SETAPPARENTMODE = '/SetApparentMode';
+
+ // only valid if caps support IM xfers
+
+ {
+ CCSDATA: Yes
+ wParam : HTRANSFER
+ lParam : null terminated string containing the path
+ Affect : Allow a file transfer to begin, see notes
+ Returns: A handle to the transfer to be used from now on.
+ Notes : If the path does not point to a directory then:
+ if a single file is being transfered and the protocol supports
+ file renaming (PF1_CANRENAMEFILE) then the file is given
+ this name, othewise the file is removed and file(s) are placed
+ into the resulting directory.
+ -
+ File transfers are marked by a EVENTTYPE_FILE added to the database.
+ The format is :
+ hTransfer: DWORD
+ filename(s), description: ASCIIZ
+ }
+ PSS_FILEALLOW = '/FileAllow';
+
+ {
+ CCSDATA: Yes
+ wParam : HTRANSFER
+ lparam : Pointer to a buffer to be filled with reason
+ Affect : Refuses a file transfer request
+ Returns: 0 on success, [non zero] on failure
+ }
+ PSS_FILEDENY = '/FileDeny';
+
+ {
+ CCSDATA: Yes
+ wParam : HTRANSFER
+ lParam : 0
+ Affect : Cancel an in-progress file transfer
+ Returns: 0 on success, [non zero] on failure
+ }
+ PSS_FILECANCEL = '/FileCancel';
+
+ {
+ CCSDATA: Yes
+ wParam : null terminated string containing description
+ lParam : pointer to an array of pchar's containing file paths/directories
+ Affect : Start a file(s) send, see notes
+ Returns: A HTRANSFER handle on success, NULL(0) on failur
+ Notes : All notifications are done thru acks :
+ -
+ type=ACKTYPE_FILE, if result=ACKRESULT_FAILED then
+ lParam=null terminated string containing reason
+ }
+ PSS_FILE = '/SendFile';
+
+ // Receiving Services
+ {>>/
+ Receiving Services:
+ Before a message is sent to /RecvMessage it goes through a MS_PROTO_CHAINRECV
+ which allows any other module to change data (for decryption, etc),
+ this then reaches /RecvMessage.
+
+ This does not have to be the same structure/memory contained within that
+ structure that started the chain call.
+
+ /RecvMessage adds the event to the database, any other modules who
+ are interested in what message the user will see should hook at this point.
+ />>}
+
+ {
+ CCSDATA: Yes
+ wParam : 0
+ lParam : Pointer to a TPROTORECVEVENT
+ Affect : An instant message has beeen received, see notes
+ Returns: 0
+ Notes : lParam^.lParam^.szMessage has the message, see structure above
+ stored as DB event EVENTTYPE_MESSAGE, blob contains message
+ string without null termination.
+ }
+ PSR_MESSAGE = '/RecvMessage';
+
+ {
+ CCSDATA: Yes
+ wParam : 0
+ lParam : Pointer to a TPROTORECVEVENT, see notes
+ Affect : A URL has been received
+ Notes : szMessage is encoded the same as PSS_URL
+ -
+ Stored in the database : EVENTTYPE_URL, blob contains message
+ without null termination
+ }
+ PSR_URL = '/RecvUrl';
+
+ {
+ CCSDATA: Yes
+ wParam : 0
+ lParam : Pointer to a TPROTORECVEVENT
+ Affect : Contacts have been received, see notes
+ Notes : pre.szMessage is actually a PROTOSEARCHRESULT list
+ pre.lParam is the number of contains in that list.
+ -
+ PS_ADDTOLIST can be used to add contacts to the list
+ -
+ repeat [
+ ASCIIZ userNick
+ ASCIIZ userId
+ ]
+ userNick should be a human-readable description of the user. It need not
+ be the nick, or even confined to displaying just one type of
+ information.
+ userId should be a machine-readable representation of the unique
+ protocol identifying field of the user. Because of the need to be
+ zero-terminated, binary data should be converted to text.
+ Use PS_ADDTOLISTBYEVENT to add the contacts from one of these to the list.
+ }
+ PSR_CONTACTS = '/RecvContacts';
+
+ {
+ CCSDATA: Yes
+ wParam : 0
+ lParam : Pointer to a TPROTORECVFILE
+ Affect : File(s) have been received
+ }
+ PSR_FILE = '/RecvFile';
+
+{$ENDIF}
+
diff --git a/plugins/ShlExt/inc/m_skin.inc b/plugins/ShlExt/inc/m_skin.inc
new file mode 100644
index 0000000000..aee28ae4d9
--- /dev/null
+++ b/plugins/ShlExt/inc/m_skin.inc
@@ -0,0 +1,120 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+{$IFNDEF M_SKIN}
+{$DEFINE M_SKIN}
+
+const
+
+ // event icons
+
+ SKINICON_EVENT_MESSAGE = 100;
+ SKINICON_EVENT_URL = 101;
+ SKINICON_EVENT_FILE = 102;
+
+ // other icons
+ SKINICON_OTHER_MIRANDA = 200;
+ SKINICON_OTHER_EXIT = 201;
+ SKINICON_OTHER_SHOWHIDE = 202;
+ SKINICON_OTHER_GROUPOPEN = 203; // v0.1.1.0+
+ SKINICON_OTHER_GROUPSHUT = 205; // v0.1.1.0+
+ SKINICON_OTHER_USERONLINE = 204; // v0.1.0.1+
+
+ // menu icons are owned by the module that uses them so are not and should not
+ // be skinnable. Except exit and show/hide
+
+ // status mode icons. NOTE: These are deprecated in favour of LoadSkinnedProtoIcon()
+ SKINICON_STATUS_OFFLINE = 0;
+ SKINICON_STATUS_ONLINE = 1;
+ SKINICON_STATUS_AWAY = 2;
+ SKINICON_STATUS_NA = 3;
+ SKINICON_STATUS_OCCUPIED = 4;
+ SKINICON_STATUS_DND = 5;
+ SKINICON_STATUS_FREE4CHAT = 6;
+ SKINICON_STATUS_INVISIBLE = 7;
+ SKINICON_STATUS_ONTHEPHONE = 8;
+ SKINICON_STATUS_OUTTOLUNCH = 9;
+
+type
+
+ PSKINSOUNDDESC = ^TSKINSOUNDDESC;
+ TSKINSOUNDDESC = record
+ cbSize: int;
+ { name to refer to sound when playing and in DB }
+ pszName: PChar;
+ { description to use for it in options dialog }
+ pszDescription: PChar;
+ { the default sound file to use, WITHOUT path }
+ pszDefaultFile: PChar;
+ end;
+
+const
+
+ {
+ wParam : ICON_ID
+ lParam : 0
+ Affect : Load an icon from the user's custom skin lib, or from the exe
+ if there isn't one loaded, see notes
+ Return : HICON for the new icon, do *not* DestroyIcon() the return value
+ returns NULL(0) if ICON_ID is invalid, but always success for a valid
+ ID.
+ }
+ MS_SKIN_LOADICON = 'Skin/Icons/Load';
+
+ {
+ wParam : null terminated string containing the protocol name
+ lParam : status_wanted
+ Affect : Load an icon representing the status_wanted for a particular protocol, see notes
+ Returns: an HICON for the new icon, do NOT DestroyIcon() the return value
+ returns NULL(0) on failure.
+ Notes : If wParam is NULL(0) the service will load the user's selected
+ 'all protocols' status icon
+ }
+ MS_SKIN_LOADPROTOICON = 'Skin/Icons/LoadProto';
+
+ {
+ wParam : 0
+ lParam : Pointer to a initialised SKINSOUNDDESC
+ Affect : Add a new sound so it has a default and can be changed in the options dialog
+ Returns: 0 on success, [non zero] on failure
+ }
+ MS_SKIN_ADDNEWSOUND = 'Skin/Sounds/AddNew';
+
+ {
+ wParam : 0
+ lParam : Pointer to a null terminated string containing the name of the sound to play
+ Affect : play a named sound event, play name should of been added
+ with MS_SKIN_ADDNEWSOUND, see notes
+ Notes : function will not fail, it will play the Windows
+ }
+ MS_SKIN_PLAYSOUND = 'Skin/Sounds/Play';
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Sent when the icons DLL has been changed in the options dialog
+ and everyone should remake their image lists.
+ }
+ ME_SKIN_ICONSCHANGED = 'Skin/IconsChanged';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_system.inc b/plugins/ShlExt/inc/m_system.inc
new file mode 100644
index 0000000000..90d7f53858
--- /dev/null
+++ b/plugins/ShlExt/inc/m_system.inc
@@ -0,0 +1,170 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+{$IFNDEF M_SYSTEM}
+{$DEFINE M_SYSTEM}
+
+type
+
+ TMM_INTERFACE = record
+ cbSize: int;
+ _malloc: function(cbSize: Integer): Pointer; cdecl;
+ _realloc: function (pb: Pointer; cbSize: Integer): Pointer; cdecl;
+ _free: procedure(pb: Pointer); cdecl;
+ end;
+
+const
+
+ MIRANDANAME = 'Miranda';
+
+ {
+ wParam : 0
+ lParam : 0
+ affect : called after all modules have been successfully initialised
+ used to resolve double-dependencies in the module load order, see notes
+ return : 0
+ notes : Can be used to call services, etc that have not yet loaded
+ when your module has.
+ }
+ ME_SYSTEM_MODULESLOADED = 'Miranda/System/ModulesLoaded';
+
+ {
+ wParam : 0
+ lParam : 0
+ affect : called just before Miranda terminates, the database is still running
+ during this hook
+ return : 0
+ }
+ ME_SYSTEM_SHUTDOWN = 'Miranda/System/Shutdown';
+
+ {
+ wParam : 0
+ lParam : 0
+ affect : called before Miranda actually shuts down -- everyone has to agree
+ or it is not shut down.
+ return : non zero to stop the shutdown
+ }
+ ME_SYSTEM_OKTOEXIT = 'Miranda/System/OkToExitEvent';
+
+ {
+ wParam : 0
+ lParam : 0
+ affect : service which sends everyone the ME_SYSTEM_OKTOEXIT event
+ return : true if everyone is okay to exit, otherwise false
+ }
+ MS_SYSTEM_OKTOEXIT = 'Miranda/System/OkToExit';
+
+ {
+ wParam : 0
+ lParam : 0
+ return : returns the version number -- each byte set with version index,
+ e.g. 1.2.3.4 $01020304
+ }
+ MS_SYSTEM_GETVERSION = 'Miranda/System/GetVersion';
+
+ {
+ wParam : size in bytes of the buffer to be filled
+ lParam : pointer to the buffer to be filled
+ affect : returns Miranda's version as text with build type such as '1.2.3.4 alpha'
+ return : 0 on success -- non zero on failure
+ }
+ MS_SYSTEM_GETVERSIONTEXT = 'Miranda/System/GetVersionText';
+
+ {
+ wParam : Handle of a wait object to be used
+ lParam : pointer to service name
+ affect : causes the service name to be called whenever the wait object
+ is signalled with CallService(Service, wParam=hWaitObjeect, lParam=0)
+ return : 0 on success, non zero on failure, will always fail if
+ more than 64 event objects are already being waited on because
+ of the limit imposed by Windows.
+ version: implemented after v0.1.2.0+
+ other : QueueUserAPC() can be used instead of this service to wait
+ for notifications, BUT *only* after v0.1.2.2+ since that deals
+ with APC's
+ }
+ MS_SYSTEM_WAITONHANDLE = 'Miranda/System/WaitOnHandle';
+
+ {
+ wParam : hWaitObject to be removed
+ lParam : 0
+ affect : removes the wait object from the list, see above.
+ returns: 0 on success, nonzero on failure
+ version: implemented after v0.1.2.0+
+ }
+ MS_SYSTEM_REMOVEWAIT = 'Miranda/System/RemoveWait';
+
+ {
+ wParam : 0
+ lParam : Pointer to an initialised TMM_INTERFACE
+ affect : Get function pointers to, malloc(), free() and realloc() used by Miranda
+ note : this should only be used carefully, make sure .cbSize is initialised with sizeof(TMM_INTERFACE)
+ version: 0.1.2.2+
+ }
+ MS_SYSTEM_GET_MMI = 'Miranda/System/GetMMI';
+
+ {
+ wParam=0
+ lParam=0
+
+ Add a thread to the unwind wait stack that Miranda will poll on
+ when it is tearing down modules.
+
+ This must be called in the context of the thread that is to be pushed
+ i.e. there are no args, it works out what thread is being called
+ and gets itself a handle to the calling thread.
+ }
+ MS_SYSTEM_THREAD_PUSH = 'Miranda/Thread/Push';
+
+ {
+ wParam=0
+ lParam=0
+
+ Remove a thread from the unwind wait stack -- it is expected
+ that the call be made in the context of the thread to be removed.
+
+ Miranda will begin to tear down modules and plugins if/when the
+ last thread from the unwind stack is removed.
+ }
+ MS_SYSTEM_THREAD_POP = 'Miranda/Thread/Pop';
+
+ {
+ wParam=0
+ lParam=0
+
+ This hook is fired just before the thread unwind stack is used,
+ it allows MT plugins to shutdown threads if they have any special
+ processing to do, etc.
+ }
+ ME_SYSTEM_PRESHUTDOWN = 'Miranda/System/PShutdown';
+
+ {
+ wParam=0
+ lParam=0
+
+ Returns TRUE when Miranda has got WM_QUIT and is in the process
+ of shutting down
+ }
+ MS_SYSTEM_TERMINATED = 'Miranda/SysTerm';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_url.inc b/plugins/ShlExt/inc/m_url.inc
new file mode 100644
index 0000000000..a541576329
--- /dev/null
+++ b/plugins/ShlExt/inc/m_url.inc
@@ -0,0 +1,39 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_URL}
+{$DEFINE M_URL}
+
+const
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affects: bring up the send URL dialogbox for a user
+ Returns: 0 on success, nonzero on failure, see notes
+ Notes : service returns before the URL is sent.
+ }
+ MS_URL_SENDURL = 'SRUrl/SendCommand';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_userinfo.inc b/plugins/ShlExt/inc/m_userinfo.inc
new file mode 100644
index 0000000000..8f5e1eb654
--- /dev/null
+++ b/plugins/ShlExt/inc/m_userinfo.inc
@@ -0,0 +1,84 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_USERINFO}
+{$DEFINE M_USERINFO}
+
+const
+
+ {
+ wParam : HCONTACT
+ lParam : 0
+ Affects: Show the user details dialog box for a contact, see notes
+ Notes : I think this can be used to display "My User Details"... if NULL(0) is used
+ }
+ MS_USERINFO_SHOWDIALOG = 'UserInfo/ShowDialog';
+
+ {
+ wParam : 0
+ lParam : HCONTACT
+ Affects: The details dialog box was opened for a contact maybe NULL(0)
+ showing the user details -- see notes
+ Notes : The module should do whatever initialisation they need and
+ call MS_USERINFO_ADDPAGE one or more times if they want
+ pages displayed in the options dialog -- wParam should be passed
+ straight as the wParam of MS_USERINFO_ADDPAGE.
+ -
+ The builtin userinfo module is not loaded til after all plugins
+ have loaded -- therefore a HookEvent() for this event will fail,
+ use ME_SYSTEM_MODULESLOADED event to know when everything has
+ loaded and it's okay to hook this event.
+ Version: v0.1.2.0+
+ }
+ ME_USERINFO_INITIALISE = 'UserInfo/Initialise';
+
+ {
+ wParam : wParam from ME_USERINFO_INITIALISE
+ lParam : pointer to an initialised OPTIONSDIALOGPAGE (see m_options.inc)
+ Affects: Adds a page to the details dialog, see notes
+ Notes : this service should only be called within the ME_USERINFO_INITIALISE
+ event -- when the pages get (WM_INITDIALOG lParam=HCONTACT) strings
+ in the passed dialog structure can be freed soon as the service returns
+ icons must be kept around (not a problem if you're loading from resource).
+ -
+ The group elements within the OPTIONSDIALOGPAGE are ignored,
+ details dialog page should be 222x132 DLU's -- the details dialog
+ box currently has no cancel button, pages will be sent PSN_INFOCHANGED
+ thru WM_NOTIFY (idFrom=0) when a protocol ACK is broadcast for
+ the correct contact with the type ACKTYPE_GETINFO.
+ -
+ PSN_INFOCHANGED will also be sent just after the page is created
+ to help you out.
+ -
+ All PSN_* WM_NOTIFY messages have PSHNOTIFY.lParam=(LPARAM)hContact
+ Version: v0.1.2.0+
+ }
+
+ PSN_INFOCHANGED = 1;
+ { force-send a PSN_INFOCHANGED to all pages }
+ PSM_FORCECHANGED = ($0400 + 100);
+
+ MS_USERINFO_ADDPAGE = 'UserInfo/AddPage';
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/m_utils.inc b/plugins/ShlExt/inc/m_utils.inc
new file mode 100644
index 0000000000..325b61db24
--- /dev/null
+++ b/plugins/ShlExt/inc/m_utils.inc
@@ -0,0 +1,279 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF M_UTILS}
+{$DEFINE M_UTILS}
+
+const
+
+ RD_ANCHORX_CUSTOM = 0; // function did everything required to the x axis, do no more processing
+ RD_ANCHORX_LEFT = 0; // move the control to keep it constant distance from the left edge of the dialog
+ RD_ANCHORX_RIGHT = 1; // move the control to keep it constant distance from the right edge of the dialog
+ RD_ANCHORX_WIDTH = 2; // size the control to keep it constant distance from both edges of the dialog
+ RD_ANCHORX_CENTRE = 4; // move the control to keep it constant distance from the centre of the dialog
+ RD_ANCHORY_CUSTOM = 0;
+ RD_ANCHORY_TOP = 0;
+ RD_ANCHORY_BOTTOM = 8;
+ RD_ANCHORY_HEIGHT = 16;
+ RD_ANCHORY_CENTRE = 32;
+
+ // for MS_UTILS_RESTOREWINDOWPOSITION
+
+ RWPF_NOSIZE = 1; // don't use stored size info: leave dialog same size
+ RWPF_NOMOVE = 2; // don't use stored position
+
+ // for WNDCLASS_COLOURPICKER
+
+ CPM_SETCOLOUR = $1000;// lParam=new colour
+ CPM_GETCOLOUR = $1001;// returns colour
+ CPM_SETDEFAULTCOLOUR = $1002;// lParam=default, used as first custom colour
+ CPM_GETDEFAULTCOLOUR = $1003;// returns colour
+ CPN_COLOURCHANGED = 1; // sent through WM_COMMAND
+
+type
+
+ PUTILRESIZECONTROL = ^TUTILRESIZECONTROL;
+ TUTILRESIZECONTROL = record
+ cbSize: int;
+ wId: int; // control ID
+ rcItem: TRect; // original control rectangle, relative to dialog
+ // modify in-placee to specify the new position
+ dlgOriginalSize: TSize; // size of dialog client area in template
+ dlgNewSize: TSize; // current size of dialog client area
+ end;
+
+ TDIALOGRESIZERPROC = function(hwndDlg: THandle; lParam: LPARAM; urc: PUTILRESIZECONTROL): int; cdecl;
+
+ PUTILRESIZEDIALOG = ^TUTILRESIZEDIALOG;
+ TUTILRESIZEDIALOG = record
+ cbSize: int;
+ hwndDlg: THandle;
+ hInstance: THandle;
+ lpTemplate: PChar;
+ lParam: LPARAM;
+ pfnResizer: TDIALOGRESIZERPROC;
+ end;
+
+ PCountryListEntry = ^TCountryListEntry;
+ TCountryListEntry = record
+ id: int;
+ szName: PChar;
+ end;
+
+ PWINDOWLISTENTRY = ^TWINDOWLISTENTRY;
+ TWINDOWLISTENTRY = record
+ hList: THandle;
+ hWnd: THandle;
+ hContact: THandle;
+ end;
+
+ PSAVEWINDOWPOS = ^TSAVEWINDOWPOS;
+ TSAVEWINDOWPOS = record
+ hWnd: THandle;
+ hContact: THandle;
+ szModule: PChar; // module name eto store the settings in
+ szNamePrefix: PChar; // text to prefix on 'x', 'width', etc
+ end;
+
+const
+
+ {
+ wParam : bOpenInNewWindow
+ lParam : Pointer to a null terminated string containing Url
+ Affect : Open a URRL in the user's default web browser, see notes
+ Returns: 0 on success, [non zero on failure]
+ Notes : bOpenInWindow should be zero to open the URL in the browoser window
+ the user last used, or nonzero to open in a new browser window,
+ if there's no browser running, it will be started to show the URL
+ Version: v0.1.0.1+
+ }
+ MS_UTILS_OPENURL = 'Utils/OpenURL';
+
+ {
+ wParam : 0
+ lParam : Pointer to an initalised TUTILRESIZEDIALOG structure
+ Affect : Resize a dialog by calling a custom routine to move each control, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : Does not support DIALOGTEMPLATEEX dialogboxes, and will return
+ failure if you try to resize one.-
+ the dialog iteself should have been resized prior to calling this
+ service, .pfnResizer is called once for each control in the dialog
+ .pfnResizer should return a combination of one RD_ANCHORx_ and one RD_ANCHORy constant
+ Version: v0.1.0.1+
+ }
+ MS_UTILS_RESIZEDIALOG = 'Utils/ResizeDialog';
+
+ {
+ wParam : countryID
+ lParam : 0
+ Affect : Get the name of a country given it's number, e.g. 44 = UK
+ Returns: Returns a pointer to a string containing the country name on success
+ NULL(0) on failure
+ Version: v0.1.2.0+
+ }
+ MS_UTILS_GETCOUNTRYBYNUMBER = 'Utils/GetCountryByNumber';
+
+ {
+ wParam : Pointer to an int to be filled with count -- !TODO! test.
+ lParam : Pointer to an PCountryListEntry, see notes
+ Affect : Get the full list of country IDs, see notes
+ Returns: 0 always
+ Notes : the list is sorted alphabetically by name, on the assumption
+ it's quicker to search numbers that are out of outer, than strings
+ that are out of order. a NULL(0) entry terminates
+ -
+ Neither wParam or lParam can be NULL(0)
+ -
+ lParam is filled with the first entry, it can be accessed as a pointer,
+ to get the next entry, increment the pointer by sizeof(Pointer) NOT
+ sizeof(TCountryList), only increment the pointer as many times as
+ given by iCount.
+ -
+ this data can NOT be copied if an array of TCountryListEntry's is passed
+ so don't try it.
+ Version: v0.1.2.0+
+ }
+ MS_UTILS_GETCOUNTRYLIST = 'Utils/GetCountryList';
+
+ // see WindowList_* functions below
+
+ {
+ wParam : 0
+ lParam : 0
+ Affect : Allocate a window list
+ Returns: A handle to the new window list
+ Version: v0.1.0.1+
+ }
+ MS_UTILS_ALLOCWINDOWLIST = 'Utils/AllocWindowList';
+
+ {
+ wParam : 0
+ lParam : Pointer to an initalised TWINDOWLISTENTRY structure
+ Affect : Add a window to a given window list handle
+ Returns: 0 on success, [non zero] on failure
+ Version: v0.1.0.1+
+ }
+ MS_UTILS_ADDTOWINDOWLIST = 'Utils/AddToWindowList';
+
+ {
+ wParam : Handle to window list to remove from
+ lParam : Window handle to remove
+ Affect : Remove a window from the specified window list
+ Returns: 0 on success, [non zero] on failure
+ Version: v0.1.0.1+
+ }
+ MS_UTILS_REMOVEFROMWINDOWLIST = 'Utils/RemoveFromWindowList';
+
+ {
+ wParam : Handle to the window list to look in
+ lParam : Handle to a HCONTACT to find in the window list
+ Affect : Find a window handle given the hContact
+ Returns: The found window handle or NULL(0) on failure
+ Version: v0.1.0.1+
+ }
+ MS_UTILS_FINDWINDOWINLIST = 'Utils/FindWindowInList';
+
+ {
+ wParam : Handle to window list
+ lParam : Pointer to TMSG (initalised with what to broadcast)
+ Affect : Broadcast a message to all windows in a list, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : only TMSG.Message, .wParam, .lParam are used
+ Version: v0.1.0.1+
+ }
+ MS_UTILS_BROADCASTTOWINDOWLIST = 'Utils/BroadcastToWindowList';
+
+ {
+ There aren't any services here, there's no need for them, the control class
+ will obey the SS_LEFT (0), SS_CENTER (1), SS_RIGHT(2) styles
+ the control will send STN_CLICKED via WM_COMMAND when the link itself is clicked
+ -
+ These are defined by STATIC controls and STN_CLICKED is sent to standard
+ STATIC classes when they're clicked -- look at WINAPI docs for more info
+ }
+ WNDCLASS_HYPERLINK = 'Hyperlink';
+
+ {
+ wParam : 0
+ lParam : Pointer to a initialised TSAVEWINDOWPOS structure
+ Affect :
+ Returns: 0 on success, [non zero] on failure
+ Notes :
+ Version: v0.1.1.0+
+ }
+ MS_UTILS_SAVEWINDOWPOSITION = 'Utils/SaveWindowPos';
+
+ {
+ wParam : see RWPF_* flags
+ lParam : Pointer to a initalised TSAVEWINDOWPOS
+ Affect : Restores the position of a window from the database, see notes
+ Returns: 0 on success, [non zero] on failure
+ Notes : If no position info was found, the service will return 1.
+ The NoSize version won't use stored information size, the window
+ is left the same size
+ -
+ See Utils_RestoreWindowPosition() Helper function, this function is
+ a bit different from the C function (which can be inlined too! dammit)
+ that there's only one function and not three (which just passed different flags)
+ Version: v0.1.1.0+
+ }
+ MS_UTILS_RESTOREWINDOWPOSITION = 'Utils/RestoreWindowPos';
+
+ {
+ Colour picker control, see CPM_* and CPN_* constants above
+ }
+ WNDCLASS_COLOURPICKER = 'ColourPicker';
+
+ {
+ wParam : 0
+ lParam : Pointer to a null terminated string containing filename
+ Affect : Loads a bitmap (or other graphic type, see Notes
+ Returns: HBITMAP on success, NULL(0) on failure
+ Notes : This function also supports JPEG, GIF (and maybe PNG too)
+ For speed, if the file extention is .bmp or .rle it will use LoadImage()
+ and not load OLE for the extra image support
+ -
+ Remember to delete the returned handle with DeleteObject (see GDI documentation for WINAPI)
+ Version: v0.1.2.1+
+ }
+ MS_UTILS_LOADBITMAP = 'Utils/LoadBitmap';
+
+ {
+ wParam : byte length of buffer (not to be confused with byte range)
+ lParam : Pointer to buffer
+ Affect : Get the filter strings for use in the open file dialog, see notes
+ Returns: 0 on success [non zero] on failure
+ Notes : See the WINAPI under OPENFILENAME.lpStrFiler for formatting,
+ an 'All bitmaps' item is alway first, and 'All files' is always last
+ -
+ The returned string is always formatted
+ -
+ To build this filter, the filter string consists of
+ filter followed by a descriptive text
+ followed by more filters and their descriptive texts -- end with double NULL(0)
+ e.g. *.bmp' #0 'All bitmaps' #0 '*.*' #0 'All Files' #0 #0
+ }
+ MS_UTILS_GETBITMAPFILTERSTRINGS = 'Utils/GetBitmapFilterStrings';
+
+{$endif}
diff --git a/plugins/ShlExt/inc/m_v8.inc b/plugins/ShlExt/inc/m_v8.inc
new file mode 100644
index 0000000000..e0c9d1e0c8
--- /dev/null
+++ b/plugins/ShlExt/inc/m_v8.inc
@@ -0,0 +1,62 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+{$IFNDEF V8}
+{$DEFINE V8}
+
+type
+
+ PMUUID = ^TMUUID;
+ TMUUID = record
+ a: LongWord;
+ b, c: Word;
+ d: array[0..7] of Byte;
+ end;
+
+ PPLUGININFOEX = ^TPLUGININFOEX;
+ TPLUGININFOEX = record
+ cbSize: int;
+ shortName: PChar;
+ version: DWORD;
+ description: PChar;
+ author: PChar;
+ authorEmail: PChar;
+ copyright: PChar;
+ homepage: PChar;
+ isTransient: Byte; // leave zero for now
+ { one of the DEFMOD_* consts in m_plugin or zero, if non zero, this will
+ suppress loading of the specified builtin module }
+ replacesDefaultModule: int;
+ uuid: TMUUID;
+ end;
+
+ { any module must export the below functions to be valid plugin
+ the export names MUST be 'MirandaPluginInfo' 'Load' 'Unload' }
+
+ // defined in newpluginapi.inc
+
+ //TMirandaPluginInfo = function(mirandaVersion: DWORD): PPLUGININFO; cdecl;
+ //TLoad = function(link: PPLUGINLINK): int; cdecl;
+ //TUnload = function: int; cdecl;
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/newpluginapi.inc b/plugins/ShlExt/inc/newpluginapi.inc
new file mode 100644
index 0000000000..85a7df9a2b
--- /dev/null
+++ b/plugins/ShlExt/inc/newpluginapi.inc
@@ -0,0 +1,94 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+{$IFNDEF NEWPLUGINAPI}
+{$DEFINE NEWPLUGINAPI}
+
+const
+
+ MAXMODULELABELLENGTH = 64;
+
+type
+
+ PPLUGININFO = ^TPLUGININFO;
+ TPLUGININFO = record
+ cbSize: int;
+ shortName: PChar;
+ version: DWORD;
+ description: PChar;
+ author: PChar;
+ authorEmail: PChar;
+ copyright: PChar;
+ homepage: PChar;
+ isTransient: Byte; // leave zero for now
+ { one of the DEFMOD_* consts in m_plugin or zero, if non zero, this will
+ suppress loading of the specified builtin module }
+ replacesDefaultModule: int;
+ end;
+
+ { modules.h is never defined -- no check needed }
+
+ TMIRANDAHOOK = function(wParam: WPARAM; lParam: LPARAM): int; cdecl;
+ TMIRANDASERVICE = function(wParam: WPARAM; lParam: LPARAM): int; cdecl;
+
+ //see modules.h tor what all this stuff is
+
+ TCreateHookableEvent = function(const char: PChar): THandle; cdecl;
+ TDestroyHookableEvent = function(Handle: THandle): int; cdecl;
+ TNotifyEventHooks = function(Handle: THandle; wParam: WPARAM; lParam: LPARAM): int; cdecl;
+ THookEvent = function(const char: PChar; MIRANDAHOOK: TMIRANDAHOOK): THandle; cdecl;
+ THookEventMessage = function(const char: PChar; Wnd: THandle; wMsg: Integer): THandle; cdecl;
+ TUnhookEvent = function(Handle: THandle): int; cdecl;
+ TCreateServiceFunction = function(const char: PChar; MIRANDASERVICE: TMIRANDASERVICE): THandle; cdecl;
+ TCreateTransientServiceFunction = function(const char: PChar; MIRANDASERVICE: TMIRANDASERVICE): THandle; cdecl;
+ TDestroyServiceFunction = function(Handle: THandle): int; cdecl;
+ TCallService = function(const char: PChar; wParam: WPARAM; lParam: LPARAM): int; cdecl;
+ TServiceExists = function(const char: PChar): int; cdecl;
+
+ PPLUGINLINK = ^TPLUGINLINK;
+ TPLUGINLINK = record
+ CreateHookableEvent: TCreateHookableEvent;
+ DestroyHookableEvent: TDestroyHookableEvent;
+ NotifyEventHooks: TNotifyEventHooks;
+ HookEvent: THookEvent;
+ HookEventMessage: THookEventMessage;
+ UnhookEvent: TUnhookEvent;
+ CreateServiceFunction: TCreateServiceFunction;
+ CreateTransientServiceFunction: TCreateTransientServiceFunction;
+ DestroyServiceFunction: TDestroyServiceFunction;
+ CallService: TCallService;
+ ServiceExists: TServiceExists; // v0.1.0.1+
+ end;
+
+ { any module must export the below functions to be valid plugin
+ the export names MUST be 'MirandaPluginInfo' 'Load' 'Unload' }
+
+ TMirandaPluginInfo = function(mirandaVersion: DWORD): PPLUGININFO; cdecl;
+ TLoad = function(link: PPLUGINLINK): int; cdecl;
+ TUnload = function: int; cdecl;
+
+const
+
+ CALLSERVICE_NOTFOUND = $80000000;
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/statusmodes.inc b/plugins/ShlExt/inc/statusmodes.inc
new file mode 100644
index 0000000000..2cf180569f
--- /dev/null
+++ b/plugins/ShlExt/inc/statusmodes.inc
@@ -0,0 +1,54 @@
+(*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2004 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.
+
+*)
+
+{$IFNDEF STATUSMODES}
+{$DEFINE STATUSMODES}
+
+const
+
+ // add 1 to the ID_STATUS_CONNECTING to mark retries (v0.1.0.1+)
+ // e.g. ID_STATUS_CONNECTING+2 is the third connection attempt, or the second retry
+
+ ID_STATUS_CONNECTING = 1;
+
+ // max retries is just a marker, so that the clist knows what
+ // numbers represent retries, it should set any kind of limit on the number
+ // of retries you can and/or should do
+
+ MAX_CONNECT_RETRIES = 10000;
+
+ // and the modes!
+
+ ID_STATUS_OFFLINE = 40071;
+ ID_STATUS_ONLINE = 40072;
+ ID_STATUS_AWAY = 40073;
+ ID_STATUS_DND = 40074;
+ ID_STATUS_NA = 40075;
+ ID_STATUS_OCCUPIED = 40076;
+ ID_STATUS_FREECHAT = 40077;
+ ID_STATUS_INVISIBLE = 40078;
+ ID_STATUS_ONTHEPHONE = 40079;
+ ID_STATUS_OUTTOLUNCH = 40080;
+
+{$ENDIF}
diff --git a/plugins/ShlExt/inc/testdll.dpr b/plugins/ShlExt/inc/testdll.dpr
new file mode 100644
index 0000000000..478212d82c
--- /dev/null
+++ b/plugins/ShlExt/inc/testdll.dpr
@@ -0,0 +1,60 @@
+library testdll;
+
+uses
+
+ m_globaldefs, m_api, Windows;
+
+ {$include m_helpers.inc}
+
+ function MirandaPluginInfo(mirandaVersion: DWORD): PPLUGININFO; cdecl;
+ begin
+ Result := @PLUGININFO;
+ PLUGININFO.cbSize := sizeof(TPLUGININFO);
+ PLUGININFO.shortName := 'Plugin Template';
+ PLUGININFO.version := PLUGIN_MAKE_VERSION(0,0,0,1);
+ PLUGININFO.description := 'The long description of your plugin, to go in the plugin options dialog';
+ PLUGININFO.author := 'J. Random Hacker';
+ PLUGININFO.authorEmail := 'noreply@sourceforge.net';
+ PLUGININFO.copyright := '(c) 2003 J. Random Hacker';
+ PLUGININFO.homepage := 'http://miranda-icq.sourceforge.net/';
+ PLUGININFO.isTransient := 0;
+ PLUGININFO.replacesDefaultModule := 0;
+ end;
+
+ function PluginMenuCommand(wParam: WPARAM; lParam: LPARAM): Integer; cdecl;
+ begin
+ Result := 0;
+ // this is called by Miranda, thus has to use the cdecl calling convention
+ // all services and hooks need this.
+ MessageBox(0, 'Just groovy, baby!', 'Plugin-o-rama', MB_OK);
+ end;
+
+ function Load(link: PPLUGINLINK): int; cdecl;
+ var
+ mi: TCListMenuItem;
+ begin
+ // this line is VERY VERY important, if it's not present, expect crashes.
+ PLUGINLINK := Pointer(link);
+ pluginLink^.CreateServiceFunction('TestPlug/MenuCommand', @PluginMenuCommand);
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize := sizeof(mi);
+ mi.position := $7FFFFFFF;
+ mi.flags := 0;
+ mi.hIcon := LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ mi.pszName := '&Test Plugin...';
+ mi.pszService := 'TestPlug/MenuCommand';
+ pluginLink^.CallService(MS_CLIST_ADDMAINMENUITEM, 0, lParam(@mi));
+ Result := 0;
+ end;
+
+ function Unload: int; cdecl;
+ begin
+ Result := 0;
+ end;
+
+ exports
+
+ MirandaPluginInfo, Load, Unload;
+
+begin
+end.
diff --git a/plugins/ShlExt/make.bat b/plugins/ShlExt/make.bat
new file mode 100644
index 0000000000..40131550cf
--- /dev/null
+++ b/plugins/ShlExt/make.bat
@@ -0,0 +1,9 @@
+@echo off
+REM -Fi/u are for include/unit dirs
+REM -Mdelphi is delphi mode
+REM -WG - graphical app
+REM -v0 turn off warnings
+REM -O2 -Os // optimise
+REM -Rintel (intel style asm)
+REM -WB (relocatable) -WR (relocate)
+fpc shlext.dpr -Fiinc -Fuinc -Mdelphi -WG -O2 -Os -Rintel -WR -WB49ac0000 -v0 \ No newline at end of file
diff --git a/plugins/ShlExt/resource.h b/plugins/ShlExt/resource.h
new file mode 100644
index 0000000000..c89660a88d
--- /dev/null
+++ b/plugins/ShlExt/resource.h
@@ -0,0 +1,13 @@
+#define IDD_SHLOPTS 101
+#define IDC_USEGROUPS 1014
+#define IDC_CLISTGROUPS 1015
+#define IDC_SHOWFULL 1016
+#define IDC_NOPROF 1020
+#define IDC_SHOWINVISIBLES 1021
+#define IDC_HIDEOFFLINE 1022
+#define IDC_STATUS 1023
+#define IDC_CAPMENUS 1025
+#define IDC_CAPSTATUS 1026
+#define IDC_CAPSHLSTATUS 1027
+#define IDC_REMOVE 1028
+#define IDC_USEOWNERDRAW 1029
diff --git a/plugins/ShlExt/shlc.inc b/plugins/ShlExt/shlc.inc
new file mode 100644
index 0000000000..122e0fee14
--- /dev/null
+++ b/plugins/ShlExt/shlc.inc
@@ -0,0 +1,168 @@
+{$IFDEF SHL_IDC}
+const
+ IDD_SHLOPTS = 101;
+ IDC_USEGROUPS = 1014;
+ IDC_CLISTGROUPS = 1015;
+// Show "HIT"
+ IDC_SHOWFULL = 1016;
+ IDC_NOPROF = 1020;
+ IDC_SHOWINVISIBLES = 1021;
+ IDC_HIDEOFFLINE = 1022;
+// only in the options dialog
+ IDC_STATUS = 1023;
+ IDC_CAPMENUS = 1025;
+ IDC_CAPSTATUS = 1026;
+ IDC_CAPSHLSTATUS = 1027;
+ IDC_REMOVE = 1028;
+ IDC_USEOWNERDRAW = 1029;
+{$ENDIF}
+{$IFDEF SHL_KEYS}
+const
+ SHLExt_Name: PChar = 'shlext15';
+ SHLExt_MRU: PChar = 'MRU';
+ SHLExt_UseGroups: PChar = 'UseGroups';
+ SHLExt_UseCListSetting:PChar = 'UseCLGroups';
+ SHLExt_UseHITContacts:PChar = 'UseHITContacts';
+// HIT2 contacts will get your messages but don't know your state
+ SHLExt_UseHIT2Contacts: PChar = 'UseHIT2Contacts';
+ SHLExt_ShowNoProfile:PChar = 'ShowNoProfile';
+ SHLExt_ShowNoIcons:PChar = 'ShowNoIcons';
+ SHLExt_ShowNoOffline:PChar = 'ShowNoOffline';
+{$ENDIF}
+{$IFDEF SHLCOM}
+const
+
+ S_OK = 0;
+ S_FALSE = 1;
+
+ E_UNEXPECTED = $8000FFFF;
+ E_NOTIMPL = $80004001;
+ E_INVALIDARG = $80070057;
+
+ CLASS_E_NOAGGREGATION = $80040110;
+ CLASS_E_CLASSNOTAVAILABLE = $80040111;
+
+ CLSCTX_INPROC_SERVER = $1;
+
+ { for FORMATETC }
+
+ TYMED_HGLOBAL = 1;
+ DVASPECT_CONTENT = 1;
+
+type
+
+ PGUID = ^TGUID;
+ TGUID = record
+ D1: Longint;
+ D2: Word;
+ D3: Word;
+ D4: array[0..7] of Byte;
+ end;
+
+ TIID = TGUID;
+ TCLSID = TGUID;
+
+ TStgMedium = record
+ tymed: Longint;
+ case Integer of
+ 0: (hBitmap: HBitmap; unkForRelease: Pointer{IUnknown});
+ 1: (hMetaFilePict: THandle);
+ 2: (hEnhMetaFile: THandle);
+ 3: (hGlobal: HGlobal);
+ 4: (lpszFileName: Pointer{POleStr});
+ 5: (stm: Pointer{IUnknown});
+ 6: (stg: Pointer{IStorage});
+ end;
+
+ PFormatEtc = ^TFormatEtc;
+ TFormatEtc = record
+ cfFormat: Word; {TClipFormat;}
+ ptd: Pointer; {PDVTargetDevice;}
+ dwAspect: Longint;
+ lindex: Longint;
+ tymed: Longint;
+ end;
+
+{$ENDIF}
+{$IFDEF COM_STRUCTS}
+const
+
+ IID_IUnknown : TGUID = (
+ D1:$00000000;
+ D2:$0000;
+ D3:$0000;
+ D4:($C0,$00,$00,$00,$00,$00,$00,$46)
+ );
+
+ IID_IClassFactory : TGUID = (
+ D1:$00000001;
+ D2:$0000;
+ D3:$0000;
+ D4:($C0,$00,$00,$00,$00,$00,$00,$46)
+ );
+
+ IID_IShellExtInit : TGUID = (
+ D1:$000214E8;
+ D2:$0000;
+ D3:$0000;
+ D4:($C0,$00,$00,$00,$00,$00,$00,$46)
+ );
+
+ IID_IContextMenu : TGUID = (
+ D1:$000214E4;
+ D2:$0000;
+ D3:$0000;
+ D4:($C0,$00,$00,$00,$00,$00,$00,$46)
+ );
+
+ IID_IContextMenu2 : TGUID = (
+ D1:$000214F4;
+ D2:$0000;
+ D3:$0000;
+ D4:($C0,$00,$00,$00,$00,$00,$00,$46)
+ );
+
+ IID_IContextMenu3 : TGUID = (
+ D1:$BCFCE0A0;
+ D2:$EC17;
+ D3:$11D0;
+ D4:($8D,$10,$00,$A0,$C9,$0F,$27,$19)
+ );
+
+ IID_WICImagingFactory: TGUID = (
+ D1:$EC5EC8A9;
+ D2:$C395;
+ D3:$4314;
+ D4:($9C,$77,$54,$D7,$A9,$35,$FF,$70)
+ );
+
+
+ // Vista+ only
+
+ CLSID_WICImagingFactory : TGUID = (
+ D1:$cacaf262;
+ D2:$9370;
+ D3:$4615;
+ D4:($A1,$3B,$9F,$55,$39,$DA,$4C,$0A)
+ );
+
+ // anything before 0.0.1.5 was : {A321A032-7976-11d6-A310-ED893982BF28}
+ // changed to a new GUID to avoid older plugins
+ // {72013A26-A94C-11d6-8540-A5E62932711D}
+ // the IPC header now checks the plugin version given anyway.
+
+ CLSID_ISHLCOM : TGUID = (
+ D1: $72013a26;
+ D2: $a94c;
+ D3: $11d6;
+ D4: ($85,$40,$a5,$e6,$29,$32,$71,$1d);
+ );
+{$ENDIF}
+{$IFDEF COMAPI}
+ function CoCreateInstance(const rclsid: TCLSID; pUnkOuter: Pointer; dwClsContext: DWORD; const riid: TIID; var ppv): HResult; stdcall; external 'ole32.dll' name 'CoCreateInstance';
+ procedure ReleaseStgMedium(var medium: TStgMedium); stdcall; external 'ole32.dll' name 'ReleaseStgMedium';
+ function IsEqualGUID(const guid1, guid2: TGUID): Boolean; stdcall; external 'ole32.dll' name 'IsEqualGUID';
+ function IsEqualIID(const iid1, iid2: TIID): Boolean; stdcall; external 'ole32.dll' name 'IsEqualGUID';
+ function IsEqualCLSID(const clsid1, clsid2: TCLSID): Boolean; stdcall; external 'ole32.dll' name 'IsEqualGUID';
+ function QueueUserAPC(pfnAPC: Pointer; hThread: THandle; dwData: DWORD): BOOL; stdcall; external 'kernel32' name 'QueueUserAPC';
+{$ENDIF}
diff --git a/plugins/ShlExt/shlcom.pas b/plugins/ShlExt/shlcom.pas
new file mode 100644
index 0000000000..79a9134012
--- /dev/null
+++ b/plugins/ShlExt/shlcom.pas
@@ -0,0 +1,2383 @@
+unit shlcom;
+
+{$IFDEF FPC}
+ {$PACKRECORDS 4}
+ {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+uses
+
+ Windows, m_globaldefs, shlipc, shlicons;
+
+ {$define COM_STRUCTS}
+ {$define SHLCOM}
+ {$include shlc.inc}
+ {$undef SHLCOM}
+ {$undef COM_STRUCTS}
+
+ function DllGetClassObject(const CLSID: TCLSID; const IID: TIID;
+ var Obj): HResult; stdcall;
+ function DllCanUnloadNow: HResult; stdcall;
+
+ procedure InvokeThreadServer;
+
+ procedure CheckRegisterServer;
+
+ procedure CheckUnregisterServer;
+
+ function RemoveCOMRegistryEntries: HResult;
+
+ function ExtractIcon(hInst: THandle; pszExe: PChar; nIndex: Integer): HICON; stdcall; external 'shell32.dll' name 'ExtractIconA';
+
+implementation
+
+var
+
+ dllpublic: record
+ FactoryCount: Integer;
+ ObjectCount: Integer;
+ end;
+
+ VistaOrLater: Boolean;
+
+ {$include m_database.inc}
+ {$include m_clist.inc}
+ {$include m_protocols.inc}
+ {$include m_protosvc.inc}
+ {$include m_ignore.inc}
+ {$include m_skin.inc}
+ {$include m_file.inc}
+ {$include m_system.inc}
+ {$include m_langpack.inc}
+ {$include m_skin.inc}
+ {$include statusmodes.inc}
+
+ {$define COMAPI}
+ {$include shlc.inc}
+ {$undef COMAPI}
+
+ {$include m_helpers.inc}
+
+const
+
+ IPC_PACKET_SIZE = $1000 * 32;
+ //IPC_PACKET_NAME = 'm.mi.miranda.ipc'; // prior to 1.0.6.6
+ //IPC_PACKET_NAME = 'mi.miranda.IPCServer'; // prior to 2.0.0.9
+ IPC_PACKET_NAME = 'm.mi.miranda.ipc.server';
+
+const
+
+{ Flags returned by IContextMenu*:QueryContextMenu() }
+
+ CMF_NORMAL = $00000000;
+ CMF_DEFAULTONLY = $00000001;
+ CMF_VERBSONLY = $00000002;
+ CMF_EXPLORE = $00000004;
+ CMF_NOVERBS = $00000008;
+ CMF_CANRENAME = $00000010;
+ CMF_NODEFAULT = $00000020;
+ CMF_INCLUDESTATIC = $00000040;
+ CMF_RESERVED = $FFFF0000; { view specific }
+
+{ IContextMenu*:GetCommandString() uType flags }
+
+ GCS_VERBA = $00000000; // canonical verb
+ GCS_HELPTEXTA = $00000001; // help text (for status bar)
+ GCS_VALIDATEA = $00000002; // validate command exists
+ GCS_VERBW = $00000004; // canonical verb (unicode)
+ GC_HELPTEXTW = $00000005; // help text (unicode version)
+ GCS_VALIDATEW = $00000006; // validate command exists (unicode)
+ GCS_UNICODE = $00000004; // for bit testing - Unicode string
+ GCS_VERB = GCS_VERBA; //
+ GCS_HELPTEXT = GCS_HELPTEXTA;
+ GCS_VALIDATE = GCS_VALIDATEA;
+
+type
+
+ { this structure is returned by InvokeCommand() }
+
+ PCMInvokeCommandInfo = ^TCMInvokeCommandInfo;
+ TCMInvokeCommandInfo = packed record
+ cbSize: DWORD;
+ fMask: DWORD;
+ hwnd: HWND;
+ lpVerb: PChar; { maybe index, type cast as Integer }
+ lpParams: PChar;
+ lpDir: PChar;
+ nShow: Integer;
+ dwHotkey: DWORD;
+ hIcon: THandle;
+ end;
+
+{ completely stolen from modules.c: 'NameHashFunction' modified slightly }
+
+ function StrHash(const szStr: PChar): DWORD; cdecl;
+ asm
+ // esi content has to be preserved with basm
+ push esi
+ xor edx,edx
+ xor eax,eax
+ mov esi,szStr
+ mov al,[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
+ xor eax,eax
+ and cl,31
+ mov al,[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
+ pop esi
+ end;
+
+ function CreateProcessUID(const pid: Cardinal): string;
+ var
+ pidrep: string[16];
+ begin
+ str(pid, pidrep);
+ Result := Concat('mim.shlext.', pidrep, '$');
+ end;
+
+ function CreateUID: string;
+ var
+ pidrep, tidrep: string[16];
+ begin
+ str(GetCurrentProcessId(), pidrep);
+ str(GetCurrentThreadId(), tidrep);
+ Result := Concat('mim.shlext.caller', pidrep, '$', tidrep);
+ end;
+
+ // FPC doesn't support array[0..n] of Char extended syntax with Str()
+
+ function wsprintf(lpOut, lpFmt: PChar; ArgInt: Integer): Integer; cdecl; external 'user32.dll' name 'wsprintfA';
+
+ procedure Str(i: Integer; S: PChar);
+ begin
+ i := wsprintf(s, '%d', i);
+ if i > 2 then PChar(s)[i] := #0;
+ end;
+
+{ IShlCom }
+
+type
+
+ PLResult = ^LResult;
+
+ // bare minimum interface of IDataObject, since GetData() is only required.
+
+ PVTable_IDataObject = ^TVTable_IDataObject;
+ TVTable_IDataObject = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: function(Self: Pointer): Cardinal; stdcall;
+ Release: function(Self: Pointer): Cardinal; stdcall;
+ { IDataObject }
+ GetData: function(Self: Pointer; var formatetcIn: TFormatEtc; var medium: TStgMedium): HResult; stdcall;
+ GetDataHere: Pointer;
+ QueryGetData: Pointer;
+ GetCanonicalFormatEtc: Pointer;
+ SetData: Pointer;
+ EnumFormatEtc: Pointer;
+ DAdvise: Pointer;
+ DUnadvise: Pointer;
+ EnumDAdvise: Pointer;
+ end;
+
+ PDataObject_Interface = ^TDataObject_Interface;
+ TDataObject_Interface = record
+ ptrVTable: PVTable_IDataObject;
+ end;
+
+ { TShlComRec inherits from different interfaces with different function tables
+ all "compiler magic" is lost in this case, but it's pretty easy to return
+ a different function table for each interface, IContextMenu is returned
+ as IContextMenu'3' since it inherits from '2' and '1' }
+
+ PVTable_IShellExtInit = ^TVTable_IShellExtInit;
+ TVTable_IShellExtInit = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: Pointer;
+ Release: Pointer;
+ { IShellExtInit }
+ Initialise: Pointer;
+ end;
+
+ PShlComRec = ^TShlComRec;
+ PShellExtInit_Interface = ^TShellExtInit_Interface;
+ TShellExtInit_Interface = record
+ { pointer to function table }
+ ptrVTable: PVTable_IShellExtInit;
+ { instance data }
+ ptrInstance: PShlComRec;
+ { function table itself }
+ vTable: TVTable_IShellExtInit;
+ end;
+
+ PVTable_IContextMenu3 = ^TVTable_IContextMenu3;
+ TVTable_IContextMenu3 = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: Pointer;
+ Release: Pointer;
+ { IContextMenu }
+ QueryContextMenu: Pointer;
+ InvokeCommand: Pointer;
+ GetCommandString: Pointer;
+ { IContextMenu2 }
+ HandleMenuMsg: Pointer;
+ { IContextMenu3 }
+ HandleMenuMsg2: Pointer;
+ end;
+
+ PContextMenu3_Interface = ^TContextMenu3_Interface;
+ TContextMenu3_Interface = record
+ ptrVTable: PVTable_IContextMenu3;
+ ptrInstance: PShlComRec;
+ vTable: TVTable_IContextMenu3;
+ end;
+
+ PCommon_Interface = ^TCommon_Interface;
+ TCommon_Interface = record
+ ptrVTable: Pointer;
+ ptrInstance: PShlComRec;
+ end;
+
+ TShlComRec = record
+ ShellExtInit_Interface: TShellExtInit_Interface;
+ ContextMenu3_Interface: TContextMenu3_Interface;
+ {fields}
+ RefCount: LongInt;
+ // this is owned by the shell after items are added 'n' is used to
+ // grab menu information directly via id rather than array indexin'
+ hRootMenu: THandle;
+ idCmdFirst: Integer;
+ // most of the memory allocated is on this heap object so HeapDestroy()
+ // can do most of the cleanup, extremely lazy I know.
+ hDllHeap: THandle;
+ // This is a submenu that recently used contacts are inserted into
+ // the contact is inserted twice, once in its normal list (or group) and here
+ // Note: These variables are global data, but refered to locally by each instance
+ // Do not rely on these variables outside the process enumeration.
+ hRecentMenu: THandle;
+ RecentCount: Cardinal; // number of added items
+ // array of all the protocol icons, for every running instance!
+ ProtoIcons: ^TSlotProtoIconsArray;
+ ProtoIconsCount: Cardinal;
+ // maybe null, taken from IShellExtInit_Initalise() and AddRef()'d
+ // only used if a Miranda instance is actually running and a user
+ // is selected
+ pDataObject: PDataObject_Interface;
+ // DC is used for font metrics and saves on creating and destroying lots of DC handles
+ // during WM_MEASUREITEM
+ hMemDC: HDC;
+ end;
+
+ { this is passed to the enumeration callback so it can process PID's with
+ main windows by the class name MIRANDANAME loaded with the plugin
+ and use the IPC stuff between enumerations -- }
+
+ PEnumData = ^TEnumData;
+ TEnumData = record
+ Self: PShlComRec;
+ // autodetected, don't hard code since shells that don't support it
+ // won't send WM_MEASUREITETM/WM_DRAWITEM at all.
+ bOwnerDrawSupported: LongBool;
+ // as per user setting (maybe of multiple Mirandas)
+ bShouldOwnerDraw: LongBool;
+ idCmdFirst: Integer;
+ ipch: PHeaderIPC;
+ // OpenEvent()'d handle to give each IPC server an object to set signalled
+ hWaitFor: THandle;
+ pid: DWORD; // sub-unique value used to make work object name
+ end;
+
+ procedure FreeGroupTreeAndEmptyGroups(hParentMenu: THandle; pp, p: PGroupNode);
+ var
+ q: PGroupNode;
+ begin
+ while p <> nil do
+ begin
+ q := p^.Right;
+ if p^.Left <> nil then
+ begin
+ FreeGroupTreeAndEmptyGroups(p^.Left^.hMenu, p, p^.Left);
+ end; //if
+ if p^.dwItems = 0 then
+ begin
+ if pp <> nil then
+ begin
+ DeleteMenu(pp^.hMenu, p^.hMenuGroupID, MF_BYCOMMAND)
+ end else begin
+ DeleteMenu(hParentMenu, p^.hMenuGroupID, MF_BYCOMMAND);
+ end; //if
+ end else begin
+ // make sure this node's parent know's it exists
+ if pp <> nil then inc (pp^.dwItems);
+ end;
+ Dispose(p);
+ p := q;
+ end;
+ end;
+
+ procedure DecideMenuItemInfo(pct: PSlotIPC; pg: PGroupNode; var mii: TMenuItemInfo; lParam: PEnumData);
+ var
+ psd: PMenuDrawInfo;
+ hDllHeap: THandle;
+ j, c: Cardinal;
+ pp: ^TSlotProtoIconsArray;
+ begin
+ mii.wID := lParam^.idCmdFirst;
+ Inc(lParam^.idCmdFirst);
+ // get the heap object
+ hDllHeap := lParam^.Self^.hDllHeap;
+ psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
+ if pct <> nil then
+ begin
+ psd^.cch := pct^.cbStrSection-1; // no null;
+ psd^.szText := HeapAlloc(hDllHeap, 0, pct^.cbStrSection);
+ strcpy(psd^.szText, PChar(Integer(pct)+sizeof(TSlotIPC)));
+ psd^.hContact := pct^.hContact;
+ psd^.fTypes := [dtContact];
+ // find the protocol icon array to use and which status
+ c := lParam^.Self^.protoIconsCount;
+ pp := lParam^.Self^.protoIcons;
+ psd^.hStatusIcon := 0;
+ while c > 0 do
+ begin
+ dec(c);
+ if (pp[c].hProto = pct^.hProto) and (pp[c].pid = lParam^.pid) then
+ begin
+ psd^.hStatusIcon := pp[c].hIcons[pct^.Status - ID_STATUS_OFFLINE];
+ psd^.hStatusBitmap := pp[c].hBitmaps[pct^.Status - ID_STATUS_OFFLINE];
+ break;
+ end;
+ end; //while
+ psd^.pid := lParam^.pid;
+ end else if pg <> nil then
+ begin
+ // store the given ID
+ pg^.hMenuGroupID := mii.wID;
+ // steal the pointer from the group node it should be on the heap
+ psd^.cch := pg^.cchGroup;
+ psd^.szText := pg^.szGroup;
+ psd^.fTypes := [dtGroup];
+ end; //if
+ psd^.wID := mii.wID;
+ psd^.szProfile := nil;
+ // store
+ mii.dwItemData := Integer(psd);
+
+ if ((lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw)) then
+ begin
+ mii.fType := MFT_OWNERDRAW;
+ Pointer(mii.dwTypeData) := psd;
+ end else begin
+ // normal menu
+ mii.fType := MFT_STRING;
+ if pct <> nil then
+ begin
+ Integer(mii.dwTypeData) := Integer(pct) + sizeof(TSlotIPC);
+ end else begin
+ mii.dwTypeData := pg^.szGroup;
+ end;
+ { For Vista + let the system draw the theme and icons, pct = contact associated data }
+ if VistaOrLater and ( pct <> nil ) and ( psd <> nil) then
+ begin
+ mii.fMask := MIIM_BITMAP or MIIM_FTYPE or MIIM_ID or MIIM_DATA or MIIM_STRING;
+ // BuildSkinIcons() built an array of bitmaps which we can use here
+ mii.hBmpItem := psd^.hStatusBitmap;
+ end;
+ end; //if
+ end;
+
+ // must be called after DecideMenuItemInfo()
+ procedure BuildMRU(pct: PSlotIPC; var mii: TMenuItemInfo; lParam: PEnumData);
+ begin
+ if pct^.MRU > 0 then
+ begin
+ Inc(lParam^.Self^.RecentCount);
+ // lParam^.Self == pointer to object data
+ InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
+ end;
+ end;
+
+ procedure BuildContactTree(group: PGroupNode; lParam: PEnumData);
+ label
+ grouploop;
+ var
+ pct: PSlotIPC;
+ pg, px: PGroupNode;
+ str: TStrTokRec;
+ sz: PChar;
+ Hash: Cardinal;
+ Depth: Cardinal;
+ mii: TMenuItemInfo;
+ begin
+ // set up the menu item
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_TYPE or MIIM_DATA;
+ // set up the scanner
+ str.szSet := ['\'];
+ str.bSetTerminator := False;
+ // go thru all the contacts
+ pct := lParam^.ipch^.ContactsBegin;
+ while (pct <> nil) and (pct^.cbSize=sizeof(TSlotIPC)) and (pct^.fType=REQUEST_CONTACTS) do
+ begin
+ if pct^.hGroup <> 0 then
+ begin
+ // at the end of the slot header is the contact's display name
+ // and after a double NULL char there is the group string, which has the full path of the group
+ // this must be tokenised at '\' and we must walk the in memory group tree til we find our group
+ // this is faster than the old version since we only ever walk one or at most two levels of the tree
+ // per tokenised section, and it doesn't matter if two levels use the same group name (which is valid)
+ // as the tokens processed is equatable to depth of the tree
+ str.szStr := PChar(Integer(pct)+sizeof(TSlotIPC)+pct^.cbStrSection+1);
+ sz := StrTok(str);
+ // restore the root
+ pg := group;
+ Depth := 0;
+ while sz <> nil do
+ begin
+ Hash := StrHash(sz);
+ // find this node within
+ while pg <> nil do
+ begin
+ // does this node have the right hash and the right depth?
+ if (Hash = pg^.Hash) and (Depth = pg^.Depth) then Break;
+ // each node may have a left pointer going to a sub tree
+ // the path syntax doesn't know if a group is a group at the same level
+ // or a nested one, which means the search node can be anywhere
+ px := pg^.Left;
+ if px <> nil then
+ begin
+ // keep searching this level
+ while px <> nil do
+ begin
+ if (hash = px^.Hash) and (Depth = px^.Depth) then
+ begin
+ // found the node we're looking for at the next level to pg, px is now pq for next time
+ pg := px;
+ goto grouploop;
+ end; //if
+ px := px^.Right;
+ end; //if
+ end; //if
+ pg := pg^.Right;
+ end; //while
+ grouploop:
+ inc (Depth);
+ // process next token
+ sz := StrTok(str);
+ end; //while
+ // tokenisation finished, if pg <> nil then the group is found
+ if pg <> nil then
+ begin
+ DecideMenuItemInfo(pct, nil, mii, lParam);
+ BuildMRU(pct, mii, lParam);
+ InsertMenuitem(pg^.hMenu, $FFFFFFFF,True, mii);
+ Inc(pg^.dwItems);
+ end;
+ end; //if
+ pct := pct^.Next;
+ end; //while
+ end;
+
+ procedure BuildMenuGroupTree(p: PGroupNode; lParam: PEnumData; hLastMenu: HMENU);
+ var
+ mii: TMenuItemInfo;
+ begin
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_DATA or MIIM_TYPE or MIIM_SUBMENU;
+ // go thru each group and create a menu for it adding submenus too.
+ while p <> nil do
+ begin
+ mii.hSubMenu := CreatePopupMenu();
+ if p^.Left <> nil then BuildMenuGroupTree(p^.Left, lParam, mii.hSubMenu);
+ p^.hMenu := mii.hSubMenu;
+ DecideMenuItemInfo(nil, P, mii, lParam);
+ InsertMenuItem(hLastMenu, $FFFFFFFF, True, mii);
+ p := p^.Right;
+ end; //while
+ end;
+
+ { this callback is triggered by the menu code and IPC is already taking place,
+ just the transfer type+data needs to be setup }
+ function ClearMRUIPC(
+ pipch: PHeaderIPC; // IPC header info, already mapped
+ hWorkThreadEvent: THandle; // event object being waited on on miranda thread
+ hAckEvent: THandle; // ack event object that has been created
+ psd: PMenuDrawInfo // command/draw info
+ ): Integer; stdcall;
+ begin
+ Result := S_OK;
+ ipcPrepareRequests(IPC_PACKET_SIZE, pipch, REQUEST_CLEARMRU);
+ ipcSendRequest(hWorkThreadEvent, hAckEvent, pipch, 100) ;
+ end;
+
+ procedure RemoveCheckmarkSpace(hMenu: HMENU);
+ const
+ MIM_STYLE = $00000010;
+ MNS_CHECKORBMP = $4000000;
+ type
+ TMENUINFO = record
+ cbSize: DWORD;
+ fMask: DWORD;
+ dwStyle: DWORD;
+ cyMax: LongInt;
+ hbrBack: THandle;
+ dwContextHelpID: DWORD;
+ dwMenuData: Pointer;
+ end;
+ var
+ SetMenuInfo: function(hMenu: HMENU; var mi: TMenuInfo): Boolean; stdcall;
+ mi: TMENUINFO;
+ begin
+ if not VistaOrLater then Exit;
+ SetMenuInfo := GetProcAddress(GetModuleHandle('user32'), 'SetMenuInfo');
+ if @SetMenuInfo = nil then Exit;
+ mi.cbSize := sizeof(mi);
+ mi.fMask := MIM_STYLE;
+ mi.dwStyle := MNS_CHECKORBMP;
+ SetMenuInfo(hMenu, mi);
+ end;
+
+ procedure BuildMenus(lParam: PEnumData);
+ {$define SHL_IDC}
+ {$define SHL_KEYS}
+ {$include shlc.inc}
+ {$undef SHL_KEYS}
+ {$undef SHL_IDC}
+ var
+ hBaseMenu: HMENU;
+ hGroupMenu: HMENU;
+ pg: PSlotIPC;
+ szProf: PChar;
+ mii: TMenuItemInfo;
+ j: TGroupNodeList;
+ p, q: PGroupNode;
+ Depth, Hash: Cardinal;
+ Token: PChar;
+ tk: TStrTokRec;
+ szBuf: PChar;
+ hDllHeap: THandle;
+ psd: PMenuDrawInfo;
+ c: Cardinal;
+ pp: ^TSlotProtoIconsArray;
+ begin
+ ZeroMemory(@mii, sizeof(mii));
+ hDllHeap := lParam^.Self^.hDllHeap;
+ hBaseMenu := lParam^.Self^.hRootMenu;
+ // build an in memory tree of the groups
+ pg := lParam^.ipch^.GroupsBegin;
+ tk.szSet := ['\'];
+ tk.bSetTerminator := False;
+ j.First := nil;
+ j.Last := nil;
+ while pg <> nil do
+ begin
+ if (pg^.cbSize <> sizeof(TSlotIPC)) or (pg^.fType <> REQUEST_GROUPS) then Break;
+ Depth := 0;
+ p := j.First; // start at root again
+ // get the group
+ Integer(tk.szStr) := (Integer(pg) + sizeof(TSlotIPC));
+ // find each word between \ and create sub groups if needed.
+ Token := StrTok(tk);
+ while Token <> nil do
+ begin
+ Hash := StrHash(Token);
+ // if the (sub)group doesn't exist, create it.
+ q := FindGroupNode(p, Hash, Depth);
+ if q = nil then
+ begin
+ q := AllocGroupNode(@j, p, Depth);
+ q^.Depth := Depth;
+ // this is the hash of this group node, but it can be anywhere
+ // i.e. Foo\Foo this is because each node has a different depth
+ // trouble is contacts don't come with depths!
+ q^.Hash := Hash;
+ // don't assume that pg^.hGroup's hash is valid for this token
+ // since it maybe Miranda\Blah\Blah and we have created the first node
+ // which maybe Miranda, thus giving the wrong hash
+ // since "Miranda" can be a group of it's own and a full path
+ q^.cchGroup := lstrlen(Token);
+ q^.szGroup := HeapAlloc(hDllHeap, 0, q^.cchGroup+1);
+ strcpy(q^.szGroup, Token);
+ q^.dwItems := 0;
+ end;
+ p := q;
+ Inc(Depth);
+ Token := StrTok(tk);
+ end; //while
+ pg := pg^.Next;
+ end; // while
+ // build the menus inserting into hGroupMenu which will be a submenu of
+ // the instance menu item. e.g. Miranda -> [Groups ->] contacts
+ hGroupMenu := CreatePopupMenu();
+
+ // allocate MRU menu, this will be associated with the higher up menu
+ // so doesn't need to be freed (unless theres no MRUs items attached)
+ // This menu is per process but the handle is stored globally (like a stack)
+ lParam^.Self^.hRecentMenu := CreatePopupMenu();
+ lParam^.Self^.RecentCount := 0;
+ // create group menus only if they exist!
+ if lParam^.ipch^.GroupsBegin <> nil then
+ begin
+ BuildMenuGroupTree(j.First, lParam, hGroupMenu);
+ // add contacts that have a group somewhere
+ BuildContactTree(j.First, lParam);
+ end;
+ //
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_TYPE or MIIM_DATA;
+ // add all the contacts that have no group (which maybe all of them)
+ pg := lParam^.ipch^.ContactsBegin;
+ while pg <> nil do
+ begin
+ if (pg^.cbSize <> sizeof(TSlotIPC)) or (pg^.fType <> REQUEST_CONTACTS) then Break;
+ if pg^.hGroup = 0 then
+ begin
+ DecideMenuItemInfo(pg, nil, mii, lParam);
+ BuildMRU(pg, mii, lParam);
+ InsertMenuItem(hGroupMenu, $FFFFFFFF, True, mii);
+ end; //if
+ pg := pg^.Next;
+ end; //while
+
+ // insert MRU menu as a submenu of the contact menu only if
+ // the MRU list has been created, the menu popup will be deleted by itself
+ if lParam^.Self^.RecentCount > 0 then
+ begin
+
+ // insert seperator and 'clear list' menu
+ mii.fType := MFT_SEPARATOR;
+ mii.fMask := MIIM_TYPE;
+ InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
+
+ // insert 'clear MRU' item and setup callback
+ mii.fMask := MIIM_TYPE or MIIM_ID or MIIM_DATA;
+ mii.wID := lParam^.idCmdFirst;
+ Inc(lParam^.idCmdFirst);
+ mii.fType := MFT_STRING;
+ mii.dwTypeData := lParam^.ipch^.ClearEntries; // "Clear entries"
+ //allocate menu substructure
+ psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
+ psd^.fTypes := [dtCommand];
+ psd^.MenuCommandCallback := @ClearMRUIPC;
+ psd^.wID := mii.wID;
+ // this is needed because there is a clear list command per each process.
+ psd^.pid := lParam^.pid;
+ mii.dwItemData := Integer(psd);
+ InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
+
+ // insert MRU submenu into group menu (with) ownerdraw support as needed
+ psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
+ psd^.szProfile := 'MRU';
+ psd^.fTypes := [dtGroup];
+ // the IPC string pointer wont be around forever, must make a copy
+ psd^.cch := strlen(lParam^.ipch^.MRUMenuName);
+ psd^.szText := HeapAlloc(hDllHeap, 0, psd^.cch+1);
+ lstrcpyn(psd^.szText, lParam^.ipch^.MRUMenuName, sizeof(lParam^.ipch^.MRUMenuName)-1);
+
+ mii.dwItemData := Integer(psd);
+ if (lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw) then
+ begin
+ mii.fType := MFT_OWNERDRAW;
+ Pointer(mii.dwTypeData) := psd;
+ end else
+ begin
+ mii.dwTypeData := lParam^.ipch^.MRUMenuName; // 'Recent';
+ end;
+ mii.wID := lParam^.idCmdFirst;
+ Inc(lParam^.idCmdFirst);
+ mii.fMask := MIIM_TYPE or MIIM_SUBMENU or MIIM_DATA or MIIM_ID;
+ mii.hSubMenu := lParam^.Self^.hRecentMenu;
+ InsertMenuItem(hGroupMenu, 0, True, mii);
+ end
+ else
+ begin
+ // no items were attached to the MRU, delete the MRU menu
+ DestroyMenu(lParam^.Self^.hRecentMenu);
+ lParam^.Self^.hRecentMenu := 0;
+ end;
+
+ // allocate display info/memory for "Miranda" string
+
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_DATA or MIIM_TYPE or MIIM_SUBMENU;
+ if VistaOrLater then
+ begin
+ mii.fMask := MIIM_ID or MIIM_DATA or MIIM_FTYPE or MIIM_SUBMENU or MIIM_STRING or MIIM_BITMAP;
+ end;
+ mii.hSubMenu := hGroupMenu;
+
+ // by default, the menu will have space for icons and checkmarks (on Vista+) and we don't need this
+ RemoveCheckmarkSpace(hGroupMenu);
+
+
+ psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
+ psd^.cch := strlen(lParam^.ipch^.MirandaName);
+ psd^.szText := HeapAlloc(hDllHeap, 0, psd^.cch+1);
+ lstrcpyn(psd^.szText, lParam^.ipch^.MirandaName, sizeof(lParam^.ipch^.MirandaName)-1);
+ // there may not be a profile name
+ pg := lParam^.ipch^.DataPtr;
+ psd^.szProfile := nil;
+ if ((pg <> nil) and (pg^.Status = STATUS_PROFILENAME)) then
+ begin
+ psd^.szProfile := HeapAlloc(hDllHeap, 0, pg^.cbStrSection);
+ strcpy(psd^.szProfile, PChar(Integer(pg) + sizeof(TSlotIPC)));
+ end; //if
+ // owner draw menus need ID's
+ mii.wID := lParam^.idCmdFirst;
+ Inc(lParam^.idCmdFirst);
+ psd^.fTypes := [dtEntry];
+ psd^.wID := mii.wID;
+ psd^.hContact := 0;
+ // get Miranda's icon or bitmap
+ c := lParam^.Self^.protoIconsCount;
+ pp := lParam^.Self^.protoIcons;
+ while c > 0 do
+ begin
+ dec(c);
+ if (pp[c].pid = lParam^.pid) and (pp[c].hProto = 0) then
+ begin
+ // either of these can be 0
+ psd^.hStatusIcon := pp[c].hIcons[0];
+ mii.hBmpItem := pp[c].hBitmaps[0];
+ break;
+ end; //if
+ end; //while
+ mii.dwItemData := Integer(psd);
+ if ((lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw)) then
+ begin
+ mii.fType := MFT_OWNERDRAW;
+ Pointer(mii.dwTypeData) := psd;
+ end else begin
+ mii.fType := MFT_STRING;
+ mii.dwTypeData := lParam^.ipch^.MirandaName;
+ mii.cch := sizeof(lParam^.ipch^.MirandaName)-1;
+ end;
+ // add it all
+ InsertMenuItem(hBaseMenu, 0, True, mii);
+ // free the group tree
+ FreeGroupTreeAndEmptyGroups(hGroupMenu, nil, j.First);
+ end;
+
+ procedure BuildSkinIcons(lParam: PEnumData);
+ var
+ pct: PSlotIPC;
+ p, d: PSlotProtoIcons;
+ Self: PShlComRec;
+ j: Cardinal;
+ imageFactory: PImageFactory_Interface;
+ begin
+ pct := lParam^.ipch^.NewIconsBegin;
+ self := lParam^.Self;
+ while (pct <> nil) do
+ begin
+ if (pct^.cbSize <> sizeof(TSlotIPC)) or (pct^.fType <> REQUEST_NEWICONS) then Break;
+ Integer(p) := Integer(pct) + sizeof(TSlotIPC);
+ ReAllocMem(self^.protoIcons, (self^.protoIconsCount+1)*sizeof(TSlotProtoIcons));
+ d := @self^.protoIcons[self^.protoIconsCount];
+ CopyMemory(d,p,sizeof(TSlotProtoIcons));
+
+ {
+ If using Vista (or later), clone all the icons into bitmaps and keep these around,
+ if using anything older, just use the default code, the bitmaps (and or icons) will be freed
+ with the shell object.
+ }
+
+ imageFactory := nil;
+
+ for j := 0 to 9 do
+ begin
+ if imageFactory = nil then imageFactory := ARGB_GetWorker();
+ if VistaOrLater then
+ begin
+ d^.hBitmaps[j] := ARGB_BitmapFromIcon(imageFactory, self^.hMemDC, p^.hIcons[j] );
+ d^.hIcons[j] := 0;
+ end else
+ begin
+ d^.hBitmaps[j] := 0;
+ d^.hIcons[j] := CopyIcon(p^.hIcons[j]);
+ end;
+ end;
+
+ if imageFactory <> nil then
+ begin
+ imageFactory^.ptrVTable^.Release(imageFactory);
+ imageFactory := nil;
+ end;
+
+ inc(self^.protoIconsCount);
+ pct := pct^.Next;
+ end;
+ end;
+
+ function ProcessRequest(hwnd: HWND; lParam: PEnumData): BOOL; stdcall;
+ var
+ pid: Integer;
+ hMirandaWorkEvent: THandle;
+ replyBits: Integer;
+ hScreenDC: THandle;
+ szBuf: array[0..MAX_PATH] of Char;
+ begin
+ Result := True;
+ pid := 0;
+ GetWindowThreadProcessId(hwnd, @pid);
+ If pid <> 0 then
+ begin
+ // old system would get a window's pid and the module handle that created it
+ // and try to OpenEvent() a event object name to it (prefixed with a string)
+ // this was fine for most Oses (not the best way) but now actually compares
+ // the class string (a bit slower) but should get rid of those bugs finally.
+ hMirandaWorkEvent := OpenEvent(EVENT_ALL_ACCESS, False, PChar(CreateProcessUID(pid)));
+ if (hMirandaWorkEvent <> 0) then
+ begin
+ GetClassName(hwnd, szBuf, sizeof(szBuf));
+ if lstrcmp(szBuf, MIRANDANAME) <> 0 then
+ begin
+ // opened but not valid.
+ CloseHandle(hMirandaWorkEvent); Exit;
+ end; //if
+ end; //if
+ { If the event object exists, then a shlext.dll running in the instance must of created it. }
+ If hMirandaWorkEvent <> 0 then
+ begin
+ { prep the request }
+ ipcPrepareRequests(IPC_PACKET_SIZE, lParam^.ipch, REQUEST_ICONS or REQUEST_GROUPS or REQUEST_CONTACTS or REQUEST_NEWICONS);
+ // slots will be in the order of icon data, groups then contacts, the first
+ // slot will contain the profile name
+ replyBits := ipcSendRequest(hMirandaWorkEvent, lParam^.hWaitFor, lParam^.ipch, 1000);
+ { replyBits will be REPLY_FAIL if the wait timed out, or it'll be the request
+ bits as sent or a series of *_NOTIMPL bits where the request bit were, if there are no
+ contacts to speak of, then don't bother showing this instance of Miranda }
+ if (replyBits <> REPLY_FAIL) and (lParam^.ipch^.ContactsBegin <> nil) then
+ begin
+ // load the address again, the server side will always overwrite it
+ lParam^.ipch^.pClientBaseAddress := lParam^.ipch;
+ // fixup all the pointers to be relative to the memory map
+ // the base pointer of the client side version of the mapped file
+ ipcFixupAddresses(False, lParam^.ipch);
+ // store the PID used to create the work event object
+ // that got replied to -- this is needed since each contact
+ // on the final menu maybe on a different instance and another OpenEvent() will be needed.
+ lParam^.pid := pid;
+ // check out the user options from the server
+ lParam^.bShouldOwnerDraw := (lParam^.ipch^.dwFlags and HIPC_NOICONS) = 0;
+ // process the icons
+ BuildSkinIcons(lParam);
+ // process other replies
+ BuildMenus(lParam);
+ end;
+ { close the work object }
+ CloseHandle(hMirandaWorkEvent);
+ end; //if
+ end; //if
+ end;
+
+ function TShlComRec_QueryInterface(Self: PCommon_Interface;
+ const IID: TIID; var Obj): HResult; stdcall;
+ begin
+ Pointer(Obj) := nil;
+ { IShellExtInit is given when the TShlRec is created }
+ if IsEqualIID(IID, IID_IContextMenu)
+ or IsEqualIID(IID, IID_IContextMenu2)
+ or IsEqualIID(IID, IID_IContextMenu3) then
+ begin
+ with Self^.ptrInstance^ do
+ begin
+ Pointer(Obj) := @ContextMenu3_Interface;
+ Inc(RefCount);
+ end; {with}
+ Result := S_OK;
+ end else begin
+ // under XP, it may ask for IShellExtInit again, this fixes the -double- click to see menus issue
+ // which was really just the object not being created
+ if IsEqualIID(IID, IID_IShellExtInit) then
+ begin
+ with Self^.ptrInstance^ do
+ begin
+ Pointer(Obj) := @ShellExtInit_Interface;
+ Inc(RefCount);
+ end; //if
+ Result := S_OK;
+ end else begin
+ Result := CLASS_E_CLASSNOTAVAILABLE;
+ end; //if
+ end; //if
+ end;
+
+ function TShlComRec_AddRef(Self: PCommon_Interface): LongInt; stdcall;
+ begin
+ with Self^.ptrInstance^ do
+ begin
+ Inc(RefCount);
+ Result := RefCount;
+ end; {with}
+ end;
+
+ function TShlComRec_Release(Self: PCommon_Interface): LongInt; stdcall;
+ var
+ j, c: Cardinal;
+ begin
+ with Self^.ptrInstance^ do
+ begin
+ Dec(RefCount);
+ Result := RefCount;
+ If RefCount = 0 then
+ begin
+ // time to go byebye.
+ with Self^.ptrInstance^ do
+ begin
+ // Note MRU menu is associated with a window (indirectly) so windows will free it.
+ // free icons!
+ if protoIcons <> nil then
+ begin
+ c := protoIconsCount;
+ while c > 0 do
+ begin
+ dec(c);
+ for j := 0 to 9 do
+ begin
+ with protoIcons[c] do
+ begin
+ if hIcons[j] <> 0 then DestroyIcon(hIcons[j]);
+ if hBitmaps[j] <> 0 then DeleteObject(hBitmaps[j]);
+ end;
+ end;
+ end;
+ FreeMem(protoIcons); protoIcons := nil;
+ end; //if
+ // free IDataObject reference if pointer exists
+ if pDataObject <> nil then
+ begin
+ pDataObject^.ptrvTable^.Release(pDataObject);
+ end; //if
+ pDataObject := nil;
+ // free the heap and any memory allocated on it
+ HeapDestroy(hDllHeap);
+ // destroy the DC
+ if hMemDC <> 0 then DeleteDC(hMemDC);
+ end; //with
+ // free the instance (class record) created
+ Dispose(Self^.ptrInstance);
+ Dec(dllpublic.ObjectCount);
+ end; {if}
+ end; {with}
+ end;
+
+ function TShlComRec_Initialise(Self: PContextMenu3_Interface;
+ pidLFolder: Pointer; DObj: PDataObject_Interface; hKeyProdID: HKEY): HResult; stdcall;
+ begin
+ // DObj is a pointer to an instance of IDataObject which is a pointer itself
+ // it contains a pointer to a function table containing the function pointer
+ // address of GetData() - the instance data has to be passed explicitly since
+ // all compiler magic has gone.
+ with Self^.ptrInstance^ do
+ begin
+ if DObj <> nil then
+ begin
+ Result := S_OK;
+ // if an instance already exists, free it.
+ if pDataObject <> nil then pDataObject^.ptrVTable^.Release(pDataObject);
+ // store the new one and AddRef() it
+ pDataObject := DObj;
+ pDataObject^.ptrVTable^.AddRef(pDataObject);
+ end else begin
+ Result := E_INVALIDARG;
+ end; //if
+ end; //if
+ end;
+
+ function MAKE_HRESULT(Severity, Facility, Code: Integer): HResult;
+ {$ifdef FPC}
+ inline;
+ {$endif}
+ begin
+ Result := (Severity shl 31) or (Facility shl 16) or Code;
+ end;
+
+ function TShlComRec_QueryContextMenu(Self: PContextMenu3_Interface;
+ Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;
+ type
+ TDllVersionInfo = record
+ cbSize: DWORD;
+ dwMajorVersion: DWORD;
+ dwMinorVersion: DWORD;
+ dwBuildNumber: DWORD;
+ dwPlatformID: DWORD;
+ end;
+ TDllGetVersionProc = function(var dv: TDllVersionInfo): HResult; stdcall;
+ var
+ hShellInst: THandle;
+ bMF_OWNERDRAW: Boolean;
+ DllGetVersionProc: TDllGetVersionProc;
+ dvi: TDllVersionInfo;
+ ed: TEnumData;
+ hMap: THandle;
+ pipch: PHeaderIPC;
+ begin
+ Result := 0;
+ if ((LOWORD(uFlags) and CMF_VERBSONLY) <> CMF_VERBSONLY) and ((LOWORD(uFlags) and CMF_DEFAULTONLY) <> CMF_DEFAULTONLY) then
+ begin
+ bMF_OWNERDRAW := False;
+ // get the shell version
+ hShellInst := LoadLibrary('shell32.dll');
+ if hShellInst <> 0 then
+ begin
+ DllGetVersionProc := GetProcAddress(hShellInst, 'DllGetVersion');
+ if @DllGetVersionProc <> nil then
+ begin
+ dvi.cbSize := sizeof(TDllVersionInfo);
+ if DllGetVersionProc(dvi) >= 0 then
+ begin
+ // it's at least 4.00
+ bMF_OWNERDRAW := (dvi.dwMajorVersion > 4) or (dvi.dwMinorVersion >= 71);
+ end; //if
+ end; //if
+ FreeLibrary(hShellInst);
+ end; //if
+
+ // if we're using Vista (or later), then the ownerdraw code will be disabled, because the system draws the icons.
+ if VistaOrLater then bMF_OWNERDRAW := False;
+
+ hMap := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, IPC_PACKET_SIZE, IPC_PACKET_NAME);
+ If (hMap <> 0) and (GetLastError <> ERROR_ALREADY_EXISTS) then
+ begin
+ { map the memory to this address space }
+ pipch := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ If pipch <> nil then
+ begin
+ { let the callback have instance vars }
+ ed.Self := Self^.ptrInstance;
+ // not used 'ere
+ ed.Self^.hRootMenu := Menu;
+ // store the first ID to offset with index for InvokeCommand()
+ Self^.ptrInstance^.idCmdFirst := idCmdFirst;
+ // store the starting index to offset
+ Result := idCmdFirst;
+ ed.bOwnerDrawSupported := bMF_OWNERDRAW;
+ ed.bShouldOwnerDraw := True;
+ ed.idCmdFirst := idCmdFirst;
+ ed.ipch := pipch;
+ { allocate a wait object so the ST can signal us, it can't be anon
+ since it has to used by OpenEvent() }
+ strcpy(@pipch^.SignalEventName, PChar(CreateUID()));
+ { create the wait wait-for-wait object }
+ ed.hWaitFor := CreateEvent(nil, False, False, pipch^.SignalEventName);
+ If ed.hWaitFor <> 0 then
+ begin
+ { enumerate all the top level windows to find all loaded MIRANDANAME
+ classes -- }
+ EnumWindows(@ProcessRequest, lParam(@ed));
+ { close the wait-for-reply object }
+ CloseHandle(ed.hWaitFor);
+ end;
+ { unmap the memory from this address space }
+ UnmapViewOfFile(pipch);
+ end; {if}
+ { close the mapping }
+ CloseHandle(hMap);
+ // use the MSDN recommended way, thou there ain't much difference
+ Result := MAKE_HRESULT(0, 0, (ed.idCmdFirst - Result) + 1);
+ end else begin
+ // the mapping file already exists, which is not good!
+ end;
+ end else begin
+ // same as giving a SEVERITY_SUCCESS, FACILITY_NULL, since that
+ // just clears the higher bits, which is done anyway
+ Result := MAKE_HRESULT(0, 0, 1);
+ end; //if
+ end;
+
+ function TShlComRec_GetCommandString(Self: PContextMenu3_Interface;
+ idCmd, uType: UINT; pwReserved: PUINT; pszName: PChar; cchMax: UINT): HResult; stdcall;
+ begin
+ Result := E_NOTIMPL;
+ end;
+
+ function ipcGetFiles(pipch: PHeaderIPC; pDataObject: PDataObject_Interface; const hContact: THandle): Integer;
+ type
+ TDragQueryFile = function(hDrop: THandle; fileIndex: Integer; FileName: PChar; cbSize: Integer): Integer; stdcall;
+ var
+ fet: TFormatEtc;
+ stgm: TStgMedium;
+ pct: PSlotIPC;
+ iFile: Cardinal;
+ iFileMax: Cardinal;
+ hShell: THandle;
+ DragQueryFile: TDragQueryFile;
+ cbSize: Integer;
+ hDrop: THandle;
+ begin
+ Result := E_INVALIDARG;
+ hShell := LoadLibrary('shell32.dll');
+ if hShell <> 0 then
+ begin
+ DragQueryFile := GetProcAddress(hShell, 'DragQueryFileA');
+ if @DragQueryFile <> nil then
+ begin
+ fet.cfFormat := CF_HDROP;
+ fet.ptd := nil;
+ fet.dwAspect := DVASPECT_CONTENT;
+ fet.lindex := -1;
+ fet.tymed := TYMED_HGLOBAL;
+ Result := pDataObject^.ptrVTable^.GetData(pDataObject, fet, stgm);
+ if Result = S_OK then
+ begin
+ // FIX, actually lock the global object and get a pointer
+ Pointer(hDrop) := GlobalLock(stgm.hGlobal);
+ if hDrop <> 0 then
+ begin
+ // get the maximum number of files
+ iFileMax := DragQueryFile(stgm.hGlobal, $FFFFFFFF, nil, 0);
+ iFile := 0;
+ while iFile < iFileMax do
+ begin
+ // get the size of the file path
+ cbSize := DragQueryFile(stgm.hGlobal, iFile, nil, 0);
+ // get the buffer
+ pct := ipcAlloc(pipch, cbSize + 1); // including null term
+ // allocated?
+ if pct = nil then Break;
+ // store the hContact
+ pct^.hContact := hContact;
+ // copy it to the buffer
+ DragQueryFile(stgm.hGlobal, iFile, PChar(Integer(pct)+sizeof(TSlotIPC)), pct^.cbStrSection);
+ // next file
+ Inc(iFile);
+ end; //while
+ // store the number of files
+ pipch^.Slots := iFile;
+ GlobalUnlock(stgm.hGlobal);
+ end; //if hDrop check
+ // release the mediumn the lock may of failed
+ ReleaseStgMedium(stgm);
+ end; //if
+ end; //if
+ // free the dll
+ FreeLibrary(hShell);
+ end; //if
+ end;
+
+ function RequestTransfer(Self: PShlComRec; idxCmd: Integer): Integer;
+ var
+ hMap: THandle;
+ pipch: PHeaderIPC;
+ mii: TMenuItemInfo;
+ hTransfer: THandle;
+ psd: PMenuDrawInfo;
+ hReply: THandle;
+ replyBits: Integer;
+ begin
+ Result := E_INVALIDARG;
+ // get the contact information
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_DATA;
+ if GetMenuItemInfo(Self^.hRootMenu, Self^.idCmdFirst + idxCmd, False, mii) then
+ begin
+ // get the pointer
+ Integer(psd) := mii.dwItemData;
+ // the ID stored in the item pointer and the ID for the menu must match
+ if (psd = nil) or (psd^.wID <> mii.wID) then
+ begin
+ //MessageBox(0,'ptr assocated with menu is NULL','',MB_OK);
+ Exit;
+ end; //if
+ end else begin
+ //MessageBox(0,'GetMenuItemInfo failed?','',MB_OK);
+ // couldn't get the info, can't start the transfer
+ Result := E_INVALIDARG; Exit;
+ end; //if
+ // is there an IDataObject instance?
+ if Self^.pDataObject <> nil then
+ begin
+ // OpenEvent() the work object to see if the instance is still around
+ hTransfer := OpenEvent(EVENT_ALL_ACCESS, False, PChar(CreateProcessUID(psd^.pid)));
+ if hTransfer <> 0 then
+ begin
+ // map the ipc file again
+ hMap := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, IPC_PACKET_SIZE, IPC_PACKET_NAME);
+ if (hMap <> 0) and (GetLastError <> ERROR_ALREADY_EXISTS) then
+ begin
+ // map it to process
+ pipch := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if pipch <> nil then
+ begin
+ // create the name of the object to be signalled by the ST
+ strcpy(pipch^.SignalEventName, PChar(CreateUID()));
+ // create it
+ hReply := CreateEvent(nil, False, False, pipch^.SignalEventName);
+ if hReply <> 0 then
+ begin
+ if dtCommand in psd^.fTypes then
+ begin
+ if Assigned(psd^.MenuCommandCallback) then
+ Result := psd^.MenuCommandCallback(pipch, hTransfer, hReply, psd);
+ end
+ else
+ begin
+
+ // prepare the buffer
+ ipcPrepareRequests(IPC_PACKET_SIZE, pipch, REQUEST_XFRFILES);
+ // get all the files into the packet
+ if ipcGetFiles(pipch, Self^.pDataObject, psd^.hContact) = S_OK then
+ begin
+ // need to wait for the ST to open the mapping object
+ // since if we close it before it's opened it the data it
+ // has will be undefined
+ replyBits := ipcSendRequest(hTransfer, hReply, pipch, 200);
+ if replyBits <> REPLY_FAIL then
+ begin
+ // they got the files!
+ Result := S_OK;
+ end; //if
+ end;
+
+ end;
+ // close the work object name
+ CloseHandle(hReply);
+ end; //if
+ // unmap it from this process
+ UnmapViewOfFile(pipch);
+ end; //if
+ // close the map
+ CloseHandle(hMap);
+ end; //if
+ // close the handle to the ST object name
+ CloseHandle(hTransfer);
+ end; //if
+ end //if;
+ end;
+
+ function TShlComRec_InvokeCommand(Self: PContextMenu3_Interface;
+ var lpici: TCMInvokeCommandInfo): HResult; stdcall;
+ begin
+ Result := RequestTransfer(Self^.ptrInstance, LOWORD(Integer(lpici.lpVerb)));
+ end;
+
+ function TShlComRec_HandleMenuMsgs(Self: PContextMenu3_Interface;
+ uMsg: UINT; wParam: WPARAM; lParam: LPARAM; pResult: PLResult): HResult;
+ const
+ WM_DRAWITEM = $002B;
+ WM_MEASUREITEM = $002C;
+ var
+ dwi: PDrawItemStruct;
+ msi: PMeasureItemStruct;
+ psd: PMenuDrawInfo;
+ ncm: TNonClientMetrics;
+ hOldFont: THandle;
+ hFont: THandle;
+ tS: TSize;
+ dx: Integer;
+ hBr: HBRUSH;
+ icorc: TRect;
+ hMemDC: HDC;
+ begin
+ pResult^ := Integer(True);
+ if (uMsg = WM_DRAWITEM) and (wParam = 0) then
+ begin
+ // either a main sub menu, a group menu or a contact
+ dwi := PDrawItemStruct(lParam);
+ Integer(psd) := dwi^.itemData;
+ // don't fill
+ SetBkMode(dwi^.hDC, TRANSPARENT);
+ // where to draw the icon?
+ icorc.Left := 0;
+ // center it
+ with dwi^ do
+ icorc.Top := rcItem.Top + ((rcItem.Bottom - rcItem.Top) div 2) - (16 div 2);
+ icorc.Right := icorc.Left + 16;
+ icorc.Bottom := icorc.Top + 16;
+ // draw for groups
+ if (dtGroup in psd^.fTypes) or (dtEntry in psd^.fTypes) then
+ begin
+ hBr := GetSysColorBrush(COLOR_MENU);
+ FillRect(dwi^.hDC, dwi^.rcItem, hBr);
+ DeleteObject(hBr);
+ //
+ if (ODS_SELECTED and dwi^.itemState = ODS_SELECTED) then
+ begin
+ // only do this for entry menu types otherwise a black mask
+ // is drawn under groups
+ hBr := GetSysColorBrush(COLOR_HIGHLIGHT);
+ FillRect(dwi^.hDC, dwi^.rcItem, hBr);
+ DeleteObject(hBr);
+ SetTextColor(dwi^.hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ end; //if
+ // draw icon
+ with dwi^, icorc do
+ begin
+ if (ODS_SELECTED and dwi^.itemState) = ODS_SELECTED then
+ begin
+ hBr := GetSysColorBrush(COLOR_HIGHLIGHT);
+ end else begin
+ hBr := GetSysColorBrush(COLOR_MENU);
+ end; //if
+ DrawIconEx(hDC, Left+1, Top, psd^.hStatusIcon,
+ 16, 16, // width, height
+ 0, // step
+ hBr, // brush
+ DI_NORMAL);
+ DeleteObject(hBr);
+ end; //with
+ // draw the text
+ with dwi^ do
+ begin
+ Inc(rcItem.Left, ((rcItem.Bottom-rcItem.Top)-2));
+ DrawText(hDC, psd^.szText, psd^.cch, rcItem, DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER);
+ // draw the name of the database text if it's there
+ if psd^.szProfile <> nil then
+ begin
+ GetTextExtentPoint32(dwi^.hDC, psd^.szText, psd^.cch, tS);
+ Inc(rcItem.Left, tS.cx+8);
+ SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
+ DrawText(hDC, psd^.szProfile, lstrlen(psd^.szProfile), rcItem, DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER);
+ end; //if
+ end; //with
+ end else
+ begin
+ // it's a contact!
+ hBr := GetSysColorBrush(COLOR_MENU);
+ FillRect(dwi^.hDC, dwi^.rcItem, hBr);
+ DeleteObject(hBr);
+ if ODS_SELECTED and dwi^.itemState = ODS_SELECTED then
+ begin
+ hBr := GetSysColorBrush(COLOR_HIGHLIGHT);
+ FillRect(dwi^.hDC, dwi^.rcItem, hBr);
+ DeleteObject(hBr);
+ SetTextColor(dwi^.hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ end;
+ // draw icon
+ with dwi^, icorc do
+ begin
+ if (ODS_SELECTED and dwi^.itemState) = ODS_SELECTED then
+ begin
+ hBr := GetSysColorBrush(COLOR_HIGHLIGHT);
+ end else begin
+ hBr := GetSysColorBrush(COLOR_MENU);
+ end; //if
+ DrawIconEx(hDC, Left+2, Top, psd^.hStatusIcon,
+ 16, 16, // width, height
+ 0, // step
+ hBr, // brush
+ DI_NORMAL);
+ DeleteObject(hBr);
+ end; //with
+ // draw the text
+ with dwi^ do
+ begin
+ Inc(rcItem.Left, (rcItem.Bottom-rcItem.Top) + 1);
+ DrawText(hDC, psd^.szText, psd^.cch, rcItem, DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER);
+ end; //with
+ end; //if
+ end
+ else if (uMsg = WM_MEASUREITEM) then
+ begin
+ // don't check if it's really a menu
+ msi := PMeasureItemStruct(lParam);
+ Integer(psd) := msi^.itemData;
+ ncm.cbSize := sizeof(TNonClientMetrics);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @ncm, 0);
+ // create the font used in menus, this font should be cached somewhere really
+ {$IFDEF FPC}
+ hFont := CreateFontIndirect(@ncm.lfMenuFont);
+ {$ELSE}
+ hFont := CreateFontIndirect(ncm.lfMenuFont);
+ {$ENDIF}
+ hMemDC := Self^.ptrInstance^.hMemDC;
+ // select in the font
+ hOldFont := SelectObject(hMemDC, hFont);
+ // default to an icon
+ dx := 16;
+ // get the size 'n' account for the icon
+ GetTextExtentPoint32(hMemDC, psd^.szText, psd^.cch, tS);
+ Inc(dx, tS.cx);
+ // main menu item?
+ if psd^.szProfile <> nil then
+ begin
+ GetTextExtentPoint32(hMemDC, psd^.szProfile, lstrlen(psd^.szProfile), tS);
+ Inc(dx, tS.cx);
+ end;
+ // store it
+ msi^.itemWidth := dx + Integer(ncm.iMenuWidth);
+ msi^.itemHeight := Integer(ncm.iMenuHeight)+2;
+ if tS.cy > msi^.itemHeight then Inc(msi^.itemHeight, tS.cy - msi^.itemHeight);
+ // clean up
+ SelectObject(hMemDC, hOldFont);
+ DeleteObject(hFont);
+ end;
+ Result := S_OK;
+ end;
+
+ function TShlComRec_HandleMenuMsg(Self: PContextMenu3_Interface;
+ uMsg: UINT; wParam: WPARAM; lParam: LPARAM): HResult; stdcall;
+ var
+ Dummy: HResult;
+ begin
+ Result := TShlComRec_HandleMenuMsgs(Self, uMsg, wParam, lParam, @Dummy);
+ end;
+
+ function TShlComRec_HandleMenuMsg2(Self: PContextMenu3_Interface;
+ uMsg: UINT; wParam: WPARAM; lParam: LPARAM; plResult: Pointer{^LResult}): HResult; stdcall;
+ var
+ Dummy: HResult;
+ begin
+ // this will be null if a return value isn't needed.
+ if plResult = nil then plResult := @Dummy;
+ Result := TShlComRec_HandleMenuMsgs(Self, uMsg, wParam, lParam, plResult);
+ end;
+
+ function TShlComRec_Create: PShlComRec;
+ var
+ DC: HDC;
+ begin
+ New(Result);
+ { build all the function tables for interfaces }
+ with Result^.ShellExtInit_Interface do
+ begin
+ { this is only owned by us... }
+ ptrVTable := @vTable;
+ { IUnknown }
+ vTable.QueryInterface := @TShlComRec_QueryInterface;
+ vTable.AddRef := @TShlComRec_AddRef;
+ vTable.Release := @TShlComRec_Release;
+ { IShellExtInit }
+ vTable.Initialise := @TShlComRec_Initialise;
+ { instance of a TShlComRec }
+ ptrInstance := Result;
+ end;
+ with Result^.ContextMenu3_Interface do
+ begin
+ ptrVTable := @vTable;
+ { IUnknown }
+ vTable.QueryInterface := @TShlComRec_QueryInterface;
+ vTable.AddRef := @TShlComRec_AddRef;
+ vTable.Release := @TShlComRec_Release;
+ { IContextMenu }
+ vTable.QueryContextMenu := @TShlComRec_QueryContextMenu;
+ vTable.InvokeCommand := @TShlComRec_InvokeCommand;
+ vTable.GetCommandString := @TShlComRec_GetCommandString;
+ { IContextMenu2 }
+ vTable.HandleMenuMsg := @TShlComRec_HandleMenuMsg;
+ { IContextMenu3 }
+ vTable.HandleMenuMsg2 := @TShlComRec_HandleMenuMsg2;
+ { instance data }
+ ptrInstance := Result;
+ end;
+ { initalise variables }
+ Result^.RefCount := 1;
+ Result^.hDllHeap := HeapCreate(0, 0, 0);
+ Result^.hRootMenu := 0;
+ Result^.hRecentMenu := 0;
+ Result^.RecentCount := 0;
+ Result^.idCmdFirst := 0;
+ Result^.pDataObject := nil;
+ Result^.ProtoIcons := nil;
+ Result^.ProtoIconsCount := 0;
+ // create an inmemory DC
+ DC := GetDC(0);
+ Result^.hMemDC := CreateCompatibleDC(DC);
+ ReleaseDC(0,DC);
+ { keep count on the number of objects }
+ Inc(dllpublic.ObjectCount);
+ end;
+
+{ IClassFactory }
+
+type
+
+ PVTable_IClassFactory = ^TVTable_IClassFactory;
+ TVTable_IClassFactory = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: Pointer;
+ Release: Pointer;
+ { IClassFactory }
+ CreateInstance: Pointer;
+ LockServer: Pointer;
+ end;
+ PClassFactoryRec = ^TClassFactoryRec;
+ TClassFactoryRec = record
+ ptrVTable: PVTable_IClassFactory;
+ vTable: TVTable_IClassFactory;
+ {fields}
+ RefCount: LongInt;
+ end;
+
+ function TClassFactoryRec_QueryInterface(Self: PClassFactoryRec;
+ const IID: TIID; var Obj): HResult; stdcall;
+ begin
+ Pointer(Obj) := nil;
+ Result := E_NOTIMPL;
+ end;
+
+ function TClassFactoryRec_AddRef(Self: PClassFactoryRec): LongInt; stdcall;
+ begin
+ Inc(Self^.RefCount);
+ Result := Self^.RefCount;
+ end;
+
+ function TClassFactoryRec_Release(Self: PClassFactoryRec): LongInt; stdcall;
+ begin
+ Dec(Self^.RefCount);
+ Result := Self^.RefCount;
+ if Result = 0 then
+ begin
+ Dispose(Self);
+ Dec(dllpublic.FactoryCount);
+ end; {if}
+ end;
+
+ function TClassFactoryRec_CreateInstance(Self: PClassFactoryRec;
+ unkOuter: Pointer; const IID: TIID; var Obj): HResult; stdcall;
+ var
+ ShlComRec: PShlComRec;
+ begin
+ Pointer(Obj) := nil;
+ Result := CLASS_E_NOAGGREGATION;
+ if unkOuter = nil then
+ begin
+ { Before Vista, the system queried for a IShell interface then queried for a context menu, Vista now
+ queries for a context menu (or a shell menu) then QI()'s the other interface }
+ if IsEqualIID(IID, IID_IContextMenu) then
+ begin
+ Result := S_OK;
+ ShlComRec := TShlComRec_Create;
+ Pointer(Obj) := @ShlComRec^.ContextMenu3_Interface;
+ end;
+ if IsEqualIID(IID, IID_IShellExtInit) then
+ begin
+ Result := S_OK;
+ ShlComRec := TShlComRec_Create;
+ Pointer(Obj) := @ShlComRec^.ShellExtInit_Interface;
+ end; //if
+ end; //if
+ end;
+ function TClassFactoryRec_LockServer(Self: PClassFactoryRec; fLock: BOOL): HResult; stdcall;
+ begin
+ Result := E_NOTIMPL;
+ end;
+
+ function TClassFactoryRec_Create: PClassFactoryRec;
+ begin
+ New(Result);
+ Result^.ptrVTable := @Result^.vTable;
+ { IUnknown }
+ Result^.vTable.QueryInterface := @TClassFactoryRec_QueryInterface;
+ Result^.vTable.AddRef := @TClassFactoryRec_AddRef;
+ Result^.vTable.Release := @TClassFactoryRec_Release;
+ { IClassFactory }
+ Result^.vTable.CreateInstance := @TClassFactoryRec_CreateInstance;
+ Result^.vTable.LockServer := @TClassFactoryRec_LockServer;
+ { inital the variables }
+ Result^.RefCount := 1;
+ { count the number of factories }
+ Inc(dllpublic.FactoryCount);
+ end;
+
+ //
+ // IPC part
+ //
+
+
+ type
+ PFileList = ^TFileList;
+ TFileList = array[0..0] of PChar;
+ PAddArgList = ^TAddArgList;
+ TAddArgList = record
+ szFile: PChar; // file being processed
+ cch: Cardinal; // it's length (with space for NULL char)
+ count: Cardinal; // number we have so far
+ files: PFileList;
+ hContact: THandle;
+ hEvent: THandle;
+ end;
+
+ function AddToList(var args: TAddArgList): LongBool;
+ var
+ attr: Cardinal;
+ p: Pointer;
+ hFind: THandle;
+ fd: TWIN32FINDDATA;
+ szBuf: array[0..MAX_PATH] of Char;
+ szThis: PChar;
+ cchThis: Cardinal;
+ begin
+ Result := False;
+ attr := GetFileAttributes(args.szFile);
+ if (attr <> $FFFFFFFF) and ((attr and FILE_ATTRIBUTE_HIDDEN) = 0) then
+ begin
+ if args.count mod 10 = 5 then
+ begin
+ if CallService(MS_SYSTEM_TERMINATED,0,0) <> 0 then
+ begin
+ Result := True; Exit;
+ end; //if
+ end;
+ if attr and FILE_ATTRIBUTE_DIRECTORY <> 0 then
+ begin
+ // add the directory
+ strcpy(szBuf,args.szFile);
+ ReallocMem(args.files,(args.count+1)*sizeof(PChar));
+ GetMem(p,strlen(szBuf)+1);
+ strcpy(p,szBuf);
+ args.files^[args.count] := p;
+ inc(args.count);
+ // tack on ending search token
+ strcat(szBuf,'\*');
+ hFind := FindFirstFile(szBuf,fd);
+ while True do
+ begin
+ if fd.cFileName[0] <> '.' then
+ begin
+ strcpy(szBuf,args.szFile);
+ strcat(szBuf,'\');
+ strcat(szBuf,fd.cFileName);
+ // keep a copy of the current thing being processed
+ szThis := args.szFile; args.szFile := szBuf;
+ cchThis := args.cch; args.cch := strlen(szBuf)+1;
+ // recurse
+ Result := AddToList(args);
+ // restore
+ args.szFile := szThis;
+ args.cch := cchThis;
+ if Result then Break;
+ end; //if
+ if not FindNextFile(hFind,fd) then Break;
+ end; //while
+ FindClose(hFind);
+ end else begin
+ // add the file
+ ReallocMem(args.files,(args.count+1)*sizeof(PChar));
+ GetMem(p,args.cch);
+ strcpy(p,args.szFile);
+ args.files^[args.count] := p;
+ inc (args.count);
+ end; //if
+ end;
+ end;
+
+ procedure MainThreadIssueTransfer(p: PAddArgList); stdcall;
+ {$define SHL_IDC}
+ {$define SHL_KEYS}
+ {$include shlc.inc}
+ {$undef SHL_KEYS}
+ {$undef SHL_IDC}
+ begin
+ DBWriteContactSettingByte(p^.hContact, SHLExt_Name, SHLExt_MRU, 1);
+ CallService(MS_FILE_SENDSPECIFICFILES,p^.hContact,lParam(p^.files));
+ SetEvent(p^.hEvent);
+ end;
+
+ function IssueTransferThread(pipch: PHeaderIPC): Cardinal; stdcall;
+ var
+ szBuf: array[0..MAX_PATH] of Char;
+ pct: PSlotIPC;
+ args: TAddArgList;
+ bQuit: LongBool;
+ j,c: Cardinal;
+ p: Pointer;
+ hMainThread: THandle;
+ begin
+ CallService(MS_SYSTEM_THREAD_PUSH,0,0);
+ hMainThread := THandle(pipch^.Param);
+ GetCurrentDirectory(sizeof(szBuf),szBuf);
+ args.count := 0;
+ args.files := nil;
+ pct := pipch^.DataPtr;
+ bQuit := False;
+ while pct <> nil do
+ begin
+ if (pct^.cbSize <> sizeof(TSlotIPC)) then break;
+ args.szFile := PChar(Integer(pct) + sizeof(TSlotIPC));
+ args.hContact := pct^.hContact;
+ args.cch := pct^.cbStrSection+1;
+ bQuit := AddToList(args);
+ if bQuit then Break;
+ pct := pct^.next;
+ end; //while
+ if args.files <> nil then
+ begin
+ ReallocMem(args.files,(args.count+1)*sizeof(PChar));
+ args.files^[args.count] := nil;
+ inc (args.count);
+ if (not bQuit) then
+ begin
+ args.hEvent := CreateEvent(nil, True, False, nil);
+ QueueUserAPC(@MainThreadIssueTransfer,hMainThread,DWORD(@args));
+ while True do
+ begin
+ if WaitForSingleObjectEx(args.hEvent,INFINITE,True) <> WAIT_IO_COMPLETION then Break;
+ end;
+ CloseHandle(args.hEvent);
+ end; //if
+ c := args.count-1;
+ for j := 0 to c do
+ begin
+ p := args.files^[j];
+ if p <> nil then FreeMem(p);
+ end;
+ FreeMem(args.files);
+ end;
+ SetCurrentDirectory(szBuf);
+ FreeMem(pipch);
+ CloseHandle(hMainThread);
+ CallService(MS_SYSTEM_THREAD_POP,0,0);
+ ExitThread(0);
+ end;
+
+type
+
+ PSlotInfo = ^TSlotInfo;
+ TSlotInfo = record
+ hContact: THandle;
+ hProto: Cardinal;
+ dwStatus: Integer; // will be aligned anyway
+ end;
+ TSlotArray = array[0..$FFFFFF] of TSlotInfo;
+ PSlotArray = ^TSlotArray;
+
+ function SortContact(var Item1, Item2: TSlotInfo): Integer; stdcall;
+ begin
+ Result := PluginLink^.CallService(MS_CLIST_CONTACTSCOMPARE, item1.hContact, item2.hContact);
+ end;
+
+ // from FP FCL
+
+ procedure QuickSort (FList: PSlotArray; L, R: LongInt);
+ var
+ I, J: LongInt;
+ P, Q: TSlotInfo;
+ begin
+ repeat
+ I := L;
+ J := R;
+ P := FList^[ (L+R) div 2 ];
+ repeat
+ while SortContact(P, FList^[i]) > 0 do Inc(i);
+ while SortContact(P, FList^[j]) < 0 do Dec(j);
+ if I <= J then
+ begin
+ Q := FList^[I];
+ FList^[I] := FList^[J];
+ FList^[J] := Q;
+ Inc(I);
+ Dec(J);
+ end; //if
+ until I > J;
+ if L < J then QuickSort (FList, L, J);
+ L := I;
+ until I >= R;
+ end;
+
+ {$define SHL_KEYS}
+ {$include shlc.inc}
+ {$undef SHL_KEYS}
+
+ procedure ipcGetSkinIcons(ipch: PHeaderIPC);
+ var
+ protoCount: Integer;
+ pp: ^PPROTOCOLDESCRIPTOR;
+ spi: TSlotProtoIcons;
+ j: Cardinal;
+ pct: PSlotIPC;
+ szTmp: array[0..63] of Char;
+ dwCaps: Cardinal;
+ begin
+ if (CallService(MS_PROTO_ENUMPROTOCOLS,wParam(@protoCount),lParam(@pp)) = 0) and (protoCount <> 0) then
+ begin
+ spi.pid := GetCurrentProcessId();
+ while protoCount > 0 do
+ begin
+ if (pp^.type_ = PROTOTYPE_PROTOCOL) then
+ begin
+ strcpy(szTmp,pp^.szName);
+ strcat(szTmp,PS_GETCAPS);
+ dwCaps := CallService(szTmp,PFLAGNUM_1,0);
+ if (dwCaps and PF1_FILESEND) <> 0 then
+ begin
+ pct := ipcAlloc(ipch,sizeof(TSlotProtoIcons));
+ if pct <> nil then
+ begin
+ // capture all the icons!
+ spi.hProto := StrHash(pp^.szName);
+ for j := 0 to 9 do
+ begin
+ spi.hIcons[j] := LoadSkinnedProtoIcon(pp^.szName,ID_STATUS_OFFLINE+j);
+ end; //for
+ pct^.fType := REQUEST_NEWICONS;
+ CopyMemory(Pointer(Integer(pct)+sizeof(TSlotIPC)),@spi,sizeof(TSlotProtoIcons));
+ if ipch^.NewIconsBegin = nil then ipch^.NewIconsBegin := pct;
+ end; //if
+ end; //if
+ end; //if
+ inc (pp);
+ dec (protoCount);
+ end; //while
+ end; //if
+ // add Miranda icon
+ pct := ipcAlloc(ipch, sizeof(TSlotProtoIcons));
+ if pct <> nil then
+ begin
+ ZeroMemory(@spi.hIcons, sizeof(spi.hIcons));
+ spi.hProto := 0; // no protocol
+ spi.hIcons[0] := LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ pct^.fType := REQUEST_NEWICONS;
+ CopyMemory(Pointer(Integer(pct)+sizeof(TSlotIPC)),@spi,sizeof(TSlotProtoIcons));
+ if ipch^.NewIconsBegin = nil then ipch^.NewIconsBegin := pct;
+ end; //if
+ end;
+
+ function ipcGetSortedContacts(ipch: PHeaderIPC; pSlot: pint; bGroupMode: Boolean): Boolean;
+ var
+ dwContacts: Cardinal;
+ pContacts: PSlotArray;
+ hContact: THandle;
+ I: Integer;
+ dwOnline: Cardinal;
+ szProto: PChar;
+ dwStatus: Integer;
+ pct: PSlotIPC;
+ szContact: PChar;
+ dbv: TDBVariant;
+ bHideOffline: Boolean;
+ szTmp: array[0..63] of Char;
+ dwCaps: Cardinal;
+ szSlot: PChar;
+ n, rc, cch: Cardinal;
+ begin
+ Result := False;
+ // hide offliners?
+ bHideOffline := DBGetContactSettingByte(0, 'CList', 'HideOffline', 0) = 1;
+ // do they wanna hide the offline people anyway?
+ if DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoOffline, 0) = 1 then
+ begin
+ // hide offline people
+ bHideOffline := True;
+ end;
+ // get the number of contacts
+ dwContacts := PluginLink^.CallService(MS_DB_CONTACT_GETCOUNT, 0, 0);
+ if dwContacts = 0 then Exit;
+ // get the contacts in the array to be sorted by status, trim out anyone
+ // who doesn't wanna be seen.
+ GetMem(pContacts, (dwContacts+2) * sizeof(TSlotInfo));
+ i := 0;
+ dwOnline := 0;
+ hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact <> 0) do
+ begin
+ if i >= dwContacts then Break;
+ (* do they have a running protocol? *)
+ Integer(szProto) := PluginLink^.CallService(MS_PROTO_GETCONTACTBASEPROTO, hContact, 0);
+ if szProto <> nil then
+ begin
+ (* does it support file sends? *)
+ strcpy(szTmp, szProto);
+ strcat(szTmp, PS_GETCAPS);
+ dwCaps := CallService(szTmp,PFLAGNUM_1,0);
+ if (dwCaps and PF1_FILESEND) = 0 then
+ begin
+ hContact := CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ continue;
+ end;
+ dwStatus := DBGetContactSettingWord(hContact, szProto, 'Status', ID_STATUS_OFFLINE);
+ if dwStatus <> ID_STATUS_OFFLINE then Inc(dwOnline)
+ else if bHideOffline then
+ begin
+ hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ continue;
+ end; //if
+ // is HIT on?
+ if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHITContacts, BST_UNCHECKED) then
+ begin
+ // don't show people who are "Hidden" "NotOnList" or Ignored
+ if (DBGetContactSettingByte(hContact, 'CList', 'Hidden', 0) = 1)
+ or (DBGetContactSettingByte(hContact, 'CList', 'NotOnList', 0) = 1)
+ or (PluginLink^.CallService(MS_IGNORE_ISIGNORED, hContact, IGNOREEVENT_MESSAGE or IGNOREEVENT_URL or IGNOREEVENT_FILE) <> 0) then
+ begin
+ hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ continue;
+ end; //if
+ end; //if
+ // is HIT2 off?
+ if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHIT2Contacts, BST_UNCHECKED) then
+ begin
+ if DBGetContactSettingWord(hContact, szProto, 'ApparentMode', 0) = ID_STATUS_OFFLINE then
+ begin
+ hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ continue;
+ end; //if
+ end; //if
+ // store
+ pContacts^[i].hContact := hContact;
+ pContacts^[i].dwStatus := dwStatus;
+ pContacts^[i].hProto := StrHash(szProto);
+ Inc(i);
+ end else begin
+ // contact has no protocol!
+ end; //if
+ hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ end; //while
+ // if no one is online and the CList isn't showing offliners, quit
+ if (dwOnline = 0) and (bHideOffline) then
+ begin
+ FreeMem(pContacts); Exit;
+ end; //if
+ dwContacts := i; i := 0;
+ // sort the array
+ QuickSort(pContacts, 0, dwContacts-1);
+ // create an IPC slot for each contact and store display name, etc
+ while i < dwContacts do
+ begin
+ Integer(szContact) := PluginLink^.CallService(MS_CLIST_GETCONTACTDISPLAYNAME, pContacts^[i].hContact, 0);
+ if (szContact <> nil) then
+ begin
+ n := 0;
+ rc := 1;
+ if bGroupMode then
+ begin
+ rc := DBGetContactSetting(pContacts^[i].hContact,'CList','Group',@dbv);
+ if rc = 0 then
+ begin
+ n := lstrlen(dbv.pszVal)+1;
+ end;
+ end; //if
+ cch := lstrlen(szContact)+1;
+ pct := ipcAlloc(ipch, cch + 1 + n);
+ if pct = nil then
+ begin
+ DBFreeVariant(@dbv);
+ break;
+ end;
+ // lie about the actual size of the TSlotIPC
+ pct^.cbStrSection := cch;
+ szSlot := PChar(Integer(pct) + sizeof(TSlotIPC));
+ strcpy(szSlot, szContact);
+ pct^.fType := REQUEST_CONTACTS;
+ pct^.hContact := pContacts^[i].hContact;
+ pct^.Status := pContacts^[i].dwStatus;
+ pct^.hProto := pContacts^[i].hProto;
+ pct^.MRU := DBGetContactSettingByte(pct^.hContact, SHLExt_Name, SHLExt_MRU, 0);
+ if ipch^.ContactsBegin = nil then ipch^.ContactsBegin := pct;
+ inc(szSlot,cch+1);
+ if rc=0 then
+ begin
+ pct^.hGroup := StrHash(dbv.pszVal);
+ strcpy(szSlot,dbv.pszVal);
+ DBFreeVariant(@dbv);
+ end else begin
+ pct^.hGroup := 0;
+ szSlot^ := #0;
+ end;
+ inc(pSlot^);
+ end; //if
+ Inc(i);
+ end; //while
+ FreeMem(pContacts);
+ //
+ Result := True;
+ end;
+
+ // worker thread to clear MRU, called by the IPC bridge
+ function ClearMRUThread(notused: Pointer): Cardinal; stdcall;
+ {$define SHL_IDC}
+ {$define SHL_KEYS}
+ {$include shlc.inc}
+ {$undef SHL_KEYS}
+ {$undef SHL_IDC}
+ var
+ hContact: THandle;
+ begin
+ CallService(MS_SYSTEM_THREAD_PUSH,0,0);
+ begin
+ hContact := pluginLink^.CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while hContact <> 0 do
+ begin
+ if DBGetContactSettingByte(hContact, SHLExt_Name, SHLExt_MRU, 0) > 0 then
+ begin
+ DBWriteContactSettingByte(hContact, SHLExt_Name, SHLExt_MRU, 0);
+ end;
+ hContact := pluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ end;
+ end;
+ CallService(MS_SYSTEM_THREAD_POP,0,0);
+ ExitThread(0);
+ end;
+
+ // this function is called from an APC into the main thread
+ procedure ipcService(dwParam: DWORD); stdcall;
+ label
+ Reply;
+ var
+ hMap: THandle;
+ pMMT: PHeaderIPC;
+ hSignal: THandle;
+ pct: PSlotIPC;
+ hContact: THandle;
+ szContact: PChar;
+ Status: int;
+ szBuf: PChar;
+ iSlot: Integer;
+ szGroupStr: array[0..31] of Char;
+ dbv: TDBVARIANT;
+ bits: pint;
+ hIcon: THandle;
+ I: Integer;
+ bGroupMode: Boolean;
+ tid: Cardinal;
+ cloned: PHeaderIPC;
+ szMiranda: PChar;
+ begin
+ { try to open the file mapping object the caller must make sure no other
+ running instance is using this file }
+ hMap := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, IPC_PACKET_NAME);
+ If hMap <> 0 then
+ begin
+ { map the file to this process }
+ pMMT := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ { if it fails the caller should of had some timeout in wait }
+ if (pMMT <> nil) and (pMMT^.cbSize = sizeof(THeaderIPC)) and (pMMT^.dwVersion = PLUGIN_MAKE_VERSION(2,0,1,2)) then
+ begin
+ // toggle the right bits
+ bits := @pMMT^.fRequests;
+ // jump right to a worker thread for file processing?
+ if (bits^ and REQUEST_XFRFILES) = REQUEST_XFRFILES then
+ begin
+ GetMem(cloned, IPC_PACKET_SIZE);
+ // translate from client space to cloned heap memory
+ pMMT^.pServerBaseAddress := pMMT^.pClientBaseAddress;
+ pMMT^.pClientBaseAddress := cloned;
+ CopyMemory(cloned, pMMT, IPC_PACKET_SIZE);
+ ipcFixupAddresses(True, cloned);
+ DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),@cloned^.Param,THREAD_SET_CONTEXT,FALSE,0);
+ CloseHandle(CreateThread(nil,0,@IssueTransferThread,cloned,0,tid));
+ goto Reply;
+ end;
+ // the request was to clear the MRU entries, we have no return data
+ if (bits^ and REQUEST_CLEARMRU) = REQUEST_CLEARMRU then
+ begin
+ CloseHandle(CreateThread(nil,0,@ClearMRUThread,nil,0,tid));
+ goto reply;
+ end;
+ // the IPC header may have pointers that need to be translated
+ // in either case the supplied data area pointers has to be
+ // translated to this address space.
+ // the server base address is always removed to get an offset
+ // to which the client base is added, this is what ipcFixupAddresses() does
+ pMMT^.pServerBaseAddress := pMMT^.pClientBaseAddress;
+ pMMT^.pClientBaseAddress := pMMT;
+ // translate to the server space map
+ ipcFixupAddresses(True, pMMT);
+ // store the address map offset so the caller can retranslate
+ pMMT^.pServerBaseAddress := pMMT;
+ // return some options to the client
+ if DBGetContactSettingByte(0,SHLExt_Name,SHLExt_ShowNoIcons,0) <> 0 then
+ begin
+ pMMT^.dwFlags := HIPC_NOICONS;
+ end;
+ // see if we have a custom string for 'Miranda'
+ szMiranda := Translate('Miranda');
+ lstrcpyn(pMMT^.MirandaName,szMiranda,sizeof(pMMT^.MirandaName)-1);
+
+ // for the MRU menu
+ szBuf := Translate('Recently');
+ lstrcpyn(pMMT^.MRUMenuName, szBuf, sizeof(pMMT^.MRUMenuName)-1);
+
+ // and a custom string for "clear entries"
+ szBuf := Translate('Clear entries');
+ lstrcpyn(pMMT^.ClearEntries,szBuf, sizeof(pMMT^.ClearEntries)-1);
+
+ // if the group mode is on, check if they want the CList setting
+ bGroupMode := BST_CHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseGroups, BST_UNCHECKED);
+ if bGroupMode and (BST_CHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseCListSetting, BST_UNCHECKED)) then
+ begin
+ bGroupMode := 1 = DBGetContactSettingByte(0, 'CList', 'UseGroups', 0);
+ end;
+ iSlot := 0;
+ // return profile if set
+ if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoProfile, BST_UNCHECKED) then
+ begin
+ pct := ipcAlloc(pMMT, 50);
+ if pct <> nil then
+ begin
+ // will actually return with .dat if there's space for it, not what the docs say
+ pct^.Status := STATUS_PROFILENAME;
+ PluginLink^.CallService(MS_DB_GETPROFILENAME, 49, Integer(pct)+sizeof(TSlotIPC));
+ end; //if
+ end; //if
+ if (bits^ and REQUEST_NEWICONS) = REQUEST_NEWICONS then
+ begin
+ ipcGetSkinIcons(pMMT);
+ end;
+ if (bits^ and REQUEST_GROUPS = REQUEST_GROUPS) then
+ begin
+ // return contact's grouping if it's present
+ while bGroupMode do
+ begin
+ Str(iSlot, szGroupStr);
+ if DBGetContactSetting(0, 'CListGroups', szGroupStr, @dbv) <> 0 then Break;
+ pct := ipcAlloc(pMMT, lstrlen(dbv.pszVal+1)+1); // first byte has flags, need null term
+ if pct <> nil then
+ begin
+ if pMMT^.GroupsBegin = nil then pMMT^.GroupsBegin := pct;
+ pct^.fType := REQUEST_GROUPS;
+ pct^.hContact := 0;
+ Integer (szBuf) := Integer(pct) + sizeof(TSlotIPC); // get the end of the slot
+ strcpy(szBuf, dbv.pszVal+1);
+ pct^.hGroup := 0;
+ DBFreeVariant(@dbv); // free the string
+ end else begin
+ // outta space
+ DBFreeVariant(@dbv);
+ Break;
+ end; //if
+ Inc(iSlot);
+ end; {while}
+ // if there was no space left, it'll end on null
+ if pct = nil then bits^ := (bits^ or GROUPS_NOTIMPL) and not REQUEST_GROUPS;
+ end; {if: group request}
+ // SHOULD check slot space.
+ if (bits^ and REQUEST_CONTACTS = REQUEST_CONTACTS) then
+ begin
+ if not ipcGetSortedContacts(pMMT, @iSlot, bGroupMode) then
+ begin
+ // fail if there were no contacts AT ALL
+ bits^ := (bits^ or CONTACTS_NOTIMPL) and not REQUEST_CONTACTS;
+ end; //if
+ end; // if:contact request
+ // store the number of slots allocated
+ pMMT^.Slots := iSlot;
+ Reply:
+ { get the handle the caller wants to be signalled on }
+ hSignal := OpenEvent(EVENT_ALL_ACCESS, False, pMMT^.SignalEventName);
+ { did it open? }
+ If hSignal <> 0 then
+ begin
+ { signal and close }
+ SetEvent(hSignal); CloseHandle(hSignal);
+ end;
+ { unmap the shared memory from this process }
+ UnmapViewOfFile(pMMT);
+ end;
+ { close the map file }
+ CloseHandle(hMap);
+ end; {if}
+ //
+ end;
+
+ function ThreadServer(hMainThread: Pointer): Cardinal;
+ {$ifdef FPC}
+ stdcall;
+ {$endif}
+ var
+ hEvent: THandle;
+ begin
+ CallService(MS_SYSTEM_THREAD_PUSH,0,0);
+ hEvent := CreateEvent(nil, False, False, PChar(CreateProcessUID(GetCurrentProcessId())));
+ while True do
+ begin
+ Result := WaitForSingleObjectEx(hEvent,INFINITE,True);
+ if Result = WAIT_OBJECT_0 then
+ begin
+ QueueUserAPC(@ipcService,THandle(hMainThread),0);
+ end; //if
+ if CallService(MS_SYSTEM_TERMINATED,0,0)=1 then Break;
+ end; //while
+ CloseHandle(hEvent);
+ CloseHandle(THandle(hMainThread));
+ CallService(MS_SYSTEM_THREAD_POP,0,0);
+ ExitThread(0);
+ end;
+
+ procedure InvokeThreadServer;
+ var
+ {$ifdef FPC}
+ TID: LongWord;
+ {$else}
+ TID: Cardinal;
+ {$endif}
+ var
+ hMainThread: THandle;
+ begin
+ hMainThread := 0;
+ DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),@hMainThread,THREAD_SET_CONTEXT,FALSE,0);
+ if hMainThread <> 0 then
+ begin
+ {$ifdef FPC}
+ CloseHandle(CreateThread(nil,0,@ThreadServer,Pointer(hMainThread),0,TID));
+ {$else}
+ CloseHandle(BeginThread(nil,0,@ThreadServer,Pointer(hMainThread),0,TID));
+ {$endif}
+ end; //if
+ end;
+
+{ exported functions }
+
+ function DllGetClassObject(const CLSID: TCLSID; const IID: TIID; var Obj): HResult; stdcall;
+ begin
+ Pointer(Obj) := nil;
+ Result := CLASS_E_CLASSNOTAVAILABLE;
+ if (IsEqualCLSID(CLSID,CLSID_ISHLCOM))
+ and (IsEqualIID(IID,IID_IClassFactory))
+ and (FindWindow(MIRANDANAME,nil) <> 0) then
+ begin
+ Pointer(Obj) := TClassFactoryRec_Create;
+ Result := S_OK;
+ end; //if
+ end;
+
+ function DllCanUnloadNow: HResult;
+ begin
+ if ((dllpublic.FactoryCount = 0) and (dllpublic.ObjectCount = 0)) then
+ begin
+ Result := S_OK;
+ end else begin
+ Result := S_FALSE;
+ end; //if
+ end;
+
+{ helper functions }
+
+type
+
+ PSHELLEXECUTEINFO = ^TSHELLEXECUTEINFO;
+ TSHELLEXECUTEINFO = record
+ cbSize: DWORD;
+ fMask: LongInt;
+ hwnd: THandle;
+ lpVerb: PChar;
+ lpFile: PChar;
+ lpParameters: PChar;
+ lpDirectory: PChar;
+ nShow: Integer;
+ hInstApp: THandle;
+ lpIDLIst: Pointer;
+ lpClass: PChar;
+ hKey: THandle;
+ dwHotKey: DWORD;
+ hIcon: THandle; // is union
+ hProcess: THandle;
+ end;
+
+ function ShellExecuteEx(var se: TSHELLEXECUTEINFO): Boolean; stdcall; external 'shell32.dll' name 'ShellExecuteExA';
+
+ function wsprintfs(lpOut, lpFmt: PChar; ArgS: PChar): Integer; cdecl; external 'user32.dll' name 'wsprintfA';
+
+ function RemoveCOMRegistryEntries: HResult;
+ var
+ hRootKey: HKEY;
+ begin
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRootKey) = ERROR_SUCCESS then
+ begin
+ (* need to delete the subkey before the parent key is deleted under NT/2000/XP *)
+ RegDeleteKey(hRootKey, 'CLSID');
+ (* close the key *)
+ RegCloseKey(hRootKey);
+ (* delete it *)
+ if RegDeleteKey(HKEY_CLASSES_ROOT, 'miranda.shlext') <> ERROR_SUCCESS then
+ begin
+ MessageBox(0, 'Unable to delete registry key for "shlext COM", this key may already be deleted or you may need admin rights.', 'Problem', MB_ICONERROR);
+ end; //if
+ end; //if
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, '\*\shellex\ContextMenuHandlers', 0, KEY_ALL_ACCESS, hRootKey) = ERROR_SUCCESS then
+ begin
+ if RegDeleteKey(hRootKey, 'miranda.shlext') <> ERROR_SUCCESS then
+ begin
+ MessageBox(0, 'Unable to delete registry key for "File context menu handlers", this key may already be deleted or you may need admin rights.', 'Problem', MB_ICONERROR);
+ end; //if
+ RegCloseKey(hRootKey);
+ end; //if
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, 'Directory\shellex\ContextMenuHandlers', 0, KEY_ALL_ACCESS, hRootKey) = ERROR_SUCCESS then
+ begin
+ if RegDeleteKey(hRootKey, 'miranda.shlext') <> ERROR_SUCCESS then
+ begin
+ MessageBox(0, 'Unable to delete registry key for "Directory context menu handlers", this key may already be deleted or you may need admin rights.', 'Problem', MB_ICONERROR);
+ end; //if
+ RegCloseKey(hRootKey);
+ end; //if
+ if ERROR_SUCCESS = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', 0, KEY_ALL_ACCESS, hRootKey) then
+ begin
+ if RegDeleteValue(hRootKey, '{72013A26-A94C-11d6-8540-A5E62932711D}') <> ERROR_SUCCESS then
+ begin
+ MessageBox(0, 'Unable to delete registry entry for "Approved context menu handlers", this key may already be deleted or you may need admin rights.', 'Problem', MB_ICONERROR);
+ end; //if
+ RegCloseKey(hRootKey);
+ end; //if
+ Result := S_OK;
+ end;
+
+ { called by the options code to remove COM entries, and before that, get permission, if required.
+ }
+
+ procedure CheckUnregisterServer;
+ var
+ sei: TSHELLEXECUTEINFO;
+ szBuf: array[0..MAX_PATH*2] of Char;
+ szFileName: array[0..MAX_PATH] of Char;
+ begin
+ if not VistaOrLater then
+ begin
+ RemoveCOMRegistryEntries();
+ Exit;
+ end;
+ // launches regsvr to remove the dll under admin.
+ GetModuleFileName(System.hInstance, szFileName, sizeof(szFileName));
+ wsprintfs(szBuf, '/s /u "%s"', szFileName);
+ ZeroMemory(@sei, sizeof(sei));
+ sei.cbSize := sizeof(sei);
+ sei.lpVerb := 'runas';
+ sei.lpFile := 'regsvr32';
+ sei.lpParameters := szBuf;
+ ShellExecuteEx(sei);
+ Sleep(1000);
+ RemoveCOMRegistryEntries();
+ end;
+
+ { Wow, I can't believe there isn't a direct API for this - 'runas' will invoke the UAC and ask
+ for permission before installing the shell extension. note the filepath arg has to be quoted }
+ procedure CheckRegisterServer;
+ var
+ hRegKey: HKEY;
+ sei: TSHELLEXECUTEINFO;
+ szBuf: array[0..MAX_PATH*2] of Char;
+ szFileName: array[0..MAX_PATH] of Char;
+ begin
+ if ERROR_SUCCESS = RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRegKey) then
+ begin
+ RegCloseKey(hRegKey);
+ end else begin
+ if VistaOrLater then
+ begin
+ MessageBox(0, 'Shell context menus requires your permission to register with Windows Explorer (one time only).',
+ 'Miranda IM - Shell context menus (shlext.dll)', MB_OK or MB_ICONINFORMATION);
+ // /s = silent
+ GetModuleFileName(System.hInstance, szFileName, sizeof(szFileName));
+ wsprintfs(szBuf, '/s "%s"', szFileName);
+ ZeroMemory(@sei, sizeof(sei));
+ sei.cbSize := sizeof(sei);
+ sei.lpVerb := 'runas';
+ sei.lpFile := 'regsvr32';
+ sei.lpParameters := szBuf;
+ ShellExecuteEx(sei);
+ end;
+ end;
+ end;
+
+initialization
+begin
+ FillChar(dllpublic, sizeof(dllpublic), 0);
+ IsMultiThread := True;
+ VistaOrLater := GetProcAddress(GetModuleHandle('kernel32'), 'GetProductInfo') <> nil;
+end;
+
+end.
+
diff --git a/plugins/ShlExt/shldlgs.rc b/plugins/ShlExt/shldlgs.rc
new file mode 100644
index 0000000000..0e9cd82b04
--- /dev/null
+++ b/plugins/ShlExt/shldlgs.rc
@@ -0,0 +1,93 @@
+#include "resource.h"
+//#include "afxres.h"
+#define WS_POPUP 0x80000000L
+#define WS_CHILD 0x40000000L
+#define BS_AUTOCHECKBOX 0x00000003L
+#define WS_TABSTOP 0x00010000L
+#define SS_ETCHEDHORZ 0x00000010L
+#define WS_GROUP 0x00020000L
+#ifndef IDC_STATIC
+#define IDC_STATIC (-1)
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_SHLOPTS DIALOG DISCARDABLE 0, 0, 312, 238
+STYLE WS_POPUP
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "Display contacts in their assigned groups (if any)",
+ IDC_USEGROUPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,
+ 35,281,8
+ CONTROL "Only if/when the contact list is using them",
+ IDC_CLISTGROUPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,29,
+ 50,267,8
+ CONTROL "Display hidden, ignored or temporary contacts",
+ IDC_SHOWFULL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,65,
+ 281,8
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,26,21,192,1
+ LTEXT "Menus",IDC_CAPMENUS,10,17,24,8
+ LTEXT "",IDC_STATIC,214,16,10,11,NOT WS_GROUP
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,34,145,183,1
+ LTEXT "Shell Status",IDC_CAPSHLSTATUS,10,141,43,8
+ LTEXT "",IDC_STATIC,214,111,10,11,NOT WS_GROUP
+ LTEXT "...",IDC_STATUS,15,154,253,12
+ GROUPBOX "Shell context menus",IDC_STATIC,0,0,311,238
+ CONTROL "Do not display the profile name in use",IDC_NOPROF,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,80,285,8
+ CONTROL "Show contacts that you have set privacy rules for",
+ IDC_SHOWINVISIBLES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 15,110,290,8
+ PUSHBUTTON "Remove",IDC_REMOVE,14,173,42,14
+ CONTROL "Do not show status icons in menus",IDC_USEOWNERDRAW,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,95,290,8
+ LTEXT "",IDC_STATIC,214,136,10,11,NOT WS_GROUP
+ CONTROL "Do not show contacts that are offline, even if my contact list does",IDC_HIDEOFFLINE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,125,290,8
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,6,6
+ PRODUCTVERSION 1,0,6,6
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "\0"
+ VALUE "FileDescription", "'Click ''n'' send support from Explorer/Common dialogs/Desktop, Right click on a file/folder to be presented with all your Miranda contact lists and then select the profile/contact you want to send to.\0"
+ VALUE "FileVersion", "1, 0, 6, 6\0"
+ VALUE "InternalName", "shlext\0"
+ VALUE "LegalCopyright", "\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "shlext.dll\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "\0"
+ VALUE "ProductVersion", "1, 0, 6, 6\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1200
+ END
+END
+
diff --git a/plugins/ShlExt/shldlgs.res b/plugins/ShlExt/shldlgs.res
new file mode 100644
index 0000000000..3de576e992
--- /dev/null
+++ b/plugins/ShlExt/shldlgs.res
Binary files differ
diff --git a/plugins/ShlExt/shlext.dpr b/plugins/ShlExt/shlext.dpr
new file mode 100644
index 0000000000..eb772e98a8
--- /dev/null
+++ b/plugins/ShlExt/shlext.dpr
@@ -0,0 +1,379 @@
+{$IFDEF FPC}
+ {$PACKRECORDS 4}
+ {$MODE Delphi}
+ {$ASMMODE intel}
+ {$INLINE ON}
+ {$MACRO ON}
+ {$APPTYPE GUI}
+ {$IMAGEBASE $49ac0000}
+{$ELSE}
+ {$IMAGEBASE $49ac0000} // this is ignored with FPC, must be set via the command line
+{$ENDIF}
+
+library shlext;
+uses
+
+ Windows, shlcom, shlipc, m_globaldefs;
+
+ // use the registry to store the COM information needed by the shell
+
+ function DllRegisterServer: HResult; stdcall;
+ var
+ szData: PChar;
+ hRegKey: HKEY;
+ begin
+
+ {$IFDEF INSTALLER_REGISTER}
+ Result := S_OK;
+ {$ELSE}
+ // progID
+ szData := 'shlext (1.0.6.6) - shell context menu support for Miranda v0.3.0.0+';
+ if ERROR_SUCCESS = RegSetValue(HKEY_CLASSES_ROOT, 'miranda.shlext', REG_SZ, szData, Length(szData)) then
+ begin
+ // CLSID related to ProgID
+ szData := '{72013A26-A94C-11d6-8540-A5E62932711D}';
+ if ERROR_SUCCESS = RegSetValue(HKEY_CLASSES_ROOT, 'miranda.shlext\CLSID', REG_SZ, szData, Length(szData)) then
+ begin
+ // CLSID link back to progID
+ szData := 'miranda.shlext';
+ if ERROR_SUCCESS = RegSetValue(HKEY_CLASSES_ROOT, 'CLSID\{72013A26-A94C-11d6-8540-A5E62932711D}', REG_SZ, szData, Length(szData)) then
+ begin
+ // CLSID link back to ProgID under \ProgID again?
+ szData := 'miranda.shlext';
+ if ERROR_SUCCESS = RegSetValue(HKEY_CLASSES_ROOT, 'CLSID\{72013A26-A94C-11d6-8540-A5E62932711D}\ProgID', REG_SZ, szData, Length(szData)) then
+ begin
+ GetMem(szData, MAX_PATH);
+ GetModuleFileName(hInstance, szData, MAX_PATH-1);
+ Result := RegSetValue(HKEY_CLASSES_ROOT, 'CLSID\{72013A26-A94C-11d6-8540-A5E62932711D}\InprocServer32', REG_SZ, szData, Length(szData));
+ FreeMem(szData);
+ if Result = ERROR_SUCCESS then
+ begin
+ // have to add threading model
+ szData := 'CLSID\{72013A26-A94C-11d6-8540-A5E62932711D}\InprocServer32';
+ Result := RegCreateKeyEx(HKEY_CLASSES_ROOT, szData, 0, nil, 0, KEY_SET_VALUE or KEY_CREATE_SUB_KEY, nil, hRegKey, nil);
+ if Result = ERROR_SUCCESS then
+ begin
+ szData := 'Apartment';
+ RegSetValueEx(hRegKey, 'ThreadingModel', 0, REG_SZ, PByte(szData), Length(szData)+1);
+ RegCloseKey(hRegKey);
+ // write which file types to show under
+ szData := '{72013A26-A94C-11d6-8540-A5E62932711D}';
+ // note that *\ should use AllFilesystemObjects for 4.71+
+ if ERROR_SUCCESS = RegSetValue(HKEY_CLASSES_ROOT, '*\shellex\ContextMenuHandlers\miranda.shlext', REG_SZ, szData, Length(szData)) then
+ begin
+ // don't support directories
+ if ERROR_SUCCESS = RegSetValue(HKEY_CLASSES_ROOT, 'Directory\shellex\ContextMenuHandlers\miranda.shlext', REG_SZ, szData, Length(szData)) then
+ begin
+ Result := S_OK;
+ // have to add to the approved list under NT/2000/XP with {CLSID}="<description>"
+ szData := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved';
+ Result := RegCreateKeyEx(HKEY_LOCAL_MACHINE, szData, 0, nil, 0, KEY_SET_VALUE or KEY_CREATE_SUB_KEY, nil, hRegKey, nil);
+ if Result = ERROR_SUCCESS then
+ begin
+ szData := 'shlext (1.0.6.6) - context menu support for Miranda v0.3.0.0+';
+ RegSetValueEx(hRegKey, '{72013A26-A94C-11d6-8540-A5E62932711D}', 0, REG_SZ, PByte(szData), Length(szData)+1);
+ RegCloseKey(hRegKey);
+ end; //if
+ end else Result := E_FAIL;
+ end else Result := E_FAIL;
+ end else Result := E_FAIL;
+ end else Result := E_FAIL;
+ end else Result := E_FAIL;
+ end else Result := E_FAIL;
+ end else Result := E_FAIL;
+ end else Result := E_FAIL;
+ //
+ {$ENDIF}
+
+ end;
+
+ function DllUnregisterServer: HResult; stdcall;
+ begin
+ Result := RemoveCOMRegistryEntries();
+ end;
+
+ // - miranda section ----
+
+ {$include m_options.inc}
+ {$include m_system.inc}
+ {$include m_database.inc}
+ {$include m_file.inc}
+ {$include m_langpack.inc}
+ {$include m_helpers.inc}
+ {$include m_v8.inc}
+
+const
+
+ COMREG_UNKNOWN = $00000000;
+ COMREG_OK = $00000001;
+ COMREG_APPROVED = $00000002;
+
+ function IsCOMRegistered: Integer;
+ var
+ hRegKey: HKEY;
+ lpType: Integer;
+ begin
+ Result := 0;
+ // these arent the BEST checks in the world
+ if ERROR_SUCCESS = RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRegKey) then
+ begin
+ Result := Result or COMREG_OK;
+ RegCloseKey(hRegKey);
+ end; //if
+ lpType := REG_SZ;
+ if ERROR_SUCCESS = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', 0, KEY_READ, hRegKey) then
+ begin
+ if ERROR_SUCCESS = RegQueryValueEx(hRegKey, '{72013A26-A94C-11d6-8540-A5E62932711D}', nil, @lpType, nil, nil) then
+ begin
+ Result := Result or COMREG_APPROVED;
+ end; //if
+ RegCloseKey(hRegKey);
+ end; // if
+ end;
+
+ procedure AutoSize(hwnd: THandle);
+ var
+ szBuf: array[0..MAX_PATH] of Char;
+ DC: HDC;
+ tS: TSize;
+ i: Integer;
+ hFont, hOldFont: THandle;
+ begin
+ DC := GetDC(hwnd);
+ hFont := GetStockObject(DEFAULT_GUI_FONT);
+ hOldFont := SelectObject(DC, hFont);
+ i := GetWindowText(hwnd, szBuf, MAX_PATH);
+ GetTextExtentPoint32(DC, szBuf, i, tS);
+ SelectObject(DC, hOldFont);
+ DeleteObject(hFont);
+ ReleaseDC(hwnd, DC);
+ SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, tS.cx + 10, tS.cy, SWP_NOMOVE or SWP_FRAMECHANGED);
+ end;
+
+ function OptDialogProc(hwndDlg: THandle; wMsg: Integer; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
+ // don't wanna bring in CommCtrl just for a few constants
+ const
+ {$IFNDEF FPC}
+ WM_INITDIALOG = $0110;
+ WM_COMMAND = $0111;
+ WM_USER = $0400;
+ WM_NOTIFY = $004E;
+ {$ENDIF}
+ { propsheet notifications/msessages }
+ //PSN_APPLY = (-200) - 2;
+ PSM_CHANGED = WM_USER + 104;
+ { button styles }
+ BCM_SETSHIELD = ({BCM_FIRST}$1600 + $000C);
+ { hotkey }
+ // bring in the IDC's and storage key names
+ {$define SHL_IDC}
+ {$define SHL_KEYS}
+ {$include shlc.inc}
+ {$undef SHL_KEYS}
+ {$undef SHL_IDC}
+ const
+ COM_OKSTR: array[Boolean] of PChar = (
+ 'Problem, registration missing/deleted.',
+ 'Successfully created shell registration.'
+ );
+ COM_APPROVEDSTR: array[Boolean] of PChar = (
+ 'Not Approved',
+ 'Approved'
+ );
+ var
+ comReg: Integer;
+ iCheck: Integer;
+ szBuf: array[0..MAX_PATH] of Char;
+ cgs: TDBCONTACTGETSETTING;
+ begin
+ Result := wMsg = WM_INITDIALOG;
+ case wMsg of
+ WM_NOTIFY:
+ begin
+ {* FP 2.2.2 seems to have a bug, 'Code' is supposed to be signed
+ but isn't signed, so when comparing -202 (=PSN_APPLY) It doesn't work
+ so here, -202 is converted into hex, what you are looking at is the
+ code == PSN_APPLY check. *}
+ if $FFFFFF36 = pNMHDR(lParam)^.code then
+ begin
+ DBWriteContactSettingByte(0, SHLExt_Name, SHLExt_UseGroups, IsDlgButtonChecked(hwndDlg, IDC_USEGROUPS));
+ DBWriteContactSettingByte(0, SHLExt_Name, SHLExt_UseCListSetting, IsDlgButtonChecked(hwndDlg, IDC_CLISTGROUPS));
+ DBWriteContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoProfile, IsDlgButtonChecked(hwndDlg, IDC_NOPROF));
+ DBWriteContactSettingByte(0, SHLExt_Name, SHLExt_UseHITContacts, IsDlgButtonChecked(hwndDlg, IDC_SHOWFULL));
+ DBWriteContactSettingByte(0, SHLExt_Name, SHLExt_UseHIT2Contacts, IsDlgButtonChecked(hwndDlg, IDC_SHOWINVISIBLES));
+ DBWriteContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoIcons, IsDlgButtonChecked(hwndDlg, IDC_USEOWNERDRAW));
+ DBWriteContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoOffline, IsDlgButtonChecked(hwndDlg, IDC_HIDEOFFLINE));
+ end; //if
+ end;
+ WM_INITDIALOG:
+ begin
+ TranslateDialogDefault(hwndDlg);
+ comReg := IsCOMRegistered();
+ FillChar(szBuf, MAX_PATH, 0);
+ lstrcat(szBuf, Translate(COM_OKSTR[comReg and COMREG_OK = COMREG_OK]));
+ lstrcat(szBuf, ' (');
+ lstrcat(szBuf, Translate(COM_APPROVEDSTR[comReg and COMREG_APPROVED = COMREG_APPROVED]));
+ lstrcat(szBuf, ')');
+ SetWindowText(GetDlgItem(hwndDlg, IDC_STATUS), szBuf);
+ // auto size the static windows to fit their text
+ // they're rendering in a font not selected into the DC.
+ AutoSize(GetDlgItem(hwndDlg, IDC_CAPMENUS));
+ AutoSize(GetDlgItem(hwndDlg, IDC_CAPSTATUS));
+ AutoSize(GetDlgItem(hwndDlg, IDC_CAPSHLSTATUS));
+ // show all the options
+ iCheck := DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseGroups, BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_USEGROUPS, iCheck);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CLISTGROUPS), iCheck = BST_CHECKED);
+ CheckDlgButton(hwndDlg, IDC_CLISTGROUPS, DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseCListSetting, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_NOPROF, DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoProfile, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_SHOWFULL, DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHITContacts, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_SHOWINVISIBLES, DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHIT2Contacts, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_USEOWNERDRAW, DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoIcons, BST_UNCHECKED));
+ CheckDlgButton(hwndDlg, IDC_HIDEOFFLINE, DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoOffline, BST_UNCHECKED));
+ // give the Remove button a Vista icon
+ SendMessage(GetDlgItem(hwndDlg, IDC_REMOVE), BCM_SETSHIELD, 0, 1);
+ end;
+ WM_COMMAND:
+ begin
+ // don't send the changed message if remove is clicked
+ if LOWORD(wParam) <> IDC_REMOVE then
+ begin
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ end; //if
+ case LOWORD(wParam) of
+ IDC_USEGROUPS:
+ begin
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CLISTGROUPS), BST_CHECKED = IsDlgButtonChecked(hwndDlg, IDC_USEGROUPS));
+ end; //if
+ IDC_REMOVE:
+ begin
+ if IDYES = MessageBox(0, Translate('Are you sure? this will remove all the settings stored in your database and all registry entries created for shlext to work with Explorer'), Translate('Disable/Remove shlext'), MB_YESNO or MB_ICONQUESTION) then
+ begin
+ cgs.szModule := SHLExt_Name;
+
+ cgs.szSetting := SHLExt_UseGroups;
+ CallService(MS_DB_CONTACT_DELETESETTING, 0, Integer(@cgs));
+
+ cgs.szSetting := SHLExt_UseCListSetting;
+ CallService(MS_DB_CONTACT_DELETESETTING, 0, Integer(@cgs));
+
+ cgs.szSetting := SHLExt_UseHITContacts;
+ CallService(MS_DB_CONTACT_DELETESETTING, 0, Integer(@cgs));
+
+ cgs.szSetting := SHLExt_UseHIT2Contacts;
+ CallService(MS_DB_CONTACT_DELETESETTING, 0, Integer(@cgs));
+
+ cgs.szSetting := SHLExt_ShowNoProfile;
+ CallService(MS_DB_CONTACT_DELETESETTING, 0, Integer(@cgs));
+
+ cgs.szSetting := SHLExt_ShowNoIcons;
+ CallService(MS_DB_CONTACT_DELETESETTING, 0, Integer(@cgs));
+
+ cgs.szSetting := SHLExt_ShowNoOffline;
+ CallService(MS_DB_CONTACT_DELETESETTING, 0, Integer(@cgs));
+
+ (* remove from Explorer *)
+ //DllUnregisterServer();
+ CheckUnregisterServer();
+ (* show all the settings have gone... *)
+ SendMessage(hwndDlg, WM_INITDIALOG, 0, 0);
+ end; //if
+ end; //if
+ end; //case
+ // LOWORD(wParam) == IDC_*
+ end;{outercase}
+ end; //case
+ end;
+
+ function InitialiseOptionPages(wParam: WPARAM; lParam: LPARAM): int; cdecl;
+ const
+ IDD_SHLOPTS = 101;
+ var
+ optDialog: TOPTIONSDIALOGPAGE;
+ begin
+ Result := 0;
+ FillChar(optDialog, sizeof(TOPTIONSDIALOGPAGE), 0);
+ optDialog.cbSize := sizeof(TOPTIONSDIALOGPAGE);
+ optDialog.flags := ODPF_BOLDGROUPS;
+ optDialog.groupPosition := 0;
+ optDialog.pszGroup := 'Plugins';
+ optDialog.position := -1066;
+ optDialog.pszTitle := Translate('Shell context menus');
+ optDialog.pszTemplate := MAKEINTRESOURCE(IDD_SHLOPTS);
+ {$ifdef VER140}
+ optDialog.hInstance := HInstance;
+ {$else}
+ optDialog.hInstance := System.hInstance;
+ {$endif}
+ optDialog.pfnDlgProc := @OptDialogProc;
+ PluginLink^.CallService(MS_OPT_ADDPAGE, wParam, Integer(@optDialog));
+ end;
+
+ function MirandaPluginInfoEx(mirandaVersion: DWORD): PPLUGININFOEX; cdecl;
+ begin
+ Result := nil;
+ { only support v0.3.0.0+ }
+ if PLUGIN_MAKE_VERSION(0,3,0,0) > MirandaVersion then Exit;
+ { fill in plugininfo }
+ PLUGININFOEX.cbSize := sizeof(PLUGININFOEX);
+ PLUGININFOEX.shortName := 'Shell context menus for transfers';
+ PLUGININFOEX.version := PLUGIN_MAKE_VERSION(2,0,1,2);
+ {$ifdef FPC}
+ PLUGININFOEX.description := 'Click ''n'' send support from Explorer/Common dialogs/Desktop, Right click on a file/folder to be presented with all your Miranda contact lists and then select the profile/contact you want to send to. Built on ' + {$I %DATE%} + ' at ' + {$I %TIME%} + ' with FPC ' + {$I %FPCVERSION%};
+ {$else}
+ PLUGININFOEX.description := '';
+ {$endif}
+ PLUGININFOEX.author := 'egoDust';
+ PLUGININFOEX.authorEmail := 'egodust@users.sourceforge.net';
+ PLUGININFOEX.copyright := '(c) 2009 Sam Kothari (egoDust)';
+ PLUGININFOEX.homePage := 'http://addons.miranda-im.org/details.php?action=viewfile&id=534';
+ PLUGININFOEX.isTransient := 0;
+ PLUGININFOEX.replacesDefaultModule := 0;
+ { This UUID is fetched twice }
+ CopyMemory(@PLUGININFOEX.uuid, @CLSID_ISHLCOM, sizeof(TMUUID));
+ { return info }
+ Result := @PLUGININFOEX;
+ end;
+
+ function MirandaPluginInterfaces: PMUUID; cdecl;
+ const
+ ifaces: array[0..1] of TMUUID =
+ (
+ // same as CLSID_ISHLCOM
+ (a: $72013a26; b: $a94c; c: $11d6; d: ($85,$40,$a5,$e6,$29,$32,$71,$1d) ),
+
+ // MUUID_LASTPLUGIN
+ (a: 0; b: 0; c: 0; d:(0,0,0,0,0,0,0,0) )
+ );
+ begin
+ Result := @ifaces;
+ end;
+
+ function Load(link: PPLUGINLINK): int; cdecl;
+ begin
+ Result := 0;
+ PLUGINLINK := Pointer(link);
+ InvokeThreadServer;
+ PluginLink^.HookEvent(ME_OPT_INITIALISE, InitialiseOptionPages);
+ DllRegisterServer();
+ CheckRegisterServer();
+ //DisableThreadLibraryCalls(System.hInstance);
+ end;
+
+ function Unload: int; cdecl;
+ begin
+ Result := 0;
+ end;
+
+ {$R shldlgs.res}
+
+ exports
+
+ MirandaPluginInfoEx, MirandaPluginInterfaces, Load, Unload;
+
+ exports
+
+ DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer;
+
+begin
+end.
diff --git a/plugins/ShlExt/shlicons.pas b/plugins/ShlExt/shlicons.pas
new file mode 100644
index 0000000000..a5c2e5d393
--- /dev/null
+++ b/plugins/ShlExt/shlicons.pas
@@ -0,0 +1,162 @@
+unit shlicons;
+
+interface
+
+uses
+
+ Windows;
+
+type
+
+ PVTable_IWICBitmap = ^TVTable_IWICBitmap;
+ TVTable_IWICBitmap = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: function(Self: Pointer): Cardinal; stdcall;
+ Release: function(Self: Pointer): Cardinal; stdcall;
+ { IWICBitmapSource }
+ GetSize: function(Self: Pointer; var Width, Height: LongInt): HResult; stdcall;
+ GetPixelFormat: Pointer;
+ GetResolution: Pointer;
+ CopyPalette: Pointer;
+ CopyPixels: function(Self: Pointer; prc: Pointer; cbStride, cbBufferSize: LongWord; pbBuffer: PByte): HResult; stdcall;
+ { IWICBitmap }
+ // .... not used
+
+ end;
+
+ PWICBitmap_Interface = ^TWICBitmap_Interface;
+ TWICBitmap_Interface = record
+ ptrVTable: PVTable_IWICBitmap;
+ end;
+
+ // bare minmum interface to ImagingFactory
+
+ PVTable_ImagingFactory = ^TVTable_ImagingFactory;
+ TVTable_ImagingFactory = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: function(Self: Pointer): Cardinal; stdcall;
+ Release: function(Self: Pointer): Cardinal; stdcall;
+ { ImagingFactory }
+ CreateDecoderFromFilename: Pointer;
+ CreateDecoderFromStream: Pointer;
+ CreateDecoderFromFileHandle: Pointer;
+ CreateComponentInfo: Pointer;
+ CreateDecoder: Pointer;
+ CreateEncoder: Pointer;
+ CreatePalette: Pointer;
+ CreateFormatConverter: Pointer;
+ CreateBitmapScaler: Pointer;
+ CreateBitmapClipper: Pointer;
+ CreateBitmapFlipRotator: Pointer;
+ CreateStream: Pointer;
+ CreateColorContext: Pointer;
+ CreateColorTransformer: Pointer;
+ CreateBitmap: Pointer;
+ CreateBitmapFromSource: Pointer;
+ CreateBitmapFromSourceRect: Pointer;
+ CreateBitmapFromMemory: Pointer;
+ CreateBitmapFromHBITMAP: Pointer;
+ CreateBitmapFromHICON: function(Self: Pointer; hIcon: Windows.HICON; var foo: Pointer): HResult; stdcall;
+ { rest ommited }
+ end;
+
+ PImageFactory_Interface = ^TImageFactory_Interface;
+ TImageFactory_Interface = record
+ ptrVTable: PVTable_ImagingFactory;
+ end;
+
+ function ARGB_GetWorker: PImageFactory_Interface;
+
+ function ARGB_BitmapFromIcon(Factory: PImageFactory_Interface; hdc: Windows.HDC; hIcon: HICON): HBitmap;
+
+implementation
+
+ {$define SHLCOM}
+ {$define COM_STRUCTS}
+ {$define COMAPI}
+ {$include shlc.inc}
+ {$undef SHLCOM}
+ {$undef COM_STRUCTS}
+ {$undef COMAPI}
+
+ {
+ The following implementation has been ported from:
+
+ http://web.archive.org/web/20080121112802/http://shellrevealed.com/blogs/shellblog/archive/2007/02/06/Vista-Style-Menus_2C00_-Part-1-_2D00_-Adding-icons-to-standard-menus.aspx
+
+ It uses WIC (Windows Imaging Codec) to convert the given Icon into a bitmap in ARGB format, this is required
+ by Windows for use as an icon (but in bitmap format), so that Windows draws everything (including theme)
+ so we don't have to.
+
+ Why didn't they just do this themselves? ...
+ }
+
+ {
+ The object returned from this function has to be released using the QI COM interface, don't forget.
+ Note this function won't work on anything where WIC isn't installed (XP can have it installed, but not by default)
+ anything less won't work.
+ }
+ function ARGB_GetWorker: PImageFactory_Interface;
+ var
+ hr: HRESULT;
+ begin
+ hr := CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER, IID_WICImagingFactory, Result);
+ end;
+
+ function ARGB_BitmapFromIcon(Factory: PImageFactory_Interface; hdc: Windows.HDC; hIcon: HICON): HBitmap;
+ var
+ bmi: BITMAPINFO;
+ hr: HRESULT;
+ bitmap: PWICBitmap_Interface;
+ cx, cy: LongInt;
+ pbBuffer: PByte;
+ hBmp: HBITMAP;
+ cbStride, cbBuffer: LongInt;
+ begin
+ { This code gives an icon to WIC and gets a bitmap object in return, it then creates a DIB section
+ which is 32bits and the same H*W as the icon. It then asks the bitmap object to copy itself into the DIB }
+ Result := 0;
+ ZeroMemory(@bmi, sizeof(bmi));
+ bmi.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biPlanes := 1;
+ bmi.bmiHeader.biCompression := BI_RGB;
+
+ bmi.bmiHeader.biBitCount := 32;
+
+ hr := factory^.ptrVTable^.CreateBitmapFromHICON(factory, hIcon, bitmap);
+ if hr = S_OK then
+ begin
+ hr := bitmap^.ptrVTable^.GetSize(bitmap, cx, cy);
+ if hr = S_OK then
+ begin
+
+ bmi.bmiHeader.biWidth := cx;
+ bmi.bmiHeader.biHeight := -cy;
+
+ hBmp := CreateDIBSection(hdc, bmi, DIB_RGB_COLORS, pbBuffer, 0, 0);
+ if hBmp <> 0 then
+ begin
+ cbStride := cx * sizeof(DWORD); // ARGB = DWORD
+ cbBuffer := cy * cbStride;
+ // note: the pbBuffer memory is owned by the DIB and will be freed when the bitmap is released
+ hr := bitmap^.ptrVTable^.CopyPixels(bitmap, nil, cbStride, cbBuffer, pbBuffer);
+ if hr = S_OK then
+ begin
+ Result := hBmp;
+ end else begin
+ // the copy failed, delete the DIB
+ DeleteObject(hBmp);
+ end;
+ end;
+ end;
+ // release the bitmap object now
+ bitmap^.ptrVTable^.Release(bitmap);
+ bitmap := nil;
+ end;
+
+ end;
+
+end
+. \ No newline at end of file
diff --git a/plugins/ShlExt/shlipc.pas b/plugins/ShlExt/shlipc.pas
new file mode 100644
index 0000000000..377a82f294
--- /dev/null
+++ b/plugins/ShlExt/shlipc.pas
@@ -0,0 +1,369 @@
+unit shlIPC;
+
+interface
+
+uses
+
+ m_globaldefs, Windows;
+
+const
+
+ REPLY_FAIL = $88888888;
+ REPLY_OK = $00000000;
+
+ REQUEST_ICONS = 1;
+ REQUEST_GROUPS = (REQUEST_ICONS) shl 1;
+ REQUEST_CONTACTS = (REQUEST_GROUPS) shl 1;
+ REQUEST_XFRFILES = (REQUEST_CONTACTS) shl 1;
+ REQUEST_NEWICONS = (REQUEST_XFRFILES) shl 1;
+ REQUEST_CLEARMRU = (REQUEST_NEWICONS) shl 1;
+
+ ICONS_NOTIMPL = $00000008;
+ GROUPS_NOTIMPL = $00000080;
+ CONTACTS_NOTIMPL = $00000800;
+
+ STATUS_PROFILENAME = 2;
+
+
+ // there maybe more than one reason why any request type wasn't returned
+
+type
+
+ { this can be a group entry, if it is, hContact = <index>
+ the string contains the full group path }
+
+ PSlotIPC = ^TSlotIPC;
+ TSlotIPC = packed record
+ cbSize: Byte;
+ fType: int; // a REQUEST_* type
+ Next: PSlotIPC;
+ hContact: THandle;
+ hProto: Cardinal; // hash of the protocol the user is on
+ hGroup: Cardinal; // hash of the entire path (not defined for REQUEST_GROUPS slots)
+ Status: Word; // only used for contacts -- can be STATUS_PROFILENAME -- but that is because returning the profile name is optional
+ MRU: Byte; // if set, contact has been recently used
+ cbStrSection: int;
+ end;
+
+ // if the slot contains a nickname, after the NULL, there is another NULL or a group path string
+
+ PSlotProtoIcons = ^TSlotProtoIcons;
+ TSlotProtoIcons = packed record
+ pid: Cardinal; // pid of Miranda this protocol was on
+ hProto: Cardinal; // hash of the protocol
+ hIcons: array[0..9] of HICON; // each status in order of ID_STATUS_*
+ hBitmaps: array[0..9] of HBITMAP; // each status "icon" as a bitmap
+ end;
+ TSlotProtoIconsArray=array[0..0] of TSlotProtoIcons;
+ // the process space the thread is running in WILL use a different mapping
+ // address than the client's process space, addresses need to be adjusted
+ // to the client's process space.. this is done by the following means :
+
+ //
+ // new_addr := (old_address - serverbase) + client base
+ //
+ // this isn't the best of solutions, the link list should be a variant array
+ // without random access, which would mean each element's different
+ // size would need to be computed each time it is accessed or read past
+
+ PHeaderIPC = ^THeaderIPC;
+ THeaderIPC = record
+ cbSize: Cardinal;
+ dwVersion: Cardinal;
+ pServerBaseAddress: Pointer;
+ pClientBaseAddress: Pointer;
+ fRequests: Cardinal;
+ dwFlags: Cardinal;
+ Slots: Cardinal;
+ Param: Cardinal;
+ SignalEventName: array[0..63] of Char;
+ // Translate() won't work via Explorer
+ MirandaName: array[0..63] of Char;
+ MRUMenuName: array[0..63] of Char; // for the MRU menu itself
+ ClearEntries: array[0..63] of Char; // for the "clear entries"
+ IconsBegin: PSlotIPC;
+ ContactsBegin: PSlotIPC;
+ GroupsBegin: PSlotIPC;
+ NewIconsBegin: PSlotIPC;
+ // start of an flat memory stack, which is referenced as a linked list
+ DataSize: int;
+ DataPtr: PSlotIPC;
+ DataPtrEnd: PSlotIPC;
+ DataFramePtr: Pointer;
+ end;
+
+ const HIPC_NOICONS = 1;
+
+ procedure ipcPrepareRequests(ipcPacketSize: int; pipch: PHeaderIPC; fRequests: Cardinal);
+ function ipcSendRequest(hSignal, hWaitFor: THandle; pipch: PHeaderIPC; dwTimeoutMsecs: DWORD): Cardinal;
+ function ipcAlloc(pipch: PHeaderIPC; nSize: Integer): PSlotIPC;
+ procedure ipcFixupAddresses(FromServer: LongBool; pipch: PHeaderIPC);
+
+type
+
+ TStrTokRec = record
+ szStr: PChar;
+ szSet: set of Char;
+ // need a delimiter after the token too?, e.g. FOO^BAR^ if FOO^BAR
+ // is the string then only FOO^ is returned, could cause infinite loops
+ // if the condition isn't accounted for thou.
+ bSetTerminator: Boolean;
+ end;
+
+ function StrTok(var strr: TStrTokRec): PChar;
+
+type
+
+ PGroupNode = ^TGroupNode;
+ TGroupNode = record
+ Left, Right, _prev, _next: PGroupNode;
+ Depth: Cardinal;
+ Hash: Cardinal; // hash of the group name alone
+ szGroup: PChar;
+ cchGroup: Integer;
+ hMenu: THandle;
+ hMenuGroupID: Integer;
+ dwItems: Cardinal;
+ end;
+
+ PGroupNodeList = ^TGroupNodeList;
+ TGroupNodeList = record
+ First, Last: PGroupNode;
+ end;
+
+ function AllocGroupNode(list: PGroupNodeList; Root: PGroupNode; Depth: Integer): PGroupNode;
+ function FindGroupNode(P: PGroupNode; const Hash, Depth: Integer): PGroupNode;
+
+type
+
+ // a contact can never be a submenu too.
+ TSlotDrawType = (dtEntry, dtGroup, dtContact, dtCommand);
+ TSlotDrawTypes = set of TSlotDrawType;
+
+ PMenuDrawInfo = ^TMenuDrawInfo;
+
+ TMenuCommandCallback = function(
+ pipch: PHeaderIPC; // IPC header info, already mapped
+ hWorkThreadEvent: THandle; // event object being waited on on miranda thread
+ hAckEvent: THandle; // ack event object that has been created
+ psd: PMenuDrawInfo // command/draw info
+ ): Integer; stdcall;
+
+ TMenuDrawInfo = record
+ szText: PChar;
+ szProfile: PChar;
+ cch: Integer;
+ wID: Integer; // should be the same as the menu item's ID
+ fTypes: TSlotDrawTypes;
+ hContact: THandle;
+ hStatusIcon: THandle; // HICON from Self^.ProtoIcons[index].hIcons[status]; Do not DestroyIcon()
+ hStatusBitmap: THandle; // HBITMAP, don't free.
+ pid: Integer;
+ MenuCommandCallback: TMenuCommandCallback; // dtCommand must be set also.
+ end;
+
+
+
+implementation
+
+ {$include m_helpers.inc}
+
+ function FindGroupNode(P: PGroupNode; const Hash, Depth: Integer): PGroupNode;
+ begin
+ Result := P;
+ while Result <> nil do
+ begin
+ if (Result^.Hash = Hash) and (Result^.Depth = Depth) then Exit;
+ If Result^.Left <> nil then
+ begin
+ P := Result;
+ Result := FindGroupNode(Result^.Left, Hash, Depth);
+ If Result <> nil then Exit;
+ Result := P;
+ end;
+ Result := Result^.Right;
+ end; //while
+ end;
+
+ function AllocGroupNode(list: PGroupNodeList; Root: PGroupNode; Depth: Integer): PGroupNode;
+ begin
+ New(Result);
+ Result^.Left := nil;
+ Result^.Right := nil;
+ Result^.Depth := Depth;
+ if Depth > 0 then
+ begin
+ if root^.left = nil then root^.left := Result
+ else begin
+ root := root^.left;
+ while root^.right <> nil do root := root^.right;
+ root^.right := Result;
+ end;
+ end else
+ begin
+ if list^.first = nil then list^.first := Result;
+ if list^.last <> nil then list^.last^.right := Result;
+ list^.last := Result;
+ end; //if
+ end;
+
+ procedure ipcPrepareRequests(ipcPacketSize: int; pipch: PHeaderIPC; fRequests: Cardinal);
+ begin
+ // some fields may already have values like the event object name to open
+ pipch^.cbSize := sizeof(THeaderIPC);
+ pipch^.dwVersion := PLUGIN_MAKE_VERSION(2,0,1,2);
+ pipch^.dwFlags := 0;
+ pipch^.pServerBaseAddress := nil;
+ pipch^.pClientBaseAddress := pipch;
+ pipch^.fRequests := fRequests;
+ pipch^.Slots := 0;
+ pipch^.IconsBegin := nil;
+ pipch^.ContactsBegin := nil;
+ pipch^.GroupsBegin := nil;
+ pipch^.NewIconsBegin := nil;
+ pipch^.DataSize := ipcPacketSize - pipch^.cbSize;
+ // the server side will adjust these pointers as soon as it opens
+ // the mapped file to it's base address, these are set 'ere because ipcAlloc()
+ // maybe used on the client side and are translated by the server side.
+ // ipcAlloc() is used on the client side when transferring filenames
+ // to the ST thread.
+ Integer(pipch^.DataPtr) := Integer(pipch) + sizeof(THeaderIPC);
+ Integer(pipch^.DataPtrEnd) := Integer(pipch^.DataPtr) + pipch^.DataSize;
+ pipch^.DataFramePtr := pipch^.DataPtr;
+ // fill the data area
+ FillChar(pipch^.DataPtr^, pipch^.DataSize, 0);
+ end;
+
+ function ipcSendRequest(hSignal, hWaitFor: THandle; pipch: PHeaderIPC; dwTimeoutMsecs: DWORD): Cardinal;
+ begin
+ { signal ST to work }
+ SetEvent(hSignal);
+ { wait for reply, it should open a handle to hWaitFor... }
+ while True do
+ begin
+ Result := WaitForSingleObjectEx(hWaitFor, dwTimeoutMsecs, True);
+ if Result = WAIT_OBJECT_0 then
+ begin
+ Result := pipch^.fRequests; break;
+ end else if Result = WAIT_IO_COMPLETION then
+ begin
+ (* APC call... *)
+ end else begin
+ Result := REPLY_FAIL; break;
+ end; //if
+ end; //while
+ end;
+
+ function ipcAlloc(pipch: PHeaderIPC; nSize: Integer): PSlotIPC;
+ var
+ PSP: int;
+ begin
+ Result := nil;
+ { nSize maybe zero, in that case there is no string section --- }
+ PSP := int( pipch^.DataFramePtr ) + sizeof(TSlotIPC) + nSize;
+ { is it past the end? }
+ If PSP >= int(pipch^.DataPtrEnd) then Exit;
+ { return the pointer }
+ Result := pipch^.DataFramePtr;
+ { set up the item }
+ Result^.cbSize := sizeof(TSlotIPC);
+ Result^.cbStrSection := nSize;
+ { update the frame ptr }
+ pipch^.DataFramePtr := Pointer(PSP);
+ { let this item jump to the next yet-to-be-allocated-item which should be null anyway }
+ Result^.Next := Pointer(PSP);
+ end;
+
+ procedure ipcFixupAddresses(FromServer: LongBool; pipch: PHeaderIPC);
+ var
+ pct: PSlotIPC;
+ q: ^PSlotIPC;
+ iServerBase: Integer;
+ iClientBase: Integer;
+ begin
+ if pipch^.pServerBaseAddress = pipch^.pClientBaseAddress then Exit;
+ iServerBase := Integer(pipch^.pServerBaseAddress);
+ iClientBase := Integer(pipch^.pClientBaseAddress);
+ // fix up all the pointers in the header
+ if pipch^.iconsBegin <> nil then
+ begin
+ Integer(pipch^.IconsBegin) := (Integer(pipch^.IconsBegin) - iServerBase) + iClientBase;
+ end; //if
+ if pipch^.contactsBegin <> nil then
+ begin
+ Integer(pipch^.ContactsBegin) := (Integer(pipch^.ContactsBegin) - iServerBase) + iClientBase;
+ end; //if
+ if pipch^.groupsBegin <> nil then
+ begin
+ Integer(pipch^.GroupsBegin) := (Integer(pipch^.GroupsBegin) - iServerBase) + iClientBase;
+ end; //if
+ if pipch^.NewIconsBegin <> nil then
+ begin
+ Integer(pipch^.NewIconsBegin) := (Integer(pipch^.NewIconsBegin) - iServerBase) + iClientBase;
+ end;
+ Integer(pipch^.DataPtr) := (Integer(pipch^.DataPtr) - iServerBase) + iClientBase;
+ Integer(pipch^.DataPtrEnd) := (Integer(pipch^.DataPtrEnd) - iServerBase) + iClientBase;
+ Integer(pipch^.DataFramePtr) := (Integer(pipch^.DataFramePtr) - iServerBase) + iClientBase;
+ // and the link list
+ pct := pipch^.DataPtr;
+ while (pct <> nil) do
+ begin
+ // the first pointer is already fixed up, have to get a pointer
+ // to the next pointer and modify where it jumps to
+ q := @pct^.Next;
+ if q^ <> nil then
+ begin
+ Integer(q^) := (Integer(q^) - iServerBase) + iClientBase;
+ end; //if
+ pct := q^;
+ end; //while
+ end;
+
+ function StrTok(var strr: TStrTokRec): PChar;
+ begin
+ Result := nil;
+ { don't allow #0's in sets or null strings }
+ If (strr.szStr = nil) or (#0 in strr.szSet) then Exit;
+ { strip any leading delimiters }
+ while strr.szStr^ in strr.szSet do Inc(strr.szStr);
+ { end on null? full of delimiters }
+ If strr.szStr^ = #0 then
+ begin
+ // wipe out the pointer
+ strr.szStr := nil;
+ Exit;
+ end;
+ { store the start of the token }
+ Result := strr.szStr;
+ { process til start of another delim }
+ while not (strr.szStr^ in strr.szSet) do
+ begin
+ { don't process past the real null, is a delimter required to cap the token? }
+ If strr.szStr^ = #0 then Break;
+ Inc(strr.szStr);
+ end;
+ { if we end on a null stop reprocessin' }
+ If strr.szStr^ = #0 then
+ begin
+ // no more tokens can be read
+ strr.szStr := nil;
+ // is a ending delimiter required?
+ If strr.bSetTerminator then
+ begin
+ // rollback
+ strr.szStr := Result;
+ Result := nil;
+ end;
+ //
+ end else
+ begin
+ { mark the end of the token, may AV if a constant pchar is passed }
+ strr.szStr^ := #0;
+ { skip past this fake null for next time }
+ Inc(strr.szStr);
+ end;
+ end;
+
+end.
+
+