line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package AnyEvent::MSN; |
2
|
|
|
|
|
|
|
{ $AnyEvent::MSN::VERSION = 0.002 } |
3
|
2
|
|
|
2
|
|
2153
|
use lib '../../lib'; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
18
|
|
4
|
2
|
|
|
2
|
|
260
|
use 5.012; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
58
|
|
5
|
2
|
|
|
2
|
|
857
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
use Moose::Util::TypeConstraints; |
7
|
|
|
|
|
|
|
use AnyEvent qw[]; |
8
|
|
|
|
|
|
|
use AnyEvent::Handle qw[]; |
9
|
|
|
|
|
|
|
use AnyEvent::HTTP qw[]; |
10
|
|
|
|
|
|
|
use Try::Tiny; |
11
|
|
|
|
|
|
|
use XML::Twig; |
12
|
|
|
|
|
|
|
use AnyEvent::MSN::Protocol; |
13
|
|
|
|
|
|
|
use AnyEvent::MSN::Types; |
14
|
|
|
|
|
|
|
use MIME::Base64 qw[]; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# |
17
|
|
|
|
|
|
|
#use Data::Dump; |
18
|
|
|
|
|
|
|
# |
19
|
|
|
|
|
|
|
our $DEBUG = 0; |
20
|
|
|
|
|
|
|
sub DEBUG {$DEBUG} |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# XXX - During dev only |
23
|
|
|
|
|
|
|
#use Data::Printer; |
24
|
|
|
|
|
|
|
sub DEMOLISH { |
25
|
|
|
|
|
|
|
my $s = shift; |
26
|
|
|
|
|
|
|
$s->handle->destroy if $s->_has_handle && $s->handle; |
27
|
|
|
|
|
|
|
$s->_clear_soap_requests; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Basic connection info |
31
|
|
|
|
|
|
|
has host => (is => 'ro', |
32
|
|
|
|
|
|
|
writer => '_set_host', |
33
|
|
|
|
|
|
|
isa => 'Str', |
34
|
|
|
|
|
|
|
default => 'messenger.hotmail.com' |
35
|
|
|
|
|
|
|
); |
36
|
|
|
|
|
|
|
has port => (is => 'ro', |
37
|
|
|
|
|
|
|
writer => '_set_port', |
38
|
|
|
|
|
|
|
isa => 'Int', |
39
|
|
|
|
|
|
|
default => 1863 |
40
|
|
|
|
|
|
|
); |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Authentication info from user |
43
|
|
|
|
|
|
|
has passport => ( |
44
|
|
|
|
|
|
|
is => 'ro', |
45
|
|
|
|
|
|
|
isa => 'AnyEvent::MSN::Types::Passport', |
46
|
|
|
|
|
|
|
required => 1, |
47
|
|
|
|
|
|
|
handles => { |
48
|
|
|
|
|
|
|
username => sub { |
49
|
|
|
|
|
|
|
shift->passport =~ m[^(.+)\@.+$]; |
50
|
|
|
|
|
|
|
$1; |
51
|
|
|
|
|
|
|
}, |
52
|
|
|
|
|
|
|
userhost => sub { shift->passport =~ m[^.+\@(.+)$]; $1 } |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
); |
55
|
|
|
|
|
|
|
has password => (is => 'ro', isa => 'Str', required => 1); |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Extra stuff from user |
58
|
|
|
|
|
|
|
has [qw[friendly_name personal_message]] => |
59
|
|
|
|
|
|
|
(is => 'ro', isa => 'Str', default => ''); |
60
|
|
|
|
|
|
|
has status => ( |
61
|
|
|
|
|
|
|
is => 'ro', |
62
|
|
|
|
|
|
|
isa => 'AnyEvent::MSN::Types::OnlineStatus', |
63
|
|
|
|
|
|
|
default => 'NLN', |
64
|
|
|
|
|
|
|
writer => 'set_status' # exposed publicly |
65
|
|
|
|
|
|
|
); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Client info for MSNP21 |
68
|
|
|
|
|
|
|
has protocol_version => ( |
69
|
|
|
|
|
|
|
is => 'ro', |
70
|
|
|
|
|
|
|
isa => subtype( |
71
|
|
|
|
|
|
|
as 'Str' => where {m[^(?:MSNP\d+\s*)+$]} => message { |
72
|
|
|
|
|
|
|
'Protocol versions look like: MSNP18 MSNP21'; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
), |
75
|
|
|
|
|
|
|
writer => '_set_protocol_version', |
76
|
|
|
|
|
|
|
clearer => '_reset_protocol_version', |
77
|
|
|
|
|
|
|
default => 'MSNP21', |
78
|
|
|
|
|
|
|
lazy => 1 |
79
|
|
|
|
|
|
|
); |
80
|
|
|
|
|
|
|
map { has $_->[0] => (is => 'ro', isa => 'Str', default => $_->[1]) } |
81
|
|
|
|
|
|
|
[qw[product_id PROD0120PW!CCV9@]], |
82
|
|
|
|
|
|
|
[qw[product_key C1BX{V4W}Q3*10SM]], |
83
|
|
|
|
|
|
|
[qw[locale_id 0x0409]], |
84
|
|
|
|
|
|
|
[qw[os_type winnt]], |
85
|
|
|
|
|
|
|
[qw[os_ver 6.1.1]], |
86
|
|
|
|
|
|
|
[qw[arch i386]], |
87
|
|
|
|
|
|
|
[qw[client_name MSNMSGR]], |
88
|
|
|
|
|
|
|
[qw[client_version 15.4.3508.1109]], |
89
|
|
|
|
|
|
|
[qw[client_string MSNMSGR]]; |
90
|
|
|
|
|
|
|
has guid => ( |
91
|
|
|
|
|
|
|
is => 'ro', |
92
|
|
|
|
|
|
|
=> isa => subtype( |
93
|
|
|
|
|
|
|
as 'Str' => where { |
94
|
|
|
|
|
|
|
my $hex = qr[[\da-f]]; |
95
|
|
|
|
|
|
|
m[{$hex{8}(?:-$hex{4}){3}-$hex{12}}$]; |
96
|
|
|
|
|
|
|
} => message { |
97
|
|
|
|
|
|
|
'Malformed GUID. Should look like: {12345678-abcd-1234-abcd-123456789abc}'; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
), |
100
|
|
|
|
|
|
|
builder => '_build_guid' |
101
|
|
|
|
|
|
|
); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub _build_guid { |
104
|
|
|
|
|
|
|
state $r //= sub { |
105
|
|
|
|
|
|
|
join '', map { ('a' .. 'f', 0 .. 9)[rand 15] } 1 .. shift; |
106
|
|
|
|
|
|
|
}; |
107
|
|
|
|
|
|
|
sprintf '{%8s-%4s-%4s-%4s-%12s}', $r->(8), $r->(4), $r->(4), $r->(4), |
108
|
|
|
|
|
|
|
$r->(12); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
has location => (is => 'ro', isa => 'Str', default => 'Perl/AnyEvent::MSN'); |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# Internals |
113
|
|
|
|
|
|
|
has handle => ( |
114
|
|
|
|
|
|
|
is => 'ro', |
115
|
|
|
|
|
|
|
isa => 'Object', |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# weak_ref => 1, |
118
|
|
|
|
|
|
|
predicate => '_has_handle', |
119
|
|
|
|
|
|
|
writer => '_set_handle', |
120
|
|
|
|
|
|
|
clearer => '_reset_handle', |
121
|
|
|
|
|
|
|
handles => { |
122
|
|
|
|
|
|
|
send => sub { |
123
|
|
|
|
|
|
|
my $s = shift; |
124
|
|
|
|
|
|
|
$s->handle->push_write('AnyEvent::MSN::Protocol' => @_) |
125
|
|
|
|
|
|
|
if $s->_has_handle; # XXX - Else mention it... |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
); |
129
|
|
|
|
|
|
|
has tid => (is => 'ro', |
130
|
|
|
|
|
|
|
isa => 'Int', |
131
|
|
|
|
|
|
|
lazy => 1, |
132
|
|
|
|
|
|
|
clearer => '_reset_tid', |
133
|
|
|
|
|
|
|
builder => '_build_tid', |
134
|
|
|
|
|
|
|
traits => ['Counter'], |
135
|
|
|
|
|
|
|
handles => {'_inc_tid' => 'inc'} |
136
|
|
|
|
|
|
|
); |
137
|
|
|
|
|
|
|
sub _build_tid {0} |
138
|
|
|
|
|
|
|
after tid => sub { shift->_inc_tid }; # Auto inc |
139
|
|
|
|
|
|
|
has ping_timer => (is => 'ro', |
140
|
|
|
|
|
|
|
isa => 'Ref', # AE::timer |
141
|
|
|
|
|
|
|
writer => '_set_ping_timer' |
142
|
|
|
|
|
|
|
); |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# Server configuration |
145
|
|
|
|
|
|
|
has policies => ( |
146
|
|
|
|
|
|
|
is => 'bare', |
147
|
|
|
|
|
|
|
isa => 'HashRef', |
148
|
|
|
|
|
|
|
clearer => '_reset_policies', |
149
|
|
|
|
|
|
|
writer => '_set_policies', |
150
|
|
|
|
|
|
|
traits => ['Hash'], |
151
|
|
|
|
|
|
|
handles => {_add_policy => 'set', |
152
|
|
|
|
|
|
|
_del_policy => 'delete', |
153
|
|
|
|
|
|
|
policy => 'get', |
154
|
|
|
|
|
|
|
policies => 'kv' # XXX - Really? |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
); |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# SOAP |
159
|
|
|
|
|
|
|
has SSOsites => ( |
160
|
|
|
|
|
|
|
is => 'ro', # Single Sign On |
161
|
|
|
|
|
|
|
isa => 'ArrayRef[ArrayRef]', |
162
|
|
|
|
|
|
|
traits => ['Array'], |
163
|
|
|
|
|
|
|
default => sub { |
164
|
|
|
|
|
|
|
[['http://Passport.NET/tb', ''], |
165
|
|
|
|
|
|
|
['messengerclear.live.com', 'MBI_KEY_OLD'], |
166
|
|
|
|
|
|
|
['messenger.msn.com', '?id=507'], |
167
|
|
|
|
|
|
|
['messengersecure.live.com', 'MBI_SSL'], |
168
|
|
|
|
|
|
|
['contacts.msn.com', 'MBI'], |
169
|
|
|
|
|
|
|
['storage.msn.com', 'MBI'], |
170
|
|
|
|
|
|
|
['sup.live.com', 'MBI'] |
171
|
|
|
|
|
|
|
]; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
); |
174
|
|
|
|
|
|
|
has auth_tokens => (is => 'bare', |
175
|
|
|
|
|
|
|
isa => 'HashRef', |
176
|
|
|
|
|
|
|
clearer => '_reset_auth_tokens', |
177
|
|
|
|
|
|
|
writer => '_set_auth_tokens', |
178
|
|
|
|
|
|
|
traits => ['Hash'], |
179
|
|
|
|
|
|
|
handles => {_add_auth_token => 'set', |
180
|
|
|
|
|
|
|
_del_auth_token => 'delete', |
181
|
|
|
|
|
|
|
auth_token => 'get', |
182
|
|
|
|
|
|
|
auth_tokens => 'kv' |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
); |
185
|
|
|
|
|
|
|
has contacts => (is => 'ro', |
186
|
|
|
|
|
|
|
isa => 'HashRef', |
187
|
|
|
|
|
|
|
clearer => '_reset_contacts', |
188
|
|
|
|
|
|
|
writer => '_set_contacts', |
189
|
|
|
|
|
|
|
traits => ['Hash'], |
190
|
|
|
|
|
|
|
); |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# Simple callbacks |
193
|
|
|
|
|
|
|
has 'on_' . $_ => ( |
194
|
|
|
|
|
|
|
traits => ['Code'], |
195
|
|
|
|
|
|
|
is => 'ro', |
196
|
|
|
|
|
|
|
isa => 'CodeRef', |
197
|
|
|
|
|
|
|
default => sub { |
198
|
|
|
|
|
|
|
sub {1} |
199
|
|
|
|
|
|
|
}, |
200
|
|
|
|
|
|
|
handles => {'_trigger_' . $_ => 'execute_method'}, |
201
|
|
|
|
|
|
|
) |
202
|
|
|
|
|
|
|
for qw[ |
203
|
|
|
|
|
|
|
im nudge |
204
|
|
|
|
|
|
|
error fatal_error connect |
205
|
|
|
|
|
|
|
addressbook_update |
206
|
|
|
|
|
|
|
buddylist_update |
207
|
|
|
|
|
|
|
user_notification |
208
|
|
|
|
|
|
|
create_circle |
209
|
|
|
|
|
|
|
]; |
210
|
|
|
|
|
|
|
has connected => ( |
211
|
|
|
|
|
|
|
is => 'ro', |
212
|
|
|
|
|
|
|
isa => 'Bool', |
213
|
|
|
|
|
|
|
traits => ['Bool'], |
214
|
|
|
|
|
|
|
default => 0, |
215
|
|
|
|
|
|
|
handles => {_set_connected => 'set', _unset_connected => 'unset'} |
216
|
|
|
|
|
|
|
); |
217
|
|
|
|
|
|
|
has redirect => ( |
218
|
|
|
|
|
|
|
is => 'ro', |
219
|
|
|
|
|
|
|
isa => 'Str', |
220
|
|
|
|
|
|
|
predicate => '_has_redirect', |
221
|
|
|
|
|
|
|
writer => '_set_redirect', |
222
|
|
|
|
|
|
|
clearer => '_reset_redirect' # XXX - Currently unused internally |
223
|
|
|
|
|
|
|
); |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# Auto connect |
226
|
|
|
|
|
|
|
sub BUILD { |
227
|
|
|
|
|
|
|
my ($s, $p) = @_; |
228
|
|
|
|
|
|
|
return if $p->{no_autoconnect}; |
229
|
|
|
|
|
|
|
$s->connect; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub connect { |
233
|
|
|
|
|
|
|
my $s = shift; |
234
|
|
|
|
|
|
|
$s->_unset_connected; |
235
|
|
|
|
|
|
|
$s->_set_handle( |
236
|
|
|
|
|
|
|
AnyEvent::Handle->new( |
237
|
|
|
|
|
|
|
connect => [$s->host, $s->port], |
238
|
|
|
|
|
|
|
on_connect => sub { |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# Get ready to read data from server |
241
|
|
|
|
|
|
|
$s->handle->push_read( |
242
|
|
|
|
|
|
|
'AnyEvent::MSN::Protocol' => sub { |
243
|
|
|
|
|
|
|
my ($cmd, $tid, @data) = @_; |
244
|
|
|
|
|
|
|
my $method = $s->can('_handle_packet_' . lc($cmd)); |
245
|
|
|
|
|
|
|
$method ||= sub { |
246
|
|
|
|
|
|
|
$s->_trigger_error( |
247
|
|
|
|
|
|
|
'Unhandled command type: ' . $cmd, |
248
|
|
|
|
|
|
|
0); |
249
|
|
|
|
|
|
|
}; |
250
|
|
|
|
|
|
|
if ($cmd =~ m[^(?:GCF|MSG|NFY|NOT|SDG|UBX|PUT)$]) |
251
|
|
|
|
|
|
|
{ # payload types |
252
|
|
|
|
|
|
|
$s->handle->unshift_read( |
253
|
|
|
|
|
|
|
chunk => $data[-1] // $tid, # GFC:0, MSG:2 |
254
|
|
|
|
|
|
|
sub { |
255
|
|
|
|
|
|
|
my ($_h, $_c) = @_; |
256
|
|
|
|
|
|
|
$s->$method( |
257
|
|
|
|
|
|
|
$tid, @data, |
258
|
|
|
|
|
|
|
$cmd =~ m[GCF] ? $s->_parse_xml($_c) |
259
|
|
|
|
|
|
|
: $cmd =~ m[(?:MSG|NFY|SDG)] ? |
260
|
|
|
|
|
|
|
AnyEvent::MSN::Protocol::__parse_msn_headers( |
261
|
|
|
|
|
|
|
$_c) |
262
|
|
|
|
|
|
|
: $_c |
263
|
|
|
|
|
|
|
); |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
elsif ($cmd =~ m[^\d+$]) { # Error! |
268
|
|
|
|
|
|
|
$s->_trigger_error( |
269
|
|
|
|
|
|
|
AnyEvent::MSN::Protocol::err2str($cmd, @data) |
270
|
|
|
|
|
|
|
); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
else { |
273
|
|
|
|
|
|
|
$s->$method($tid, @data); |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# Send version negotiation |
279
|
|
|
|
|
|
|
$s->send('VER %d %s CVR0', $s->tid, $s->protocol_version); |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Schedule first PNG in two mins |
282
|
|
|
|
|
|
|
$s->_set_ping_timer(AE::timer 120, |
283
|
|
|
|
|
|
|
180, sub { $s->send('PNG') }); |
284
|
|
|
|
|
|
|
}, |
285
|
|
|
|
|
|
|
on_connect_error => |
286
|
|
|
|
|
|
|
sub { shift; $s->_trigger_fatal_error(shift) }, |
287
|
|
|
|
|
|
|
on_error => sub { |
288
|
|
|
|
|
|
|
my $h = shift; |
289
|
|
|
|
|
|
|
$s->_trigger_fatal_error(reverse @_); |
290
|
|
|
|
|
|
|
$h->destroy; |
291
|
|
|
|
|
|
|
}, |
292
|
|
|
|
|
|
|
on_eof => sub { |
293
|
|
|
|
|
|
|
$_[0]->destroy; |
294
|
|
|
|
|
|
|
$s->cleanup('connection closed'); |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
) |
297
|
|
|
|
|
|
|
); |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# Commands from notification server |
301
|
|
|
|
|
|
|
sub _handle_packet_adl { |
302
|
|
|
|
|
|
|
my $s = shift; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# ACK for outgoing ADL |
305
|
|
|
|
|
|
|
# $s->send('BLP %d AL', $s->tid); |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub _handle_packet_chl { # Official client challenge |
309
|
|
|
|
|
|
|
my ($s, $tid, @data) = @_; |
310
|
|
|
|
|
|
|
my $data = |
311
|
|
|
|
|
|
|
AnyEvent::MSN::Protocol::CreateQRYHash($data[0], $s->product_id, |
312
|
|
|
|
|
|
|
$s->product_key); |
313
|
|
|
|
|
|
|
$s->send("QRY %d %s %d\r\n%s", |
314
|
|
|
|
|
|
|
$s->tid, $s->product_id, length($data), $data); |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sub _handle_packet_cvr { # Client version recommendation |
318
|
|
|
|
|
|
|
my ($s, $tid, $r, $min_a, $min_b, $url_dl, $url_info) = @_; |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# We don't do anything with this yet but... |
321
|
|
|
|
|
|
|
# The first parameter is a recommended version of |
322
|
|
|
|
|
|
|
# the client for you to use, or "1.0.0000" if your |
323
|
|
|
|
|
|
|
# client information is not recognised. |
324
|
|
|
|
|
|
|
# The second parameter is identical to the first. |
325
|
|
|
|
|
|
|
# The third parameter is the minimum version of the |
326
|
|
|
|
|
|
|
# client it's safe for you to use, or the current |
327
|
|
|
|
|
|
|
# version if your client information is not |
328
|
|
|
|
|
|
|
# recognised. |
329
|
|
|
|
|
|
|
# The fourth parameter is a URL you can download the |
330
|
|
|
|
|
|
|
# recommended version of the client from. |
331
|
|
|
|
|
|
|
# The fifth parameter is a URL the user can go to to |
332
|
|
|
|
|
|
|
# get more information about the client. |
333
|
|
|
|
|
|
|
$s->send('USR %d SSO I %s', $s->tid, $s->passport); |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub _handle_packet_gcf { # Get config |
337
|
|
|
|
|
|
|
my ($s, $tid, $len, $r) = @_; |
338
|
|
|
|
|
|
|
if ($tid == 0) { # probably Policy list |
339
|
|
|
|
|
|
|
$s->_set_policies($r->{Policy}); |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
#for (@{$s->policy('SHIELDS')->{config}{block}{regexp}{imtext}}) { |
342
|
|
|
|
|
|
|
# my $regex = MIME::Base64::decode_base64($_); |
343
|
|
|
|
|
|
|
# warn 'Blocking ' . qr[$regex]; |
344
|
|
|
|
|
|
|
#} |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
else { |
347
|
|
|
|
|
|
|
...; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
sub _handle_packet_msg { |
352
|
|
|
|
|
|
|
my ($s, $from, $about, $len, $head, $body) = @_; |
353
|
|
|
|
|
|
|
given ($head->{'Content-Type'}) { |
354
|
|
|
|
|
|
|
when (m[text/x-msmsgsprofile]) { |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# |
357
|
|
|
|
|
|
|
# http://msnpiki.msnfanatic.com/index.php/MSNP8:Messages#Profile_Messages |
358
|
|
|
|
|
|
|
# My profile message. Expect no body. |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
when (m[text/x-msmsgsinitialmdatanotification]) { # Expect no body |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
when (m[text/x-msmsgsoimnotification]) { |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
# Offline Message Waiting. |
365
|
|
|
|
|
|
|
# Expect no body |
366
|
|
|
|
|
|
|
# XXX - How do I request it? |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
when (m[text/x-msmsgsactivemailnotification]) { |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
#warn 'You\'ve got mail!/aol' |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
when (m[text/x-msmsgsinitialmdatanotification]) { |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
#warn 'You\'ve got mail!/aol' |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
default { $s->_trigger_error('Unknown message type: ' . $_) } |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub _handle_packet_nfy { |
381
|
|
|
|
|
|
|
my ($s, $type, $len, $headers, $data) = @_; |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=begin comment |
384
|
|
|
|
|
|
|
use Data::Printer; |
385
|
|
|
|
|
|
|
dd $type, $len, $headers, $data; |
386
|
|
|
|
|
|
|
dd $s->_parse_xml($data); |
387
|
|
|
|
|
|
|
=cut |
388
|
|
|
|
|
|
|
given ($headers->{Uri}) { |
389
|
|
|
|
|
|
|
when ('/user') { |
390
|
|
|
|
|
|
|
given ($type) { |
391
|
|
|
|
|
|
|
when ('PUT') { |
392
|
|
|
|
|
|
|
my $xml = $s->_parse_xml($data); |
393
|
|
|
|
|
|
|
if ((!defined $headers->{By}) |
394
|
|
|
|
|
|
|
&& $headers->{From} eq '1:' . $s->passport) |
395
|
|
|
|
|
|
|
{ # Without guid |
396
|
|
|
|
|
|
|
$s->set_status($s->status) |
397
|
|
|
|
|
|
|
; # Not fully logged in until sent |
398
|
|
|
|
|
|
|
$s->_set_connected(); |
399
|
|
|
|
|
|
|
$s->_trigger_connect; |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
else { |
402
|
|
|
|
|
|
|
$s->_trigger_user_notification($headers, $xml); |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
when ('DEL') { |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# Remove from list |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
default {...} |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
when ('/circle') { |
413
|
|
|
|
|
|
|
my $xml = $s->_parse_xml($data); |
414
|
|
|
|
|
|
|
$s->_trigger_create_circle($headers, $xml); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
default {...} |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
sub _handle_packet_not { my $s = shift; } |
420
|
|
|
|
|
|
|
sub _handle_packet_out { my $s = shift; } |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
sub _handle_packet_put { |
423
|
|
|
|
|
|
|
my $s = shift; |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# ACK for our PUT packets |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
sub _handle_packet_qng { |
429
|
|
|
|
|
|
|
my ($s, $next) = @_; |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# PONG in reply to our PNG |
432
|
|
|
|
|
|
|
$s->_set_ping_timer(AE::timer $next, $next, sub { $s->send('PNG') }); |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
sub _handle_packet_qry { |
436
|
|
|
|
|
|
|
my ($s, $tid) = @_; |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# |
439
|
|
|
|
|
|
|
my $token = $s->auth_token('contacts.msn.com') |
440
|
|
|
|
|
|
|
->{'wst:RequestedSecurityToken'}{'wsse:BinarySecurityToken'}{content}; |
441
|
|
|
|
|
|
|
$token =~ s/&/&/sg; |
442
|
|
|
|
|
|
|
$token =~ s/</</sg; |
443
|
|
|
|
|
|
|
$token =~ s/>/>/sg; |
444
|
|
|
|
|
|
|
$token =~ s/"/"/sg; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# Reply to good challenge. Expect no body. |
447
|
|
|
|
|
|
|
$s->_soap_request( |
448
|
|
|
|
|
|
|
'https://local-bay.contacts.msn.com/abservice/SharingService.asmx', |
449
|
|
|
|
|
|
|
{ 'content-type' => 'text/xml; charset=utf-8', |
450
|
|
|
|
|
|
|
SOAPAction => |
451
|
|
|
|
|
|
|
'"http://www.msn.com/webservices/AddressBook/FindMembership"' |
452
|
|
|
|
|
|
|
}, |
453
|
|
|
|
|
|
|
sprintf(<<'XML', $token), |
454
|
|
|
|
|
|
|
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> |
455
|
|
|
|
|
|
|
<soap:Header> |
456
|
|
|
|
|
|
|
<ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook"> |
457
|
|
|
|
|
|
|
<ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId> |
458
|
|
|
|
|
|
|
<IsMigration>false</IsMigration> |
459
|
|
|
|
|
|
|
<PartnerScenario>Initial</PartnerScenario> |
460
|
|
|
|
|
|
|
</ABApplicationHeader> |
461
|
|
|
|
|
|
|
<ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook"> |
462
|
|
|
|
|
|
|
<TicketToken>%s</TicketToken> |
463
|
|
|
|
|
|
|
<ManagedGroupRequest>false</ManagedGroupRequest> |
464
|
|
|
|
|
|
|
</ABAuthHeader> |
465
|
|
|
|
|
|
|
</soap:Header> |
466
|
|
|
|
|
|
|
<soap:Body> |
467
|
|
|
|
|
|
|
<FindMembership xmlns="http://www.msn.com/webservices/AddressBook"> |
468
|
|
|
|
|
|
|
<ServiceFilter> |
469
|
|
|
|
|
|
|
<Types> |
470
|
|
|
|
|
|
|
<Space></Space> |
471
|
|
|
|
|
|
|
<SocialNetwork></SocialNetwork> |
472
|
|
|
|
|
|
|
<Profile></Profile> |
473
|
|
|
|
|
|
|
<Invitation></Invitation> |
474
|
|
|
|
|
|
|
<Messenger></Messenger> |
475
|
|
|
|
|
|
|
</Types> |
476
|
|
|
|
|
|
|
</ServiceFilter> |
477
|
|
|
|
|
|
|
</FindMembership> |
478
|
|
|
|
|
|
|
</soap:Body> |
479
|
|
|
|
|
|
|
</soap:Envelope> |
480
|
|
|
|
|
|
|
XML |
481
|
|
|
|
|
|
|
sub { |
482
|
|
|
|
|
|
|
my $contacts = shift; |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# XXX - Do something with these contacts |
485
|
|
|
|
|
|
|
#... |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
); |
488
|
|
|
|
|
|
|
$s->_soap_request( |
489
|
|
|
|
|
|
|
'https://local-bay.contacts.msn.com/abservice/abservice.asmx', |
490
|
|
|
|
|
|
|
{ 'content-type' => 'text/xml; charset=utf-8', |
491
|
|
|
|
|
|
|
SOAPAction => |
492
|
|
|
|
|
|
|
'"http://www.msn.com/webservices/AddressBook/ABFindContactsPaged"' |
493
|
|
|
|
|
|
|
}, |
494
|
|
|
|
|
|
|
sprintf(<<'XML', $token), |
495
|
|
|
|
|
|
|
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> |
496
|
|
|
|
|
|
|
<soap:Header> |
497
|
|
|
|
|
|
|
<ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook"> |
498
|
|
|
|
|
|
|
<ApplicationId>3794391A-4816-4BAC-B34B-6EC7FB5046C6</ApplicationId> |
499
|
|
|
|
|
|
|
<IsMigration>false</IsMigration> |
500
|
|
|
|
|
|
|
<PartnerScenario>Initial</PartnerScenario> |
501
|
|
|
|
|
|
|
</ABApplicationHeader> |
502
|
|
|
|
|
|
|
<ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook"> |
503
|
|
|
|
|
|
|
<TicketToken>%s</TicketToken> |
504
|
|
|
|
|
|
|
<ManagedGroupRequest>false</ManagedGroupRequest> |
505
|
|
|
|
|
|
|
</ABAuthHeader> |
506
|
|
|
|
|
|
|
</soap:Header> |
507
|
|
|
|
|
|
|
<soap:Body> |
508
|
|
|
|
|
|
|
<ABFindall xmlns="http://www.msn.com/webservices/AddressBook"> |
509
|
|
|
|
|
|
|
<abID>00000000-0000-0000-0000-000000000000</abID> |
510
|
|
|
|
|
|
|
</ABFindall> |
511
|
|
|
|
|
|
|
<ABFindContactsPaged xmlns="http://www.msn.com/webservices/AddressBook"> |
512
|
|
|
|
|
|
|
<extendedContent>AB AllGroups CircleResult</extendedContent> |
513
|
|
|
|
|
|
|
<abView>MessengerClient8</abView> |
514
|
|
|
|
|
|
|
<filterOptions> |
515
|
|
|
|
|
|
|
<DeltasOnly>false</DeltasOnly> |
516
|
|
|
|
|
|
|
<ContactFilter> |
517
|
|
|
|
|
|
|
<IncludeShellContacts>true</IncludeShellContacts> |
518
|
|
|
|
|
|
|
<IncludeHiddenContacts>true</IncludeHiddenContacts> |
519
|
|
|
|
|
|
|
</ContactFilter> |
520
|
|
|
|
|
|
|
<LastChanged>0001-01-01T00:00:00.00-08:00</LastChanged> |
521
|
|
|
|
|
|
|
</filterOptions> |
522
|
|
|
|
|
|
|
<pageContext> |
523
|
|
|
|
|
|
|
<PageSize>1500</PageSize> |
524
|
|
|
|
|
|
|
<Direction>Forward</Direction> |
525
|
|
|
|
|
|
|
</pageContext> |
526
|
|
|
|
|
|
|
</ABFindContactsPaged> |
527
|
|
|
|
|
|
|
</soap:Body> |
528
|
|
|
|
|
|
|
</soap:Envelope> |
529
|
|
|
|
|
|
|
XML |
530
|
|
|
|
|
|
|
sub { |
531
|
|
|
|
|
|
|
my $contacts = shift; |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
# XXX - Do something with these contacts |
534
|
|
|
|
|
|
|
$s->_set_contacts($contacts); |
535
|
|
|
|
|
|
|
my $ticket |
536
|
|
|
|
|
|
|
= __html_unescape( |
537
|
|
|
|
|
|
|
$s->contacts->{'soap:Body'}{'ABFindContactsPagedResponse'} |
538
|
|
|
|
|
|
|
{'ABFindContactsPagedResult'}{'CircleResult'} |
539
|
|
|
|
|
|
|
{'CircleTicket'}); |
540
|
|
|
|
|
|
|
$s->send('USR %d SHA A %s', |
541
|
|
|
|
|
|
|
$s->tid, MIME::Base64::encode_base64($ticket, '')); |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# |
544
|
|
|
|
|
|
|
my $x = # XML modules get it wrong if we only have 1 buddy |
545
|
|
|
|
|
|
|
$s->contacts->{'soap:Body'}{'ABFindContactsPagedResponse'} |
546
|
|
|
|
|
|
|
{'ABFindContactsPagedResult'}{'Contacts'}{'Contact'}; |
547
|
|
|
|
|
|
|
$x = [$x] if ref $x ne 'ARRAY'; |
548
|
|
|
|
|
|
|
$s->add_temporary_contact(map { $_->{contactInfo}{passportName} } |
549
|
|
|
|
|
|
|
@$x); |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
); |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
sub _handle_packet_rml { |
555
|
|
|
|
|
|
|
my ($s, $tid, $ok) = @_; |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=begin comment |
558
|
|
|
|
|
|
|
use Data::Printer; |
559
|
|
|
|
|
|
|
dd @_; |
560
|
|
|
|
|
|
|
=cut |
561
|
|
|
|
|
|
|
...; |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
sub _handle_packet_sbs { |
565
|
|
|
|
|
|
|
my $s = shift; |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# No one seems to know what this is. Official client ignores it? |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
sub _handle_packet_sdg { |
571
|
|
|
|
|
|
|
my ($s, $tid, $size, $head, $body) = @_; |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
#dd [$head, $body]; |
574
|
|
|
|
|
|
|
given ($head->{'Message-Type'}) { |
575
|
|
|
|
|
|
|
when ('Text') { |
576
|
|
|
|
|
|
|
given ($head->{'Service-Channel'}) { |
577
|
|
|
|
|
|
|
$s->_trigger_im($head, $body) when 'IM/Online'; |
578
|
|
|
|
|
|
|
$s->_trigger_im($head, $body) when undef; |
579
|
|
|
|
|
|
|
warn 'Offline Msg!' when 'IM/Offline'; |
580
|
|
|
|
|
|
|
default { |
581
|
|
|
|
|
|
|
warn 'unknown IM!!!!!' |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
} |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
$s->_trigger_nudge($head) when 'Nudge'; |
586
|
|
|
|
|
|
|
when ('Wink') { warn 'Wink' } |
587
|
|
|
|
|
|
|
when ('CustomEmoticon') { warn 'Custom Emoticon' } |
588
|
|
|
|
|
|
|
when ('Control/Typing') { warn 'Typing!' } |
589
|
|
|
|
|
|
|
when ('Data') { |
590
|
|
|
|
|
|
|
my ($header, $packet, $footer); |
591
|
|
|
|
|
|
|
if ($head->{To} !~ m[{.+}]) { |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
# 0 1 2 3 4 5 |
594
|
|
|
|
|
|
|
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 |
595
|
|
|
|
|
|
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
596
|
|
|
|
|
|
|
# | SID | ID | Data Offset | Total Size |Length | Flags | AckID |AckUID | Ack Data Size |DATA.... |
597
|
|
|
|
|
|
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
598
|
|
|
|
|
|
|
# |
599
|
|
|
|
|
|
|
# The 48-byte binary header consists of 6 DWORDs and 3 QWORDS, which are all in little endian (little end first) order, where a DWORD is a 32-bit (4 byte) unsigned integer and QWORD is 64 bits (8 bytes). |
600
|
|
|
|
|
|
|
# |
601
|
|
|
|
|
|
|
# 1 0 DWORD SessionID The SessionID, which is zero when the Clients are negotiating about the session. |
602
|
|
|
|
|
|
|
# 2 4 DWORD Identifier The first message you receive from the other client is the BaseIndentifier, the other messages contains a number near the BaseIdentifier. |
603
|
|
|
|
|
|
|
# 3 8 QWORD Data offset Explained under Splitting big messages. Most often the messages are not split, and this value is 0. |
604
|
|
|
|
|
|
|
# 4 16 QWORD Total data size Case 1: The byte size of all data sent between the header and footer of all of the message parts. This is the same independent of how many pieces the message is split in. Case 2: If this is an acknowledgement, this field is a copy of the same field in the message acknowledged. Sending acknowledgements |
605
|
|
|
|
|
|
|
# 5 24 DWORD Message length The byte size of the data between the header and footer of this particular message. |
606
|
|
|
|
|
|
|
# 6 28 DWORD Flag Identifies the message type. See the flags section |
607
|
|
|
|
|
|
|
# 7 32 DWORD Acknowledged identifier In case the message is an acknowledgement, this is a copy of the Identifier of the acknowledged message. Else this is some random generated number. |
608
|
|
|
|
|
|
|
# 8 36 DWORD Acknowledged unique ID In case the message is an acknowledgement, this is a copy of the previous field of the acknowledged message. Else this is 0. |
609
|
|
|
|
|
|
|
# 9 40 QWORD Acknowledged data size In case the message is an acknowledgement, this is a copy of the Total data size field of the acknowledged message. Else this is 0. |
610
|
|
|
|
|
|
|
sub _quad { |
611
|
|
|
|
|
|
|
state $little//= unpack 'C', pack 'S', 1; |
612
|
|
|
|
|
|
|
my $str = shift; |
613
|
|
|
|
|
|
|
my $big; |
614
|
|
|
|
|
|
|
if (!eval { $big = unpack('Q', $str); 1; }) { |
615
|
|
|
|
|
|
|
my ($lo, $hi) = unpack 'LL', $str; |
616
|
|
|
|
|
|
|
($hi, $lo) = ($lo, $hi) if !$little; |
617
|
|
|
|
|
|
|
$big = $lo + $hi * (1 + ~0); |
618
|
|
|
|
|
|
|
if ($big + 1 == $big) { |
619
|
|
|
|
|
|
|
warn 'A-pprox-i-mate!'; |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
return $big; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
(my ($sessionid, $identifier, $offset, |
625
|
|
|
|
|
|
|
$total_size, $msg_len, $flag, |
626
|
|
|
|
|
|
|
$ack_id, $ack_uid, $ack_data_size |
627
|
|
|
|
|
|
|
), |
628
|
|
|
|
|
|
|
$packet |
629
|
|
|
|
|
|
|
) = unpack 'NNa8a8NNNNa8a*', $body; |
630
|
|
|
|
|
|
|
($packet, $footer) |
631
|
|
|
|
|
|
|
= unpack 'a' . (_quad($total_size)) . ' a', |
632
|
|
|
|
|
|
|
$packet; |
633
|
|
|
|
|
|
|
$header = {sessionid => $sessionid, |
634
|
|
|
|
|
|
|
identifier => $identifier, |
635
|
|
|
|
|
|
|
offset => _quad($offset), |
636
|
|
|
|
|
|
|
total_size => _quad($total_size), |
637
|
|
|
|
|
|
|
msg_len => $msg_len, |
638
|
|
|
|
|
|
|
flag => $flag, |
639
|
|
|
|
|
|
|
ack_id => $ack_id, |
640
|
|
|
|
|
|
|
ack_uid => $ack_uid, |
641
|
|
|
|
|
|
|
ack_data_size => _quad($ack_data_size) |
642
|
|
|
|
|
|
|
}; |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
else { |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
# 0 1 2 3 |
647
|
|
|
|
|
|
|
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
648
|
|
|
|
|
|
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
649
|
|
|
|
|
|
|
# |L|O|Len|Base ID|if L>8 then TLVs = read(L - 8) else skip .... |
650
|
|
|
|
|
|
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
651
|
|
|
|
|
|
|
# | if Len > 0 then Payload = (DH and D) else skip .... |
652
|
|
|
|
|
|
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
653
|
|
|
|
|
|
|
# |
654
|
|
|
|
|
|
|
# For all of them - (DWORD is a 32-bit, 4 byte, unsigned integer and QWORD is 64 bits, 8 bytes.) |
655
|
|
|
|
|
|
|
# |
656
|
|
|
|
|
|
|
# 1 BYTE HL Length of header. |
657
|
|
|
|
|
|
|
# 2 BYTE OP Operation code. 0: None, 2: Ack, 3: Init session. |
658
|
|
|
|
|
|
|
# 3 WORD ML Message length without header length. (but included the header's message length) |
659
|
|
|
|
|
|
|
# 4 DWORD BaseID Initially random (?) To get the next one add the payload length. |
660
|
|
|
|
|
|
|
# TLVs BYTE[HL-8] TLV Data TLV list consists of TLV-encoded pairs (type, length, value). A whole TLV list is padded with zeros to fit 4-byte boundary. TLVs: T=0x1(1) L=0xc(12): IPv6 address of sender/receiver. T=0x2(2) L=0x4(4): ACK identifier. |
661
|
|
|
|
|
|
|
# DH DHL Data Header |
662
|
|
|
|
|
|
|
# BYTE DHL: Data header length |
663
|
|
|
|
|
|
|
# BYTE TFCombination: 0x1=First, 0x4=Msn object (display picture, emoticon etc), 0x6=File transfer |
664
|
|
|
|
|
|
|
# WORD PackageNumber: Package number |
665
|
|
|
|
|
|
|
# DWORD SessionID: Session Identifier |
666
|
|
|
|
|
|
|
# BYTE[DHL-8] Data packets TLVs: if (DHL>8) then read bytes(DHL - 8). T=0x1(1) L=0x8(8): Data remaining. |
667
|
|
|
|
|
|
|
# D ML-DHL Data Packet SLP messsage or data packet |
668
|
|
|
|
|
|
|
# F DWORD Footer The footer. |
669
|
|
|
|
|
|
|
p $body; |
670
|
|
|
|
|
|
|
my ($hl, $op, $ml, $baseid, $etc) = unpack 'CCnNa*', $body; |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
#warn sprintf 'HL = %d', $hl; |
673
|
|
|
|
|
|
|
#warn sprintf 'OP = %d (%s)', $op, |
674
|
|
|
|
|
|
|
# ( $op == 0 ? 'None' |
675
|
|
|
|
|
|
|
# : $op == 2 ? 'Ack' |
676
|
|
|
|
|
|
|
# : $op == 3 ? 'Init' |
677
|
|
|
|
|
|
|
# : 'BROKEN' |
678
|
|
|
|
|
|
|
# ); |
679
|
|
|
|
|
|
|
#warn sprintf 'ML = %d', $ml; |
680
|
|
|
|
|
|
|
#warn sprintf 'BaseID = %s', $baseid; |
681
|
|
|
|
|
|
|
# |
682
|
|
|
|
|
|
|
my $_tlv_len = $hl - 8; |
683
|
|
|
|
|
|
|
$_tlv_len += $_tlv_len % 8; |
684
|
|
|
|
|
|
|
my ($tlv, $moar) = unpack "a$_tlv_len a*", $etc; |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
#warn sprintf 'TLV = %s', $tlv; |
687
|
|
|
|
|
|
|
sub _tlv { |
688
|
|
|
|
|
|
|
my ($t, $v, $m) = unpack 'CC/a', shift; |
689
|
|
|
|
|
|
|
{ shift // (), t => $t, v => $v, $m ? _tlv($m) : () } |
690
|
|
|
|
|
|
|
} |
691
|
|
|
|
|
|
|
my ($dhlen, $tf_combo, $pac, $ses, $XXX) |
692
|
|
|
|
|
|
|
= unpack 'CCnNa*', |
693
|
|
|
|
|
|
|
$moar; |
694
|
|
|
|
|
|
|
warn length($moar); |
695
|
|
|
|
|
|
|
($packet, $footer) = unpack 'a' . ($ml - $dhlen) . 'a*', $XXX |
696
|
|
|
|
|
|
|
if $XXX; |
697
|
|
|
|
|
|
|
$header = {tlv => ($tlv ? _tlv($tlv) : ()), |
698
|
|
|
|
|
|
|
header_len => $hl, |
699
|
|
|
|
|
|
|
operation => $op, |
700
|
|
|
|
|
|
|
( $op == 0 ? 'None' |
701
|
|
|
|
|
|
|
: $op == 2 ? 'Ack' |
702
|
|
|
|
|
|
|
: $op == 3 ? 'Init' |
703
|
|
|
|
|
|
|
: 'BROKEN' |
704
|
|
|
|
|
|
|
), |
705
|
|
|
|
|
|
|
base_id => $baseid, |
706
|
|
|
|
|
|
|
msg_len => $ml |
707
|
|
|
|
|
|
|
}; |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
# |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
#dd $header; |
713
|
|
|
|
|
|
|
#p($packet =~ m[^(.+?)\r\n(.+)\r\n\r\n(.)$]s); |
714
|
|
|
|
|
|
|
my ($p2p_action, $p2p_head, $p2p_body) |
715
|
|
|
|
|
|
|
= ($packet =~ m[^(.+?)\r\n(.+)\r\n\r\n(.)$]s); |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
#dd $head, $p2p_action, |
718
|
|
|
|
|
|
|
# AnyEvent::MSN::Protocol::__parse_msn_headers($p2p_head), |
719
|
|
|
|
|
|
|
# $p2p_body; |
720
|
|
|
|
|
|
|
#warn 'Data' |
721
|
|
|
|
|
|
|
# XXX - trigger a callback of some sort |
722
|
|
|
|
|
|
|
} |
723
|
|
|
|
|
|
|
when ('Signal/P2P') { warn 'P2P' } |
724
|
|
|
|
|
|
|
when ('Signal/ForceAbchSync') { } |
725
|
|
|
|
|
|
|
when ('Signal/CloseIMWindow') { } |
726
|
|
|
|
|
|
|
when ('Signal/MarkIMWindowRead') { } |
727
|
|
|
|
|
|
|
when ('Signal/Turn') { }; |
728
|
|
|
|
|
|
|
when ('Signal/AudioMeta') { } |
729
|
|
|
|
|
|
|
when ('Signal/AudioTunnel') { } |
730
|
|
|
|
|
|
|
default {...} |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
sub _handle_packet_usr { |
735
|
|
|
|
|
|
|
my ($s, $tid, $subtype, $_s, $policy, $nonce) = @_; |
736
|
|
|
|
|
|
|
if ($subtype eq 'OK') { |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
# Sent after we send ADL command. Lastcommand in the logon? |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
elsif ($subtype eq 'SSO') { |
741
|
|
|
|
|
|
|
my $x = 1; |
742
|
|
|
|
|
|
|
my @tokens = map { |
743
|
|
|
|
|
|
|
sprintf <<'TOKEN', $x++, $_->[0], $_->[1] } @{$s->SSOsites}; |
744
|
|
|
|
|
|
|
<wst:RequestSecurityToken Id="RST%d"> |
745
|
|
|
|
|
|
|
<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType> |
746
|
|
|
|
|
|
|
<wsp:AppliesTo> |
747
|
|
|
|
|
|
|
<wsa:EndpointReference> |
748
|
|
|
|
|
|
|
<wsa:Address>%s</wsa:Address> |
749
|
|
|
|
|
|
|
</wsa:EndpointReference> |
750
|
|
|
|
|
|
|
</wsp:AppliesTo> |
751
|
|
|
|
|
|
|
<wsse:PolicyReference URI="%s"></wsse:PolicyReference> |
752
|
|
|
|
|
|
|
</wst:RequestSecurityToken> |
753
|
|
|
|
|
|
|
TOKEN |
754
|
|
|
|
|
|
|
$s->_soap_request( |
755
|
|
|
|
|
|
|
($s->passport =~ m[\@msn.com$]i |
756
|
|
|
|
|
|
|
? |
757
|
|
|
|
|
|
|
'https://msnia.login.live.com/pp550/RST.srf' |
758
|
|
|
|
|
|
|
: 'https://login.live.com/RST.srf' |
759
|
|
|
|
|
|
|
), |
760
|
|
|
|
|
|
|
{}, # headers |
761
|
|
|
|
|
|
|
sprintf(<<'XML', $s->password, $s->passport, join '', @tokens), |
762
|
|
|
|
|
|
|
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc" xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust"> |
763
|
|
|
|
|
|
|
<Header> |
764
|
|
|
|
|
|
|
<wsse:Security> |
765
|
|
|
|
|
|
|
<wsse:UsernameToken Id="user"> |
766
|
|
|
|
|
|
|
<wsse:Password>%s</wsse:Password> |
767
|
|
|
|
|
|
|
<wsse:Username>%s</wsse:Username> |
768
|
|
|
|
|
|
|
</wsse:UsernameToken> |
769
|
|
|
|
|
|
|
</wsse:Security> |
770
|
|
|
|
|
|
|
<ps:AuthInfo Id="PPAuthInfo" xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL"> |
771
|
|
|
|
|
|
|
<ps:Cookies></ps:Cookies> |
772
|
|
|
|
|
|
|
<ps:UIVersion>1</ps:UIVersion> |
773
|
|
|
|
|
|
|
<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp> |
774
|
|
|
|
|
|
|
<ps:BinaryVersion>4</ps:BinaryVersion> |
775
|
|
|
|
|
|
|
<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams> |
776
|
|
|
|
|
|
|
</ps:AuthInfo> |
777
|
|
|
|
|
|
|
</Header> |
778
|
|
|
|
|
|
|
<Body> |
779
|
|
|
|
|
|
|
<ps:RequestMultipleSecurityTokens Id="RSTS" xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL"> |
780
|
|
|
|
|
|
|
%s </ps:RequestMultipleSecurityTokens> |
781
|
|
|
|
|
|
|
</Body> |
782
|
|
|
|
|
|
|
</Envelope> |
783
|
|
|
|
|
|
|
XML |
784
|
|
|
|
|
|
|
sub { |
785
|
|
|
|
|
|
|
my $d = shift; |
786
|
|
|
|
|
|
|
for my $token ( |
787
|
|
|
|
|
|
|
@{ $d->{'S:Body'} |
788
|
|
|
|
|
|
|
{'wst:RequestSecurityTokenResponseCollection'} |
789
|
|
|
|
|
|
|
{'wst:RequestSecurityTokenResponse'} |
790
|
|
|
|
|
|
|
} |
791
|
|
|
|
|
|
|
) |
792
|
|
|
|
|
|
|
{ $s->_add_auth_token( |
793
|
|
|
|
|
|
|
$token->{'wsp:AppliesTo'}{'wsa:EndpointReference'} |
794
|
|
|
|
|
|
|
{'wsa:Address'}, |
795
|
|
|
|
|
|
|
$token |
796
|
|
|
|
|
|
|
); |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
# |
800
|
|
|
|
|
|
|
if ($policy =~ m[MBI]) { |
801
|
|
|
|
|
|
|
my $token = $s->auth_token('messengerclear.live.com') |
802
|
|
|
|
|
|
|
; # or http://Passport.NET/tb |
803
|
|
|
|
|
|
|
my $token_ |
804
|
|
|
|
|
|
|
= __html_escape($token->{'wst:RequestedSecurityToken'} |
805
|
|
|
|
|
|
|
{'wsse:BinarySecurityToken'}{'content'}); |
806
|
|
|
|
|
|
|
$s->send('USR %d SSO S %s %s %s', |
807
|
|
|
|
|
|
|
$s->tid, |
808
|
|
|
|
|
|
|
$token->{'wst:RequestedSecurityToken'} |
809
|
|
|
|
|
|
|
{'wsse:BinarySecurityToken'}{'content'}, |
810
|
|
|
|
|
|
|
AnyEvent::MSN::Protocol::SSO( |
811
|
|
|
|
|
|
|
$nonce, |
812
|
|
|
|
|
|
|
$token->{'wst:RequestedProofToken'} |
813
|
|
|
|
|
|
|
{'wst:BinarySecret'} |
814
|
|
|
|
|
|
|
), |
815
|
|
|
|
|
|
|
$s->guid |
816
|
|
|
|
|
|
|
); |
817
|
|
|
|
|
|
|
} |
818
|
|
|
|
|
|
|
elsif ($policy =~ m[^\?]) { |
819
|
|
|
|
|
|
|
...; |
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
} |
822
|
|
|
|
|
|
|
); |
823
|
|
|
|
|
|
|
} |
824
|
|
|
|
|
|
|
elsif ($subtype eq 'OK') { |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
# XXX - logged in okay. What now? |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
else { |
829
|
|
|
|
|
|
|
...; |
830
|
|
|
|
|
|
|
} |
831
|
|
|
|
|
|
|
} |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
sub _handle_packet_ubx { # Buddy has changed something |
834
|
|
|
|
|
|
|
my ($s, $passport, $len, $payload) = @_; |
835
|
|
|
|
|
|
|
my $xml = $s->_parse_xml($payload); |
836
|
|
|
|
|
|
|
if ($len == 0 && $passport eq '1:' . $s->passport) { |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
else { |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
#dd $xml; |
841
|
|
|
|
|
|
|
my ($user) = ($passport =~ m[:(.+)$]); |
842
|
|
|
|
|
|
|
$s->_add_temporary_contact($user, $xml); |
843
|
|
|
|
|
|
|
} |
844
|
|
|
|
|
|
|
} |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
sub _handle_packet_uux { # ACK for UUX |
847
|
|
|
|
|
|
|
} |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
sub _handle_packet_ver { # Negotiated protocol version |
850
|
|
|
|
|
|
|
my ($s, $tid, $r) = @_; |
851
|
|
|
|
|
|
|
$s->_set_protocol_version($r); |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
# Send basic client info |
854
|
|
|
|
|
|
|
$s->send('CVR %d %s %s %s %s %s %s %s %s%s', |
855
|
|
|
|
|
|
|
$s->tid, |
856
|
|
|
|
|
|
|
$s->locale_id, |
857
|
|
|
|
|
|
|
$s->os_type, |
858
|
|
|
|
|
|
|
$s->os_ver, |
859
|
|
|
|
|
|
|
$s->arch, |
860
|
|
|
|
|
|
|
$s->client_name, |
861
|
|
|
|
|
|
|
$s->client_version, |
862
|
|
|
|
|
|
|
$s->client_string, |
863
|
|
|
|
|
|
|
$s->passport, |
864
|
|
|
|
|
|
|
(' ' . ($s->_has_redirect ? $s->redirect : ' 0')) |
865
|
|
|
|
|
|
|
); |
866
|
|
|
|
|
|
|
} |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
sub _handle_packet_xfr { # Transver to another switchboard |
869
|
|
|
|
|
|
|
my $s = shift; |
870
|
|
|
|
|
|
|
my ($tid, $type, $addr, $u, $d, $redirect) = @_; |
871
|
|
|
|
|
|
|
$s->send('OUT'); |
872
|
|
|
|
|
|
|
$s->handle->destroy; |
873
|
|
|
|
|
|
|
my ($host, $port) = ($addr =~ m[^(.+):(\d+)$]); |
874
|
|
|
|
|
|
|
$s->_set_host($host); |
875
|
|
|
|
|
|
|
$s->_set_port($port); |
876
|
|
|
|
|
|
|
$s->_set_redirect($redirect); |
877
|
|
|
|
|
|
|
$s->connect; |
878
|
|
|
|
|
|
|
} |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
# SOAP client |
881
|
|
|
|
|
|
|
has soap_requests => (isa => 'HashRef[AnyEvent::Util::guard]', |
882
|
|
|
|
|
|
|
traits => ['Hash'], |
883
|
|
|
|
|
|
|
handles => {_add_soap_request => 'set', |
884
|
|
|
|
|
|
|
_del_soap_request => 'delete', |
885
|
|
|
|
|
|
|
_clear_soap_requests => 'clear' |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
); |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
sub _soap_request { |
890
|
|
|
|
|
|
|
my ($s, $uri, $headers, $content, $cb) = @_; |
891
|
|
|
|
|
|
|
my %headers = ( |
892
|
|
|
|
|
|
|
'user-agent' => 'MSNPM 1.0', |
893
|
|
|
|
|
|
|
'content-type' => 'application/soap+xml; charset=utf-8; action=""', |
894
|
|
|
|
|
|
|
'Expect' => '100-continue', |
895
|
|
|
|
|
|
|
'connection' => 'Keep-Alive' |
896
|
|
|
|
|
|
|
); |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
#warn $content; |
899
|
|
|
|
|
|
|
@headers{keys %$headers} = values %$headers; |
900
|
|
|
|
|
|
|
$s->_add_soap_request( |
901
|
|
|
|
|
|
|
$uri, |
902
|
|
|
|
|
|
|
AnyEvent::HTTP::http_request( |
903
|
|
|
|
|
|
|
POST => $uri, |
904
|
|
|
|
|
|
|
headers => \%headers, |
905
|
|
|
|
|
|
|
timeout => 15, |
906
|
|
|
|
|
|
|
persistent => 1, |
907
|
|
|
|
|
|
|
body => $content, |
908
|
|
|
|
|
|
|
sub { |
909
|
|
|
|
|
|
|
my ($body, $hdr) = @_; |
910
|
|
|
|
|
|
|
my $xml = $s->_parse_xml($body); |
911
|
|
|
|
|
|
|
$s->_del_soap_request($uri); |
912
|
|
|
|
|
|
|
return $cb->($xml) |
913
|
|
|
|
|
|
|
if $hdr->{Status} =~ /^2/ |
914
|
|
|
|
|
|
|
&& !defined $xml->{'S:Fault'}; |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
#dd $hdr; |
917
|
|
|
|
|
|
|
#dd $xml; |
918
|
|
|
|
|
|
|
$s->_trigger_error( |
919
|
|
|
|
|
|
|
$xml->{'soap:Body'}{'soap:Fault'}{'soap:Reason'} |
920
|
|
|
|
|
|
|
{'soap:Text'}{'content'} |
921
|
|
|
|
|
|
|
// $xml->{'soap:Body'}{'soap:Fault'}{'faultstring'} |
922
|
|
|
|
|
|
|
// $hdr->{Reason}); |
923
|
|
|
|
|
|
|
} |
924
|
|
|
|
|
|
|
) |
925
|
|
|
|
|
|
|
); |
926
|
|
|
|
|
|
|
} |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
# Methods exposed publicly |
929
|
|
|
|
|
|
|
sub disconnect { # cleanly disconnect from switchboard |
930
|
|
|
|
|
|
|
my $s = shift; |
931
|
|
|
|
|
|
|
$s->send('OUT'); |
932
|
|
|
|
|
|
|
$s->handle->on_drain( |
933
|
|
|
|
|
|
|
sub { |
934
|
|
|
|
|
|
|
$s->handle->destroy; |
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
); |
937
|
|
|
|
|
|
|
$s->_clear_redirect; # Start from scratch next time |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
sub send_message { |
941
|
|
|
|
|
|
|
my ($s, $to, $msg, $format) = @_; |
942
|
|
|
|
|
|
|
$to = '1:' . $to if $to !~ m[^\d+:]; |
943
|
|
|
|
|
|
|
$format //= 'FN=Segoe%20UI; EF=; CO=0; CS=1; PF=0'; |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
# FN: Font name (url safe) |
946
|
|
|
|
|
|
|
# EF: String containing... |
947
|
|
|
|
|
|
|
# - B for Bold |
948
|
|
|
|
|
|
|
# - U for Underline |
949
|
|
|
|
|
|
|
# - I for Italics |
950
|
|
|
|
|
|
|
#ã- S for Strikethrough |
951
|
|
|
|
|
|
|
# CO: Color (hex without #) |
952
|
|
|
|
|
|
|
my $data |
953
|
|
|
|
|
|
|
= sprintf |
954
|
|
|
|
|
|
|
qq[Routing: 1.0\r\nTo: %s\r\nFrom: 1:%s;epid=%s\r\n\r\nReliability: 1.0\r\n\r\nMessaging: 2.0\r\nMessage-Type: Text\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Length: %d\r\nX-MMS-IM-Format: %s\r\n\r\n%s], |
955
|
|
|
|
|
|
|
$to, $s->passport, $s->guid, length($msg), $format, $msg; |
956
|
|
|
|
|
|
|
$s->send(qq'SDG 0 %d\r\n%s', length($data), $data); |
957
|
|
|
|
|
|
|
} |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
sub nudge { |
960
|
|
|
|
|
|
|
my ($s, $to) = @_; |
961
|
|
|
|
|
|
|
$to = '1:' . $to if $to !~ m[^\d+:]; |
962
|
|
|
|
|
|
|
my $data |
963
|
|
|
|
|
|
|
= sprintf |
964
|
|
|
|
|
|
|
qq[Routing: 1.0\r\nTo: %s\r\nFrom: 1:%s;epid=%s\r\n\r\nReliability: 1.0\r\n\r\nMessaging: 2.0\r\nMessage-Type: Nudge\r\nService-Channel: IM/Online\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Length: 0\r\n\r\n], |
965
|
|
|
|
|
|
|
$to, $s->passport, $s->guid; |
966
|
|
|
|
|
|
|
$s->send("SDG 0 %d\r\n%s", length($data), $data); |
967
|
|
|
|
|
|
|
} |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
sub add_contact { |
970
|
|
|
|
|
|
|
my ($s, $contact) = @_; |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
# |
973
|
|
|
|
|
|
|
my $token = $s->auth_token('contacts.msn.com') |
974
|
|
|
|
|
|
|
->{'wst:RequestedSecurityToken'}{'wsse:BinarySecurityToken'}{content}; |
975
|
|
|
|
|
|
|
$token =~ s/&/&/sg; |
976
|
|
|
|
|
|
|
$token =~ s/</</sg; |
977
|
|
|
|
|
|
|
$token =~ s/>/>/sg; |
978
|
|
|
|
|
|
|
$token =~ s/"/"/sg; |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
# |
981
|
|
|
|
|
|
|
$s->_soap_request( |
982
|
|
|
|
|
|
|
'https://local-bay.contacts.msn.com/abservice/abservice.asmx', |
983
|
|
|
|
|
|
|
{ 'content-type' => 'text/xml; charset=utf-8', |
984
|
|
|
|
|
|
|
SOAPAction => |
985
|
|
|
|
|
|
|
'"http://www.msn.com/webservices/AddressBook/ABContactAdd"' |
986
|
|
|
|
|
|
|
}, |
987
|
|
|
|
|
|
|
sprintf(<<'XML', $token, $contact), |
988
|
|
|
|
|
|
|
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" |
989
|
|
|
|
|
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
990
|
|
|
|
|
|
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
991
|
|
|
|
|
|
|
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"> |
992
|
|
|
|
|
|
|
<soap:Header> |
993
|
|
|
|
|
|
|
<ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook"> |
994
|
|
|
|
|
|
|
<ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId> |
995
|
|
|
|
|
|
|
<IsMigration>false</IsMigration> |
996
|
|
|
|
|
|
|
<PartnerScenario>ContactSave</PartnerScenario> |
997
|
|
|
|
|
|
|
</ABApplicationHeader> |
998
|
|
|
|
|
|
|
<ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook"> |
999
|
|
|
|
|
|
|
<TicketToken>%s</TicketToken> |
1000
|
|
|
|
|
|
|
<ManagedGroupRequest>false</ManagedGroupRequest> |
1001
|
|
|
|
|
|
|
</ABAuthHeader> |
1002
|
|
|
|
|
|
|
</soap:Header> |
1003
|
|
|
|
|
|
|
<soap:Body> |
1004
|
|
|
|
|
|
|
<ABContactAdd xmlns="http://www.msn.com/webservices/AddressBook"> |
1005
|
|
|
|
|
|
|
<abId> |
1006
|
|
|
|
|
|
|
00000000-0000-0000-0000-000000000000 |
1007
|
|
|
|
|
|
|
</abId> |
1008
|
|
|
|
|
|
|
<contacts> |
1009
|
|
|
|
|
|
|
<Contact xmlns="http://www.msn.com/webservices/AddressBook"> |
1010
|
|
|
|
|
|
|
<contactInfo> |
1011
|
|
|
|
|
|
|
<contactType>LivePending</contactType> |
1012
|
|
|
|
|
|
|
<passportName>%s</passportName> |
1013
|
|
|
|
|
|
|
<isMessengerUser>true</isMessengerUser> |
1014
|
|
|
|
|
|
|
<MessengerMemberInfo> |
1015
|
|
|
|
|
|
|
<DisplayName>minimum clorpvfgt</DisplayName> |
1016
|
|
|
|
|
|
|
</MessengerMemberInfo> |
1017
|
|
|
|
|
|
|
</contactInfo> |
1018
|
|
|
|
|
|
|
</Contact> |
1019
|
|
|
|
|
|
|
</contacts> |
1020
|
|
|
|
|
|
|
<options> |
1021
|
|
|
|
|
|
|
<EnableAllowListManagement> |
1022
|
|
|
|
|
|
|
true |
1023
|
|
|
|
|
|
|
</EnableAllowListManagement> |
1024
|
|
|
|
|
|
|
</options> |
1025
|
|
|
|
|
|
|
</ABContactAdd> |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
</soap:Body> |
1028
|
|
|
|
|
|
|
</soap:Envelope> |
1029
|
|
|
|
|
|
|
XML |
1030
|
|
|
|
|
|
|
sub { |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
#dd @_; |
1033
|
|
|
|
|
|
|
$s->add_temporary_contact($contact); |
1034
|
|
|
|
|
|
|
} |
1035
|
|
|
|
|
|
|
); |
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
sub remove_contact { |
1039
|
|
|
|
|
|
|
my ($s, $contact) = @_; |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
# |
1042
|
|
|
|
|
|
|
my $token = $s->auth_token('contacts.msn.com') |
1043
|
|
|
|
|
|
|
->{'wst:RequestedSecurityToken'}{'wsse:BinarySecurityToken'}{content}; |
1044
|
|
|
|
|
|
|
$token =~ s/&/&/sg; |
1045
|
|
|
|
|
|
|
$token =~ s/</</sg; |
1046
|
|
|
|
|
|
|
$token =~ s/>/>/sg; |
1047
|
|
|
|
|
|
|
$token =~ s/"/"/sg; |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
# |
1050
|
|
|
|
|
|
|
$s->_soap_request( |
1051
|
|
|
|
|
|
|
'https://contacts.msn.com/abservice/abservice.asmx', |
1052
|
|
|
|
|
|
|
{'content-type' => 'text/xml; charset=utf-8', |
1053
|
|
|
|
|
|
|
SOAPAction => |
1054
|
|
|
|
|
|
|
'"http://www.msn.com/webservices/AddressBook/ABContactDelete"' |
1055
|
|
|
|
|
|
|
}, |
1056
|
|
|
|
|
|
|
sprintf(<<'XML', $token, $contact), |
1057
|
|
|
|
|
|
|
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" |
1058
|
|
|
|
|
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
1059
|
|
|
|
|
|
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
1060
|
|
|
|
|
|
|
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"> |
1061
|
|
|
|
|
|
|
<soap:Header> |
1062
|
|
|
|
|
|
|
<ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook"> |
1063
|
|
|
|
|
|
|
<ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId> |
1064
|
|
|
|
|
|
|
<IsMigration>false</IsMigration> |
1065
|
|
|
|
|
|
|
<PartnerScenario>ContactSave</PartnerScenario> |
1066
|
|
|
|
|
|
|
</ABApplicationHeader> |
1067
|
|
|
|
|
|
|
<ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook"> |
1068
|
|
|
|
|
|
|
<TicketToken>%s</TicketToken> |
1069
|
|
|
|
|
|
|
<ManagedGroupRequest>false</ManagedGroupRequest> |
1070
|
|
|
|
|
|
|
</ABAuthHeader> |
1071
|
|
|
|
|
|
|
</soap:Header> |
1072
|
|
|
|
|
|
|
<soap:Body> |
1073
|
|
|
|
|
|
|
<ABContactAdd xmlns="http://www.msn.com/webservices/AddressBook"> |
1074
|
|
|
|
|
|
|
<abId> |
1075
|
|
|
|
|
|
|
00000000-0000-0000-0000-000000000000 |
1076
|
|
|
|
|
|
|
</abId> |
1077
|
|
|
|
|
|
|
<contacts> |
1078
|
|
|
|
|
|
|
<Contact xmlns="http://www.msn.com/webservices/AddressBook"> |
1079
|
|
|
|
|
|
|
<contactInfo> |
1080
|
|
|
|
|
|
|
<contactType>LivePending</contactType> |
1081
|
|
|
|
|
|
|
<passportName>%s</passportName> |
1082
|
|
|
|
|
|
|
<isMessengerUser>true</isMessengerUser> |
1083
|
|
|
|
|
|
|
<MessengerMemberInfo> |
1084
|
|
|
|
|
|
|
<DisplayName>minimum clorpvfgt</DisplayName> |
1085
|
|
|
|
|
|
|
</MessengerMemberInfo> |
1086
|
|
|
|
|
|
|
</contactInfo> |
1087
|
|
|
|
|
|
|
</Contact> |
1088
|
|
|
|
|
|
|
</contacts> |
1089
|
|
|
|
|
|
|
<options> |
1090
|
|
|
|
|
|
|
<EnableAllowListManagement> |
1091
|
|
|
|
|
|
|
true |
1092
|
|
|
|
|
|
|
</EnableAllowListManagement> |
1093
|
|
|
|
|
|
|
</options> |
1094
|
|
|
|
|
|
|
</ABContactAdd> |
1095
|
|
|
|
|
|
|
</soap:Body> |
1096
|
|
|
|
|
|
|
</soap:Envelope> |
1097
|
|
|
|
|
|
|
XML |
1098
|
|
|
|
|
|
|
sub { |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
#dd @_; |
1101
|
|
|
|
|
|
|
$s->remove_temporary_contact($contact); |
1102
|
|
|
|
|
|
|
...; |
1103
|
|
|
|
|
|
|
} |
1104
|
|
|
|
|
|
|
); |
1105
|
|
|
|
|
|
|
} |
1106
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
# Remove a contact: |
1108
|
|
|
|
|
|
|
# RML 12 112\r\n |
1109
|
|
|
|
|
|
|
# <ml><d n="penilecolada.com"><c n="junk" t="1"><s l="3" n="IM" /><s l="3" n="PE" /><s l="3" n="PF"/></c></d></ml> |
1110
|
|
|
|
|
|
|
sub add_temporary_contact { |
1111
|
|
|
|
|
|
|
my $s = shift; |
1112
|
|
|
|
|
|
|
my %contacts; |
1113
|
|
|
|
|
|
|
for my $contact (@_) { |
1114
|
|
|
|
|
|
|
my ($user, $domain) = split /\@/, $contact, 2; |
1115
|
|
|
|
|
|
|
push @{$contacts{$domain}}, $user; |
1116
|
|
|
|
|
|
|
} |
1117
|
|
|
|
|
|
|
my $data = sprintf '<ml%s>%s</ml>', ($s->connected ? '' : ' l="1"'), |
1118
|
|
|
|
|
|
|
join '', map { |
1119
|
|
|
|
|
|
|
sprintf '<d n="%s">%s</d>', $_, join '', map { |
1120
|
|
|
|
|
|
|
sprintf '<c n="%s" t="1">%s</c>', $_, join '', |
1121
|
|
|
|
|
|
|
map {"<s l='3' n='$_' />"} |
1122
|
|
|
|
|
|
|
qw[IM PE PF] |
1123
|
|
|
|
|
|
|
} sort @{$contacts{$_}} |
1124
|
|
|
|
|
|
|
} sort keys %contacts; |
1125
|
|
|
|
|
|
|
my $tid = $s->tid; |
1126
|
|
|
|
|
|
|
$s->send("ADL %d %d\r\n%s", $tid, length($data), $data); |
1127
|
|
|
|
|
|
|
$tid; |
1128
|
|
|
|
|
|
|
} |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
sub remove_buddy { |
1131
|
|
|
|
|
|
|
my $s = shift; |
1132
|
|
|
|
|
|
|
my $data = sprintf <<'', reverse split '@', shift, 2; |
1133
|
|
|
|
|
|
|
<ml> |
1134
|
|
|
|
|
|
|
<d n="%s"> |
1135
|
|
|
|
|
|
|
<c n="%s" t="1"> |
1136
|
|
|
|
|
|
|
<s l="3" n="IM" /> |
1137
|
|
|
|
|
|
|
<s l="3" n="PE" /> |
1138
|
|
|
|
|
|
|
<s l="3" n="PF" /> |
1139
|
|
|
|
|
|
|
</c> |
1140
|
|
|
|
|
|
|
</d> |
1141
|
|
|
|
|
|
|
</ml> |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
my $tid = $s->tid; |
1144
|
|
|
|
|
|
|
$s->send("RML %d %d\r\n%s", $tid, length($data), $data); |
1145
|
|
|
|
|
|
|
$tid; |
1146
|
|
|
|
|
|
|
} |
1147
|
|
|
|
|
|
|
after set_status => sub { |
1148
|
|
|
|
|
|
|
my ($s, $status) = @_; |
1149
|
|
|
|
|
|
|
my $body = sprintf '<user>' . '<s n="PE"> |
1150
|
|
|
|
|
|
|
<UserTileLocation>0</UserTileLocation><FriendlyName>%s</FriendlyName><PSM>%s</PSM><RUM></RUM><RLT>0</RLT></s>' |
1151
|
|
|
|
|
|
|
. '<s n="IM"><Status>%s</Status><CurrentMedia></CurrentMedia></s>' |
1152
|
|
|
|
|
|
|
. '<sep n="PD"><ClientType>1</ClientType><EpName>%s</EpName><Idle>false</Idle><State>%s</State></sep>' |
1153
|
|
|
|
|
|
|
. '<sep n="PE" epid="%s"><VER>MSNMSGR:15.4.3508.1109</VER><TYP>1</TYP><Capabilities>2952790016:557056</Capabilities></sep>' |
1154
|
|
|
|
|
|
|
. '<sep n="IM"><Capabilities>2953838624:132096</Capabilities></sep>' |
1155
|
|
|
|
|
|
|
. '</user>', __html_escape($s->friendly_name), |
1156
|
|
|
|
|
|
|
__html_escape($s->personal_message), |
1157
|
|
|
|
|
|
|
$status, |
1158
|
|
|
|
|
|
|
__html_escape($s->location), $status, $s->guid; |
1159
|
|
|
|
|
|
|
my $out |
1160
|
|
|
|
|
|
|
= sprintf |
1161
|
|
|
|
|
|
|
qq[To: 1:%s\r\nRouting: 1.0\r\nFrom: 1:%s;epid=%s\r\n\r\nStream: 1\r\nFlags: ACK\r\nReliability: 1.0\r\n\r\nContent-Length: %d\r\nContent-Type: application/user+xml\r\nPublication: 1.0\r\nUri: /user\r\n\r\n%s], |
1162
|
|
|
|
|
|
|
$s->passport, |
1163
|
|
|
|
|
|
|
$s->passport, $s->guid, length($body), $body; |
1164
|
|
|
|
|
|
|
$s->send("PUT %d %d\r\n%s", $s->tid, length($out), $out); |
1165
|
|
|
|
|
|
|
}; |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
# Testing/Incomplete stuff |
1168
|
|
|
|
|
|
|
sub create_group_chat { |
1169
|
|
|
|
|
|
|
my $s = shift; |
1170
|
|
|
|
|
|
|
my $body = ''; # For now. |
1171
|
|
|
|
|
|
|
my $out |
1172
|
|
|
|
|
|
|
= sprintf |
1173
|
|
|
|
|
|
|
qq[To: 10:00000000-0000-0000-0000-000000000000\@live.com\r\nRouting: 1.0\r\nFrom: 1:%s;epid=%s\r\n\r\nStream: 1\r\nFlags: ACK\r\nReliability: 1.0\r\n\r\nContent-Length: %d\r\nContent-Type: application/multiparty+xml\r\nPublication: 1.0\r\nUri: /circle\r\n\r\n%s], |
1174
|
|
|
|
|
|
|
$s->passport, $s->guid, length($body), $body; |
1175
|
|
|
|
|
|
|
$s->send("PUT %d %d\r\n%s", $s->tid, length($out), $out); |
1176
|
|
|
|
|
|
|
} |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
# Random private methods |
1179
|
|
|
|
|
|
|
sub _parse_xml { |
1180
|
|
|
|
|
|
|
my ($s, $data) = @_; |
1181
|
|
|
|
|
|
|
state $xml_twig //= XML::Twig->new(); |
1182
|
|
|
|
|
|
|
my $xml = {}; |
1183
|
|
|
|
|
|
|
use Carp; |
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
=begin comment Carp::confess('...') if ! length $data ; |
1186
|
|
|
|
|
|
|
=cut |
1187
|
|
|
|
|
|
|
try { |
1188
|
|
|
|
|
|
|
$xml_twig->parse($data); |
1189
|
|
|
|
|
|
|
$xml = $xml_twig->simplify(keyattr => [qw[type id value]]); |
1190
|
|
|
|
|
|
|
} |
1191
|
|
|
|
|
|
|
catch { $s->_trigger_fatal_error(qq[parsing XML: $_]) }; |
1192
|
|
|
|
|
|
|
$xml; |
1193
|
|
|
|
|
|
|
} |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
# Non-OOP utility functions |
1196
|
|
|
|
|
|
|
sub __html_escape { |
1197
|
|
|
|
|
|
|
my $x = shift; |
1198
|
|
|
|
|
|
|
$x =~ s[&][&]sg; |
1199
|
|
|
|
|
|
|
$x =~ s[<][<]sg; |
1200
|
|
|
|
|
|
|
$x =~ s[>][>]sg; |
1201
|
|
|
|
|
|
|
$x =~ s["]["]sg; |
1202
|
|
|
|
|
|
|
$x; |
1203
|
|
|
|
|
|
|
} |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
sub __html_unescape { |
1206
|
|
|
|
|
|
|
my $x = shift; |
1207
|
|
|
|
|
|
|
$x =~ s[<][<]sg; |
1208
|
|
|
|
|
|
|
$x =~ s[>][>]sg; |
1209
|
|
|
|
|
|
|
$x =~ s["]["]sg; |
1210
|
|
|
|
|
|
|
$x =~ s[&][&]sg; |
1211
|
|
|
|
|
|
|
$x; |
1212
|
|
|
|
|
|
|
} |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
# |
1215
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable(); |
1216
|
|
|
|
|
|
|
no Moose; |
1217
|
|
|
|
|
|
|
1; |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
=pod |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
=head1 NAME |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
AnyEvent::MSN - Simple, Less Annoying Client for Microsoft's Windows Live Messenger Network |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
=head1 Synopsis |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
use AnyEvent::MSN; |
1228
|
|
|
|
|
|
|
my $msn = AnyEvent::MSN->new( |
1229
|
|
|
|
|
|
|
passport => 'you@hotmail.com', |
1230
|
|
|
|
|
|
|
password => 'sekrit', |
1231
|
|
|
|
|
|
|
on_im => sub { # Simiple echo bot |
1232
|
|
|
|
|
|
|
my ($msn, $head, $body) = @_; |
1233
|
|
|
|
|
|
|
$msn->im($head->{From}, $body) |
1234
|
|
|
|
|
|
|
} |
1235
|
|
|
|
|
|
|
); |
1236
|
|
|
|
|
|
|
AnyEvent->condvar->recv; |
1237
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
=head1 Description |
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
TODO |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
=head1 Methods |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
Well, the public bits anyway... |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
=over |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
=item new |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
my $msn = AnyEvent::MSN->new(passport => 'you@hotmail.com', password => 'password'); |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
This constructs a new L<AnyEvent::MSN> object. Required parameters are: |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
=over |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
=item C<passport> |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
This is an email address. |
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
Microsoft calls them C<passport>s in some documentation, C<username> and plain |
1261
|
|
|
|
|
|
|
ol' C<address> in other places. For future versions of the API (think 1.0), |
1262
|
|
|
|
|
|
|
I'm leaning towards the least awkward: C<username>. Just... keep that in mind. |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
=item C<password> |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
It's... your... password. |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
=back |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
Optional parameters to C<new> include... |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
=over |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
=item C<status> |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
This will be used as your initial online status. Please see the section |
1277
|
|
|
|
|
|
|
entitled L<Online Status|/"Online Status"> for more information. |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=item C<friendly_name> |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
This sets the display or friendly name for the client. This is what your |
1282
|
|
|
|
|
|
|
friends see on their buddylists. |
1283
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
=item C<personal_message> |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
This is the short message typically shown below the friendly name. |
1287
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
=item C<no_autoconnect> |
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
Normally, L<AnyEvent::MSN-E<gt>new( ... )|/new> automatically initiates the |
1291
|
|
|
|
|
|
|
L<client login|/connect> stage. If this is set to a true value, that doesn't |
1292
|
|
|
|
|
|
|
happen and you'll need to call L<connect|/connect> yourself. |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
=item C<on_connect> |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
This is callback is triggered when we have completed the login stage but |
1297
|
|
|
|
|
|
|
before we set our initial status. |
1298
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
=item C<on_im> |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
This callback is triggered when we receive an instant message. It is passed |
1302
|
|
|
|
|
|
|
the raw headers (which contain a 'From' value) and the actual message. |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
=item C<on_nudge> |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
This callback is triggered when we recieve a nudge. The callback is passed the |
1307
|
|
|
|
|
|
|
raw headers (which contain a 'From' value). |
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
=item C<on_error> |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
This callback is triggered when we meet any sort of non-fatal error. This |
1312
|
|
|
|
|
|
|
callback is passed a texual message for display. |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
=item C<on_fatal_error> |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
This callback is triggered when we meet an error which prevents normal client |
1317
|
|
|
|
|
|
|
operations. This could be a major SOAP error or even an unexpected disconnect. |
1318
|
|
|
|
|
|
|
This callback is passed a textual message for display. |
1319
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
=item C<on_user_notification> |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
... |
1323
|
|
|
|
|
|
|
on_user_notification => sub { my ($s, $head, $presence) = @_; ... } |
1324
|
|
|
|
|
|
|
... |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
This callback is triggered when a contact updates their public information. |
1327
|
|
|
|
|
|
|
Simple Online/Offline status changes are included in this as well as friendly |
1328
|
|
|
|
|
|
|
name changes and current media (now playing) status. |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
=back |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
=item connect |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
Initiates the logon process. You should only need to call this if you passed |
1335
|
|
|
|
|
|
|
C<no_autoconnect =E<gt> 1> to L<the constructor|/new>. |
1336
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
=item im |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
$msn->send_message('buddy@hotmail.com', 'oh hai!'); |
1340
|
|
|
|
|
|
|
|
1341
|
|
|
|
|
|
|
This sends an instant message. |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
C<send_message( ... )> supports a third parameter, a string to indicate how |
1344
|
|
|
|
|
|
|
the message shoud be displayed. The default is |
1345
|
|
|
|
|
|
|
C<FN=Segoe%20UI; EF=; CO=0; CS=1; PF=0>. Uh, we break that down a little in |
1346
|
|
|
|
|
|
|
L<the notes|/"Text Format"> below. |
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
=item nudge |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
$msn->nudge('buddy@hotmail.com'); |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
This sends a nudge to the other person. It's called nudge in the protocol |
1353
|
|
|
|
|
|
|
itself and in pidgin but in the the official client it's called 'Attention' |
1354
|
|
|
|
|
|
|
and may (depending on the buddy's settings) make the IM window jiggle on |
1355
|
|
|
|
|
|
|
screen for a second. ...which, I suppose, won the contest for the most |
1356
|
|
|
|
|
|
|
annoying behaviour they could come up with. |
1357
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
=item add_contact |
1359
|
|
|
|
|
|
|
|
1360
|
|
|
|
|
|
|
$msn->add_contact('silas@live.com'); # Temporary |
1361
|
|
|
|
|
|
|
$msn->add_contact('mark@hotmail.com'); # Persistant |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
This adds a buddy to your temporary list of contacts. |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
'Add List' command, uses XML to identify each contact, and works as a payload |
1366
|
|
|
|
|
|
|
message. Each ADL command may contain up to 150 contacts (a payload of roughly |
1367
|
|
|
|
|
|
|
7500 bytes). The format of the payload looks like this: |
1368
|
|
|
|
|
|
|
<ml l="1"> |
1369
|
|
|
|
|
|
|
<d n="domain"> |
1370
|
|
|
|
|
|
|
<c n="email" l="3" t="1" /> |
1371
|
|
|
|
|
|
|
</d> |
1372
|
|
|
|
|
|
|
</ml> |
1373
|
|
|
|
|
|
|
Elements: |
1374
|
|
|
|
|
|
|
ml: the meaning of l is unknown (thought to mean initial listing due to the |
1375
|
|
|
|
|
|
|
fact that it is only sent in the initial ADL) |
1376
|
|
|
|
|
|
|
d (domain): contacts are grouped by domain, where n is the domain name (the |
1377
|
|
|
|
|
|
|
part after the @ symbol in the email address) |
1378
|
|
|
|
|
|
|
c (contact): n is the name or the part before the @ symbol in the email |
1379
|
|
|
|
|
|
|
address, l is the list bit flag (i.e. 1 for FL, 2 for AL, 4 for BL) and t |
1380
|
|
|
|
|
|
|
is the contact type (1 for a Passport, 4 for a mobile phone, other values |
1381
|
|
|
|
|
|
|
are still unknown) |
1382
|
|
|
|
|
|
|
Note: you can send all your contacts in just one ADL command by putting |
1383
|
|
|
|
|
|
|
multiple 'd' elements (with the sub-elements of course) for each contact |
1384
|
|
|
|
|
|
|
e.g.: |
1385
|
|
|
|
|
|
|
<ml l="1"> |
1386
|
|
|
|
|
|
|
<d n="domain1"> |
1387
|
|
|
|
|
|
|
<c n="email1" l="3" t="1" /> |
1388
|
|
|
|
|
|
|
</d> |
1389
|
|
|
|
|
|
|
<d n="domain2"> |
1390
|
|
|
|
|
|
|
<c n="email2" l="5" t="4" /> |
1391
|
|
|
|
|
|
|
</d> |
1392
|
|
|
|
|
|
|
</ml> |
1393
|
|
|
|
|
|
|
Sending ADL to the server: |
1394
|
|
|
|
|
|
|
>>> ADL (TrId) (PayloadLength)\r\n |
1395
|
|
|
|
|
|
|
Then send your payload: |
1396
|
|
|
|
|
|
|
>>> <ml l="1"><d n="domain"><c n="email" l="3" t="1" /></d></ml> |
1397
|
|
|
|
|
|
|
The payload must not contain any 'whitespace' characters (i.e. returns, tabs or spaces) between tags or at the beginning or end, or the server will reply with error 240 or 241. |
1398
|
|
|
|
|
|
|
The server responds to a successful ADL command with: |
1399
|
|
|
|
|
|
|
ADL (TrId) OK |
1400
|
|
|
|
|
|
|
Initial ADL listing |
1401
|
|
|
|
|
|
|
Once the client has retrieved the contact list with a new set of SOAP requests (see MSNP13:Contact_List), it will send the information about the contacts on the list to the server with an ADL command. In this ADL, the <ml> node often seems to contain the attribute l, set to 1. However, the client does not always appear to send this attribute in the official listing! |
1402
|
|
|
|
|
|
|
You must include everyone on your Forward List (FL), Allow List (AL) and Block List (BL). If you don't, anyone you fail to include will be removed from their respective lists. Also note that the official client does not include contacts on the RL and PL in the initial listing. In fact, if you send the RL and PL bits in the ADL, the server will reject your ADL command, and possibly disconnect you. |
1403
|
|
|
|
|
|
|
You MUST send your privacy settings (BLP command), then ADL and finally your display name (PRP command) in that order or the server will ignore your ADL. These are retrieved using the ABFindAll SOAP request. |
1404
|
|
|
|
|
|
|
After receiving ADL (TrId) OK, you must set your initial presence (CHG command). If you send CHG before ADL, the servers will not dispatch your presence to other clients. |
1405
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
=item remove_buddy |
1407
|
|
|
|
|
|
|
|
1408
|
|
|
|
|
|
|
$msn->remove_buddy('buddy@hotmail.com'); |
1409
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
The remove contacts from your lists. Note that you may only remove people from |
1411
|
|
|
|
|
|
|
the FL, AL and BL using this command (which makes sense, seeing as you can |
1412
|
|
|
|
|
|
|
also only add people to the FL, AL and BL with the L<add_contact|/add_contact> |
1413
|
|
|
|
|
|
|
command). Also note that the contact will not be removed from your server-side |
1414
|
|
|
|
|
|
|
address book - for this, you will have to use the ABContactDelete SOAP |
1415
|
|
|
|
|
|
|
request. ...which we don't support yet. |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
=back |
1418
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
=head1 Notes |
1420
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
This is where random stuff will go. The sorts of things which may make life |
1422
|
|
|
|
|
|
|
somewhat easier for you but are easily skipped. |
1423
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
=head2 Online Status |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
Your online status not only affects your appearance on other's buddylists, but |
1427
|
|
|
|
|
|
|
can change how your buddies are shown. |
1428
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
=over |
1430
|
|
|
|
|
|
|
|
1431
|
|
|
|
|
|
|
=item NLN |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
Make the client appear Online (after logging in) and send and receive |
1434
|
|
|
|
|
|
|
notifications about buddies. |
1435
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
This is the default. |
1437
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
=item FLN |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
Make the client Offline. If the client is already online, offline |
1441
|
|
|
|
|
|
|
notifications will be sent to users on the RL. No message activity is allowed. |
1442
|
|
|
|
|
|
|
In this state, the client can only synchronize the lists as described above. |
1443
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
=item HDN |
1445
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
Make the client appear Hidden/Invisible. If the client is already |
1447
|
|
|
|
|
|
|
online, offline notifications will be sent to users on the RL. The client will |
1448
|
|
|
|
|
|
|
appear as Offline to others but can receive online/offline notifications from |
1449
|
|
|
|
|
|
|
other users, and can also synchronize the lists. Clients cannot receive any |
1450
|
|
|
|
|
|
|
instant messages in this state. |
1451
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
=item BSY |
1453
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
Make the client appear Busy. This is a sub-state of NLN. |
1455
|
|
|
|
|
|
|
|
1456
|
|
|
|
|
|
|
=item IDL |
1457
|
|
|
|
|
|
|
|
1458
|
|
|
|
|
|
|
Make the client appear Idle. This is a sub-state of NLN. |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
=item BRB |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
Make the client say they'll Be Right Back. This is a sub-state of NLN. |
1463
|
|
|
|
|
|
|
|
1464
|
|
|
|
|
|
|
=item AWY |
1465
|
|
|
|
|
|
|
|
1466
|
|
|
|
|
|
|
Make the client appear to be Away from their computer. This is a sub-state of |
1467
|
|
|
|
|
|
|
NLN. |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
=item PHN |
1470
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
Makes the client appear to be on the Phone. This is a sub-state of NLN. |
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
=item LUN |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
Makes the client appear to be out to Lunch. This is a sub-state of NLN. |
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
=back |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
=back |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
=head1 Notes |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
Get by with a little help... |
1484
|
|
|
|
|
|
|
|
1485
|
|
|
|
|
|
|
=head2 Text Format |
1486
|
|
|
|
|
|
|
|
1487
|
|
|
|
|
|
|
Messages sent and recieved may contain a special parameter defining how the |
1488
|
|
|
|
|
|
|
message should be displayed. The message format specifies the font (FN), |
1489
|
|
|
|
|
|
|
effect (EF), color (CO), character set (CS) and pitch and family (PF) used for |
1490
|
|
|
|
|
|
|
rendering the text message. The value of the Format element is a string of the |
1491
|
|
|
|
|
|
|
following key/value pairs: |
1492
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
=for url http://msdn.microsoft.com/en-us/library/bb969558(v=office.12).aspx |
1494
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
=over |
1496
|
|
|
|
|
|
|
|
1497
|
|
|
|
|
|
|
=item FN |
1498
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
Specifies a font name. The font name must be URL-encoded. For example, to have |
1500
|
|
|
|
|
|
|
a font of "MS Sans Serif", you would have to specify C<FN=MS%20Sans%20Serif>. |
1501
|
|
|
|
|
|
|
Font names are not case-sensitive and only spaces should be URL-encoded. |
1502
|
|
|
|
|
|
|
URL-encoding other characters such as numbers and letters cause unpredictable |
1503
|
|
|
|
|
|
|
results in other clients. |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
According to MS, if the receiving client does not have the specified font, it |
1506
|
|
|
|
|
|
|
should make judgment based on the PF and CS parameters. Basically, the client |
1507
|
|
|
|
|
|
|
should select whichever available font supports the character set specified in |
1508
|
|
|
|
|
|
|
CS and is closest to the category specified in PF. If those parameters are not |
1509
|
|
|
|
|
|
|
present, the client should just use a default font. |
1510
|
|
|
|
|
|
|
|
1511
|
|
|
|
|
|
|
=item EF |
1512
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
Specifies optional style effects. Possible effects are bold, italic, |
1514
|
|
|
|
|
|
|
underline, and strikethrough. Each effect is referred to by its first letter. |
1515
|
|
|
|
|
|
|
For example, to make bold-italic text, include the parameter C<EF=IB> or |
1516
|
|
|
|
|
|
|
C<EF=BI>. The order does not matter. Any unknown effects are to be ignored. |
1517
|
|
|
|
|
|
|
If there are no effects, just leave the parameter value blank. |
1518
|
|
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
=item CO |
1520
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
Specifies a font color. The value of the CO field is a six-character |
1522
|
|
|
|
|
|
|
hex BGR (Note that this is I<blue-green-red>, the I<reverse> of the standard |
1523
|
|
|
|
|
|
|
RGB order seen in HTML) string. The first two characters represent a hex |
1524
|
|
|
|
|
|
|
number from C<00> to C<ff> for the intensity of blue, the second two are for |
1525
|
|
|
|
|
|
|
green, and the third two are for red. For example, to make a full red color, |
1526
|
|
|
|
|
|
|
send C<CO=0000ff>. |
1527
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
Again, this should be in BGR; the I<reverse> of the standard RGB order seen in |
1529
|
|
|
|
|
|
|
HTML. |
1530
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
=item CS |
1532
|
|
|
|
|
|
|
|
1533
|
|
|
|
|
|
|
Character sets are identified in the CS parameter with one or two hexadecimal |
1534
|
|
|
|
|
|
|
digits (leading zeros are dropped by the official client and are ignored if |
1535
|
|
|
|
|
|
|
present), representing the numerical value Windows uses for the character set. |
1536
|
|
|
|
|
|
|
The following table shows the full list of the predefined character sets that |
1537
|
|
|
|
|
|
|
are included with the Microsoft® Windows® operating system. |
1538
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
Val Description |
1540
|
|
|
|
|
|
|
------------------------------------------------------------------------- |
1541
|
|
|
|
|
|
|
00 ANSI characters |
1542
|
|
|
|
|
|
|
01 Font is chosen based solely on name and size. If the described font is |
1543
|
|
|
|
|
|
|
not available on the system, you should substitute another font. |
1544
|
|
|
|
|
|
|
02 Standard symbol set |
1545
|
|
|
|
|
|
|
4d Macintosh characters |
1546
|
|
|
|
|
|
|
80 Japanese shift-JIS characters |
1547
|
|
|
|
|
|
|
81 Korean characters (Wansung) |
1548
|
|
|
|
|
|
|
82 Korean characters (Johab) |
1549
|
|
|
|
|
|
|
86 Simplified Chinese characters (China) |
1550
|
|
|
|
|
|
|
88 Traditional Chinese characters (Taiwan) |
1551
|
|
|
|
|
|
|
a1 Greek characters |
1552
|
|
|
|
|
|
|
a2 Turkish characters |
1553
|
|
|
|
|
|
|
a3 Vietnamese characters |
1554
|
|
|
|
|
|
|
b1 Hebrew characters |
1555
|
|
|
|
|
|
|
b2 Arabic characters |
1556
|
|
|
|
|
|
|
ba Baltic characters |
1557
|
|
|
|
|
|
|
cc Cyrillic characters |
1558
|
|
|
|
|
|
|
de Thai characters |
1559
|
|
|
|
|
|
|
ee Sometimes called the "Central European" character set, this includes |
1560
|
|
|
|
|
|
|
diacritical marks for Eastern European countries |
1561
|
|
|
|
|
|
|
ff Depends on the codepage of the operating system |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
You should not assume that clients receiving your messages understand all |
1564
|
|
|
|
|
|
|
character sets. This character set is arbitrary, but it is advisable to make |
1565
|
|
|
|
|
|
|
it the one that causes the most characters to be displayed correctly. |
1566
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
=item PF |
1568
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
The PF family defines the category that the font specified in the FN parameter |
1570
|
|
|
|
|
|
|
falls into. This parameter is used by the receiving client if it does not have |
1571
|
|
|
|
|
|
|
the specified font installed. The value is a two-digit hexadecimal number. |
1572
|
|
|
|
|
|
|
If you're familiar with the Windows APIs, this value is the PitchAndFamily |
1573
|
|
|
|
|
|
|
value in RichEdit and LOGFONT. |
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
The first digit of the value represents the font family. Below is a list of |
1576
|
|
|
|
|
|
|
numbers for the first digit and the font families they represent. |
1577
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
First Digit Description |
1579
|
|
|
|
|
|
|
------------------------------------------------------------------------- |
1580
|
|
|
|
|
|
|
0_ Specifies a generic family name. This name is used when |
1581
|
|
|
|
|
|
|
information about a font does not exist or does not |
1582
|
|
|
|
|
|
|
matter. The default font is used. |
1583
|
|
|
|
|
|
|
1_ Specifies a proportional (variable-width) font with |
1584
|
|
|
|
|
|
|
serifs. An example is Times New Roman. |
1585
|
|
|
|
|
|
|
2_ Specifies a proportional (variable-width) font without |
1586
|
|
|
|
|
|
|
serifs. An example is Arial. |
1587
|
|
|
|
|
|
|
3_ Specifies a Monospace font with or without serifs. |
1588
|
|
|
|
|
|
|
Monospace fonts are usually modern; examples include Pica, |
1589
|
|
|
|
|
|
|
Elite, and Courier New. |
1590
|
|
|
|
|
|
|
4_ Specifies a font that is designed to look like |
1591
|
|
|
|
|
|
|
handwriting; examples include Script and Cursive. |
1592
|
|
|
|
|
|
|
5_ Specifies a novelty font. An example is Old English. |
1593
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
The second digit represents the pitch of the font â in other words, whether it |
1595
|
|
|
|
|
|
|
is monospace or variable-width. |
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
Second Digit Description |
1598
|
|
|
|
|
|
|
------------------------------------------------------------------------- |
1599
|
|
|
|
|
|
|
_0 Specifies a generic font pitch. This name is used when |
1600
|
|
|
|
|
|
|
information about a font does not exist or does not |
1601
|
|
|
|
|
|
|
matter. The default font pitch is used. |
1602
|
|
|
|
|
|
|
_1 Specifies a fixed-width (Monospace) font. Examples are |
1603
|
|
|
|
|
|
|
Courier New and Bitstream Vera Sans Mono. |
1604
|
|
|
|
|
|
|
_2 Specifies a variable-width (proportional) font. Examples |
1605
|
|
|
|
|
|
|
are Times New Roman and Arial. |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
Below are some PF values and example fonts that fit the category. |
1608
|
|
|
|
|
|
|
|
1609
|
|
|
|
|
|
|
Examples of PF Value Description |
1610
|
|
|
|
|
|
|
------------------------------------------------------------------------- |
1611
|
|
|
|
|
|
|
12 Times New Roman, MS Serif, Bitstream Vera Serif |
1612
|
|
|
|
|
|
|
22 Arial, Verdana, MS Sans Serif, Bitstream Vera Sans |
1613
|
|
|
|
|
|
|
31 Courier New, Courier |
1614
|
|
|
|
|
|
|
42 Comic Sans MS |
1615
|
|
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
=head1 TODO |
1617
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
These are things I have plans to do with L<AnyEvent::MSN> but haven't found |
1619
|
|
|
|
|
|
|
the time to complete them. If you'd like to help or have a suggestion for new |
1620
|
|
|
|
|
|
|
feature, see the project pages on |
1621
|
|
|
|
|
|
|
L<GitHub|http://github.com/sanko/anyevent-msn>. |
1622
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
=over |
1624
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
=item P2P Transfers |
1626
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
MSNP supports simple file transfers, handwritten IMs, voice chat, and even |
1628
|
|
|
|
|
|
|
webcam sessions through the P2P protocol. The protocol changed between MSNP18 |
1629
|
|
|
|
|
|
|
and MSNP21 and I'll need to implement both. ...I'll get to it eventually. |
1630
|
|
|
|
|
|
|
|
1631
|
|
|
|
|
|
|
=item Group Chat |
1632
|
|
|
|
|
|
|
|
1633
|
|
|
|
|
|
|
MSNP21 redefinied the switchboard concept including how group chat sessions |
1634
|
|
|
|
|
|
|
are initiated and handled. |
1635
|
|
|
|
|
|
|
|
1636
|
|
|
|
|
|
|
=item Internal State Cleanup |
1637
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
Things like the address book are very difficult to use because (for now) I |
1639
|
|
|
|
|
|
|
simply store the parsed XML Microsoft sends me. |
1640
|
|
|
|
|
|
|
|
1641
|
|
|
|
|
|
|
=item Correct Client Capabilities |
1642
|
|
|
|
|
|
|
|
1643
|
|
|
|
|
|
|
They (and a few other properties) are all hardcoded values taken from MSN 2011 |
1644
|
|
|
|
|
|
|
right now. |
1645
|
|
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
=back |
1647
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
=head1 See Also |
1649
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
=over |
1651
|
|
|
|
|
|
|
|
1652
|
|
|
|
|
|
|
=item L<Net::MSN|Net::MSN> |
1653
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
=item L<MSN::PersonalMessage|MSN::PersonalMessage> |
1655
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
=item L<POE::Component::Client::MSN|POE::Component::Client::MSN> |
1657
|
|
|
|
|
|
|
|
1658
|
|
|
|
|
|
|
=item L<Net::Msmgr::Session|Net::Msmgr::Session> |
1659
|
|
|
|
|
|
|
|
1660
|
|
|
|
|
|
|
=back |
1661
|
|
|
|
|
|
|
|
1662
|
|
|
|
|
|
|
=head1 Author |
1663
|
|
|
|
|
|
|
|
1664
|
|
|
|
|
|
|
Sanko Robinson <sanko@cpan.org> - http://sankorobinson.com/ |
1665
|
|
|
|
|
|
|
|
1666
|
|
|
|
|
|
|
CPAN ID: SANKO |
1667
|
|
|
|
|
|
|
|
1668
|
|
|
|
|
|
|
=head1 License and Legal |
1669
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
Copyright (C) 2011-2012 by Sanko Robinson <sanko@cpan.org> |
1671
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under |
1673
|
|
|
|
|
|
|
the terms of |
1674
|
|
|
|
|
|
|
L<The Artistic License 2.0|http://www.perlfoundation.org/artistic_license_2_0>. |
1675
|
|
|
|
|
|
|
See the F<LICENSE> file included with this distribution or |
1676
|
|
|
|
|
|
|
L<notes on the Artistic License 2.0|http://www.perlfoundation.org/artistic_2_0_notes> |
1677
|
|
|
|
|
|
|
for clarification. |
1678
|
|
|
|
|
|
|
|
1679
|
|
|
|
|
|
|
When separated from the distribution, all original POD documentation is |
1680
|
|
|
|
|
|
|
covered by the |
1681
|
|
|
|
|
|
|
L<Creative Commons Attribution-Share Alike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/us/legalcode>. |
1682
|
|
|
|
|
|
|
See the |
1683
|
|
|
|
|
|
|
L<clarification of the CCA-SA3.0|http://creativecommons.org/licenses/by-sa/3.0/us/>. |
1684
|
|
|
|
|
|
|
|
1685
|
|
|
|
|
|
|
Some protocol descriptions taken from text Copyright 2011, Microsoft. |
1686
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
Neither this module nor the L<Author|/Author> is affiliated with Microsoft. |
1688
|
|
|
|
|
|
|
|
1689
|
|
|
|
|
|
|
=cut |