Last active 1737100574

MSN (IRC8) compatible connection script

Revision d2e9513a7f30c3770bd044cebd265942a4425578

IRC8.mrc Raw
1; IRC8 connection by JD
2
3on ^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
9on 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
96on 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
108alias -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)
111alias -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)
114alias -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
124alias -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
141alias -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
149alias -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>
158alias 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)
165alias -l username {
166 if (@ isin $emailaddr) return $gettok($emailaddr, 1, 46)
167 else return $me
168}
169
README.md Raw

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)