IRC8.mrc
· 5.7 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-
.parseline -itq $1 AWAY $iif($gettok($3, 1, 44) == G,:Away)
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) {
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)
if ($numtok(%user, 44) = 4) {
.parseline -itq : $+ $remove($gettok(%user, $numtok(%user, 44), 44), +, @, .) AWAY $iif($gettok(%user, 1, 44) == G,:Away)
}
inc %i
}
.parseline -it $1-5 : $+ %new
}
elseif ($2 == 821) {
.parseline -it $1 AWAY
}
elseif ($2 == 822) {
.parseline -it $1 AWAY $3-
}
}
on 1:PARSELINE:out:*:{
tokenize 32 $iif($asc($right($parseline, 1)) == 10, $left($parseline, -1), $parseline)
; JOIN without key/hostkey/ownerkey
if ($1 == JOIN && $3 == $null) {
if (!$2) return
if ($gk.config($2, OWNERKEY)) .parseline -otn $1-2 $ifmatch
elseif ($gk.config($2, HOSTKEY)) .parseline -otn $1-2 $ifmatch
else .parseline -otn $1-2 $gk.ud
}
}
alias -l 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 escaping
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 ($gk.config(settings, GUID)) return $ifmatch
var %guid = $md5($time $date $gmt $ctime $ticks)
gk.config settings GUID %guid
return %guid
}
alias -l gk.ud {
if ($gk.config(settings, UserData1)) return $ifmatch
var %ud = $regsubex($str(-, 31), /-/g, $chr($r(33, 126)))
gk.config settings UserData1 %ud
return %ud
}
; Usage: $gk.config(section, key)
; /gk.config <section> <key> <data>
alias gk.config {
var %fn $scriptdir $+ gk.settings.ini
if ($isid) return $readini(%fn, $1, $2)
writeini %fn $1-
}
; 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 | .parseline -itq $1 AWAY $iif($gettok($3, 1, 44) == G,:Away) |
| 50 | if ($gettok($3, 4, 44) isin +@.) { |
| 51 | .parseline -itq $+($1 MODE $right($4, -1) +, $replace($gettok($3, 4, 44), +, v, @, o, ., q) $right($gettok($1, 1, 33), -1)) |
| 52 | } |
| 53 | } |
| 54 | elseif ($2 == PRIVMSG) { |
| 55 | if ($regex($4-, /^:\x01S\s([^\\]|\\.)([^\\]|\\.)(.*?);(\d+)\s(.*?)\x01?$/) > 0) { |
| 56 | ; Notes: |
| 57 | ; Font stored in: $regsubex($regml(3), /([^\\]|\\.)/gu, $chr($gk.asc(\1))) |
| 58 | ; Script number stored in: $regml(4) - See also https://learn.microsoft.com/pl-pl/dotnet/api/system.windows.forms.visualstyles.textmetricscharacterset |
| 59 | var %msg : |
| 60 | var %color $gk.msn2irc($calc($gk.asc($regml(1)) - 1)) |
| 61 | if (%color) %msg = $+(%msg, $chr(3), $base(%color, 10, 10, 2)) |
| 62 | var %style $calc($gk.asc($regml(2)) - 1) |
| 63 | if (%style & 1) %msg = %msg $+ $chr(2) |
| 64 | if (%style & 2) %msg = %msg $+ $chr(29) |
| 65 | if (%style & 4) %msg = %msg $+ $chr(31) |
| 66 | %msg = $+(%msg, $str($chr(2), 2), $regml(5)) |
| 67 | .parseline -it $1-3 %msg |
| 68 | } |
| 69 | } |
| 70 | elseif ($2 == 004) { |
| 71 | .parseline -itq : $+ $server 005 $me PREFIX=(qov).@+ CHANTYPES=%# |
| 72 | } |
| 73 | elseif ($2 == 353) { |
| 74 | var %original = $right($6-, -1) |
| 75 | var %new |
| 76 | var %i = 1 |
| 77 | while (%i <= $numtok(%original, 32)) { |
| 78 | var %user = $gettok(%original, %i, 32) |
| 79 | %new = %new $gettok(%user, $numtok(%user, 44), 44) |
| 80 | if ($numtok(%user, 44) = 4) { |
| 81 | .parseline -itq : $+ $remove($gettok(%user, $numtok(%user, 44), 44), +, @, .) AWAY $iif($gettok(%user, 1, 44) == G,:Away) |
| 82 | } |
| 83 | inc %i |
| 84 | } |
| 85 | .parseline -it $1-5 : $+ %new |
| 86 | } |
| 87 | elseif ($2 == 821) { |
| 88 | .parseline -it $1 AWAY |
| 89 | } |
| 90 | elseif ($2 == 822) { |
| 91 | .parseline -it $1 AWAY $3- |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | on 1:PARSELINE:out:*:{ |
| 96 | tokenize 32 $iif($asc($right($parseline, 1)) == 10, $left($parseline, -1), $parseline) |
| 97 | |
| 98 | ; JOIN without key/hostkey/ownerkey |
| 99 | if ($1 == JOIN && $3 == $null) { |
| 100 | if (!$2) return |
| 101 | if ($gk.config($2, OWNERKEY)) .parseline -otn $1-2 $ifmatch |
| 102 | elseif ($gk.config($2, HOSTKEY)) .parseline -otn $1-2 $ifmatch |
| 103 | else .parseline -otn $1-2 $gk.ud |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | alias -l gk.msn2irc return $gettok(1 0 5 3 2 7 6 10 15 14 4 9 12 8 13 11, $calc($1 + 1), 32) |
| 108 | |
| 109 | ; Usage: $gk.header(sequence) - Returns GateKeeper v3 header with specified sequence (1 or 3 for client) |
| 110 | alias -l gk.header return $+(GKSSP\0JD,$chr(3),\0\0\0,$chr($$1),\0\0\0) |
| 111 | |
| 112 | ; Usage: $gk.asc(char) - Returns $asc equivalent for a potentially escaped character (eg. \n) |
| 113 | alias -l gk.asc { |
| 114 | ; Not escaped |
| 115 | if ($left($1, 1) != \) return $asc($1) |
| 116 | ; Invalid escape |
| 117 | elseif ($mid($1, 2, 1) !isincs 0tnrbc\) return $asc($mid($1, 2, 1)) |
| 118 | ; Valid escape (\0 doesn't need replacement) |
| 119 | return $replacecs($mid($1, 2, 1), t, 9, n, 10, r, 13, b, 32, c, 44, \, 92) |
| 120 | } |
| 121 | |
| 122 | ; Usage: $gk.hex2chr(hex) - Returns an escaped $chr equivalent from hex |
| 123 | alias -l gk.hex2chr { |
| 124 | ; Normalise - Remove zero padding |
| 125 | var %h = $base($1, 16, 16) |
| 126 | |
| 127 | ; Chars need escaping |
| 128 | if (%h == 0) return \0 |
| 129 | elseif (%h == 9) return \t |
| 130 | elseif (%h == A) return \n |
| 131 | elseif (%h == D) return \r |
| 132 | elseif (%h == 20) return \b |
| 133 | elseif (%h == 2C) return \c |
| 134 | elseif (%h == 5C) return \\ |
| 135 | |
| 136 | ; Chars don't need escaping |
| 137 | else return $chr($base($1, 16, 10)) |
| 138 | } |
| 139 | |
| 140 | alias -l gk.guid { |
| 141 | if ($1 == GateKeeperPassport) return $str(00,16) |
| 142 | if ($gk.config(settings, GUID)) return $ifmatch |
| 143 | var %guid = $md5($time $date $gmt $ctime $ticks) |
| 144 | gk.config settings GUID %guid |
| 145 | return %guid |
| 146 | } |
| 147 | |
| 148 | alias -l gk.ud { |
| 149 | if ($gk.config(settings, UserData1)) return $ifmatch |
| 150 | var %ud = $regsubex($str(-, 31), /-/g, $chr($r(33, 126))) |
| 151 | gk.config settings UserData1 %ud |
| 152 | return %ud |
| 153 | } |
| 154 | |
| 155 | ; Usage: $gk.config(section, key) |
| 156 | ; /gk.config <section> <key> <data> |
| 157 | alias gk.config { |
| 158 | var %fn $scriptdir $+ gk.settings.ini |
| 159 | if ($isid) return $readini(%fn, $1, $2) |
| 160 | writeini %fn $1- |
| 161 | } |
| 162 | |
| 163 | ; Adds $username for mIRC (Built-in for AdiIRC) |
| 164 | alias -l username { |
| 165 | if (@ isin $emailaddr) return $gettok($emailaddr, 1, 46) |
| 166 | else return $me |
| 167 | } |
| 168 |
README.md
· 751 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/JOIN/353)
* [X] Supports MSN Chat colors and styles (bold/italic/underline)
* [X] Support MSN Chat UserData1 / Ownerkey (and HostKey)
## Nice to haves (might be implemented)
* [ ] IRC8 Profiles (818)
* [ ] 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/JOIN/353)
- Supports MSN Chat colors and styles (bold/italic/underline)
- Support MSN Chat UserData1 / Ownerkey (and HostKey)
Nice to haves (might be implemented)
- IRC8 Profiles (818)
- 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)