| 1 | #!/usr/bin/tclsh |
| 2 | # |
| 3 | # This is an elemental Msn client using the protocol MSNP8 (windows messenger default) |
| 4 | # Not third-party client. It's an example for your information. |
| 5 | # Written by HackeMate @ QuakeNet |
| 6 | # Sentencia @ IRC-Hispano |
| 7 | # Sentencia@librepensamiento.es |
| 8 | |
| 9 | proc msn:sdata {sock str} { |
| 10 | puts $sock "$str" |
| 11 | msn:echo "*** $sock: $str" |
| 12 | } |
| 13 | |
| 14 | proc msn:echo {str} { |
| 15 | |
| 16 | switch -- [string range $str 0 3] { |
| 17 | "::: " { |
| 18 | set prefix "Msn:>" |
| 19 | set str [string range $str 4 end] |
| 20 | } |
| 21 | "*** " { |
| 22 | set prefix "02Sending:\>02" |
| 23 | set str [string range $str 4 end] |
| 24 | } |
| 25 | "!!! " { |
| 26 | set prefix "2Incomming ERROR:02" |
| 27 | set str [string rane $str 4 end] |
| 28 | } |
| 29 | "default" { |
| 30 | set prefix "Incomming data:" |
| 31 | } |
| 32 | } |
| 33 | puts stdout "$prefix $str" |
| 34 | } |
| 35 | |
| 36 | proc msn:nexus {} { |
| 37 | |
| 38 | # Passport authentication begins with the passport nexus, just as logging into messenger begins with the Dispatch Server |
| 39 | # Using secure http request to the next path. |
| 40 | |
| 41 | package require http |
| 42 | package require tls |
| 43 | http::register https 443 ::tls::socket |
| 44 | |
| 45 | set token [http::geturl https://nexus.passport.com/rdr/pprdr.asp] |
| 46 | upvar #0 $token state |
| 47 | foreach {name value} $state(meta) { |
| 48 | if {$name == "PassportURLs"} { |
| 49 | set data [split $value ","] |
| 50 | set dalogin [lsearch -all -inline $data "DALogin=*"] |
| 51 | set dalogin [lindex [split $dalogin "="] 1] |
| 52 | msn:echo "::: PassPort saved. We are jumping to $dalogin" |
| 53 | msn:passport $dalogin |
| 54 | return |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | # I admit that this is a dirty way to know when passort auth was failed. |
| 59 | # Sorry about, I'm lazy. |
| 60 | msn:echo "!!! Authentication process was not good." |
| 61 | close $::msn(sock) |
| 62 | # This exist command will exit from the previous command executed in msn:Login vwait forever |
| 63 | exit |
| 64 | return |
| 65 | } |
| 66 | |
| 67 | proc msn:passport {url} { |
| 68 | |
| 69 | # The client sends a https get request for the url given to it by the nexus (saved in $dalogin, here $url) |
| 70 | # The login is waiting the last parameters that we saved in $::msn(auth), like a header. |
| 71 | |
| 72 | package require http |
| 73 | package require tls |
| 74 | http::register https 443 ::tls::socket |
| 75 | |
| 76 | set url "https://$url" |
| 77 | set account [string map [list "\@" "\%40"] $::msn(account)] |
| 78 | set token [http::geturl $url -headers [list Authorization "Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in=$account,pwd=$::msn(password),$::msn(auth)"]] |
| 79 | upvar #0 $token state |
| 80 | |
| 81 | # If we fail the authentication, instead HTTP 200 it will return something like HTTP 302, or if |
| 82 | # the syntax was badly, HTTP 401. If all is Ok, this foreach will connect us. |
| 83 | |
| 84 | foreach {name value} $state(meta) { |
| 85 | if {$name == "Authentication-Info"} { |
| 86 | regexp {\'(.*?)\'} $value "" cookie |
| 87 | incr ::msn(trid) |
| 88 | msn:sdata $::msn(sock) "USR $::msn(trid) TWN S $cookie" |
| 89 | } |
| 90 | } |
| 91 | } |
| 92 | # Configuration |
| 93 | |
| 94 | set ::msn(account) "Your@Messenger.account" |
| 95 | set ::msn(password) "AGoodPassword" |
| 96 | |
| 97 | # We define here the version protocol. |
| 98 | # This variable will be used only twice. |
| 99 | |
| 100 | set ::msn(version) "MSNP8 CVR0" |
| 101 | |
| 102 | # (CVR) The CVR command sends version information about a client and operating |
| 103 | # system to the server. For the official client, the server will reply with |
| 104 | # information about a recommended version of the client. It's debatable whether |
| 105 | # third-party clients should send CVR. Microsoft might start blocking users based on |
| 106 | # this command, or they might tread third-party clients better than now if there was |
| 107 | # evidence that a significant number of people used them. Any way, Microsoft |
| 108 | # has no way to verify the information given. |
| 109 | |
| 110 | # Independently of all this debat, Where are you from? :) Spanish location is 0x0C0A |
| 111 | # American users send: 0x0409 |
| 112 | # For your information: http://www.microsoft.com/globaldev/reference/oslocversion.mspx |
| 113 | # This var may define your Locale configuration |
| 114 | set ::msn(location) "0x0C0A" |
| 115 | |
| 116 | # This is our info. It is what a Windows XP & windows messenger sends. |
| 117 | |
| 118 | set ::msn(msnms) "winnt 5.1 i386 MSMSGS 4.7.3001 WindowsMessenger" |
| 119 | |
| 120 | |
| 121 | # End of parameters. |
| 122 | |
| 123 | # We are going |
| 124 | |
| 125 | proc msn:Login { { server "messenger.hotmail.com" } {port 1863} } { |
| 126 | |
| 127 | # (TrID) The Transaction IDs, abbreviated as TriDs, are used to much a client command with a server |
| 128 | # response. Commands sent by the client are required to contain a TrID, and commands from the server |
| 129 | # sent in direct response to that command will contain the same TrID (with some exception). |
| 130 | # Every TrID is a number between 0 and 4294967295. Using this TrID wrongly may cause confusion and |
| 131 | # server and client will be unstable. |
| 132 | |
| 133 | if {![info exists ::msn(trid)]} { set ::msn(trid) 1 } |
| 134 | |
| 135 | # Easy to make multi-server, almost all depend of this (and previous) var name. |
| 136 | |
| 137 | set ::msn(sock) [socket $server $port] |
| 138 | msn:echo "::: Trying to connect $server:$port- sockname: $::msn(sock)" |
| 139 | |
| 140 | # Because we can receive data in any moment, we need read it as fast as we can, that's why I used |
| 141 | # -buffering line instead full. In certain moment negotiating the connection, the socket can get |
| 142 | # stuned or we are forced to execute manually gets, if you don't use vwait forever command. |
| 143 | # I had bad experiencies with this command in x-chat. |
| 144 | |
| 145 | fconfigure $::msn(sock) -buffering line -blocking no |
| 146 | |
| 147 | fileevent $::msn(sock) readable [list msn:gdata $::msn(sock)] |
| 148 | msn:sdata $::msn(sock) "VER $::msn(trid) $::msn(version)" |
| 149 | vwait forever |
| 150 | } |
| 151 | |
| 152 | proc msn:gdata {socket} { |
| 153 | |
| 154 | if {[eof $socket]} { |
| 155 | msn:echo "::: Closed connection." |
| 156 | close $socket |
| 157 | exit |
| 158 | return |
| 159 | } |
| 160 | set datos [gets $socket] |
| 161 | foreach data [split $datos \n] { |
| 162 | msn:echo "$data" |
| 163 | if {$data == "VER $::msn(trid) $::msn(version)"} { |
| 164 | incr ::msn(trid) |
| 165 | msn:sdata $::msn(sock) "CVR $::msn(trid) $::msn(location) $::msn(msnms) $::msn(account)" |
| 166 | } |
| 167 | |
| 168 | set cvr [join [lrange [split $data] 0 1]] |
| 169 | if {$cvr == "CVR $::msn(trid)"} { |
| 170 | incr ::msn(trid) |
| 171 | msn:sdata $::msn(sock) "USR $::msn(trid) TWN I $::msn(account)" |
| 172 | } |
| 173 | |
| 174 | set usr [join [lrange [split $data] 0 3]] |
| 175 | if {$usr == "USR $::msn(trid) TWN S"} { |
| 176 | |
| 177 | # Rude way to save this parameters... |
| 178 | # Thanks thommey for advice to use a foreach loop |
| 179 | # It will work, I will change it soon |
| 180 | set udata [split $data ","] |
| 181 | set lc [lsearch -all -inline $udata "lc=*"] |
| 182 | set lc [lindex [split $lc "="] 1] |
| 183 | set id [lsearch -all -inline $udata "id=*"] |
| 184 | set id [lindex [split $id "="] 1] |
| 185 | set tw [lsearch -all -inline $udata "tw=*"] |
| 186 | set tw [lindex [split $tw "="] 1] |
| 187 | set ru [lsearch -all -inline $udata "ru=*"] |
| 188 | set ru [lindex [split $ru "="] 1] |
| 189 | set ct [lindex [lsearch -all -inline $udata "*ct=*"] 0] |
| 190 | set ct [lindex [split $ct] 4] |
| 191 | set ct [lindex [split $ct "="] 1] |
| 192 | set kpp [lsearch -all -inline $udata "kpp=*"] |
| 193 | set kpp [lindex [split $kpp "="] 1] |
| 194 | set kv [lsearch -all -inline $udata "kv=*"] |
| 195 | set kv [lindex [split $kv "="] 1] |
| 196 | set ver [lsearch -all -inline $udata "ver=*"] |
| 197 | set ver [lindex [split $ver "="] 1] |
| 198 | set rn [lsearch -all -inline $udata "rn=*"] |
| 199 | set rn [lindex [split $rn "="] 1] |
| 200 | set tpf [lsearch -all -inline $udata "tpf=*"] |
| 201 | set tpf [lindex [split $tpf "="] 1] |
| 202 | set ::msn(auth) "lc=$lc,id=$id,tw=$tw,ru=$ru,ct=$ct,kpp=$kpp,kv=$kv,ver=$ver,rn=$rn,tpf=$tpf" |
| 203 | msn:nexus |
| 204 | } |
| 205 | |
| 206 | if {$data == "USR $::msn(trid) OK $::msn(account) $::msn(account) 1 0"} { |
| 207 | msn:echo "::: Now we are logged in." |
| 208 | # This command will mark us as connected for else people. |
| 209 | msn:sdata $::msn(sock) "SYN 8 0" |
| 210 | } |
| 211 | # SYN command will return our buddy list |
| 212 | if {[join [lrange [split $data] 0 1]] == "SYN 8"} { |
| 213 | msn:sdata $::msn(sock) "CHG 9 NLN 0" |
| 214 | } |
| 215 | if {[lindex [split $data] 0] == "ClientIP:"} { |
| 216 | set ::msn(selfip) [lindex [split $data] 1] |
| 217 | } |
| 218 | |
| 219 | if {[lindex [split $data] 1] == "ClientPort:"} { |
| 220 | set ::msn(selfport) [lindex [split $data] 1] |
| 221 | msn:echo "Our IP is: $::msn(selfip) and the port: $::msn(selfport)" |
| 222 | msn:sdata "SYN 8 0" |
| 223 | } |
| 224 | |
| 225 | if {[lindex [split $data] 0] == "XFR"} { |
| 226 | close $::msn(sock) |
| 227 | set server [lindex [split $data] 3] |
| 228 | set port [lindex [split $server ":"] 1] |
| 229 | set server [lindex [split $server ":"] 0] |
| 230 | incr ::msn(trid) |
| 231 | msn:Login $server $port |
| 232 | } |
| 233 | |
| 234 | # Catching errors (this is just a few list of) |
| 235 | |
| 236 | set cmd [lindex [split $data] 0] |
| 237 | set cparm [lindex [split $data] 1] |
| 238 | switch -- $cmd { |
| 239 | "710" { |
| 240 | msn:echo "!!! Bad CVR parameters sent." |
| 241 | } |
| 242 | "711" { |
| 243 | msn:echo "!!! Write is blocking." |
| 244 | } |
| 245 | "712" { |
| 246 | msn:echo "!!! Session is overloaded." |
| 247 | } |
| 248 | "713" { |
| 249 | msn:echo "!!! Calling too rapidly. This occurs when the client repeatedly calls someone who is offline or blocking them" |
| 250 | msn:echo "!!! in response to the commands that the server rejects. This is a way to know who is blocking you." |
| 251 | } |
| 252 | "714" { |
| 253 | msn:echo "!!! Too many sessions." |
| 254 | } |
| 255 | "715" { |
| 256 | if {$cparm == "30"} { |
| 257 | msn:echo "!!! PRP setting an invalid phone type of three or less characters." |
| 258 | } |
| 259 | if {$cparm == "31"} { |
| 260 | msn:echo "!!! ERROR- Changing display name (REA) on an unverified Passport account." |
| 261 | } |
| 262 | } |
| 263 | "731" { |
| 264 | msn:echo "!!! ERROR- Not expected. Bad formatted CVR." |
| 265 | close $::msn(sock) |
| 266 | return |
| 267 | } |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | puts stdout "Msn Client loaded" |