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)