CAPAB Addon — File Browser
CAPABAddonv2.0 / CAPABAddonv2.0 / capab / io-dll-readme.txt
io-dll-readme.txt — 48.61 KB — Download this file
README for io.dll v0.8.1, I/O filter and command injection DLL for mIRC
Copyright (c) 2004-2007, David "Saturn" van Moolenbroek <saturn@quakenet.org>
This version of the DLL supports mIRC versions 6.14 - 6.3
1. Introduction
2. Description
2.1. Rules and groups
2.2. Rule matching
2.3. The callback alias
2.4. The IO alias
2.5. Multi-server support
2.6. Preserving spaces
3. Commands
3.1. ADD
3.2. DEL
3.3. GET
3.3.1. GET DATA
3.3.2. GET ID
3.3.3. GET PARAMS
3.3.4. GET RULE
3.4. SET
3.4.1. SET DATA
3.5. RECV
3.6. SEND
3.7. NOOP
4. Examples
4.1. Faking a ping timeout
4.2. Replacing swear words
4.3. Alternative debug window
4.4. Listing the size of a channel
4.5. Filling the IAL with WHOX
5. DLL support
5.1. DLLAPI
5.2. Exported functions
5.2.1. CONN
5.2.2. ADD
5.2.3. DEL
5.2.4. RECV
5.2.5. SEND
5.3. The ioapi.h file
6. License and disclaimer
7. Source code
8. Version history
1. Introduction
Once upon a time, on a private channel filled with true masterminds, the
following enlightening conversation took place about mIRC, the program they
all loved so much:
<@k0sm0s> hmm, kinda miss a on SEND_LINE_TO_SERVER event
<@Saturn> yea.. and i want a RECV_LINE_FROM_SERVER event too
...
<@k0sm0s> hmm, maybe we should make a dll to hack core :]
A few days later, one of the wizards had completed the journey into the deep
internals of mIRC, and they nobly set out to spread the merits of their hard
labour throughout the world; enabling anyone to enjoy the same power that
they now possessed, and expecting nothing but mere gratitude in return:
<@Voronoi> let's ask money for it
<@Voronoi> I get 50% for the idea
Yes, reality can be a bitch sometimes!
Thanks go out to k0sm0s and Voronoi for helping this DLL become reality.
2. Description
This DLL allows scripts to examine, and optionally block, any message that
mIRC sends to, or receives from, IRC servers. Additionally, the DLL allows
scripts to tell mIRC to pretend that it received a specific command from the
IRC server.
This is done by hooking into some of the internal functions of mIRC. The
main advantage of hooking directly into mIRC, as opposed to for example
hooking into winsock functions, is that this DLL also works for connections
over SSL. The downside of this approach is that the DLL will only work for
the mIRC versions it is written for. The range of supported mIRC versions
by this copy of the DLL, can be found at the top of this README.
The level of this DLL is ADVANCED. Please send bugs, comments and feature
suggestions to the e-mail address at the top of this file, but if you e-mail
the author regarding how to use the DLL, you might not get an answer. You
can always try, though.
Note that using this DLL is perfectly legal. Writing the DLL has been made
possible by reverse engineering parts of mIRC; even though the mIRC license
states that this is forbidden, the European law (applicable to the author
of this DLL) explicitly allows reverse engineering of computer programs in
order to achieve interoperability, in summary.
2.1. Rules and groups
The initial version of this DLL passed all incoming and outgoing messages to
(respectively) $in and $out script aliases. However, this turned out to be
rather impractical and slow. So, in order to allow scripts to easily catch
and block incoming/outgoing messages, this new version uses a system of
rules and groups.
A rule consists of a matchtext, a definition of what alias to call when the
rule is triggered, and some flags. Everytime mIRC gets an incoming or
outgoing message, the DLL tries to find matching rules, and if found, it
executes them by calling the associated script alias. This script alias then
determines whether or not to block the message. If the script alias blocks
the message, no other matching rules will be fired. For details on how to
add rules, see section 3.1 on the ADD command.
Each rule is always part of exactly one group - the ADD command can either
add the new rule to an existing group, or create a new group to put the new
rule in. In other words, a group is a collection of one or more rules;
groups are identified using ID numbers. Rules are always added to the end of
the list of rules for a group, and groups are always added to the end of the
list of groups. This means that by blocking a message, an earlier created
rule automatically prevents a later created rule from being fired, so groups
and their rules are dealt with on a "first come, first serve" basis.
This whole concept of groups may seem a bit weird and unnecessary at first,
but is actually a very convenient way of dealing with rules in practice. For
two practical examples on how to take advantage of groups, see sections 4.4
and 4.5.
Neither rules nor groups are saved anywhere else than in memory. This means
that all rules and groups will be gone after the I/O DLL is unloaded, and
there will always be zero rules and zero groups when the I/O DLL is loaded.
There are no default rules or groups.
2.2. Rule matching
As indicated above, each rule has a matchtext which is a wildcard string.
Additionally, a rule is set to either trigger on incoming messages from the
IRC server to mIRC (called INPUT), outgoing messages from mIRC to the IRC
server (called OUTPUT), or both.
The matchtext can be set to either match the whole input/output message
line, or only the command part of the line. The IRC protocol (RFC 1459)
specifies these two types of messages:
COMMAND [destination] parameters
:source COMMAND destination parameters
If a rule's matchtext is set to match against the command only, the DLL will
compare it with the COMMAND part of the messages above. The distinction
between the two is made by looking whether the first character of the line
is a ':'. The command matchtext can also contain wildcards, but is still
somewhat faster than a full line matchtext. Of course, the command can also
be a numeric.
2.3. The callback alias
When a certain rule matches an input or output message line, its associated
callback alias is called. This alias is specified when adding the rule, see
section 3.1 for details. The alias is called like this:
$aliasname(messageline,groupID,[parameters])
This means that:
$1 contains the message line that is about to be sent or received. It
really contains all of it, including commas and spaces. Don't confuse
$1 with $1- here! You can, however, use "tokenize 32 $1" to extend the
line to $1-, but then you lose the original $2 and $3 (see below), as
well as double consecutive spaces that were originally in $1.
$2 contains the ID number of the group that the triggering rule belongs
to. This allows for easy access to the group's user-specified
group data. These data can also be obtained using the "GET DATA"
command though, see section 3.3.1 for details.
$3 contains optional parameters that were specified along with the alias
when the rule was added. The same parameters can be obtained using the
"GET PARAMS" command, see section 3.3.3 for details.
If this callback alias returns the value "1", then the message line will be
blocked; that is, not sent the the IRC server or not received by mIRC. Also,
as indicated in section 2.1, no other rules will be fired in that case. If
the alias returns "2", then the line will also be blocked, but additionally,
the group to which the triggered rule belongs will be deleted. If the
callback alias returns "0" or nothing, the line will be passed on just as
usual.
Important note: the alias must be global, so do not use "alias -l". This is
because the DLL can not determine what script the rule was created from, nor
is it currently able to define what script the callback alias should be in.
This of course also means you'll have to make sure that your aliasname is
unique, so there is no other alias with the same name in your other scripts.
2.4. The IO alias
In the rest of this document, I will heavily rely upon the presence of the
following script alias:
alias io return $dll(io.dll,$$1,$2-)
This alias must be put in one of your scripts' Remote files, and the io.dll
file must be present in your mIRC directory. You are of course free not to
use this alias, or use a modified version, in your own scripts - but do
remember it's being used like that in all of the examples below.
2.5. Multi-server support
Multiple server connections are supported by this DLL: rules are by default
specific to one server connection only, so they will only be triggered for
messages sent from and received for that server connection. If the DLL
detects that mIRC is disconnecting or being disconnected from a server, it
will automatically remove all rules belonging to that connection.
The exception is rules that are explicitly created to apply to all server
connections, such rules will only be removed using an explicit 'del'
call (see section 3.2).
2.6. Preserving spaces
A short note about preserving multiple consecutive spaces: it is entirely
possible to do this - as mentioned, the $1 parameter of the callback alias
contains the unmodified incoming/outgoing line including all spaces, and if
you call the 'recv' and 'send' commands (see sections 3.5 and 3.6) with $io
instead of /io, then any multiple consecutive and leading/trailing spaces in
the given line will be preserved. Needless to say, you have to know what
you're doing or you might lose the spaces anyway. The examples in section 4
do not preserve spaces in order to keep the code a bit more readable.
3. Commands
Now that you (hopefully) have a basic idea of how the DLL works, let's move
on to the list of commands it supports.
None of the commands return error messages if executing them fails; that
should, however, not really be a problem because errors should never occur
if you use the commands correctly. Note that error handling will probably be
improved in the next version if this DLL. If no "Return value" is specified
explicitly in the reference below, a command will simply return nothing.
However, all of the commands will behave as "/halt" if the DLL was loaded on
an unsupported mIRC version, in which case it wasn't able to initialize.
3.1. ADD
The ADD command adds a rule to a new or existing group.
Syntax:
/io add [-flags] [groupID] <matchtext> [alias [params]]
Parameters:
flags
An optional set of flags that define properties of the rule. This can be
a combination of the following flags:
i This is an INPUT rule, it is checked for incoming messages.
o This is an OUTPUT rule, it is checked for outgoing messages.
m Make a new group to add this rule to. If specified, the [groupID]
field must be omitted (see below).
a Apply this rule to all server connections, instead of just the
current one. This rule will not be removed when you disconnect; see
section 2.5 for details.
n Trigger this rule only once, by deleting the rule's GROUP after it
has been triggered. This flag is typically used on the rule that
matches the last of a set of numerics (End of /LIST, End of /WHO,
End of /WHOIS, etc).
Both -i and -o can be present, in which case the rule is checked for
both incoming and outgoing messages. If neither -i nor -o are specified,
the rule is set to INPUT.
groupID
The ID of the group to add this rule to; it must only be present if the
-m flag was NOT specified.
matchtext
A string that may contain wildcards, and which is used to match against
incoming and/or outgoing messages. If this field is surrounded by quotes
(in which case it may contain spaces), it is assumed to be a matchtext
for the whole line. If no quotes are present (and the matchtext is
therefore a single word), it is assumed to be a matchtext for the
message's command only; see also section 2.2.
This DLL's wildcard string matching is identical to mIRC's; the '*', '?'
and '&' wildcards are supported, and as in mIRC, the '&' matches (the
rest of) exactly one word. Character comparison is case-insensitive.
alias
The name of the alias to call. See section 2.3 on how this alias is
called exactly. If no alias name is specified, the rule defaults to
BLOCK.
params
Optional parameters to pass to the alias, see section 2.3 for details.
Return value:
The ID number of the group that this rule was added to, or $null in case
of a failure. This value can be retrieved by either calling the /io alias
as an identifier ($io(add,..)), or, by calling it as a command (as above)
and using mIRC's built-in $result identifier to obtain the group ID.
3.2. DEL
The DEL command deletes the first matching rule from a group, deleting the
group completely if there are no rules left in it; the command can also be
used to delete a whole group.
Syntax:
/io del [-flags] <groupID> [matchtext]
Parameters:
flags
An optional set of flags, which can be a combination of the following:
i Look for an INPUT rule to delete.
o Look for an OUTPUT rule to delete.
a Delete rules added with the -a flag.
groupID
The ID number of the group to delete the rule from.
matchtext
The matchtext as specified in the ADD command. The first rule in the
group with this matchtext will be deleted. If this field is omitted,
the whole group specified by 'groupID' will be deleted; in that case,
the 'flags' field is ignored.
3.3. GET
The GET command allows you to retrieve certain information from the DLL. It
has a number of subcommands, which are listed below.
3.3.1. GET DATA
The GET DATA subcommand allows you to retrieve the user-defined data
previously assigned to a certain group using the "SET DATA" command
(see also section 3.3.1).
Syntax:
$io(get,data [groupID])
Parameters:
groupID
The ID number of the group to retrieve the group data for. If this
command is called from the callback alias of a triggered rule, the
groupID parameter may be omitted, in which case the group data for that
rule's group will be returned.
Return value:
The group's group data, or $null if none was set.
3.3.2. GET ID
The GET ID subcommand returns the group ID for the currently executing
callback alias. It is equal to the initial $2 parameter of that alias.
Syntax:
$io(get,id)
Return value:
The group ID, or $null if no callback alias is currently being evaluated.
3.3.3. GET PARAMS
The GET PARAMS subcommand returns the parameters associated with the
currently executing callback alias. It is equal to the initial $3 parameter
of that alias, see section 2.3 for details.
Syntax:
$io(get,params)
Return value:
The parameters
3.3.4. GET RULE
The GET RULE subcommand lets you enumerate the rules currently present in
the DLL. Note that you can only enumerate groups implicitly.
Syntax:
$io(get,rule <number>)
Parameters:
number
If this value is zero, the command will return the number of rules; if a
positive integer between 1 and the number of rules (inclusive) is
specified, data for that rule will be returned in the format below.
Return value:
If the specified number is zero, the number of rules are returned.
Otherwise, the rule with that number will be returned, in the following
format:
<groupID> <flags> <cid> <matchtext> [alias [params]]
The fields should be pretty self-explanatory; the flags field can contain
any of the following flags: a, d, i, n, o. The 'd' flag means that this
rule has been set by a DLL (see section 5). For the other flags, see
section 3.1.
3.4. SET
The SET command allows you to set certain data. At this time, only the SET
DATA subcommand is supported.
3.4.1. SET DATA
The SET DATA subcommand lets you associate custom data to a certain group;
these data can be retrieved using the GET DATA command later (see also
section 3.3.1).
Syntax:
/io set data <groupID> [data]
Parameters:
groupID
The ID number of the group to set the groupdata for. Unlike with the GET
DATA command, the group ID is mandatory here.
data
The custom data to associate with the specified group.
3.5. RECV
The RECV command lets the I/O DLL tell mIRC that it has received a message
line from the IRC server. It will return when mIRC has dealt with the
command.
Syntax:
/io recv <line>
Parameters:
line
The message line that mIRC receives.
Notes:
Message lines sent this way will NOT pass through the rules system of the
I/O DLL, but rather be received by mIRC directly. However, As with any
incoming messages, associated mIRC events are triggered for this message,
and the message will also be logged to the debug window, if present.
This command can not be called from a critical event within mIRC.
3.6. SEND
The SEND command lets the I/O DLL tell mIRC that it should send a message to
the IRC server.
Syntax:
/io send <line>
Parameters:
line
The message line to send to the IRC server.
Notes:
This command is different from mIRC's /raw command only at the point that
it will NOT apply any rules to message lines sent this way.
As a sidenote, it is indeed possible to recursively trigger rules from
a rule's callback alias by using /raw, although the I/O DLL has a safety
recursion limit check hardcoded at a maximum depth of 100. This should
really be sufficient to anybody.
3.7. NOOP
The NOOP command does.. nothing.
Syntax:
/io noop
Notes:
Usually, the ADD command will be the first I/O command that a script
executes. Any /io command that uses the I/O DLL, will also load the DLL
first; mIRC does that automatically for the script. However, if you want
to add rules from your own DLL instead (see section 5), the I/O DLL has to
be loaded by mIRC first. This is where the NOOP command comes in: it can
load the I/O DLL, without actually doing anything else. This saves you
from having to use ugly hacks to get the I/O DLL loaded, prior to loading
your own DLL.
If you're not using this I/O DLL in combination with your own custom DLL,
then this command will indeed be of no use to you.
4. Examples
In order to illustrate how the commands listed above can be used in
practice, here are a few examples that you can paste in Remote (without the
line numbers of course).
I provided alot of documentation along with the examples, to show how I came
to writing the examples like this, how things could be done differently, and
how the ideas behind them can be used for other, similar script snippets.
Important note: all of the examples assume that the /io alias from section
2.4 is present. No other conditions are required to get these script
snippets to work.
4.1. Faking a ping timeout
If you're directly connected to an IRC server, and not using a bouncer or
something similar, you can fake a "Ping timeout" by not replying with a PONG
to the PING command that the IRC server regulary sends to you to see if your
mIRC client is still there.
1. alias pingtimeout { io add -om PONG }
If you call /pingtimeout, a new rule will be added. The -o switch means it
is an OUTPUT rule, affecting only messages sent by mIRC to the IRC server.
The -m switch means a new group for this rule is to be created; this makes
sense, because we didn't have any group yet to add the rule to. Finally,
PONG indicates that we want this rule to match only PONG commands, that is,
only outgoing PONG commands.
Because we did not specify a callback alias, the behaviour of this rule is
to BLOCK (as mentioned in section 3.1): whenever the I/O DLL sees that mIRC
sends a PONG command to the IRC server, it will block this command. That
way, the IRC server will never receive a PONG to the PING it will send, and
eventually disconnect you with a "Ping timeout" error.
You could also block incoming PINGs instead (io add -im PING), to achieve
the same, but I prefer blocking the PONG because then you can still see the
last "Ping? Pong?" in your status window. If you block the PING instead,
mIRC will never know that the IRC server sent a PING command.
Another option would be to add the -n flag here, so that the rule only works
once; there is no real advantage or disadvantage in doing so though, as you
will be disconnected after blocking one PONG anyway.
4.2. Replacing swear words
Another example of a rule affecting outgoing messages, this time to modify
messages on-the-fly: this example changes all occurrences of the swear word
"fuck" in messages you send, into the more friendly "*peep*". Although this
can also be achieved using "on INPUT" events, this way of doing so
guarantees that ALL occurrences of the swear word will be replaced in
outgoing messages, which also includes the ones sent with the /msg command.
1. on *:START:{ io add -amo "PRIVMSG & :*fuck*" antiswear }
2. alias antiswear {
3. tokenize 32 $1
4. io send $1-2 $replace($3-,fuck,*peep*)
5. return 1
6. }
On line 1, a new rule is added right after mIRC is launched. This rule can
be added at many places, but doing it at the start makes sense because we
don't need to differentiate between various server connections in this
example. This is closely related to the presence of the -a switch here: this
flag makes the rule affect all server connections, permanently. Once again,
the -m switch creates a new group to put this rule in, and the -o switch
defines that this is an OUTPUT rule.
The next field in the ADD command is the matchtext. Because I surrounded the
matchtext by quotes ("PRIVMSG & :*fuck*"), the DLL knows it should match
this against the whole line. Constructing such a matchtext involves a bit of
knowledge of how the IRC protocol works; in most cases however, watching the
debug window (created using /debug @mydebugwindow, for example) is
sufficient to find out what's happening. In this case, all we need to know
is that mIRC sends a message using the format "PRIVMSG <destination>
:<text>", and the <text> bit here could include the swear word we'd like to
replace.
The last part of the ADD command is "antiswear", which is the name of the
alias that will be called when the rule triggers (in other words, when we
write "fuck" somewhere). We don't need any extra parameters in this case.
On lines 2-6, we define what should happen when our rule triggers. First of
all, we want to look at various parts of the message line (which is
contained in $1, as mentioned in section 2.3), so we expand $1 to $1- by
splitting up the line into space-separated words. Because of the matchtext
we used, we know that $1 is now "PRIVMSG", $2 is the destination to which
the message is being sent, and $3- is the text (including the leading ':'
character).
Using "/io send", we send a different message to the IRC server, this time
replacing all occurrences of the word "fuck" with "*peep*" in the message
text; we don't touch $1-2 as $2 just *may* contain "fuck" as part of the
destination nickname/channel.
Finally, we return 1 from the alias, which tells the DLL to block the
original message that contains the swear word(s).
A few notes. First of all, what if what if we wanted to replace more than
one swear word? There are basically two options: either we add one rule for
each swear word (possibly but not necessarily all to the same group), all
calling the /antiswear callback alias, and we extend the $replace to replace
more swear words than just "fuck", OR, we create one rule to catch ALL
outgoing PRIVMSGs (using "*" instead of "*fuck*") and always use $replace on
all outgoing text. I prefer the former, as the latter tends to slow down
mIRC (a script will be executed everytime you write a line). These are
pretty much the only options you have, as the I/O DLL does not support
multiple matchtexts for one rule in any way.
The same principle applies if you'd also like to replace "fuck" in notices;
in that case, the choice is easily made: it is very easy to simply add a
second rule that is set to match "NOTICE & :*fuck*". The /antiswear alias
need not be changed in that case.
A rather similar script snippet can be used to replace swear words in all
INCOMING text. It would involve the -i switch instead of -o, a matchtext
like ":& PRIVMSG & :*fuck*", and "/io recv" instead of "/io send"; I'll
leave the exact implementation up to the reader.
4.3. Alternative debug window
Here is an example that shows how you can imitate mIRC's debug window with a
few simple commands. It is not useful in practice, but it shows how to use
some of this DLL's features effectively.
1. on ^*:LOGON:*:{
2. io add -im * debuglog <-
3. io add -o $result * debuglog ->
4. }
5. alias debuglog { echo @debug $3 $1 }
This example assumes that there already is a custom window called "@debug".
The "on LOGON" event is used, with the ^ prefix, to ensure all of the text,
including the NICK/USER pair, gets logged to our @debug window. On line 2,
we add a new INPUT (-i) rule and put it in a new group (-m); the matchtext
is set to * so ALL incoming messages will be passed to the "debuglog" alias
that follows the matchtext in the ADD command; finally, we also specify a
parameter "<-" along with the debuglog alias. Compare the command with the
syntax of the ADD command mentioned in section 3.1 if you're not sure which
parameter is what.
Remember from the specification of the ADD command, that this command
returns the number of the group that the new rule was added to. As we called
the add command using /io (as opposed to $io()), that group number is now
automatically stored in mIRC's built-in $result identifier. We now add a
second rule, for all OUTPUT (-o), and add it to the group
Another way of doing the same would be:
var %id = $io(add,-im * debuglog <-)
io add -o %id * debuglog ->
But I prefer the somewhat neater coding style above. This is of course up to
you when you're writing your own scripts. Note that you can also create a
new group for the output rule (using "-om" instead of "-o $result"), without
any functional difference. Such choices are up to you as well.
Now that we created two rules, one for all input and one for all output, the
"debuglog" alias on line 5 will be called for each incoming and outgoing
message line. Remember from section 2.3 that $1 holds the incoming or
outgoing line, and $3 holds the parameters passed to the ADD command.
Therefore, $3 holds "<-" for incoming lines and "->" for outgoing lines. So
using the /echo command on line 5, the message line will be printed in the
@debug window, prefixed by either "<-" in case of an incoming message, or
"->" in case of an outgoing message, just like mIRC's built-in debug window.
4.4. Listing the size of a channel
On to a more advanced example, which shows how to overcome a problem with
standard mIRC: the inability to prevent the Channels List window from
appearing. An IRC server will reply to a /list command with a 321 numeric
(list header), zero or more 322 numerics (one for each channel), and a 323
numeric (end of list). mIRC always opens the Channels List window upon
receipt of a 321 numeric; this example shows how to get around that problem
and list the number of users of one channel without letting mIRC open the
Channels List window.
1. alias chansize {
2. io add -im 321
3. io add -i $result 322 csset
4. io add -in $result 323 csres $1
5. io send list $1
6. }
7. alias csset {
8. io set data $2 $gettok($1,5,32)
9. return 1
10. }
11. alias csres {
12. if ($io(get,data) isnum) $&
13. echo $color(info) -at * $3 has $ifmatch users
14. else echo $color(info) -at * unable to list $3
15. return 1
16. }
You can display the size of a channel using "/chansize #channel". The
chansize alias adds three rules: one for each numeric mentioned above. They
are all added to the same group (see also the previous example), and the
last numeric (323) is created with the -n flag, which means its group will
be deleted when it is triggered. There always will be a 323 numeric, and it
will always be the last one, so this construction has the advantage that the
set of rules will be deleted after the list reply is received.
This idea can be applied to every set of numerics, when there's always at
least one numeric that terminates the list: all of the rules for the
numerics should be added to the same group, and then the last numeric
"cleans up" by removing the whole group by means of the -n switch. Another
advantage in this case is that the resulting commands can be "queued": you
can execute two /chansize requests right after each other, and the created
rules won't interfere with eachother because the second group will always be
searched through for matching rules, AFTER the first group. Because all of
the rules in the group block, the second group will never be looked at while
the first request is still in progress. If you save all data relevant to the
request as groupdata, your script won't even have to keep state in any way.
Anyway, that's the general idea, and it's also exactly what this example
does. The 321 numeric is simply blocked, the 322 numeric is passed to the
/csset alias, and the 323 numeric is passed to the /csres alias. Both of
these aliases also block the reply, by returning 1.
The /csset alias is either called once, or not at all, because we requested
a list on a specific channel on line 5. If this alias is called, it stores
the number of users on the channel (the 5th parameter of the 322 reply which
is stored in $1) as groupdata (for the group ID stored in $2). Note that
the /csset alias does not actually check whether the returned channel is the
one we requested using the list request on line 5; even if we performed such
a check, it would have no use, as we've already blocked the 321 numeric
anyway. If this was not a reply from OUR list command (and instead, the user
requesting a list of all channels, for example), we're screwed. But that is
not really likely to happen anyway.
The /csres alias displays the result: if the /csset alias was called, then
the groupdata contains the number of users on the channel; otherwise it is
still $null as it was never touched. That is what the if-check on line 12
checks for. Once again, $3 contains the requested channelname because that
was specified as parameter on line 4. After the /csres alias is finished,
the group and all of its rules will be deleted, including the groupdata,
so there is nothing left in memory relating to the /chansize request. Nice,
don't you think? We haven't had to use a single variable.
4.5. Filling the IAL with WHOX
The following script snippet was one of the main motivations for me to write
this DLL. It is designed to work with IRC servers that support the WHOX
protocol, which allows you to create custom /who queries. This example shows
how to use the I/O DLL to update mIRC's IAL using WHOX queries, something
that is otherwise impossible because there is no way to update the IAL by
means of a script. In short, the snippet intercepts WHOX replies and turns
them into (faked) normal /who replies which are then passed to mIRC. The
advantage? You can retrieve many more entries on large channels, as you're
requesting less fields (see the WHOX documentation on this), and you can
even change this snippet to additionally request, for example, the users'
QuakeNet authname, and use those data in other parts of your script.
1. raw 366:*:if ($chan($2).ial == $false) ialwho $2
2. alias ialwho {
3. who $$1 $+ ,999 c%nuht,999
4. io add -im ":& 354 & 999 *" ialset $1
5. io add -in $result ":& 315 & $1 $+ ,999 :*" ialset $1
6. }
7. alias ialset {
8. tokenize 32 $1
9. set -u0 %ialwho 1
10. io get params
11. if ($2 == 354) io recv $1 352 $3 $result $5-6 *.* $7 x :0 *
12. else io recv $1 315 $3 $result :End of /WHO
13. return 1
14. }
16. raw 352:*:if (%ialwho) halt
17. raw 315:*:if (%ialwho) halt
The script defines an /ialwho alias, that can be used as "/ialwho #channel".
Line 1 merely shows an example of how to apply this in practice: it performs
an /ialwho request from raw 366 (End of /NAMES) if the IAL for that channel
is not already filled.
Line 3 executes a rather complicated WHOX request, and lines 4 and 5 set the
rules to catch the replies. The ",999" bit is only there to catch raw 315
(End of /WHO) without mistaking another 315 numeric for it: the same ",999"
bit can be seen on line 5. The "nuht,999" bit requests the resulting users'
nick, userid, hostname and the query ID 999. This query ID is also used to
ensure we're only catching the right replies, but this time it's for WHOX's
354 numerics; the same "999" can be seen on as fourth parameter on line 4.
Just to make sure: those 999's for raw 354 and raw 315 are different
numbers, and you can use two completely different values.
The same idea from the previous example is applies: a WHOX reply consists of
zero or more 354 numerics, and one final 315 numeric; hence, the rule that
catches the 315 numeric is once again added with the -n flag (see line 5).
Note that both rules use the same callback alias called ialset, and the same
parameter which is the requested channelname.
The /ialset alias now converts the received replies to a format that mIRC
can handle; lines 11 and 12 use "io recv" on the converted version of the
354 and the 315 numeric, respectively. The parameters retrieved into $result
on line 10 consist of the channelname, which must be used in the replies
mIRC receives, but aren't actually present in the WHOX replies. Using "io
get params" is necessary as we threw away $3 by expanding $1 to $1- on line
8. After the new, faked /who replies are received by mIRC, the alias blocks
the original WHOX replies by returning 1 on line 13.
Lines 16 and 17, finally, ensure that your status window won't be cluttered
by the fake /who replies, by halting any 352 and 315 numeric while the
%ialwho variable is set. This variable has been set on line 9, and remains
set only as long as the script runs. An important observation here is that
the raws on lines 16 and 17 actually will be triggered as a direct result of
the "io recv" commands on lines 11 and 12, respectively; so here you can see
how scripts can be stacked. Indeed, you can also use /unset to unset %ialwho
right after line 12, because the raws have already been finished by then.
It's a somewhat unusual way of using scripts, but, to quote a CS teacher of
mine, it works like hell.
5. DLL support
If mIRC's scripting language is somehow not sufficient for you to deal with
certain incoming or outgoing messages, don't worry - this DLL also comes
with built-in support for third party DLLs that want to add and delete their
own rules and process any matching lines. The assumed programming language
is C/C++.
In order to create your own DLL, you'll need the ioapi.h file. The complete
header file is included in this README in section 5.3, below. Newer versions
of the I/O DLL will most likely not be backwards compatible when it comes to
the DLL API, so the ioapi.h file will probably change for the next version,
too.
Important: the I/O DLL MUST be loaded from mIRC before it can be used by
other DLLs. You'll probably want to load your DLL from mIRC anyway, so make
sure that you have called the NOOP command before letting your DLL add
rules, that's what the NOOP command is there for (see also section 3.7).
The reason for this is that the I/O DLL has to be able to initialize based
on the parameters mIRC passes to it, before it can start doing its work.
5.1. DLLAPI
Instead of exporting two versions of each function (one for mIRC, one for
other DLLs), the I/O DLL exports one function for other DLLs. This function
is called "dllapi" and uses the stdcall calling convention. It takes no
parameters and returns a pointer to a static io_dllapi structure (see also
section 5.3) which provides pointers to the other functions that your DLL
may need.
The first field in the structure is the version number; the current version
of the DLL API is 0.8, defined in DLLAPI_VERSION. If the first field of the
returned io_dllapi structure is not equal to this value, the two DLLs are
probably incompatible.
The second field in the structure is a flag that indicates whether the I/O
DLL is actually loaded. If this flag is set to FALSE, none of the API
functions in the rest of the structure should be called.
Here is an example snippet of C code that loads io.dll and obtains a pointer
to the io_dllapi structure:
HINSTANCE hLib;
io_dllapi *(__stdcall *pDllApiCall)(void);
io_dllapi *pDllApi;
/* Obtain the base pointer of the library, which was already loaded by
mIRC before (see the "Important" note above). */
hLib = LoadLibrary("io.dll");
/* Get the address of the "dllapi" function call. */
pDllApiCall = (io_dllapi * __stdcall (*)(void))
GetProcAddress(hLib, "dllapi");
/* Now call this function, and thereby retrieve the pointer to the
io_dllapi structure. Store the pointer in the pDllApi variable. */
pDllApi = pDllApiCall();
/* If the I/O DLL wasn't loaded properly, or the DLL's version
doesn't match the header's, produce an error and stop loading. */
if (pDllApi->loaded == FALSE || pDllApi->version != DLLAPI_VERSION) {
...
}
/* Add our own I/O rules here */
... = pDllApi->add(...);
The hLib pointer should not be passed to FreeLibrary until the custom DLL
itself unloads; this way, the pDllApi pointer will remain valid at all
times, even if mIRC has already told the I/O DLL to unload. An example
of how to unload your custom DLL:
/* Check whether the I/O DLL is already unloaded. If not, the custom DLL
might be unloaded early (as opposed to when mIRC shuts down), which
means it has to clean up neatly. */
if (pDllApi->loaded == TRUE) {
/* Delete our own I/O rules here */
pDllApi->del(...);
}
/* Unload the I/O DLL now. If pDllApi->loaded was FALSE, then the whole
structure will now be automatically freed. */
FreeLibrary(hLib);
5.2. Exported functions
The rest of the io_dllapi structure, which consists of function calls, is
explained below. Unlike the exported "dllapi" function, these functions use
the normal C calling convention. Don't ask.
5.2.1. CONN
The "conn" function assumes that its caller is actually a DLL function
called by mIRC, and returns the current (what I call) connection pointer:
a pointer that is unique for each server window in mIRC. This pointer can
subsequently be used in other I/O DLL API calls (see below).
Prototype:
void *conn(void *nopause);
Parameters:
nopause
A pointer to the caller's "nopause" parameter. This is the "BOOL
nopause" parameter from the DLL routine in mIRC's "/help DLL Support".
Return value:
A pointer uniquely identifying mIRC's current server window.
Example:
void *conn = pDllApi->conn(&nopause);
Notes:
You do not necessarily need this function, especially not when you define
your rules to apply to ALL server connections (see below).
If you're interested: this function actually retrieves this pointer from
the stack, at a static distance from the "nopause" parameter, which is
also on the stack.
5.2.2. ADD
The "add" function adds a rule in a way quite similar to the ADD command
specified in section 3.1. Pay attention though, there are a few small but
important differences.
Prototype:
int add(int gid, int flags, char *match, int (*proc)([...]), void *data,
void *conn);
Parameters:
gid
The ID of an existing group to add this rule to, or zero to create a new
group for this rule.
flags
A bitwise OR combination of the following flags:
FLAG_INPUT this rule is for INPUT message lines.
FLAG_OUTPUT this rule is for OUTPUT message lines.
FLAG_ONCE delete this rule's group after being triggered.
FLAG_ALL this rule affects all server connections.
FLAG_COMMAND the match field is a command.
As usual, if none of the INPUT/OUTPUT flags are specified, FLAG_INPUT is
assumed.
match
A null-terminated string containing the matchtext for this rule; either
a command or a full line wildcard string depending on the presence of
the FLAG_COMMANd flag, so DON'T add quotes as in the script ADD command.
proc
A pointer to the callback procedure. See the notes below. If proc is
NULL, the rule will block any matching lines.
data
An arbitrary pointer which will be passed untouched to the callback
procedure; only relevant if 'proc' is not NULL.
conn
A connection pointer of the server connection to add this rule to. This
parameter should be NULL if FLAG_ALL is specified, otherwise it should
always be a valid connection pointer (see also section 5.2.1 above).
Return value:
The ID of the group that this rule was added to, or 0 in case of an error.
If "gid" was nonzero, and no error occurred, the same "gid" value will be
returned.
Example:
int gid = pDllApi->add(0, FLAG_OUTPUT|FLAG_ALL|FLAG_COMMAND, "JOIN",
myProc, NULL, conn);
Notes:
The "proc" parameter should be a pointer to a function with the following
prototype (again, C calling convention):
int io_proc(const char *line, void *conn, int gid, void *data);
Where "line" is a pointer to the message, "conn" is the connection pointer
for the connection which triggered the rule (always non-NULL), "gid" is
the rule's group ID, and "data" is the pointer as passed to the "add"
function. If the return value is 1, the message will be blocked; if the
return value is 2, the message will be blocked and the group to which the
triggered rule belongs will be deleted.
Note that the "line" parameter is constant for a good reason. Don't touch.
5.2.3. DEL
The "del" function deletes a rule, or a whole group.
Prototype:
int del(int gid, int flags, char *match, void *conn);
Parameters:
gid
The group ID of the group that this rule resides in. This value can not
be zero.
flags
The flags as specified when the rule was added; the FLAG_ONCE flag is
ignored.
match
The matchtext as specified when the rule was added. If this parameter is
NULL, the whole group as specified by 'gid' is deleted, and both 'flags'
and 'conn' are ignored.
conn
The connection pointer for the rule, or NULL if this was a rule created
with the FLAG_ALL flag.
Return value:
Nonzero if a rule or group was successfully deleted, zero otherwise.
Example:
pDllApi->del(gid, FLAG_OUTPUT|FLAG_ALL|FLAG_COMMAND, "JOIN", conn);
5.2.4. RECV
The "recv" function tells mIRC that it received a message line from the IRC
server; it will return when mIRC is done processing this line.
Prototype:
void recv(const char *line, void *conn);
Parameters:
line
The line that mIRC will receive. This line MUST NOT be terminated with
any CR/LF characters.
conn
The connection pointer of the connection that will receive the line.
This parameter can not be NULL in this case.
Example:
pDllApi->recv(":bar JOIN #foo", conn);
5.2.5. SEND
The "send" function tells mIRC to send a message to the IRC server.
Prototype:
void send(const char *line, void *conn);
Parameters:
line
The line that mIRC should send. This line MUST NOT be terminated with
any CR/LF characters.
conn
The connection pointer of the connection that the line will be sent to.
This parameter can not be NULL in this case.
Example:
pDllApi->send("JOIN #foo", conn);
5.3. The ioapi.h file
#ifndef __IOAPI_H
#define __IOAPI_H
#define DLLAPI_VERSION MAKELONG(0,8)
#define FLAG_INPUT 0x01
#define FLAG_OUTPUT 0x02
#define FLAG_ONCE 0x04
#define FLAG_ALL 0x08
#define FLAG_COMMAND 0x10
typedef struct {
DWORD version;
BOOL loaded;
void *(*conn)(void *nopause);
int (*add)(int gid, int flags, const char *match,
int (*proc)(const char *line, void *conn, int gid, void *data),
void *data, void *conn);
int (*del)(int gid, int flags, const char *match, void *conn);
void (*recv)(const char *line, void *conn);
void (*send)(const char *line, void *conn);
} io_dllapi;
#endif /*__IOAPI_H*/
6. License
Distribution of io.dll and this README.txt is UNLIMITED, provided that the
files are both UNMODIFIED. You are free to include the DLL without this
readme in your script distribution, without giving credit, as long as the
DLL is unmodified. You are not allowed to TAKE credit for this DLL (duh!).
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL THE
AUTHOR BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING INCIDENTAL OR
CONSEQUENTIAL DAMAGES, ARISING OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU ACKNOWLEDGE THAT YOU HAVE
READ THIS LICENSE, UNDERSTAND IT AND AGREE TO BE BOUND BY ITS TERMS.
7. Source code
Due to legal reasons, this DLL has to remain closed-source. I'm not going to
go into exact details about this, but although the creation of this DLL was
perfectly legal, and using the DLL is *definitely* legal, I would break a
law if I were to release its source, because this would reveal information
about how mIRC works internally. I promise I'll update it as soon as I can
after the release of new mIRC versions, though!
8. Version history
Version Released Description/changes
0.8.1 15-09-2007 Added support for mIRC 6.3
0.8.0 23-11-2006 Can now delete a whole group with "io del"
Renamed "groupdata" to "data"; "groupdata" soon to go
Updated DLL interface, also compatible with msvc/mingw
Added support for mIRC 6.21
0.7.10 08-08-2006 Now disallowing use of "io recv" from critical events
Added support for mIRC 6.2
0.7.9 15-04-2006 Fixed third callback parameter evaluation/mangling bug
Switched from x.yz to x.y.z version numbering
0.7.8 06-03-2006 Added disconnect auto-detection, removed "io flush"
0.7.7 18-02-2006 Added support for mIRC 6.17
0.7.6 05-07-2005 Fixed whole line match creation bug
Added callback retval of 2 to delete group
0.7.5 07-06-2005 Fixed deleting of groups after alias fired
0.7.4 02-06-2005 Fixed rule number creation bug
0.7.3 09-04-2005 Fixed group name lookup bug
0.7.2 23-03-2005 First public release
0.7.1 08-01-2005 Added support for mIRC 6.16
0.7 19-06-2004 First beta release
0.6 03-06-2004 Limited technology preview