IRC8.mrc
· 4.9 KiB · Text
Raw
; IRC8 connection by JD
on ^1:LOGON:*:{
raw -q IRCVERS IRC8 MSN-OCX!9.02.0310.2401
raw -q AUTH GateKeeper I : $+ $gk.header(1)
haltdef
}
on 1:PARSELINE:in:*:{
tokenize 32 $parseline
if ($1 == AUTH) {
if ($2 == GateKeeper || $2 == GateKeeperPassport) {
if ($3 == S) {
; Extended (Passport) Authentication. Send %ticket and %passport to server.
if ($2- == GateKeeperPassport S :OK) {
raw -qn $1-3 $+($base($len(%ticket), 10, 16, 8), %ticket, $base($len(%profile), 10, 16, 8), %profile)
}
; Attempt to find GateKeeper v3 Challenge (8 bytes, escaped)
elseif ($regex($gettok($parseline,4-,32), /^:GKSSP\\0(?:[^\\]|\\.){2}\x03\\0\\0\\0\x02\\0\\0\\0((?:[^\\]|\\.){8})$/u) > 0) {
; Escaped text can contain characters the client can't handle (eg. 0x00), so convert to &binvar
bset -a &challenge 1 $regsubex($regml(1) $+ $servertarget, /([^\\]|\\.)/gu, $gk.asc(\1) $+ $chr(32))
; GateKeeper authenticaiton is a HMAC-MD5 with fixed key
var %result = $hmac(&challenge, SRFMKSJANDRESKKC, md5, 1)
.parseline -it
.raw -n $1-3 $+(:,$gk.header(3),$regsubex(%result $+ $gk.guid($2), /([0-9a-f]{2})/g, $gk.hex2chr(\1)))
}
else {
echo $color(info2) -t * GateKeeper Authentication Failed
}
}
elseif ($3 == *) {
var %oid = $iif($5 != 0, $+($chr(40),OID: $5,$chr(41)))
echo $color(info) -t * Logged in as $4 %oid
.parseline -it
raw -q USER $username * * : $+ $iif($fullname, $ifmatch, ...)
raw -q NICK $me
}
else {
echo $color(info2) -t * GateKeeper Authentication Failed
}
}
else {
; This is not GateKeeper.
echo $color(info2) -t * ERROR: Unknown Authentication Package $+($chr(40),$2,$chr(41))
}
}
elseif ($2 == JOIN) {
.parseline -it $1-2 $4-
if ($gettok($3, 4, 44) isin +@.) {
.parseline -itq $+($1 MODE $right($4, -1) +, $replace($gettok($3, 4, 44), +, v, @, o, ., q) $right($gettok($1, 1, 33), -1))
}
}
elseif ($2 == PRIVMSG) {
:>JD!07215E4B28474ECBBCEF373752CAA2D1@GateKeeper PRIVMSG %#The\bLobby :S Tahoma;0 test
if ($left($4-, 2) == : $+ $chr(1) && $len($4-) > 2) {
echo -a CTCP
}
if ($regex($4-, /^:\x01S\s([^\\]|\\.)([^\\]|\\.)(.*?);(\d+)\s(.*?)\x01?$/) > 0) {
; Notes:
; Font stored in: $regsubex($regml(3), /([^\\]|\\.)/gu, $chr($gk.asc(\1)))
; Script number stored in: $regml(4) - See also https://learn.microsoft.com/pl-pl/dotnet/api/system.windows.forms.visualstyles.textmetricscharacterset
var %msg :
var %color $gk.msn2irc($calc($gk.asc($regml(1)) - 1))
if (%color)
%msg = $+(%msg, $chr(3), $base(%color, 10, 10, 2))
var %style $calc($gk.asc($regml(2)) - 1)
if (%style & 1) %msg = %msg $+ $chr(2)
if (%style & 2) %msg = %msg $+ $chr(29)
if (%style & 4) %msg = %msg $+ $chr(31)
%msg = $+(%msg, $str($chr(2), 2), $regml(5))
.parseline -it $1-3 %msg
}
}
elseif ($2 == 004) {
.parseline -itq : $+ $server 005 $me PREFIX=(qov).@+ CHANTYPES=%#
}
elseif ($2 == 353) {
var %original = $right($6-, -1)
var %new
var %i = 1
while (%i <= $numtok(%original, 32)) {
var %user = $gettok(%original, %i, 32)
%new = %new $gettok(%user, $numtok(%user, 44), 44)
inc %i
}
.parseline -it $1-5 : $+ %new
}
elseif ($2 == 821) {
.parseline -it $1 AWAY
}
elseif ($2 == 822) {
.parseline -it $1 AWAY $3-
}
}
; 3/6 matches
alias gk.msn2irc return $gettok(1 0 5 3 2 7 6 10 15 14 4 9 12 8 13 11, $calc($1 + 1), 32)
; Usage: $gk.header(sequence) - Returns GateKeeper v3 header with specified sequence (1 or 3 for client)
alias -l gk.header return $+(GKSSP\0JD,$chr(3),\0\0\0,$chr($$1),\0\0\0)
; Usage: $gk.asc(char) - Returns $asc equivalent for a potentially escaped character (eg. \n)
alias -l gk.asc {
; Not escaped
if ($left($1, 1) != \) return $asc($1)
; Invalid escape
elseif ($mid($1, 2, 1) !isincs 0tnrbc\) return $asc($mid($1, 2, 1))
; Valid escape (\0 doesn't need replacement)
return $replacecs($mid($1, 2, 1), t, 9, n, 10, r, 13, b, 32, c, 44, \, 92)
}
; Usage: $gk.hex2chr(hex) - Returns an escaped $chr equivalent from hex
alias -l gk.hex2chr {
; Normalise - Remove zero padding
var %h = $base($1, 16, 16)
; Chars need escapinh
if (%h == 0) return \0
elseif (%h == 9) return \t
elseif (%h == A) return \n
elseif (%h == D) return \r
elseif (%h == 20) return \b
elseif (%h == 2C) return \c
elseif (%h == 5C) return \\
; Chars don't need escaping
else return $chr($base($1, 16, 10))
}
alias -l gk.guid {
if ($1 == GateKeeperPassport) return $str(00,16)
if (!$var(%GK.GUID)) set %GK.GUID $md5($time $date $gmt $ctime $ticks)
return %GK.GUID
}
; Adds $username for mIRC (Built-in for AdiIRC)
alias -l username {
if (@ isin $emailaddr) return $gettok($emailaddr, 1, 46)
else return $me
}
1 | ; IRC8 connection by JD |
2 | |
3 | on ^1:LOGON:*:{ |
4 | raw -q IRCVERS IRC8 MSN-OCX!9.02.0310.2401 |
5 | raw -q AUTH GateKeeper I : $+ $gk.header(1) |
6 | haltdef |
7 | } |
8 | |
9 | on 1:PARSELINE:in:*:{ |
10 | tokenize 32 $parseline |
11 | if ($1 == AUTH) { |
12 | if ($2 == GateKeeper || $2 == GateKeeperPassport) { |
13 | if ($3 == S) { |
14 | ; Extended (Passport) Authentication. Send %ticket and %passport to server. |
15 | if ($2- == GateKeeperPassport S :OK) { |
16 | raw -qn $1-3 $+($base($len(%ticket), 10, 16, 8), %ticket, $base($len(%profile), 10, 16, 8), %profile) |
17 | } |
18 | ; Attempt to find GateKeeper v3 Challenge (8 bytes, escaped) |
19 | elseif ($regex($gettok($parseline,4-,32), /^:GKSSP\\0(?:[^\\]|\\.){2}\x03\\0\\0\\0\x02\\0\\0\\0((?:[^\\]|\\.){8})$/u) > 0) { |
20 | ; Escaped text can contain characters the client can't handle (eg. 0x00), so convert to &binvar |
21 | bset -a &challenge 1 $regsubex($regml(1) $+ $servertarget, /([^\\]|\\.)/gu, $gk.asc(\1) $+ $chr(32)) |
22 | ; GateKeeper authenticaiton is a HMAC-MD5 with fixed key |
23 | var %result = $hmac(&challenge, SRFMKSJANDRESKKC, md5, 1) |
24 | .parseline -it |
25 | .raw -n $1-3 $+(:,$gk.header(3),$regsubex(%result $+ $gk.guid($2), /([0-9a-f]{2})/g, $gk.hex2chr(\1))) |
26 | } |
27 | else { |
28 | echo $color(info2) -t * GateKeeper Authentication Failed |
29 | } |
30 | } |
31 | elseif ($3 == *) { |
32 | var %oid = $iif($5 != 0, $+($chr(40),OID: $5,$chr(41))) |
33 | echo $color(info) -t * Logged in as $4 %oid |
34 | .parseline -it |
35 | raw -q USER $username * * : $+ $iif($fullname, $ifmatch, ...) |
36 | raw -q NICK $me |
37 | } |
38 | else { |
39 | echo $color(info2) -t * GateKeeper Authentication Failed |
40 | } |
41 | } |
42 | else { |
43 | ; This is not GateKeeper. |
44 | echo $color(info2) -t * ERROR: Unknown Authentication Package $+($chr(40),$2,$chr(41)) |
45 | } |
46 | } |
47 | elseif ($2 == JOIN) { |
48 | .parseline -it $1-2 $4- |
49 | if ($gettok($3, 4, 44) isin +@.) { |
50 | .parseline -itq $+($1 MODE $right($4, -1) +, $replace($gettok($3, 4, 44), +, v, @, o, ., q) $right($gettok($1, 1, 33), -1)) |
51 | } |
52 | } |
53 | elseif ($2 == PRIVMSG) { |
54 | :>JD!07215E4B28474ECBBCEF373752CAA2D1@GateKeeper PRIVMSG %#The\bLobby :S Tahoma;0 test |
55 | if ($left($4-, 2) == : $+ $chr(1) && $len($4-) > 2) { |
56 | echo -a CTCP |
57 | } |
58 | if ($regex($4-, /^:\x01S\s([^\\]|\\.)([^\\]|\\.)(.*?);(\d+)\s(.*?)\x01?$/) > 0) { |
59 | ; Notes: |
60 | ; Font stored in: $regsubex($regml(3), /([^\\]|\\.)/gu, $chr($gk.asc(\1))) |
61 | ; Script number stored in: $regml(4) - See also https://learn.microsoft.com/pl-pl/dotnet/api/system.windows.forms.visualstyles.textmetricscharacterset |
62 | var %msg : |
63 | var %color $gk.msn2irc($calc($gk.asc($regml(1)) - 1)) |
64 | if (%color) |
65 | %msg = $+(%msg, $chr(3), $base(%color, 10, 10, 2)) |
66 | var %style $calc($gk.asc($regml(2)) - 1) |
67 | if (%style & 1) %msg = %msg $+ $chr(2) |
68 | if (%style & 2) %msg = %msg $+ $chr(29) |
69 | if (%style & 4) %msg = %msg $+ $chr(31) |
70 | %msg = $+(%msg, $str($chr(2), 2), $regml(5)) |
71 | .parseline -it $1-3 %msg |
72 | } |
73 | } |
74 | elseif ($2 == 004) { |
75 | .parseline -itq : $+ $server 005 $me PREFIX=(qov).@+ CHANTYPES=%# |
76 | } |
77 | elseif ($2 == 353) { |
78 | var %original = $right($6-, -1) |
79 | var %new |
80 | var %i = 1 |
81 | while (%i <= $numtok(%original, 32)) { |
82 | var %user = $gettok(%original, %i, 32) |
83 | %new = %new $gettok(%user, $numtok(%user, 44), 44) |
84 | inc %i |
85 | } |
86 | .parseline -it $1-5 : $+ %new |
87 | } |
88 | elseif ($2 == 821) { |
89 | .parseline -it $1 AWAY |
90 | } |
91 | elseif ($2 == 822) { |
92 | .parseline -it $1 AWAY $3- |
93 | } |
94 | } |
95 | |
96 | ; 3/6 matches |
97 | alias gk.msn2irc return $gettok(1 0 5 3 2 7 6 10 15 14 4 9 12 8 13 11, $calc($1 + 1), 32) |
98 | |
99 | ; Usage: $gk.header(sequence) - Returns GateKeeper v3 header with specified sequence (1 or 3 for client) |
100 | alias -l gk.header return $+(GKSSP\0JD,$chr(3),\0\0\0,$chr($$1),\0\0\0) |
101 | |
102 | ; Usage: $gk.asc(char) - Returns $asc equivalent for a potentially escaped character (eg. \n) |
103 | alias -l gk.asc { |
104 | ; Not escaped |
105 | if ($left($1, 1) != \) return $asc($1) |
106 | ; Invalid escape |
107 | elseif ($mid($1, 2, 1) !isincs 0tnrbc\) return $asc($mid($1, 2, 1)) |
108 | ; Valid escape (\0 doesn't need replacement) |
109 | return $replacecs($mid($1, 2, 1), t, 9, n, 10, r, 13, b, 32, c, 44, \, 92) |
110 | } |
111 | |
112 | ; Usage: $gk.hex2chr(hex) - Returns an escaped $chr equivalent from hex |
113 | alias -l gk.hex2chr { |
114 | ; Normalise - Remove zero padding |
115 | var %h = $base($1, 16, 16) |
116 | |
117 | ; Chars need escapinh |
118 | if (%h == 0) return \0 |
119 | elseif (%h == 9) return \t |
120 | elseif (%h == A) return \n |
121 | elseif (%h == D) return \r |
122 | elseif (%h == 20) return \b |
123 | elseif (%h == 2C) return \c |
124 | elseif (%h == 5C) return \\ |
125 | ; Chars don't need escaping |
126 | else return $chr($base($1, 16, 10)) |
127 | } |
128 | |
129 | alias -l gk.guid { |
130 | if ($1 == GateKeeperPassport) return $str(00,16) |
131 | if (!$var(%GK.GUID)) set %GK.GUID $md5($time $date $gmt $ctime $ticks) |
132 | return %GK.GUID |
133 | } |
134 | |
135 | ; Adds $username for mIRC (Built-in for AdiIRC) |
136 | alias -l username { |
137 | if (@ isin $emailaddr) return $gettok($emailaddr, 1, 46) |
138 | else return $me |
139 | } |
140 |
README.md
· 729 B · Markdown
Raw
# MSN (IRC8) compatible connection script
## Features
* [X] Works with both mIRC and AdiIRC
* [X] Native GateKeeper & GateKeeperPassport Authentication
* [X] Supports IRC8 JOIN messages
* [X] Supports MODE on JOIN via IRCX access entries
* [X] Supports IRC8 names reply (353)
* [X] Supports IRC8 away/unaway notifications (821/822)
* [X] Supports MSN Chat colors and styles (bold/italic/underline)
## Nice to haves (might be implemented)
* [ ] IRC8 Profiles (818)
* [ ] Support away information from 353/JOIN
* [ ] Support WHISPER (and convert PRIVMSG to & from WHISPER)
* [ ] Support CTCP "ERR NOUSERWHISPER".
* [ ] Don't default to IRC8
## Known Issues
* Auth doesn't always work with AdiIRC (probably due to UTF8 quirks)
MSN (IRC8) compatible connection script
Features
- Works with both mIRC and AdiIRC
- Native GateKeeper & GateKeeperPassport Authentication
- Supports IRC8 JOIN messages
- Supports MODE on JOIN via IRCX access entries
- Supports IRC8 names reply (353)
- Supports IRC8 away/unaway notifications (821/822)
- Supports MSN Chat colors and styles (bold/italic/underline)
Nice to haves (might be implemented)
- IRC8 Profiles (818)
- Support away information from 353/JOIN
- Support WHISPER (and convert PRIVMSG to & from WHISPER)
- Support CTCP "ERR NOUSERWHISPER".
- Don't default to IRC8
Known Issues
- Auth doesn't always work with AdiIRC (probably due to UTF8 quirks)