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) |
62 | %msg = $+(%msg, $chr(3), $base(%color, 10, 10, 2)) |
63 | var %style $calc($gk.asc($regml(2)) - 1) |
64 | if (%style & 1) %msg = %msg $+ $chr(2) |
65 | if (%style & 2) %msg = %msg $+ $chr(29) |
66 | if (%style & 4) %msg = %msg $+ $chr(31) |
67 | %msg = $+(%msg, $str($chr(2), 2), $regml(5)) |
68 | .parseline -it $1-3 %msg |
69 | } |
70 | } |
71 | elseif ($2 == 004) { |
72 | .parseline -itq : $+ $server 005 $me PREFIX=(qov).@+ CHANTYPES=%# |
73 | } |
74 | elseif ($2 == 353) { |
75 | var %original = $right($6-, -1) |
76 | var %new |
77 | var %i = 1 |
78 | while (%i <= $numtok(%original, 32)) { |
79 | var %user = $gettok(%original, %i, 32) |
80 | %new = %new $gettok(%user, $numtok(%user, 44), 44) |
81 | if ($numtok(%user, 44) = 4) { |
82 | .parseline -itq : $+ $remove($gettok(%user, $numtok(%user, 44), 44), +, @, .) AWAY $iif($gettok(%user, 1, 44) == G,:Away) |
83 | } |
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 | on 1:PARSELINE:out:*:{ |
97 | tokenize 32 $iif($asc($right($parseline, 1)) == 10, $left($parseline, -1), $parseline) |
98 | |
99 | ; JOIN without key/hostkey/ownerkey |
100 | if ($1 == JOIN && $3 == $null) { |
101 | if (!$2) return |
102 | if ($gk.config($2, OWNERKEY)) .parseline -otn $1-2 $ifmatch |
103 | elseif ($gk.config($2, HOSTKEY)) .parseline -otn $1-2 $ifmatch |
104 | else .parseline -otn $1-2 $gk.ud |
105 | } |
106 | } |
107 | |
108 | 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) |
109 | |
110 | ; Usage: $gk.header(sequence) - Returns GateKeeper v3 header with specified sequence (1 or 3 for client) |
111 | alias -l gk.header return $+(GKSSP\0JD,$chr(3),\0\0\0,$chr($$1),\0\0\0) |
112 | |
113 | ; Usage: $gk.asc(char) - Returns $asc equivalent for a potentially escaped character (eg. \n) |
114 | alias -l gk.asc { |
115 | ; Not escaped |
116 | if ($left($1, 1) != \) return $asc($1) |
117 | ; Invalid escape |
118 | elseif ($mid($1, 2, 1) !isincs 0tnrbc\) return $asc($mid($1, 2, 1)) |
119 | ; Valid escape (\0 doesn't need replacement) |
120 | return $replacecs($mid($1, 2, 1), t, 9, n, 10, r, 13, b, 32, c, 44, \, 92) |
121 | } |
122 | |
123 | ; Usage: $gk.hex2chr(hex) - Returns an escaped $chr equivalent from hex |
124 | alias -l gk.hex2chr { |
125 | ; Normalise - Remove zero padding |
126 | var %h = $base($1, 16, 16) |
127 | |
128 | ; Chars need escaping |
129 | if (%h == 0) return \0 |
130 | elseif (%h == 9) return \t |
131 | elseif (%h == A) return \n |
132 | elseif (%h == D) return \r |
133 | elseif (%h == 20) return \b |
134 | elseif (%h == 2C) return \c |
135 | elseif (%h == 5C) return \\ |
136 | |
137 | ; Chars don't need escaping |
138 | else return $chr($base($1, 16, 10)) |
139 | } |
140 | |
141 | alias -l gk.guid { |
142 | if ($1 == GateKeeperPassport) return $str(00,16) |
143 | if ($gk.config(settings, GUID)) return $ifmatch |
144 | var %guid = $md5($time $date $gmt $ctime $ticks) |
145 | gk.config settings GUID %guid |
146 | return %guid |
147 | } |
148 | |
149 | alias -l gk.ud { |
150 | if ($gk.config(settings, UserData1)) return $ifmatch |
151 | var %ud = $regsubex($str(-, 31), /-/g, $chr($r(33, 126))) |
152 | gk.config settings UserData1 %ud |
153 | return %ud |
154 | } |
155 | |
156 | ; Usage: $gk.config(section, key) |
157 | ; /gk.config <section> <key> <data> |
158 | alias gk.config { |
159 | var %fn $scriptdir $+ gk.settings.ini |
160 | if ($isid) return $readini(%fn, $1, $2) |
161 | writeini %fn $1- |
162 | } |
163 | |
164 | ; Adds $username for mIRC (Built-in for AdiIRC) |
165 | alias -l username { |
166 | if (@ isin $emailaddr) return $gettok($emailaddr, 1, 46) |
167 | else return $me |
168 | } |
169 |
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)