root/trunk/lib/DJabberd/IQ.pm @ 64

Revision 64, 5.1 kB (checked in by bradfitz, 4 years ago)

this fixes iChat and probably Psi as well

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1package DJabberd::IQ;
2use strict;
3use base qw(DJabberd::Stanza);
4use fields (
5            'connection',   # Store the connection the IQ came in on so we can respond.  may be undef, as it's a weakref.
6            );
7
8# DO NOT OVERRIDE THIS
9sub process {
10    my DJabberd::IQ $self = shift;
11    my $conn = shift;
12
13    $self->{connection} = $conn;
14    Scalar::Util::weaken($self->{connection});
15
16    my $handler = {
17        'get-{jabber:iq:roster}query' => \&process_iq_getroster,
18        'get-{jabber:iq:auth}query' => \&process_iq_getauth,
19        'set-{jabber:iq:auth}query' => \&process_iq_setauth,
20    };
21
22    $conn->run_hook_chain(phase    => "c2s-iq",
23                          args     => [ $self ],
24                          fallback => sub {
25                              my $sig = $self->signature;
26                              my $meth = $handler->{$sig};
27                              unless ($meth) {
28                                  warn "Unknown IQ packet: $sig";
29                                  return;
30                              }
31                              $meth->($conn, $self);
32                          });
33}
34
35sub signature {
36    my $iq = shift;
37    my $fc = $iq->first_element;
38    # FIXME: should signature ever get called on a bogus IQ packet?
39    return $iq->type . "-" . ($fc ? $fc->element : "(BOGUS)");
40}
41
42sub send_result_nodes {
43}
44
45# caller must send well-formed XML (but we do the wrapping element)
46sub send_result_raw {
47    my DJabberd::IQ $self = shift;
48    my $raw = shift;
49
50    my $conn = $self->{connection}
51        or return;
52
53    my $id = $self->id;
54    my $to = $conn->bound_jid->as_string;
55    my $xml = qq{<iq to='$to' type='result' id='$id'>$raw</iq>};
56    warn "About to send IQ reply: $xml\n";
57    $conn->write(\$xml);
58}
59
60sub process_iq_getroster {
61    my ($conn, $iq) = @_;
62    $conn->set_requested_roster(1);
63
64    my $send_roster = sub {
65        my $roster = shift;
66        # TODO: walk roster and add presence subscriptions for everybody
67        $iq->send_result_raw($roster->as_xml);
68    };
69
70    $conn->run_hook_chain(phase => "RosterGet",
71                          methods => {
72                              set_roster => sub {
73                                  my ($self, $roster) = @_;
74                                  $send_roster->($roster);
75                              },
76                          },
77                          fallback => sub {
78                              $send_roster->(DJabberd::Roster->new()),
79                          });
80    return 1;
81}
82
83sub process_iq_getauth {
84    my ($conn, $iq) = @_;
85    # <iq type='get' id='gaimf46fbc1e'><query xmlns='jabber:iq:auth'><username>brad</username></query></iq>
86
87    my $child = $iq->query->first_element
88        or return;
89
90    return 0 unless $child->element eq "{jabber:iq:auth}username";
91
92    my $username = $child->first_child;
93    die "Element in username field?" if ref $username;
94
95    my $id = $iq->id;
96
97    $conn->write("<iq id='$id' type='result'><query xmlns='jabber:iq:auth'><username>$username</username><digest/><resource/></query></iq>");
98
99    return 1;
100}
101
102sub process_iq_setauth {
103    my ($conn, $iq) = @_;
104    # <iq type='set' id='gaimbb822399'><query xmlns='jabber:iq:auth'><username>brad</username><resource>work</resource><digest>ab2459dc7506d56247e2dc684f6e3b0a5951a808</digest></query></iq>
105    my $id = $iq->id;
106
107    my $query = $iq->query
108        or die;
109    my @children = $query->children;
110
111    my $get = sub {
112        my $lname = shift;
113        foreach my $c (@children) {
114            next unless ref $c && $c->element eq "{jabber:iq:auth}$lname";
115            my $text = $c->first_child;
116            return undef if ref $text;
117            return $text;
118        }
119        return undef;
120    };
121
122    my $username = $get->("username");
123    my $resource = $get->("resource");
124    my $digest   = $get->("digest");
125
126    return unless $username =~ /^\w+$/;
127
128    my $accept = sub {
129        $conn->{authed}   = 1;
130        $conn->{username} = $username;
131        $conn->{resource} = $resource;
132
133        # register
134        my $sname = $conn->server->name;
135        my $jid = DJabberd::JID->new("$username\@$sname/$resource");
136
137        $conn->server->register_jid($jid, $conn);
138        $conn->set_bound_jid($jid);
139
140        # FIXME: escape, or make $iq->send_good_result, or something
141        $conn->write(qq{<iq id='$id' type='result' />});
142        return;
143    };
144
145    my $reject = sub {
146        warn " BAD LOGIN!\n";
147        # FIXME: FAIL
148        return 1;
149    };
150
151    $conn->run_hook_chain(phase => "Auth",
152                          args  => [ { username => $username, resource => $resource, digest => $digest } ],
153                          methods => {
154                              accept => $accept,
155                              reject => $reject,
156                          },
157                          fallback => $reject);
158
159    return 1;  # signal that we've handled it
160}
161
162
163sub id {
164    return $_[0]->attr("{jabber:client}id");
165}
166
167sub type {
168    return $_[0]->attr("{jabber:client}type");
169}
170
171sub query {
172    my $self = shift;
173    my $child = $self->first_element
174        or return;
175    my $ele = $child->element
176        or return;
177    return undef unless $child->element =~ /\}query$/;
178    return $child;
179}
180
1811;
Note: See TracBrowser for help on using the browser.