Codebase list telepathy-idle / ee06836
Imported Upstream version 0.1.13 Sjoerd Simons 11 years ago
33 changed file(s) with 2020 addition(s) and 122 deletion(s). Raw diff Collapse all Expand all
0 commit 1d00bd95ce88c0c1d28eb1e5561547c2cecd30dc
1 Author: Will Thompson <will.thompson@collabora.co.uk>
2 Date: 2012-11-14 19:18:56 +0000
3
4 Version 0.1.13
5
6 commit b4d42dcab0307f50c3aa488527dd11895604c01e
7 Author: Will Thompson <will.thompson@collabora.co.uk>
8 Date: 2012-11-14 17:24:14 +0000
9
10 muc-channel: remove content-free/wrong docstrings
11
12 commit 4fb86fc1be9bdea989fed9a21c5796a1eccba504
13 Author: Will Thompson <will.thompson@collabora.co.uk>
14 Date: 2012-11-14 17:03:08 +0000
15
16 muc: check that messages are marked as rescued
17
18 commit 39ee2f08cf4e2d46aae86307f6dfa023fe347248
19 Author: Will Thompson <will.thompson@collabora.co.uk>
20 Date: 2012-11-14 16:43:46 +0000
21
22 muc-channel: implement Destroy(), make Close() respawn
23
24 This fixes the issue where empathy-chat crashing means you get kicked
25 out of all your channels.
26
27 It's technically backwards-incompatible but empathy-chat has been using
28 RemoveMembers() to leave rooms for ages, and it's a pretty destructive
29 and annoying bug, so let's just get on with it.
30
31 Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=24614
32
33 commit f51c7b9cd378a561a9926d0860c26b7581dd4dc0
34 Author: Will Thompson <will.thompson@collabora.co.uk>
35 Date: 2012-11-14 17:10:24 +0000
36
37 idletest: handle PART with no message argument
38
39 commit 903885187ef029ef6a954f2dd338d5348ef5e307
40 Author: Will Thompson <will.thompson@collabora.co.uk>
41 Date: 2012-11-09 15:22:53 +0000
42
43 muc-channel: implement get_interfaces vfunc
44
45 I don't think this new API is a net improvement over making
46 TpBaseChannel do the work of merging ->interfaces (which would involve
47 zero code changes in CMs, and none of this stupid boilerplate for
48 building a list of strings in every CM) but there we go.
49
50 commit 5c34b7d2f170ff5f9ee9bf3821ececf55608d0df
51 Author: Will Thompson <will.thompson@collabora.co.uk>
52 Date: 2012-11-09 14:18:18 +0000
53
54 im-channel: chain up in get_interfaces() impl
55
56 This is a no-op right now, but it's the right thing to do.
57
58 commit 8a3a8ba64195afd03c291282233d3b28abfd85d2
59 Author: Will Thompson <will.thompson@collabora.co.uk>
60 Date: 2012-11-09 14:15:10 +0000
61
62 im-manager: use tp_base_channel_is_destroyed()
63
64 This is equivalent but neater.
65
66 commit 2a81f262441270e8f5b2bd85c5f783dff03d3547
67 Author: Will Thompson <will.thompson@collabora.co.uk>
68 Date: 2012-11-05 19:52:58 +0000
69
70 Some more NEWS.
71
72 commit 27c1ffc5ebcfcb37ad958ef451ca2772bdc4ec01
73 Merge: ba22cc0 c679fe4
74 Author: Will Thompson <will.thompson@collabora.co.uk>
75 Date: 2012-11-02 11:56:29 +0000
76
77 Merge branch 'ping'
78
79 Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=56589
80
81 commit c679fe4baaec1c29f7d0fbbed1f6b1d9c7d1266c
82 Author: Will Thompson <will.thompson@collabora.co.uk>
83 Date: 2012-11-02 11:55:53 +0000
84
85 Clarify ping timeout comparison
86
87 Jonny wanted this to be clearer on
88 <https://bugs.freedesktop.org/show_bug.cgi?id=56589#c1>.
89
90 commit ba22cc02a652a2ccfe6b59b113a87b2fe3761543
91 Author: Will Thompson <will.thompson@collabora.co.uk>
92 Date: 2012-10-30 17:32:56 +0000
93
94 connection: cope with send_async() returning synchronously
95
96 On <https://bugs.freedesktop.org/show_bug.cgi?id=49163>, Alban Browaeys
97 described a situation where idle_server_connection_send_async() would
98 call its callback synchronously, due to a bug in GLib. While I think
99 that bug is fixed, we can be more defensive against this by initializing
100 priv->msg_sending to TRUE before calling
101 idle_server_connection_send_async() rather than after. That way, if the
102 _msg_queue_timeout_ready() callback is called synchronously (due to a
103 bug), Idle won't get stuck thinking it's sending a message.
104
105 commit f7766957dbb4ea80ac86d65b83f361b8c82aab45
106 Author: Will Thompson <will.thompson@collabora.co.uk>
107 Date: 2012-10-30 16:56:48 +0000
108
109 Disable keepalive pings if server doesn't support PING.
110
111 I don't know which servers don't support PING, but irssi does this, so I
112 figure we may as well do it too.
113
114 I tested this by making Idle send and check for PNNING, which Freenode
115 doesn't support. It's not obvious how to write a good automated test for
116 this.
117
118 commit 339c7273dcb604a639ea2b54461b24bd4f252dd6
119 Author: Will Thompson <will.thompson@collabora.co.uk>
120 Date: 2012-10-30 16:30:10 +0000
121
122 Fix _force_disconnect to actually forcibly disconnect.
123
124 Previously, if the network connection had gone away _force_disconnect()
125 would not actually make the connection die any faster, because
126 g_io_stream_close_async() waits for a reply by default.
127
128 But by pulling the same trick with a cancelled cancellable as is used
129 for ping timeouts, we can fix this.
130
131 I tested this by:
132
133 * Connecting to a server (with keepalive-interval=0 to ensure that
134 doesn't interfere).
135 * Pulling out my network cable (breaking the connection) but leaving my
136 wireless connection up.
137 * calling Disconnect() on the connection.
138
139 Before, the connection would never finish disconnecting; after, the
140 _force_disconnect() timeout actually does something useful and the
141 connection dies properly.
142
143 commit 67b18831e0ea40dd3f4438426cfb0f703a3fc971
144 Author: Will Thompson <will.thompson@collabora.co.uk>
145 Date: 2012-10-30 16:14:57 +0000
146
147 Don't try to ping while we're disconnecting.
148
149 commit eb437c2f0bbc7061570889a825b131969982cc9a
150 Author: Will Thompson <will.thompson@collabora.co.uk>
151 Date: 2012-10-30 15:52:09 +0000
152
153 Connection: disconnect if we get no PONG for our PING
154
155 Previously we'd just send out a PING into the æther every n seconds, and
156 pay no attention to whether we ever got an answer. So it was perfectly
157 possible for the connection to just sit there until the TCP timeout
158 kicks in, which I think is a matter of hours by default.
159
160 With this patch, if we haven't heard a PONG 3 keepalive-intervals after
161 sending a PING, Idle throws its toys out of the pram.
162
163 This has been tested as follows:
164
165 * Put my laptop on a wired and wireless network simultaneously.
166 * Connect to an IRC server. (The wired network is used.)
167 * Pull the network cable out. Idle is too stupid to realise the link it
168 was using is gone, and because we're still ostensibly online, nothing
169 tells it to disconnect.
170 * Wait keepalive-interval * 4, and watch the connection get
171 disconnected.
172
173 It works both with a direct connection to Freenode, and with a
174 connection over an SSH tunnel to irssi-proxy.
175
176 commit 5fc2d07d025fca9a19abd09caa8b7de2ab8911ed
177 Author: Will Thompson <will.thompson@collabora.co.uk>
178 Date: 2012-10-30 14:45:33 +0000
179
180 Move comment about message interval to the right place
181
182 commit 4e0d40f034745f3867def4dfb8290146e95a252c
183 Author: Will Thompson <will.thompson@collabora.co.uk>
184 Date: 2012-10-30 14:31:33 +0000
185
186 Connection: add priv field to structure
187
188 commit 2667606678abd63820b321d3d51ebd0efc0c3e24
189 Author: Will Thompson <will.thompson@collabora.co.uk>
190 Date: 2012-10-30 14:20:43 +0000
191
192 Make the PONGs sent back by the test more realistic
193
194 commit 0bbe346a069316f2b4571834791abc6e2ed3e4d7
195 Author: Will Thompson <will.thompson@collabora.co.uk>
196 Date: 2012-10-30 14:12:04 +0000
197
198 Parser: spell out prefix vs. non-prefix logic better
199
200 commit a6e9900f65e250aa1419420708598df23aae0891
201 Author: Will Thompson <will.thompson@collabora.co.uk>
202 Date: 2012-10-30 14:09:51 +0000
203
204 Add a stupid test for Idle sending PING
205
206 commit 39ed6a0d707397fa13ba6d19580d257b7b2df8e2
207 Author: Will Thompson <will.thompson@collabora.co.uk>
208 Date: 2012-10-30 09:00:23 +0000
209
210 RoomlistManager: remove redundant check for Handle
211
212 tp_channel_manager_asv_has_unknown_properties() takes care of excluding
213 extra properties for us.
214
215 commit 78cd99642e495a0a60c66709d78d9564444779e7
216 Author: Will Thompson <will.thompson@collabora.co.uk>
217 Date: 2012-10-30 09:00:01 +0000
218
219 Add a debug flag for roomlist code.
220
221 commit 04b6b070c32e3cd54a9c8c0bb218899fc6208eb4
222 Author: Will Thompson <will.thompson@collabora.co.uk>
223 Date: 2012-10-29 12:41:52 +0000
224
225 Fix warnings in roomlist code.
226
227 Whoops.
228
229 commit 331e6c167d79bc6d98a1b23f2f072a65cfe10a14
230 Author: Will Thompson <will.thompson@collabora.co.uk>
231 Date: 2012-10-29 12:39:01 +0000
232
233 NEWS for UTF-8 fix and room listing.
234
235 commit 23602180bd3363a7fb8fd0c7b636b8a90bf77d6c
236 Author: Will Thompson <will.thompson@collabora.co.uk>
237 Date: 2012-10-29 12:00:18 +0000
238
239 idle_roomlist_manager_get_type: fix prototype
240
241 commit 35047ec5017492132307e184173890d3e416fbae
242 Merge: 79425a0 7b7f61a
243 Author: Will Thompson <will.thompson@collabora.co.uk>
244 Date: 2012-10-29 11:57:38 +0000
245
246 Merge branch 'room-list'
247
248 commit 79425a010af79070a6b93c25deb5690cc72daf87
249 Author: Will Thompson <will.thompson@collabora.co.uk>
250 Date: 2010-10-10 00:26:53 +0100
251
252 Sanitize incoming messages to remove UTF-8 non-characters.
253
254 https://bugs.freedesktop.org/show_bug.cgi?id=30741
255
256 commit 3dc023fd1a745504ed9035ebba9501bf916f7a9d
257 Author: Will Thompson <will.thompson@collabora.co.uk>
258 Date: 2012-10-28 14:23:05 +0000
259
260 Give charset conversion function a more natural type
261
262 commit dc7677013cf62c8b8e88b24d01cdb53f039b0b69
263 Author: Jonny Lamb <jonny.lamb@collabora.co.uk>
264 Date: 2012-08-28 12:14:17 +0100
265
266 NEWS: updated
267
268 Signed-off-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
269
270 commit b092172e56393fad70e472afa26df4f886dfdbc0
271 Author: Dan Winship <danw@gnome.org>
272 Date: 2012-08-24 12:19:45 -0400
273
274 build: fix for "make -j" safety
275
276 A make rule with multiple (non-pattern) targets just says that each of
277 those files can be built by the rule, not that the rule builds all of
278 them at once. So under "make -j", extensions/Makefile would run three
279 copies of glib-ginterface-gen.py at once, which could end up deleting
280 each others' files and causing a make failure. Fix.
281
282 Signed-off-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
283
284 commit d940d1a912f92d9c486464c3c6ae737fcac70be3
285 Author: Will Thompson <will.thompson@collabora.co.uk>
286 Date: 2012-08-03 08:29:36 +0100
287
288 Bump nano-version
289
0290 commit 81cffc1bd9d0e1e8778a392e23dd4f100e09a761
1291 Author: Will Thompson <will.thompson@collabora.co.uk>
2292 Date: 2012-08-02 14:05:41 +0100
457747 Set G_MESSAGES_DEBUG during testing
458748
459749 The same as Gabble commit cbfa9d06.
750
751 commit 7b7f61a8dae3f8de7436378ef3ae569a5323fcef
752 Author: Will Thompson <will.thompson@collabora.co.uk>
753 Date: 2012-04-08 11:01:50 +0100
754
755 RoomlistManager: use tp_asv_new()
756
757 commit 7b95b1c716cb1aea585e4341dfb37592b8b4e433
758 Author: Will Thompson <will.thompson@collabora.co.uk>
759 Date: 2012-04-08 11:00:28 +0100
760
761 Roomlist: modernize private structures.
762
763 commit 7d8704d1b5a5451f1013039083bd85139d89b6f3
764 Author: Will Thompson <will.thompson@collabora.co.uk>
765 Date: 2012-04-08 10:54:04 +0100
766
767 Roomlist: use Text interface constant
768
769 commit 9f5d6ceda3e7e01d589ed8e2dd88918707fc444c
770 Author: Will Thompson <will.thompson@collabora.co.uk>
771 Date: 2012-04-08 10:52:23 +0100
772
773 Roomlist: use tp_asv_new()
774
775 commit c50f12f884121dca2081435dea8976de9ac8a8d6
776 Author: Will Thompson <will.thompson@collabora.co.uk>
777 Date: 2012-04-08 10:49:26 +0100
778
779 RoomList: use TpBaseChannel
780
781 This moves IdleRoomlistChannel over to TpBaseChannel and cleans up the
782 tests a little at the same time.
783
784 commit dc777537db0f727951d51a09599c108f34b34dcd
785 Author: Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
786 Date: 2009-01-29 16:34:23 -0600
787
788 Fix a bug and add a test for requesting multiple roomlist channels
789
790 I hadn't really excercised the code for returning an existing roomlist channel
791 if we had already created one. So I added a test for it and in the process
792 discovered a bug, which is also fixed by this patch.
793
794 (The compiler found this bug when it was introduced while I was rebasing
795 Jonathon's branch, so I fixed it there and then. -Will)
796
797 commit 5ebe6a7cf1fac6df992cb65ca18cdabf8ece20be
798 Author: Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
799 Date: 2009-01-29 11:54:24 -0600
800
801 Fix the crash on closing the RoomList channel
802
803 The Act of unreffing the Channel emitted the 'closed' signal, and we were
804 connecting to the closed signal and and unreffing the channel again in the
805 handler. It is necessary to unref the channel in response to the 'closed'
806 signal in order to handle the dbus Close() method, so we store the channel in a
807 temporary variable and then NULL out the priv->channel variable before unreffing
808 so that we don't unref again in the closed handler
809
810 commit 2e852a3bb80afc785174734cf1c877ffc918df88
811 Author: Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
812 Date: 2009-01-29 10:41:31 -0600
813
814 Initial support for listing IRC channels
815
816 Added IdleRoomlistManager and IdleRoomlistChannel classes. There is a basic
817 test included as well. Things seem to work ok (e.g. I can display a list of
818 channels in empathy), but it hasn't been extensively tested and I have made some
819 possibly questionable design decisions (e.g. only creating a single
820 RoomlistChannel rather than creating a new one every time one is requested). I
821 don't fully understand the implication of that choice yet, so I need to do some
822 more work to figure out whether that needs to be changed.
823
824 There seems to be a crash when closing down the channel that also needs to be
825 investigated.
460826
461827 commit 2479ef3d6052ec03eb998496cb32f41f54f5799c
462828 Author: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
0 telepathy-idle 0.1.13 (2012-11-14)
1 ==================================
2
3 Wow, what a lot of fixes!
4
5 Enhancements:
6
7 • fd.o#23005: initial support for listing rooms. (Jonathon Jongsma)
8
9 It would be better to have support for one of the extensions which
10 lets you search rooms rather than getting the entire list from the
11 server. If someone wants to work on adding this to Idle and Empathy,
12 go for it!
13
14 • fd.o#24614: chat room channels now respawn if you Close() them. This
15 means that if empathy-chat (or your friendly neighbourhood chat UI)
16 crashes, it pops right back up with your channels in it, rather than
17 you getting kicked out of all your channels.
18
19 If your UI is using Close() to leave channels, you'll need to change
20 it to use RemoveMembers with the SelfHandle property from the Group
21 interface. empathy-chat has done this for literally years, and
22 tp_channel_leave_async() in telepathy-glib and
23 Tp::Channel::requestLeave in telepathy-qt4 do the right thing.
24
25 Fixes:
26
27 • fd.o#54016: fix for "make -j" safety (Dan Winship)
28
29 • fd.o#30741: sending well-formed but invalid UTF-8 no longer gets Idle
30 kicked off the bus.
31
32 • fd.o#56589: Idle now disconnects if it doesn't get a reply to its pings after
33 a while.
34
35 • fd.o#49163: don't wedge and stop sending messages in a situation we believe
36 was triggered by a short-lived GIO bug.
37
38
039 telepathy-idle 0.1.12 (2012-08-02)
140 ==================================
241
00 #! /bin/sh
11 # Guess values for system-dependent variables and create Makefiles.
2 # Generated by GNU Autoconf 2.69 for telepathy-idle 0.1.12.
2 # Generated by GNU Autoconf 2.69 for telepathy-idle 0.1.13.
33 #
44 # Report bugs to <https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=idle>.
55 #
590590 # Identity of this package.
591591 PACKAGE_NAME='telepathy-idle'
592592 PACKAGE_TARNAME='telepathy-idle'
593 PACKAGE_VERSION='0.1.12'
594 PACKAGE_STRING='telepathy-idle 0.1.12'
593 PACKAGE_VERSION='0.1.13'
594 PACKAGE_STRING='telepathy-idle 0.1.13'
595595 PACKAGE_BUGREPORT='https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=idle'
596596 PACKAGE_URL=''
597597
13571357 # Omit some internal or obsolete options to make the list less imposing.
13581358 # This message is too long to be a string in the A/UX 3.1 sh.
13591359 cat <<_ACEOF
1360 \`configure' configures telepathy-idle 0.1.12 to adapt to many kinds of systems.
1360 \`configure' configures telepathy-idle 0.1.13 to adapt to many kinds of systems.
13611361
13621362 Usage: $0 [OPTION]... [VAR=VALUE]...
13631363
14271427
14281428 if test -n "$ac_init_help"; then
14291429 case $ac_init_help in
1430 short | recursive ) echo "Configuration of telepathy-idle 0.1.12:";;
1430 short | recursive ) echo "Configuration of telepathy-idle 0.1.13:";;
14311431 esac
14321432 cat <<\_ACEOF
14331433
15551555 test -n "$ac_init_help" && exit $ac_status
15561556 if $ac_init_version; then
15571557 cat <<\_ACEOF
1558 telepathy-idle configure 0.1.12
1558 telepathy-idle configure 0.1.13
15591559 generated by GNU Autoconf 2.69
15601560
15611561 Copyright (C) 2012 Free Software Foundation, Inc.
18331833 This file contains any messages produced by compilers while
18341834 running configure, to aid debugging if configure makes a mistake.
18351835
1836 It was created by telepathy-idle $as_me 0.1.12, which was
1836 It was created by telepathy-idle $as_me 0.1.13, which was
18371837 generated by GNU Autoconf 2.69. Invocation command line was
18381838
18391839 $ $0 $@
26942694
26952695 # Define the identity of the package.
26962696 PACKAGE='telepathy-idle'
2697 VERSION='0.1.12'
2697 VERSION='0.1.13'
26982698
26992699
27002700 cat >>confdefs.h <<_ACEOF
1424114241 # report actual input values of CONFIG_FILES etc. instead of their
1424214242 # values after options handling.
1424314243 ac_log="
14244 This file was extended by telepathy-idle $as_me 0.1.12, which was
14244 This file was extended by telepathy-idle $as_me 0.1.13, which was
1424514245 generated by GNU Autoconf 2.69. Invocation command line was
1424614246
1424714247 CONFIG_FILES = $CONFIG_FILES
1430714307 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
1430814308 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
1430914309 ac_cs_version="\\
14310 telepathy-idle config.status 0.1.12
14310 telepathy-idle config.status 0.1.13
1431114311 configured by $0, generated by GNU Autoconf 2.69,
1431214312 with options \\"\$ac_cs_config\\"
1431314313
1010
1111 m4_define([idle_major_version], [0])
1212 m4_define([idle_minor_version], [1])
13 m4_define([idle_micro_version], [12])
13 m4_define([idle_micro_version], [13])
1414 m4_define([idle_nano_version], [0])
1515
1616 m4_define([idle_base_version],
4141 $(tools_dir)/doc-generator.xsl \
4242 $< > $@
4343
44 _gen/svc.c _gen/svc.h _gen/svc-gtk-doc.h: _gen/all.xml \
44 _gen/svc.h: _gen/svc.c
45 _gen/svc-gtk-doc.h: _gen/svc.c
46
47 _gen/svc.c: _gen/all.xml \
4548 $(tools_dir)/glib-ginterface-gen.py
4649 $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \
4750 --filename=_gen/svc --signal-marshal-prefix=_idle_ext \
589589 $(tools_dir)/doc-generator.xsl \
590590 $< > $@
591591
592 _gen/svc.c _gen/svc.h _gen/svc-gtk-doc.h: _gen/all.xml \
592 _gen/svc.h: _gen/svc.c
593 _gen/svc-gtk-doc.h: _gen/svc.c
594
595 _gen/svc.c: _gen/all.xml \
593596 $(tools_dir)/glib-ginterface-gen.py
594597 $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \
595598 --filename=_gen/svc --signal-marshal-prefix=_idle_ext \
2929 idle-parser.h \
3030 protocol.c \
3131 protocol.h \
32 idle-roomlist-channel.h \
33 idle-roomlist-channel.c \
34 idle-roomlist-manager.h \
35 idle-roomlist-manager.c \
3236 idle-server-connection.c \
3337 idle-server-connection.h \
3438 idle-text.h \
7676 idle-debug.lo idle-handles.lo idle-im-channel.lo \
7777 idle-im-manager.lo idle-muc-channel.lo idle-muc-manager.lo \
7878 room-config.lo idle-parser.lo protocol.lo \
79 idle-roomlist-channel.lo idle-roomlist-manager.lo \
7980 idle-server-connection.lo idle-text.lo
8081 nodist_libidle_convenience_la_OBJECTS =
8182 libidle_convenience_la_OBJECTS = $(am_libidle_convenience_la_OBJECTS) \
331332 idle-parser.h \
332333 protocol.c \
333334 protocol.h \
335 idle-roomlist-channel.h \
336 idle-roomlist-channel.c \
337 idle-roomlist-manager.h \
338 idle-roomlist-manager.c \
334339 idle-server-connection.c \
335340 idle-server-connection.h \
336341 idle-text.h \
485490 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-muc-channel.Plo@am__quote@
486491 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-muc-manager.Plo@am__quote@
487492 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-parser.Plo@am__quote@
493 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-roomlist-channel.Plo@am__quote@
494 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-roomlist-manager.Plo@am__quote@
488495 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-server-connection.Plo@am__quote@
489496 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-text.Plo@am__quote@
490497 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle.Po@am__quote@
4444 #include "idle-handles.h"
4545 #include "idle-im-manager.h"
4646 #include "idle-muc-manager.h"
47 #include "idle-roomlist-manager.h"
4748 #include "idle-parser.h"
4849 #include "idle-server-connection.h"
4950
5051 #include "extensions/extensions.h" /* Renaming */
52
53 #define DEFAULT_KEEPALIVE_INTERVAL 30 /* sec */
54 #define MISSED_KEEPALIVES_BEFORE_DISCONNECTING 3
5155
5256 /* From RFC 2813 :
5357 * This in essence means that the client may send one (1) message every
5458 * two (2) seconds without being adversely affected. Services MAY also
5559 * be subject to this mechanism.
5660 */
57
58 #define DEFAULT_KEEPALIVE_INTERVAL 30 /* sec */
59
6061 #define MSG_QUEUE_TIMEOUT 2
6162 static gboolean flush_queue_faster = FALSE;
6263
138139 LAST_PROPERTY_ENUM
139140 };
140141
141 /* private structure */
142 typedef struct _IdleConnectionPrivate IdleConnectionPrivate;
143142 struct _IdleConnectionPrivate {
144143 /*
145144 * network connection
146145 */
147
148146 IdleServerConnection *conn;
149147 guint sconn_status;
148
149 /* When we sent a PING to the server which it hasn't PONGed for yet, or 0 if
150 * there isn't a PING outstanding.
151 */
152 gint64 ping_time;
150153
151154 /* IRC connection properties */
152155 char *nickname;
197200 TpSimplePasswordManager *password_manager;
198201 };
199202
200 #define IDLE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_CONNECTION, IdleConnectionPrivate))
201
202203 static void _iface_create_handle_repos(TpBaseConnection *self, TpHandleRepoIface **repos);
203204 static GPtrArray *_iface_create_channel_managers(TpBaseConnection *self);
204205 static gchar *_iface_get_unique_connection_name(TpBaseConnection *self);
211212 static IdleParserHandlerResult _nick_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
212213 static IdleParserHandlerResult _nickname_in_use_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
213214 static IdleParserHandlerResult _ping_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
215 static IdleParserHandlerResult _pong_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
216 static IdleParserHandlerResult _unknown_command_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
214217 static IdleParserHandlerResult _version_privmsg_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
215218 static IdleParserHandlerResult _welcome_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
216219 static IdleParserHandlerResult _whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
223226 static void connection_connect_cb(IdleConnection *conn, gboolean success, TpConnectionStatusReason fail_reason);
224227 static void connection_disconnect_cb(IdleConnection *conn, TpConnectionStatusReason reason);
225228 static gboolean idle_connection_hton(IdleConnection *obj, const gchar *input, gchar **output, GError **_error);
226 static void idle_connection_ntoh(IdleConnection *obj, const gchar *input, gchar **output);
229 static gchar *idle_connection_ntoh(IdleConnection *obj, const gchar *input);
227230
228231 static void idle_connection_add_queue_timeout (IdleConnection *self);
229232 static void idle_connection_clear_queue_timeout (IdleConnection *self);
235238 GHashTable *attributes_hash);
236239
237240 static void idle_connection_init(IdleConnection *obj) {
238 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
239
241 IdleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, IDLE_TYPE_CONNECTION, IdleConnectionPrivate);
242
243 obj->priv = priv;
240244 priv->sconn_status = SERVER_CONNECTION_STATE_NOT_CONNECTED;
241245 priv->msg_queue = g_queue_new();
242246
257261 }
258262
259263 static void idle_connection_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) {
260 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
264 IdleConnection *self = IDLE_CONNECTION (obj);
265 IdleConnectionPrivate *priv = self->priv;
261266
262267 switch (prop_id) {
263268 case PROP_NICKNAME:
318323 }
319324
320325 static void idle_connection_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) {
321 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
326 IdleConnection *self = IDLE_CONNECTION (obj);
327 IdleConnectionPrivate *priv = self->priv;
322328
323329 switch (prop_id) {
324330 case PROP_NICKNAME:
373379
374380 static void idle_connection_dispose (GObject *object) {
375381 IdleConnection *self = IDLE_CONNECTION(object);
376 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
382 IdleConnectionPrivate *priv = self->priv;
377383
378384 if (priv->dispose_has_run)
379385 return;
406412 }
407413
408414 static void idle_connection_finalize (GObject *object) {
409 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(object);
415 IdleConnection *self = IDLE_CONNECTION (object);
416 IdleConnectionPrivate *priv = self->priv;
410417 IdleOutputPendingMsg *msg;
411418
412419 idle_contact_info_finalize(object);
507514 flush_queue_faster = TRUE;
508515 }
509516
510 static GPtrArray *_iface_create_channel_managers(TpBaseConnection *self) {
511 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
517 static GPtrArray *_iface_create_channel_managers(TpBaseConnection *base) {
518 IdleConnection *self = IDLE_CONNECTION (base);
519 IdleConnectionPrivate *priv = self->priv;
512520 GPtrArray *managers = g_ptr_array_sized_new(1);
513521 GObject *manager;
514522
518526 manager = g_object_new(IDLE_TYPE_MUC_MANAGER, "connection", self, NULL);
519527 g_ptr_array_add(managers, manager);
520528
521 priv->password_manager = tp_simple_password_manager_new(self);
529 priv->password_manager = tp_simple_password_manager_new(base);
522530 g_ptr_array_add(managers, priv->password_manager);
531
532 manager = g_object_new(IDLE_TYPE_ROOMLIST_MANAGER, "connection", self, NULL);
533 g_ptr_array_add(managers, manager);
523534
524535 return managers;
525536 }
531542 idle_handle_repos_init(repos);
532543 }
533544
534 static gchar *_iface_get_unique_connection_name(TpBaseConnection *self) {
535 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
545 static gchar *_iface_get_unique_connection_name(TpBaseConnection *base) {
546 IdleConnection *self = IDLE_CONNECTION (base);
547 IdleConnectionPrivate *priv = self->priv;
536548
537549 return g_strdup_printf("%s@%s%p", priv->nickname, priv->server, self);
538550 }
539551
540552 static gboolean _finish_shutdown_idle_func(gpointer data) {
541553 TpBaseConnection *conn = TP_BASE_CONNECTION(data);
542 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
554 IdleConnection *self = IDLE_CONNECTION(conn);
555 IdleConnectionPrivate *priv = self->priv;
543556 if (priv->force_disconnect_id != 0) {
544557 g_source_remove(priv->force_disconnect_id);
545558 }
553566 _force_disconnect (gpointer data)
554567 {
555568 IdleConnection *conn = IDLE_CONNECTION(data);
556 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
557 idle_server_connection_disconnect_async(priv->conn, NULL, NULL, NULL);
569 IdleConnectionPrivate *priv = conn->priv;
570
571 IDLE_DEBUG("gave up waiting, forcibly disconnecting");
572 idle_server_connection_force_disconnect(priv->conn);
558573 return FALSE;
559574 }
560575
561576 static void _iface_disconnected(TpBaseConnection *self) {
562577 IdleConnection *conn = IDLE_CONNECTION(self);
563 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
578 IdleConnectionPrivate *priv = conn->priv;
564579
565580 /* we never got around to actually creating the connection
566581 * iface object because we were still trying to connect, so
579594 priv->force_disconnect_id = g_timeout_add_seconds(2, _force_disconnect, conn);
580595 }
581596
582 static void _iface_shut_down(TpBaseConnection *self) {
583 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self);
597 static void _iface_shut_down(TpBaseConnection *base) {
598 IdleConnection *self = IDLE_CONNECTION (base);
599 IdleConnectionPrivate *priv = self->priv;
584600
585601 if (priv->quitting)
586602 return;
589605 * iface object because we were still trying to connect, so
590606 * don't try to send any traffic down it */
591607 if (priv->conn == NULL) {
592 g_idle_add(_finish_shutdown_idle_func, self);;
608 g_idle_add(_finish_shutdown_idle_func, self);
593609 } else {
594610 idle_server_connection_disconnect_async(priv->conn, NULL, NULL, NULL);
595611 }
623639 static void _password_prompt_cb(GObject *source, GAsyncResult *result, gpointer user_data) {
624640 IdleConnection *conn = user_data;
625641 TpBaseConnection *base_conn = TP_BASE_CONNECTION(conn);
626 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
642 IdleConnectionPrivate *priv = conn->priv;
627643 const GString *password;
628644 GError *error = NULL;
629645
650666
651667 static gboolean _iface_start_connecting(TpBaseConnection *self, GError **error) {
652668 IdleConnection *conn = IDLE_CONNECTION(self);
653 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
669 IdleConnectionPrivate *priv = conn->priv;
654670
655671 g_assert(priv->nickname != NULL);
656672 g_assert(priv->server != NULL);
674690 static void _connection_connect_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) {
675691 IdleServerConnection *sconn = IDLE_SERVER_CONNECTION(source_object);
676692 IdleConnection *conn = IDLE_CONNECTION(user_data);
677 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
693 IdleConnectionPrivate *priv = conn->priv;
678694 GError *error = NULL;
679695
680696 if (!idle_server_connection_connect_finish(sconn, res, &error)) {
696712 idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_WHOISUSER, _whois_user_handler, conn);
697713
698714 idle_parser_add_handler(conn->parser, IDLE_PARSER_CMD_PING, _ping_handler, conn);
715 idle_parser_add_handler(conn->parser, IDLE_PARSER_PREFIXCMD_PONG, _pong_handler, conn);
716 idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_UNKNOWNCOMMAND, _unknown_command_handler, conn);
699717
700718 idle_parser_add_handler_with_priority(conn->parser, IDLE_PARSER_PREFIXCMD_NICK, _nick_handler, conn, IDLE_PARSER_HANDLER_PRIORITY_FIRST);
701719 idle_parser_add_handler(conn->parser, IDLE_PARSER_PREFIXCMD_PRIVMSG_USER, _version_privmsg_handler, conn);
704722 }
705723
706724 static void _start_connecting_continue(IdleConnection *conn) {
707 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
725 IdleConnectionPrivate *priv = conn->priv;
708726 IdleServerConnection *sconn;
709727
710728 if (tp_str_empty(priv->realname)) {
735753 static gboolean keepalive_timeout_cb(gpointer user_data);
736754
737755 static void sconn_status_changed_cb(IdleServerConnection *sconn, IdleServerConnectionState state, IdleServerConnectionStateReason reason, IdleConnection *conn) {
738 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
756 IdleConnectionPrivate *priv = conn->priv;
739757 TpConnectionStatusReason tp_reason;
740758
741759 /* cancel scheduled forced disconnect since we are now disconnected */
791809 }
792810
793811 static void sconn_received_cb(IdleServerConnection *sconn, gchar *raw_msg, IdleConnection *conn) {
794 gchar *converted;
795
796 idle_connection_ntoh(conn, raw_msg, &converted);
812 gchar *converted = idle_connection_ntoh(conn, raw_msg);
797813 idle_parser_receive(conn->parser, converted);
798814
799815 g_free(converted);
801817
802818 static gboolean keepalive_timeout_cb(gpointer user_data) {
803819 IdleConnection *conn = IDLE_CONNECTION(user_data);
804 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
820 IdleConnectionPrivate *priv = conn->priv;
805821 gchar cmd[IRC_MSG_MAXLEN + 1];
806 gint64 ping_time;
807
808 if (priv->sconn_status != SERVER_CONNECTION_STATE_CONNECTED) {
822 gint64 now;
823
824 if (priv->sconn_status != SERVER_CONNECTION_STATE_CONNECTED ||
825 priv->quitting) {
809826 priv->keepalive_timeout = 0;
810827 return FALSE;
811828 }
812829
813 ping_time = g_get_real_time();
814 g_snprintf(cmd, IRC_MSG_MAXLEN + 1, "PING %" G_GINT64_FORMAT, ping_time);
830 now = g_get_real_time();
831
832 if (priv->ping_time != 0) {
833 gint64 seconds_since_ping = (now - priv->ping_time) / G_USEC_PER_SEC;
834 gint64 grace_period = priv->keepalive_interval * MISSED_KEEPALIVES_BEFORE_DISCONNECTING;
835
836 if (seconds_since_ping > grace_period) {
837 IDLE_DEBUG("haven't heard from the server in %" G_GINT64_FORMAT " seconds "
838 "(more than %u keepalive intervals)",
839 seconds_since_ping, MISSED_KEEPALIVES_BEFORE_DISCONNECTING);
840
841 idle_server_connection_force_disconnect(priv->conn);
842 return FALSE;
843 }
844
845 return TRUE;
846 }
847
848 if (priv->msg_queue->length > 0) {
849 /* No point in sending a PING if we're sending data anyway. */
850 return TRUE;
851 }
852
853 priv->ping_time = now;
854 g_snprintf(cmd, IRC_MSG_MAXLEN + 1, "PING %" G_GINT64_FORMAT, priv->ping_time);
815855 _send_with_priority(conn, cmd, SERVER_CMD_MIN_PRIORITY);
816856
817857 return TRUE;
820860 static void _msg_queue_timeout_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) {
821861 IdleServerConnection *sconn = IDLE_SERVER_CONNECTION(source_object);
822862 IdleConnection *conn = IDLE_CONNECTION (user_data);
823 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
863 IdleConnectionPrivate *priv = conn->priv;
824864 GError *error = NULL;
825865
826866 priv->msg_sending = FALSE;
836876
837877 static gboolean msg_queue_timeout_cb(gpointer user_data) {
838878 IdleConnection *conn = IDLE_CONNECTION(user_data);
839 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
879 IdleConnectionPrivate *priv = conn->priv;
840880 IdleOutputPendingMsg *output_msg;
841881
842882 IDLE_DEBUG("called");
859899 return FALSE;
860900 }
861901
902 priv->msg_sending = TRUE;
862903 idle_server_connection_send_async(priv->conn, output_msg->message, NULL, _msg_queue_timeout_ready, conn);
863904 idle_output_pending_msg_free (output_msg);
864 priv->msg_sending = TRUE;
865905
866906 return TRUE;
867907 }
869909 static void
870910 idle_connection_add_queue_timeout (IdleConnection *self)
871911 {
872 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self);
912 IdleConnectionPrivate *priv = self->priv;
873913
874914 if (priv->msg_queue_timeout == 0)
875915 {
893933 static void
894934 idle_connection_clear_queue_timeout (IdleConnection *self)
895935 {
896 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self);
936 IdleConnectionPrivate *priv = self->priv;
897937
898938 if (priv->msg_queue_timeout != 0)
899939 {
906946 * Queue a IRC command for sending, clipping it to IRC_MSG_MAXLEN bytes and appending the required <CR><LF> to it
907947 */
908948 static void _send_with_priority(IdleConnection *conn, const gchar *msg, guint priority) {
909 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
949 IdleConnectionPrivate *priv = conn->priv;
910950 gchar cmd[IRC_MSG_MAXLEN + 3];
911951 int len;
912952 gchar *converted;
946986 gsize
947987 idle_connection_get_max_message_length(IdleConnection *conn)
948988 {
949 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
989 IdleConnectionPrivate *priv = conn->priv;
950990 if (priv->relay_prefix != NULL) {
951991 /* server will add ':<relay_prefix> ' to all messages it relays on to
952992 * other users. the +2 is for the initial : and the trailing space */
10551095 return IDLE_PARSER_HANDLER_RESULT_HANDLED;
10561096 }
10571097
1098 static IdleParserHandlerResult _pong_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
1099 IdleConnection *conn = IDLE_CONNECTION(user_data);
1100 IdleConnectionPrivate *priv = conn->priv;
1101
1102 /* We could compare ping_time to g_get_real_time() and give some indication
1103 * of lag, if we were feeling enthusiastic.
1104 */
1105 priv->ping_time = 0;
1106
1107 return IDLE_PARSER_HANDLER_RESULT_HANDLED;
1108 }
1109
1110 static IdleParserHandlerResult _unknown_command_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
1111 IdleConnection *conn = IDLE_CONNECTION(user_data);
1112 IdleConnectionPrivate *priv = conn->priv;
1113 const gchar *command = g_value_get_string(g_value_array_get_nth(args, 0));
1114
1115 if (!tp_strdiff(command, "PING")) {
1116 IDLE_DEBUG("PING not supported, disabling keepalive.");
1117 g_source_remove(priv->keepalive_timeout);
1118 priv->keepalive_timeout = 0;
1119 priv->ping_time = 0;
1120
1121 return IDLE_PARSER_HANDLER_RESULT_HANDLED;
1122 }
1123
1124 return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED;
1125 }
1126
10581127 static IdleParserHandlerResult _version_privmsg_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) {
10591128 IdleConnection *conn = IDLE_CONNECTION(user_data);
10601129 const gchar *msg = g_value_get_string(g_value_array_get_nth(args, 2));
10911160 _whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data)
10921161 {
10931162 IdleConnection *conn = IDLE_CONNECTION(user_data);
1094 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
1163 IdleConnectionPrivate *priv = conn->priv;
10951164
10961165 /* message format: <nick> <user> <host> * :<real name> */
10971166 TpHandle handle = g_value_get_uint(g_value_array_get_nth(args, 0));
11191188 g_assert(conn != NULL);
11201189 g_assert(IDLE_IS_CONNECTION(conn));
11211190
1122 priv = IDLE_CONNECTION_GET_PRIVATE(conn);
1191 priv = conn->priv;
11231192
11241193 if ((priv->password != NULL) && (priv->password[0] != '\0')) {
11251194 g_snprintf(msg, IRC_MSG_MAXLEN + 1, "PASS %s", priv->password);
11381207 }
11391208
11401209 static void send_quit_request(IdleConnection *conn) {
1141 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
1210 IdleConnectionPrivate *priv = conn->priv;
11421211 gchar cmd[IRC_MSG_MAXLEN + 1] = "QUIT";
11431212
11441213 if (priv->quit_message != NULL)
11691238
11701239 static void
11711240 _queue_alias_changed(IdleConnection *conn, TpHandle handle, const gchar *alias) {
1172 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
1241 IdleConnectionPrivate *priv = conn->priv;
11731242
11741243 if (!priv->queued_aliases_owners) {
11751244 TpHandleRepoIface *handles = tp_base_connection_get_handles(TP_BASE_CONNECTION(conn), TP_HANDLE_TYPE_CONTACT);
12141283 }
12151284
12161285 void idle_connection_emit_queued_aliases_changed(IdleConnection *conn) {
1217 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn);
1286 IdleConnectionPrivate *priv = conn->priv;
12181287
12191288 if (!priv->queued_aliases)
12201289 return;
13681437 }
13691438
13701439 static gboolean idle_connection_hton(IdleConnection *obj, const gchar *input, gchar **output, GError **_error) {
1371 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
1440 IdleConnectionPrivate *priv = obj->priv;
13721441 GError *error = NULL;
13731442 gsize bytes_written;
13741443 gchar *ret;
13921461 return TRUE;
13931462 }
13941463
1395 static void idle_connection_ntoh(IdleConnection *obj, const gchar *input, gchar **output) {
1396 IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(obj);
1464 #define U_FFFD_REPLACEMENT_CHARACTER_UTF8 "\357\277\275"
1465
1466 static gchar *
1467 idle_salvage_utf8 (gchar *supposed_utf8, gssize bytes)
1468 {
1469 GString *salvaged = g_string_sized_new (bytes);
1470 const gchar *end;
1471 gchar *ret;
1472 gsize ret_len;
1473
1474 while (!g_utf8_validate (supposed_utf8, bytes, &end)) {
1475 gssize valid_bytes = end - supposed_utf8;
1476
1477 g_string_append_len (salvaged, supposed_utf8, valid_bytes);
1478 g_string_append_len (salvaged, U_FFFD_REPLACEMENT_CHARACTER_UTF8, 3);
1479
1480 supposed_utf8 += (valid_bytes + 1);
1481 bytes -= (valid_bytes + 1);
1482 }
1483
1484 g_string_append_len (salvaged, supposed_utf8, bytes);
1485
1486 ret_len = salvaged->len;
1487 ret = g_string_free (salvaged, FALSE);
1488
1489 /* It had better be valid now… */
1490 g_return_val_if_fail (g_utf8_validate (ret, ret_len, NULL), ret);
1491 return ret;
1492 }
1493
1494
1495 static gchar *
1496 idle_connection_ntoh(IdleConnection *obj, const gchar *input) {
1497 IdleConnectionPrivate *priv = obj->priv;
13971498 GError *error = NULL;
13981499 gsize bytes_written;
13991500 gchar *ret;
14001501 gchar *p;
14011502
14021503 if (input == NULL) {
1403 *output = NULL;
1404 return;
1504 return NULL;
14051505 }
14061506
14071507 ret = g_convert(input, -1, "UTF-8", priv->charset, NULL, &bytes_written, &error);
14161516 if (*p & (1 << 7))
14171517 *p = '?';
14181518 }
1419 }
1420
1421 *output = ret;
1422 return;
1519 } else if (!g_utf8_validate (ret, bytes_written, NULL)) {
1520 /* Annoyingly g_convert(UTF-8, UTF-8) doesn't filter out well-formed
1521 * non-characters, so we have to do some further processing.
1522 */
1523 gchar *salvaged;
1524
1525 IDLE_DEBUG("Invalid UTF-8, salvaging what we can...");
1526 salvaged = idle_salvage_utf8(ret, bytes_written);
1527 g_free(ret);
1528 ret = salvaged;
1529 }
1530
1531 return ret;
14231532 }
14241533
14251534 static void _aliasing_iface_init(gpointer g_iface, gpointer iface_data) {
3333
3434 typedef struct _IdleConnection IdleConnection;
3535 typedef struct _IdleConnectionClass IdleConnectionClass;
36 typedef struct _IdleConnectionPrivate IdleConnectionPrivate;
3637
3738 struct _IdleConnectionClass {
3839 TpBaseConnectionClass parent_class;
4445 TpContactsMixin contacts;
4546 IdleParser *parser;
4647 GQueue *contact_info_requests;
48 IdleConnectionPrivate *priv;
4749 };
4850
4951 GType idle_connection_get_type(void);
2929 IDLE_DEBUG_NETWORK = (1 << 4),
3030 IDLE_DEBUG_PARSER = (1 << 5),
3131 IDLE_DEBUG_TEXT = (1 << 6),
32 IDLE_DEBUG_ROOMLIST = (1 << 7),
3233 } IdleDebugFlags;
3334
3435 void idle_debug_init (void);
170170 static GPtrArray *
171171 idle_im_channel_get_interfaces (TpBaseChannel *channel)
172172 {
173 GPtrArray *interfaces = g_ptr_array_sized_new (2);
173 GPtrArray *interfaces =
174 TP_BASE_CHANNEL_CLASS (idle_im_channel_parent_class)->get_interfaces (
175 channel);
174176
175177 g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_MESSAGES);
176178 g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE);
386386 {
387387 IdleIMManager *self = IDLE_IM_MANAGER (user_data);
388388 IdleIMManagerPrivate *priv = IDLE_IM_MANAGER_GET_PRIVATE (self);
389 TpHandle handle;
389 TpBaseChannel *base = TP_BASE_CHANNEL (chan);
390390
391391 tp_channel_manager_emit_channel_closed_for_object (self,
392392 TP_EXPORTABLE_CHANNEL (chan));
393393
394394 if (priv->channels)
395395 {
396 gboolean really_destroyed;
397
398 g_object_get (chan,
399 "handle", &handle,
400 "channel-destroyed", &really_destroyed,
401 NULL);
402
403 if (really_destroyed)
396 TpHandle handle = tp_base_channel_get_target_handle (base);
397
398 if (tp_base_channel_is_destroyed (base))
404399 {
405400 IDLE_DEBUG ("removing channel with handle %u", handle);
406401 g_hash_table_remove (priv->channels, GUINT_TO_POINTER (handle));
4545
4646 static void _password_iface_init(gpointer, gpointer);
4747 static void subject_iface_init(gpointer, gpointer);
48 static void destroyable_iface_init(gpointer, gpointer);
4849
4950 static void idle_muc_channel_send (GObject *obj, TpMessage *message, TpMessageSendingFlags flags);
5051 static void idle_muc_channel_close (TpBaseChannel *base);
5859 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_SUBJECT, subject_iface_init);
5960 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_ROOM_CONFIG,
6061 tp_base_room_config_iface_init);
62 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE, destroyable_iface_init);
6163 )
6264
6365 /* property enum */
130132 TP_IFACE_CHANNEL_INTERFACE_ROOM,
131133 TP_IFACE_CHANNEL_INTERFACE_SUBJECT,
132134 TP_IFACE_CHANNEL_INTERFACE_ROOM_CONFIG,
135 TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE,
133136 NULL
134137 };
135138
308311 NULL);
309312 }
310313
314 static GPtrArray *
315 idle_muc_channel_get_interfaces (TpBaseChannel *self)
316 {
317 TpBaseChannelClass *parent_class =
318 TP_BASE_CHANNEL_CLASS (idle_muc_channel_parent_class);
319 GPtrArray *interfaces = parent_class->get_interfaces (self);
320 const gchar **interface;
321
322 for (interface = muc_channel_interfaces; *interface != NULL; interface++)
323 g_ptr_array_add (interfaces, (gchar *) *interface);
324
325 return interfaces;
326 }
327
311328 static void idle_muc_channel_class_init (IdleMUCChannelClass *idle_muc_channel_class) {
312329 GObjectClass *object_class = G_OBJECT_CLASS (idle_muc_channel_class);
313330 TpBaseChannelClass *base_channel_class = TP_BASE_CHANNEL_CLASS (idle_muc_channel_class);
335352
336353 base_channel_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT;
337354 base_channel_class->target_handle_type = TP_HANDLE_TYPE_ROOM;
338 base_channel_class->interfaces = muc_channel_interfaces;
339355
340356 base_channel_class->close = idle_muc_channel_close;
341357 base_channel_class->fill_immutable_properties = idle_muc_channel_fill_immutable_properties;
342358 base_channel_class->get_object_path_suffix = idle_muc_channel_get_path_suffix;
359 base_channel_class->get_interfaces = idle_muc_channel_get_interfaces;
343360
344361 param_spec = g_param_spec_string (
345362 "server", "Room.Server", "always empty",
13241341
13251342 IDLE_DEBUG ("called on %p", self);
13261343
1344 if (priv->state == MUC_STATE_JOINED) {
1345 tp_message_mixin_set_rescued (G_OBJECT (self));
1346 tp_base_channel_reopened (base, 0);
1347 } else {
1348 /* FIXME: this is wrong if called while JOIN is in flight. */
1349 tp_base_channel_destroyed (base);
1350 }
1351 }
1352
1353
1354 static void
1355 idle_muc_channel_destroy (
1356 TpSvcChannelInterfaceDestroyable *object,
1357 DBusGMethodInvocation *context)
1358 {
1359 TpBaseChannel *base = TP_BASE_CHANNEL (object);
1360 IdleMUCChannel *self = IDLE_MUC_CHANNEL (object);
1361 IdleMUCChannelPrivate *priv = self->priv;
1362
1363 IDLE_DEBUG ("called on %p", self);
1364
13271365 if (priv->state == MUC_STATE_JOINED)
13281366 part_from_channel (self, NULL);
13291367
13301368 /* FIXME: this is wrong if called while JOIN is in flight. */
13311369 if (priv->state < MUC_STATE_JOINED)
13321370 tp_base_channel_destroyed (base);
1333 }
1334
1335 /**
1336 * idle_muc_channel_get_password_flags
1337 *
1338 * Implements DBus method GetPasswordFlags
1339 * on interface org.freedesktop.Telepathy.Channel.Interface.Password
1340 *
1341 * @error: Used to return a pointer to a GError detailing any error
1342 * that occured, DBus will throw the error only if this
1343 * function returns false.
1344 *
1345 * Returns: TRUE if successful, FALSE if an error was thrown.
1346 */
1371
1372 tp_svc_channel_interface_destroyable_return_from_destroy (context);
1373 }
1374
1375
13471376 static void idle_muc_channel_get_password_flags (TpSvcChannelInterfacePassword *iface, DBusGMethodInvocation *context) {
13481377 IdleMUCChannel *obj = IDLE_MUC_CHANNEL(iface);
13491378 IdleMUCChannelPrivate *priv;
13571386 }
13581387
13591388
1360 /**
1361 * idle_muc_channel_provide_password
1362 *
1363 * Implements DBus method ProvidePassword
1364 * on interface org.freedesktop.Telepathy.Channel.Interface.Password
1365 *
1366 * @error: Used to return a pointer to a GError detailing any error
1367 * that occured, DBus will throw the error only if this
1368 * function returns false.
1369 *
1370 * Returns: TRUE if successful, FALSE if an error was thrown.
1371 */
13721389 static void idle_muc_channel_provide_password (TpSvcChannelInterfacePassword *iface, const gchar * password, DBusGMethodInvocation *context) {
13731390 IdleMUCChannel *obj = IDLE_MUC_CHANNEL(iface);
13741391 IdleMUCChannelPrivate *priv;
15221539 IMPLEMENT (set_subject);
15231540 #undef IMPLEMENT
15241541 }
1542
1543 static void
1544 destroyable_iface_init (
1545 gpointer g_iface,
1546 gpointer iface_data G_GNUC_UNUSED)
1547 {
1548 TpSvcChannelInterfaceDestroyableClass *klass = g_iface;
1549
1550 #define IMPLEMENT(x) \
1551 tp_svc_channel_interface_destroyable_implement_##x (klass, idle_muc_channel_##x)
1552 IMPLEMENT (destroy);
1553 #undef IMPLEMENT
1554 }
675675 static void _channel_closed_cb(IdleMUCChannel *chan, gpointer user_data) {
676676 IdleMUCManager *manager = IDLE_MUC_MANAGER(user_data);
677677 IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager);
678 TpBaseChannel *base = TP_BASE_CHANNEL (chan);
678679 GSList *reqs = take_request_tokens(user_data, chan);
679680
680681 /* If there are any tokens for this channel when it closes, the request
688689
689690 g_slist_free(reqs);
690691
692 tp_channel_manager_emit_channel_closed_for_object (manager,
693 TP_EXPORTABLE_CHANNEL (chan));
694
691695 if (priv->channels) {
692 TpHandle handle;
693 g_object_get(chan, "handle", &handle, NULL);
694 g_hash_table_remove(priv->channels, GUINT_TO_POINTER(handle));
696 TpHandle handle = tp_base_channel_get_target_handle (base);
697
698 if (tp_base_channel_is_destroyed (base))
699 g_hash_table_remove(priv->channels, GUINT_TO_POINTER(handle));
700 else
701 tp_channel_manager_emit_new_channel (manager, TP_EXPORTABLE_CHANNEL (chan),
702 NULL);
695703 }
696704 }
697705
8484 {"NOTICE", "cIr:", IDLE_PARSER_PREFIXCMD_NOTICE_CHANNEL},
8585 {"NOTICE", "cIc:", IDLE_PARSER_PREFIXCMD_NOTICE_USER},
8686 {"PART", "cIr.", IDLE_PARSER_PREFIXCMD_PART},
87 {"PONG", "IIs:", IDLE_PARSER_PREFIXCMD_PONG},
8788 {"PRIVMSG", "cIr:", IDLE_PARSER_PREFIXCMD_PRIVMSG_CHANNEL},
8889 {"PRIVMSG", "cIc:", IDLE_PARSER_PREFIXCMD_PRIVMSG_USER},
8990 {"QUIT", "cI.", IDLE_PARSER_PREFIXCMD_QUIT},
118119 {"312", "IIIcs:", IDLE_PARSER_NUMERIC_WHOISSERVER},
119120 {"311", "IIIcssI:", IDLE_PARSER_NUMERIC_WHOISUSER},
120121 {"317", "IIIcd", IDLE_PARSER_NUMERIC_WHOISIDLE},
122 {"322", "IIIrd.", IDLE_PARSER_NUMERIC_LIST},
123 {"323", "I", IDLE_PARSER_NUMERIC_LISTEND},
124 {"421", "IIIs:", IDLE_PARSER_NUMERIC_UNKNOWNCOMMAND},
121125
122126 {NULL, NULL, IDLE_PARSER_LAST_MESSAGE_CODE}
123127 };
349353 for (int i = 0; i < IDLE_PARSER_LAST_MESSAGE_CODE; i++) {
350354 const MessageSpec *spec = &(message_specs[i]);
351355
352 if ((split_msg[0] != ':') && (i <= IDLE_PARSER_CMD_PING)) {
356 if ((split_msg[0] != ':') && (i <= IDLE_PARSER_LAST_NON_PREFIX_CMD)) {
353357 if (!g_ascii_strcasecmp(tokens[0], spec->str))
354358 _parse_and_forward_one(parser, tokens, spec->code, spec->format);
355 } else if (i >= IDLE_PARSER_PREFIXCMD_INVITE) {
359 } else if (i > IDLE_PARSER_LAST_NON_PREFIX_CMD) {
356360 if (!g_ascii_strcasecmp(tokens[2], spec->str))
357361 _parse_and_forward_one(parser, tokens, spec->code, spec->format);
358362 }
4848 IDLE_PARSER_CMD_ERROR = 0,
4949 IDLE_PARSER_CMD_PING,
5050
51 IDLE_PARSER_LAST_NON_PREFIX_CMD = IDLE_PARSER_CMD_PING,
52
5153 IDLE_PARSER_PREFIXCMD_INVITE,
5254 IDLE_PARSER_PREFIXCMD_JOIN,
5355 IDLE_PARSER_PREFIXCMD_KICK,
5759 IDLE_PARSER_PREFIXCMD_NOTICE_CHANNEL,
5860 IDLE_PARSER_PREFIXCMD_NOTICE_USER,
5961 IDLE_PARSER_PREFIXCMD_PART,
62 IDLE_PARSER_PREFIXCMD_PONG,
6063 IDLE_PARSER_PREFIXCMD_PRIVMSG_CHANNEL,
6164 IDLE_PARSER_PREFIXCMD_PRIVMSG_USER,
6265 IDLE_PARSER_PREFIXCMD_QUIT,
9194 IDLE_PARSER_NUMERIC_WHOISSERVER,
9295 IDLE_PARSER_NUMERIC_WHOISUSER,
9396 IDLE_PARSER_NUMERIC_WHOISIDLE,
97 IDLE_PARSER_NUMERIC_LIST,
98 IDLE_PARSER_NUMERIC_LISTEND,
99 IDLE_PARSER_NUMERIC_UNKNOWNCOMMAND,
94100
95101 IDLE_PARSER_LAST_MESSAGE_CODE
96102 } IdleParserMessageCode;
0 /*
1 * This file is part of telepathy-idle
2 *
3 * Copyright (C) 2009 Collabora Limited
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * version 2.1 as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
20 */
21
22 #include "idle-roomlist-channel.h"
23
24 #include <time.h>
25
26 #include <dbus/dbus-glib.h>
27
28 #define IDLE_DEBUG_FLAG IDLE_DEBUG_ROOMLIST
29 #include "idle-connection.h"
30 #include "idle-debug.h"
31 #include "idle-text.h"
32
33 static void idle_roomlist_channel_close (TpBaseChannel *channel);
34 static void _roomlist_iface_init (gpointer, gpointer);
35 static void connection_status_changed_cb (IdleConnection* conn, guint status, guint reason, IdleRoomlistChannel *self);
36 static IdleParserHandlerResult _rpl_list_handler (IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
37 static IdleParserHandlerResult _rpl_listend_handler (IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data);
38
39 G_DEFINE_TYPE_WITH_CODE (IdleRoomlistChannel, idle_roomlist_channel,
40 TP_TYPE_BASE_CHANNEL,
41 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_ROOM_LIST, _roomlist_iface_init);
42 )
43
44 /* private structure */
45 struct _IdleRoomlistChannelPrivate
46 {
47 IdleConnection *connection;
48
49 GPtrArray *rooms;
50 TpHandleSet *handles;
51
52 gboolean listing;
53 gboolean closed;
54 int status_changed_id;
55
56 gboolean dispose_has_run;
57 };
58
59 static void
60 idle_roomlist_channel_init (IdleRoomlistChannel *self)
61 {
62 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, IDLE_TYPE_ROOMLIST_CHANNEL,
63 IdleRoomlistChannelPrivate);
64 }
65
66 static void idle_roomlist_channel_dispose (GObject *object);
67 static void idle_roomlist_channel_finalize (GObject *object);
68
69 static void
70 idle_roomlist_channel_constructed (GObject *obj)
71 {
72 IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (obj);
73 IdleRoomlistChannelPrivate *priv = self->priv;
74 TpBaseChannel *base = TP_BASE_CHANNEL (obj);
75
76 G_OBJECT_CLASS (idle_roomlist_channel_parent_class)->constructed (obj);
77
78 tp_base_channel_register (base);
79
80 priv->connection = IDLE_CONNECTION (tp_base_channel_get_connection (TP_BASE_CHANNEL (obj)));
81
82 priv->status_changed_id = g_signal_connect (priv->connection,
83 "status-changed", (GCallback) connection_status_changed_cb,
84 obj);
85 idle_parser_add_handler (priv->connection->parser, IDLE_PARSER_NUMERIC_LIST,
86 _rpl_list_handler, obj);
87 idle_parser_add_handler (priv->connection->parser,
88 IDLE_PARSER_NUMERIC_LISTEND, _rpl_listend_handler, obj);
89
90 priv->rooms = g_ptr_array_new ();
91 priv->handles = tp_handle_set_new(tp_base_connection_get_handles (
92 TP_BASE_CONNECTION(priv->connection), TP_HANDLE_TYPE_ROOM));
93 }
94
95 static gchar *
96 idle_roomlist_channel_get_path_suffix (TpBaseChannel *chan)
97 {
98 return g_strdup ("RoomListChannel");
99 }
100
101 static void
102 idle_roomlist_channel_get_roomlist_property (
103 GObject *object,
104 GQuark iface,
105 GQuark name,
106 GValue *value,
107 gpointer getter_data)
108 {
109 g_return_if_fail (iface == TP_IFACE_QUARK_CHANNEL_TYPE_ROOM_LIST);
110 g_return_if_fail (name == g_quark_from_static_string ("Server"));
111 g_return_if_fail (G_VALUE_HOLDS_STRING (value));
112
113 g_value_set_static_string (value, "");
114 }
115
116 static void
117 idle_roomlist_channel_fill_properties (
118 TpBaseChannel *chan,
119 GHashTable *properties)
120 {
121 TpBaseChannelClass *klass = TP_BASE_CHANNEL_CLASS (idle_roomlist_channel_parent_class);
122
123 klass->fill_immutable_properties (chan, properties);
124
125 tp_dbus_properties_mixin_fill_properties_hash (
126 G_OBJECT (chan), properties,
127 TP_IFACE_CHANNEL_TYPE_ROOM_LIST, "Server",
128 NULL);
129 }
130
131 static void
132 idle_roomlist_channel_class_init (IdleRoomlistChannelClass *idle_roomlist_channel_class)
133 {
134 GObjectClass *object_class = G_OBJECT_CLASS (idle_roomlist_channel_class);
135 TpBaseChannelClass *base_channel_class = TP_BASE_CHANNEL_CLASS (idle_roomlist_channel_class);
136 static TpDBusPropertiesMixinPropImpl roomlist_props[] = {
137 { "Server", NULL, NULL },
138 { NULL }
139 };
140
141
142 g_type_class_add_private (idle_roomlist_channel_class, sizeof (IdleRoomlistChannelPrivate));
143
144 object_class->constructed = idle_roomlist_channel_constructed;
145 object_class->dispose = idle_roomlist_channel_dispose;
146 object_class->finalize = idle_roomlist_channel_finalize;
147
148 base_channel_class->channel_type = TP_IFACE_CHANNEL_TYPE_ROOM_LIST;
149 base_channel_class->target_handle_type = TP_HANDLE_TYPE_NONE;
150 base_channel_class->close = idle_roomlist_channel_close;
151 base_channel_class->fill_immutable_properties = idle_roomlist_channel_fill_properties;
152 base_channel_class->get_object_path_suffix = idle_roomlist_channel_get_path_suffix;
153
154 tp_dbus_properties_mixin_implement_interface (object_class,
155 TP_IFACE_QUARK_CHANNEL_TYPE_ROOM_LIST,
156 idle_roomlist_channel_get_roomlist_property,
157 NULL,
158 roomlist_props);
159 }
160
161
162 void
163 idle_roomlist_channel_dispose (GObject *object)
164 {
165 IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (object);
166 IdleRoomlistChannelPrivate *priv = self->priv;
167
168 g_assert (object != NULL);
169
170 if (priv->dispose_has_run)
171 return;
172
173 priv->dispose_has_run = TRUE;
174
175 if (priv->status_changed_id != 0)
176 {
177 g_signal_handler_disconnect (priv->connection, priv->status_changed_id);
178 priv->status_changed_id = 0;
179 }
180
181 if (priv->rooms)
182 {
183 g_ptr_array_free (priv->rooms, TRUE);
184 priv->rooms = NULL;
185 }
186
187 if (G_OBJECT_CLASS (idle_roomlist_channel_parent_class)->dispose)
188 G_OBJECT_CLASS (idle_roomlist_channel_parent_class)->dispose (object);
189 }
190
191
192 void
193 idle_roomlist_channel_finalize (GObject *object)
194 {
195 IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (object);
196 IdleRoomlistChannelPrivate *priv = self->priv;
197
198 if (priv->handles)
199 {
200 tp_handle_set_destroy (priv->handles);
201 }
202
203 G_OBJECT_CLASS (idle_roomlist_channel_parent_class)->finalize (object);
204 }
205
206
207 static void
208 idle_roomlist_channel_close (TpBaseChannel *channel)
209 {
210 IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (channel);
211 IdleRoomlistChannelPrivate *priv = self->priv;
212
213 idle_parser_remove_handlers_by_data (priv->connection->parser, channel);
214 tp_base_channel_destroyed (channel);
215 }
216
217
218 /**
219 * idle_roomlist_channel_get_listing_rooms
220 *
221 * Implements D-Bus method GetListingRooms
222 * on interface org.freedesktop.Telepathy.Channel.Type.RoomList
223 */
224 static void
225 idle_roomlist_channel_get_listing_rooms (TpSvcChannelTypeRoomList *iface,
226 DBusGMethodInvocation *context)
227 {
228 IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (iface);
229 IdleRoomlistChannelPrivate *priv = self->priv;;
230
231 tp_svc_channel_type_room_list_return_from_get_listing_rooms (
232 context, priv->listing);
233 }
234
235
236 /**
237 * idle_roomlist_channel_list_rooms
238 *
239 * Implements D-Bus method ListRooms
240 * on interface org.freedesktop.Telepathy.Channel.Type.RoomList
241 */
242 static void
243 idle_roomlist_channel_list_rooms (TpSvcChannelTypeRoomList *iface,
244 DBusGMethodInvocation *context)
245 {
246 IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (iface);
247 IdleRoomlistChannelPrivate *priv = self->priv;
248
249 priv->listing = TRUE;
250 tp_svc_channel_type_room_list_emit_listing_rooms (iface, TRUE);
251
252 idle_connection_send(priv->connection, "LIST");
253
254 tp_svc_channel_type_room_list_return_from_list_rooms (context);
255 }
256
257 /**
258 * idle_roomlist_channel_stop_listing
259 *
260 * Implements D-Bus method StopListing
261 * on interface org.freedesktop.Telepathy.Channel.Type.RoomList
262 */
263 static void
264 idle_roomlist_channel_stop_listing (TpSvcChannelTypeRoomList *iface,
265 DBusGMethodInvocation *context)
266 {
267 IdleRoomlistChannel *self = IDLE_ROOMLIST_CHANNEL (iface);
268 GError error = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Can't stop listing!" };
269
270 g_assert (IDLE_IS_ROOMLIST_CHANNEL (self));
271
272 dbus_g_method_return_error (context, &error);
273
274 /*
275 priv->listing = FALSE;
276 tp_svc_channel_type_room_list_emit_listing_rooms (iface, FALSE);
277
278 stop_listing (self);
279
280 tp_svc_channel_type_room_list_return_from_stop_listing (context);
281 */
282 }
283
284
285 static void
286 _roomlist_iface_init (gpointer g_iface,
287 gpointer iface_data)
288 {
289 TpSvcChannelTypeRoomListClass *klass =
290 (TpSvcChannelTypeRoomListClass *)(g_iface);
291
292 #define IMPLEMENT(x) tp_svc_channel_type_room_list_implement_##x (\
293 klass, idle_roomlist_channel_##x)
294 IMPLEMENT (get_listing_rooms);
295 IMPLEMENT (list_rooms);
296 IMPLEMENT (stop_listing);
297 #undef IMPLEMENT
298 }
299
300
301 static IdleParserHandlerResult
302 _rpl_list_handler (IdleParser *parser,
303 IdleParserMessageCode code,
304 GValueArray *args,
305 gpointer user_data)
306 {
307 IdleRoomlistChannel* self = IDLE_ROOMLIST_CHANNEL (user_data);
308 IdleRoomlistChannelPrivate *priv = self->priv;
309 GValue room = {0,};
310 GHashTable *keys;
311
312 TpHandle room_handle = g_value_get_uint (g_value_array_get_nth (args, 0));
313 TpHandleRepoIface *handl_repo =
314 tp_base_connection_get_handles(TP_BASE_CONNECTION(priv->connection),
315 TP_HANDLE_TYPE_ROOM);
316 const gchar *room_name = tp_handle_inspect(handl_repo, room_handle);
317 guint num_users = g_value_get_uint (g_value_array_get_nth (args, 1));
318 /* topic is optional */
319 const gchar *topic = "";
320 if (args->n_values > 2)
321 {
322 topic = g_value_get_string (g_value_array_get_nth (args, 2));
323 }
324
325 keys = tp_asv_new (
326 "name", G_TYPE_STRING, room_name,
327 "members", G_TYPE_UINT, num_users,
328 "subject", G_TYPE_STRING, topic,
329 NULL);
330
331 g_value_init (&room, TP_STRUCT_TYPE_ROOM_INFO);
332 g_value_take_boxed (&room,
333 dbus_g_type_specialized_construct (TP_STRUCT_TYPE_ROOM_INFO));
334
335 dbus_g_type_struct_set (&room,
336 0, room_handle,
337 1, TP_IFACE_CHANNEL_TYPE_TEXT,
338 2, keys,
339 G_MAXUINT);
340
341 IDLE_DEBUG ("adding new room signal data to pending: %s", room_name);
342 g_ptr_array_add (priv->rooms, g_value_get_boxed (&room));
343 /* retain a ref to the room handle */
344 tp_handle_set_add (priv->handles, room_handle);
345 g_hash_table_destroy (keys);
346
347 return IDLE_PARSER_HANDLER_RESULT_HANDLED;
348 }
349
350
351 static gboolean
352 emit_room_signal (IdleRoomlistChannel *self)
353 {
354 IdleRoomlistChannelPrivate *priv = self->priv;
355
356 if (!priv->listing)
357 return FALSE;
358
359 if (priv->rooms->len == 0)
360 return TRUE;
361
362 tp_svc_channel_type_room_list_emit_got_rooms (
363 (TpSvcChannelTypeRoomList *) self, priv->rooms);
364
365 while (priv->rooms->len != 0)
366 {
367 gpointer boxed = g_ptr_array_index (priv->rooms, 0);
368 g_boxed_free (TP_STRUCT_TYPE_ROOM_INFO, boxed);
369 g_ptr_array_remove_index_fast (priv->rooms, 0);
370 }
371
372 return TRUE;
373 }
374
375 static IdleParserHandlerResult
376 _rpl_listend_handler (IdleParser *parser,
377 IdleParserMessageCode code,
378 GValueArray *args,
379 gpointer user_data)
380 {
381 IdleRoomlistChannel* self = IDLE_ROOMLIST_CHANNEL (user_data);
382 IdleRoomlistChannelPrivate *priv = self->priv;
383 emit_room_signal (self);
384
385 priv->listing = FALSE;
386 tp_svc_channel_type_room_list_emit_listing_rooms (
387 (TpSvcChannelTypeRoomList *) self, FALSE);
388
389 /*
390 g_source_remove (priv->timer_source_id);
391 priv->timer_source_id = 0;
392 */
393
394 return IDLE_PARSER_HANDLER_RESULT_HANDLED;
395 }
396
397
398 static void
399 connection_status_changed_cb (IdleConnection* conn,
400 guint status,
401 guint reason,
402 IdleRoomlistChannel *self)
403 {
404 IdleRoomlistChannelPrivate *priv = self->priv;
405
406 if (status == TP_CONNECTION_STATUS_DISCONNECTED)
407 {
408 idle_parser_remove_handlers_by_data (conn->parser, self);
409 if (priv->status_changed_id != 0)
410 {
411 g_signal_handler_disconnect (conn, priv->status_changed_id);
412 priv->status_changed_id = 0;
413 }
414 }
415 }
416
417
0 /*
1 * This file is part of telepathy-idle
2 *
3 * Copyright © 2009–2012 Collabora Limited
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * version 2.1 as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
20 * Will Thompson <will.thompson@collabora.co.uk>
21 */
22
23 #ifndef __IDLE_ROOMLIST_CHANNEL_H__
24 #define __IDLE_ROOMLIST_CHANNEL_H__
25
26 #include <glib-object.h>
27 #include <telepathy-glib/telepathy-glib.h>
28
29 G_BEGIN_DECLS
30
31 typedef struct _IdleRoomlistChannel IdleRoomlistChannel;
32 typedef struct _IdleRoomlistChannelClass IdleRoomlistChannelClass;
33 typedef struct _IdleRoomlistChannelPrivate IdleRoomlistChannelPrivate;
34
35 struct _IdleRoomlistChannelClass {
36 TpBaseChannelClass parent_class;
37 };
38
39 struct _IdleRoomlistChannel {
40 TpBaseChannel parent;
41 IdleRoomlistChannelPrivate *priv;
42 };
43
44 GType idle_roomlist_channel_get_type(void);
45
46 /* TYPE MACROS */
47 #define IDLE_TYPE_ROOMLIST_CHANNEL \
48 (idle_roomlist_channel_get_type())
49 #define IDLE_ROOMLIST_CHANNEL(obj) \
50 (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_ROOMLIST_CHANNEL, IdleRoomlistChannel))
51 #define IDLE_ROOMLIST_CHANNEL_CLASS(klass) \
52 (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_ROOMLIST_CHANNEL, IdleRoomlistChannelClass))
53 #define IDLE_IS_ROOMLIST_CHANNEL(obj) \
54 (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_ROOMLIST_CHANNEL))
55 #define IDLE_IS_ROOMLIST_CHANNEL_CLASS(klass) \
56 (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_ROOMLIST_CHANNEL))
57 #define IDLE_ROOMLIST_CHANNEL_GET_CLASS(obj) \
58 (G_TYPE_INSTANCE_GET_CLASS ((obj), IDLE_TYPE_ROOMLIST_CHANNEL, IdleRoomlistChannelClass))
59
60 G_END_DECLS
61
62 #endif /* #ifndef __IDLE_ROOMLIST_CHANNEL_H__*/
63
0 /*
1 * This file is part of telepathy-idle
2 *
3 * Copyright (C) 2009 Collabora Limited
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * version 2.1 as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
20 */
21
22 #include "idle-roomlist-manager.h"
23
24 #include <telepathy-glib/channel-manager.h>
25 #include <telepathy-glib/interfaces.h>
26 #include <telepathy-glib/dbus.h>
27 #include <telepathy-glib/util.h>
28
29 #define IDLE_DEBUG_FLAG IDLE_DEBUG_ROOMLIST
30 #include "idle-connection.h"
31 #include "idle-debug.h"
32 #include "idle-roomlist-channel.h"
33 #include "idle-parser.h"
34
35 static void _roomlist_manager_iface_init (gpointer g_iface, gpointer iface_data);
36 static GObject * _roomlist_manager_constructor (GType type, guint n_props, GObjectConstructParam *props);
37 static void _roomlist_manager_dispose (GObject *object);
38
39 G_DEFINE_TYPE_WITH_CODE(IdleRoomlistManager, idle_roomlist_manager, G_TYPE_OBJECT,
40 G_IMPLEMENT_INTERFACE(TP_TYPE_CHANNEL_MANAGER, _roomlist_manager_iface_init));
41
42 /* properties */
43 enum {
44 PROP_CONNECTION = 1,
45 LAST_PROPERTY_ENUM
46 };
47
48 static const gchar * const roomlist_channel_fixed_properties[] = {
49 TP_IFACE_CHANNEL ".ChannelType",
50 TP_IFACE_CHANNEL ".TargetHandleType",
51 NULL
52 };
53
54 static const gchar * const roomlist_channel_allowed_properties[] = {
55 NULL
56 };
57
58 struct _IdleRoomlistManagerPrivate
59 {
60 IdleConnection *conn;
61 IdleRoomlistChannel *channel;
62 int status_changed_id;
63 gboolean dispose_has_run;
64 };
65
66 static void _roomlist_manager_close_all (IdleRoomlistManager *self);
67 static void connection_status_changed_cb (IdleConnection* conn, guint status, guint reason, IdleRoomlistManager *self);
68
69 static void _roomlist_manager_foreach (TpChannelManager *self, TpExportableChannelFunc func, gpointer user_data);
70 static void _roomlist_manager_foreach_class (TpChannelManager *self, TpChannelManagerChannelClassFunc func, gpointer user_data);
71
72 static gboolean _roomlist_manager_create_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties);
73 static gboolean _roomlist_manager_request_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties);
74 static gboolean _roomlist_manager_ensure_channel (TpChannelManager *self, gpointer request_token, GHashTable *request_properties);
75 static gboolean _roomlist_manager_requestotron (IdleRoomlistManager *self, gpointer request_token, GHashTable *request_properties, gboolean require_new);
76 static IdleRoomlistChannel *_roomlist_manager_new_channel (IdleRoomlistManager *self, gpointer request);
77
78 static void _roomlist_channel_closed_cb (IdleRoomlistChannel *chan, gpointer user_data);
79
80 static void
81 idle_roomlist_manager_init (IdleRoomlistManager *self)
82 {
83 IdleRoomlistManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
84 IDLE_TYPE_ROOMLIST_MANAGER, IdleRoomlistManagerPrivate);
85
86 self->priv = priv;
87 priv->channel = NULL;
88 priv->status_changed_id = 0;
89 priv->dispose_has_run = FALSE;
90 }
91
92
93 static void
94 idle_roomlist_manager_get_property (GObject *object,
95 guint property_id,
96 GValue *value,
97 GParamSpec *pspec)
98 {
99 IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (object);
100 IdleRoomlistManagerPrivate *priv = self->priv;
101
102 switch (property_id) {
103 case PROP_CONNECTION:
104 g_value_set_object (value, priv->conn);
105 break;
106
107 default:
108 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109 break;
110 }
111 }
112
113
114 static void
115 idle_roomlist_manager_set_property (GObject *object,
116 guint property_id,
117 const GValue *value,
118 GParamSpec *pspec)
119 {
120 IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (object);
121 IdleRoomlistManagerPrivate *priv = self->priv;
122
123 switch (property_id) {
124 case PROP_CONNECTION:
125 priv->conn = g_value_get_object (value);
126 break;
127
128 default:
129 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
130 break;
131 }
132 }
133
134
135 static void
136 idle_roomlist_manager_class_init (IdleRoomlistManagerClass *klass)
137 {
138 GObjectClass *object_class = G_OBJECT_CLASS (klass);
139 GParamSpec *param_spec;
140
141 g_type_class_add_private (klass, sizeof (IdleRoomlistManagerPrivate));
142
143 object_class->constructor = _roomlist_manager_constructor;
144 object_class->dispose = _roomlist_manager_dispose;
145 object_class->get_property = idle_roomlist_manager_get_property;
146 object_class->set_property = idle_roomlist_manager_set_property;
147
148 param_spec = g_param_spec_object ("connection", "IdleConnection object",
149 "The IdleConnection object that owns this Roomlist channel manager object.",
150 IDLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
151 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
152 g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
153 }
154
155
156 static GObject *
157 _roomlist_manager_constructor (GType type,
158 guint n_props,
159 GObjectConstructParam *props)
160 {
161 GObject *obj;
162 IdleRoomlistManager *self;
163 IdleRoomlistManagerPrivate *priv;
164
165 obj = G_OBJECT_CLASS (idle_roomlist_manager_parent_class)->constructor
166 (type, n_props, props);
167
168 self = IDLE_ROOMLIST_MANAGER (obj);
169 priv = self->priv;
170
171 g_return_val_if_fail (priv->conn, obj);
172
173 priv->status_changed_id = g_signal_connect (priv->conn,
174 "status-changed", (GCallback)
175 connection_status_changed_cb,
176 self);
177
178 return obj;
179 }
180
181
182 static void
183 _roomlist_manager_close_all (IdleRoomlistManager *self)
184 {
185 IdleRoomlistManagerPrivate *priv = self->priv;
186 IdleRoomlistChannel *tmp;
187
188 if (priv->channel != NULL)
189 {
190 /* use a temporary variable and set priv->channel to NULL first
191 * because unreffing this channel will cause the
192 * _roomlist_channel_closed_cb to fire, which will try to unref it
193 * again if priv->channel is not NULL */
194 tmp = priv->channel;
195 priv->channel = NULL;
196 g_object_unref(tmp);
197 }
198 if (priv->status_changed_id != 0)
199 {
200 g_signal_handler_disconnect (priv->conn, priv->status_changed_id);
201 priv->status_changed_id = 0;
202 }
203 }
204
205
206 static void
207 connection_status_changed_cb (IdleConnection* conn,
208 guint status,
209 guint reason,
210 IdleRoomlistManager *self)
211 {
212 if (status == TP_CONNECTION_STATUS_DISCONNECTED)
213 {
214 _roomlist_manager_close_all (self);
215 }
216 }
217
218
219 static void
220 _roomlist_manager_foreach (TpChannelManager *manager,
221 TpExportableChannelFunc func,
222 gpointer user_data)
223 {
224 IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (manager);
225 IdleRoomlistManagerPrivate *priv = self->priv;
226
227 if (!priv->channel)
228 {
229 IDLE_DEBUG ("Channel missing, ignoring...");
230 return;
231 }
232
233 func (TP_EXPORTABLE_CHANNEL (priv->channel), user_data);
234 }
235
236
237 static void
238 _roomlist_manager_foreach_class (TpChannelManager *self,
239 TpChannelManagerChannelClassFunc func,
240 gpointer user_data)
241 {
242 GHashTable *table = tp_asv_new (
243 roomlist_channel_fixed_properties[0], G_TYPE_STRING,
244 TP_IFACE_CHANNEL_TYPE_ROOM_LIST,
245 roomlist_channel_fixed_properties[1], G_TYPE_UINT,
246 TP_HANDLE_TYPE_NONE,
247 NULL);
248
249 func (self, table, roomlist_channel_allowed_properties, user_data);
250
251 g_hash_table_destroy (table);
252 }
253
254
255 static gboolean
256 _roomlist_manager_create_channel (TpChannelManager *self,
257 gpointer request_token,
258 GHashTable *request_properties)
259 {
260 IdleRoomlistManager *mgr = IDLE_ROOMLIST_MANAGER (self);
261
262 return _roomlist_manager_requestotron (mgr, request_token, request_properties,
263 TRUE);
264 }
265
266
267 static gboolean
268 _roomlist_manager_request_channel (TpChannelManager *self,
269 gpointer request_token,
270 GHashTable *request_properties)
271 {
272 IdleRoomlistManager *mgr = IDLE_ROOMLIST_MANAGER (self);
273
274 return _roomlist_manager_requestotron (mgr, request_token,
275 request_properties, FALSE);
276 }
277
278
279 static gboolean
280 _roomlist_manager_ensure_channel (TpChannelManager *self,
281 gpointer request_token,
282 GHashTable *request_properties)
283 {
284 IdleRoomlistManager *mgr = IDLE_ROOMLIST_MANAGER (self);
285
286 return _roomlist_manager_requestotron (mgr, request_token,
287 request_properties, FALSE);
288 }
289
290
291 static gboolean
292 _roomlist_manager_requestotron (IdleRoomlistManager *self,
293 gpointer request_token,
294 GHashTable *request_properties,
295 gboolean require_new)
296 {
297 IdleRoomlistManagerPrivate *priv = self->priv;
298 GError *error = NULL;
299
300 IDLE_DEBUG("requesting new room list channel");
301
302 if (tp_strdiff (tp_asv_get_string (request_properties,
303 TP_IFACE_CHANNEL ".ChannelType"), TP_IFACE_CHANNEL_TYPE_ROOM_LIST))
304 return FALSE;
305
306 if (tp_asv_get_uint32 (request_properties,
307 TP_IFACE_CHANNEL ".TargetHandleType", NULL) != 0)
308 return FALSE;
309
310 /* Check if there are any other properties that we don't understand */
311 if (tp_channel_manager_asv_has_unknown_properties (request_properties,
312 roomlist_channel_fixed_properties,
313 roomlist_channel_allowed_properties,
314 &error))
315 {
316 goto error;
317 }
318
319 if (priv->channel == NULL)
320 {
321 _roomlist_manager_new_channel (self, request_token);
322 return TRUE;
323 }
324
325 if (require_new)
326 {
327 g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
328 "Only one room list channel can be created");
329 goto error;
330 }
331
332 tp_channel_manager_emit_request_already_satisfied (self, request_token,
333 TP_EXPORTABLE_CHANNEL (priv->channel));
334 return TRUE;
335
336 error:
337 tp_channel_manager_emit_request_failed (self, request_token,
338 error->domain, error->code, error->message);
339 g_error_free (error);
340 return TRUE;
341 }
342
343
344 static void
345 _roomlist_channel_closed_cb (IdleRoomlistChannel *chan,
346 gpointer user_data)
347 {
348 IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (user_data);
349 IdleRoomlistManagerPrivate *priv = self->priv;
350
351 tp_channel_manager_emit_channel_closed_for_object (self,
352 TP_EXPORTABLE_CHANNEL (chan));
353
354 if (priv->channel)
355 {
356 g_assert (priv->channel == chan);
357 g_object_unref (priv->channel);
358 priv->channel = NULL;
359 }
360 }
361
362
363 static IdleRoomlistChannel *
364 _roomlist_manager_new_channel (IdleRoomlistManager *self,
365 gpointer request)
366 {
367 IdleRoomlistManagerPrivate *priv = self->priv;
368 IdleRoomlistChannel *chan;
369 GSList *requests = NULL;
370
371 g_assert (priv->channel == NULL);
372
373 IDLE_DEBUG ("Requested room list channel");
374
375 chan = g_object_new (IDLE_TYPE_ROOMLIST_CHANNEL,
376 "connection", priv->conn,
377 NULL);
378
379 if (request != NULL)
380 requests = g_slist_prepend (requests, request);
381
382 tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan),
383 requests);
384
385 g_slist_free (requests);
386
387 g_signal_connect (chan, "closed", G_CALLBACK (_roomlist_channel_closed_cb), self);
388 priv->channel = chan;
389
390 return chan;
391 }
392
393
394 static void
395 _roomlist_manager_iface_init (gpointer g_iface,
396 gpointer iface_data)
397 {
398 TpChannelManagerIface *iface = g_iface;
399
400 iface->foreach_channel = _roomlist_manager_foreach;
401 iface->foreach_channel_class = _roomlist_manager_foreach_class;
402 iface->request_channel = _roomlist_manager_request_channel;
403 iface->create_channel = _roomlist_manager_create_channel;
404 iface->ensure_channel = _roomlist_manager_ensure_channel;
405 }
406
407
408 static void
409 _roomlist_manager_dispose (GObject *object)
410 {
411 IdleRoomlistManager *self = IDLE_ROOMLIST_MANAGER (object);
412 IdleRoomlistManagerPrivate *priv = self->priv;
413
414 if (priv->dispose_has_run)
415 return;
416
417 priv->dispose_has_run = TRUE;
418
419 _roomlist_manager_close_all (self);
420
421 if (G_OBJECT_CLASS (idle_roomlist_manager_parent_class)->dispose)
422 G_OBJECT_CLASS (idle_roomlist_manager_parent_class)->dispose (object);
423 }
424
0 /*
1 * This file is part of telepathy-idle
2 *
3 * Copyright (C) 2009 Collabora Limited
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * version 2.1 as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
20 */
21
22 #ifndef __IDLE_ROOMLIST_MANAGER_H__
23 #define __IDLE_ROOMLIST_MANAGER_H__
24
25 #include <glib-object.h>
26
27 G_BEGIN_DECLS
28
29 typedef struct _IdleRoomlistManager IdleRoomlistManager;
30 typedef struct _IdleRoomlistManagerClass IdleRoomlistManagerClass;
31 typedef struct _IdleRoomlistManagerPrivate IdleRoomlistManagerPrivate;
32
33 struct _IdleRoomlistManagerClass {
34 GObjectClass parent_class;
35 };
36
37 struct _IdleRoomlistManager {
38 GObject parent;
39 IdleRoomlistManagerPrivate *priv;
40 };
41
42 GType idle_roomlist_manager_get_type (void);
43
44 #define IDLE_TYPE_ROOMLIST_MANAGER (idle_roomlist_manager_get_type())
45 #define IDLE_ROOMLIST_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), IDLE_TYPE_ROOMLIST_MANAGER, IdleRoomlistManager))
46 #define IDLE_ROOMLIST_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), IDLE_TYPE_ROOMLIST_MANAGER, IdleRoomlistManagerClass))
47 #define IDLE_IS_ROOMLIST_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), IDLE_TYPE_ROOMLIST_MANAGER))
48 #define IDLE_IS_ROOMLIST_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), IDLE_TYPE_ROOMLIST_MANAGER))
49 #define IDLE_ROOMLIST_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), IDLE_TYPE_ROOMLIST_MANAGER, IdleRoomlistManagerClass))
50
51 G_END_DECLS
52
53 #endif /* #ifndef __IDLE_ROOMLIST_MANAGER_H__ */
54
361361 user_data);
362362 }
363363
364 void idle_server_connection_force_disconnect(IdleServerConnection *conn) {
365 /* Passing a cancelled cancellable to g_io_stream_close_async() stops us
366 * waiting for a TCP-level reply from the server which we already know
367 * has gone away. Quoth the docs for g_io_stream_close():
368 *
369 * Cancelling a close will still leave the stream closed, but some
370 * streams can use a faster close that doesn't block to e.g. check
371 * errors.
372 */
373 GCancellable *kill_me_now = g_cancellable_new();
374
375 g_cancellable_cancel(kill_me_now);
376 idle_server_connection_disconnect_full_async(conn,
377 SERVER_CONNECTION_STATE_REASON_ERROR, kill_me_now, NULL, NULL);
378 g_object_unref(kill_me_now);
379 }
380
364381 void idle_server_connection_disconnect_full_async(IdleServerConnection *conn, guint reason, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) {
365382 IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn);
366383 GSimpleAsyncResult *result;
7272 gboolean idle_server_connection_connect_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error);
7373 void idle_server_connection_disconnect_async(IdleServerConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
7474 void idle_server_connection_disconnect_full_async(IdleServerConnection *conn, guint reason, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
75 void idle_server_connection_force_disconnect(IdleServerConnection *conn);
7576 gboolean idle_server_connection_disconnect_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error);
7677 void idle_server_connection_send_async(IdleServerConnection *conn, const gchar *cmd, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
7778 gboolean idle_server_connection_send_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error);
33 connect/connect-success-ssl.py \
44 connect/connect-fail.py \
55 connect/connect-fail-ssl.py \
6 connect/ping.py \
67 connect/server-quit-ignore.py \
78 connect/server-quit-noclose.py \
89 connect/socket-closed-after-handshake.py \
1415 channels/requests-create.py \
1516 channels/requests-muc.py \
1617 channels/muc-channel-topic.py \
18 channels/muc-destroy.py \
19 channels/room-list-channel.py \
20 channels/room-list-multiple.py \
1721 messages/accept-invalid-nicks.py \
1822 messages/contactinfo-request.py \
23 messages/invalid-utf8.py \
1924 messages/messages-iface.py \
2025 messages/message-order.py \
2126 messages/leading-space.py \
270270 connect/connect-success-ssl.py \
271271 connect/connect-fail.py \
272272 connect/connect-fail-ssl.py \
273 connect/ping.py \
273274 connect/server-quit-ignore.py \
274275 connect/server-quit-noclose.py \
275276 connect/socket-closed-after-handshake.py \
281282 channels/requests-create.py \
282283 channels/requests-muc.py \
283284 channels/muc-channel-topic.py \
285 channels/muc-destroy.py \
286 channels/room-list-channel.py \
287 channels/room-list-multiple.py \
284288 messages/accept-invalid-nicks.py \
285289 messages/contactinfo-request.py \
290 messages/invalid-utf8.py \
286291 messages/messages-iface.py \
287292 messages/message-order.py \
288293 messages/leading-space.py \
0 """
1 Tests Destroy()ing a MUC.
2 """
3
4 from servicetest import call_async, wrap_channel, EventPattern, assertLength
5 from idletest import exec_test
6 import constants as cs
7
8 CHANNEL = "#everythingyoutouch"
9
10 def join(q, bus, conn):
11 call_async(q, conn.Requests, "CreateChannel", {
12 cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
13 cs.ROOM_NAME: CHANNEL,
14 })
15 q.expect('stream-JOIN')
16 event = q.expect('dbus-return', method='CreateChannel')
17 path, props = event.value
18 return wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
19 ['Destroyable', 'Messages'])
20
21 def test(q, bus, conn, stream):
22 conn.Connect()
23 q.expect('dbus-signal', signal='StatusChanged',
24 args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
25
26 chan = join(q, bus, conn)
27
28 stream.sendMessage('PRIVMSG', CHANNEL, ":who's underlined you?", prefix='marnie')
29 q.expect('dbus-signal', signal='MessageReceived')
30
31 # Without acking the message, destroy the channel.
32 call_async(q, chan.Destroyable, "Destroy")
33 q.expect_many(
34 EventPattern('stream-PART'),
35 EventPattern('dbus-signal', signal='Closed', path=chan.object_path),
36 EventPattern('dbus-signal', signal='ChannelClosed', args=[chan.object_path]),
37 EventPattern('dbus-return', method='Destroy'),
38 )
39
40 # Now Create it again. If we haven't actually left the channel, this will
41 # fail.
42 chan = join(q, bus, conn)
43
44 # The message should be gone.
45 messages = chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages')
46 assertLength(0, messages)
47
48
49 if __name__ == '__main__':
50 exec_test(test)
22 """
33
44 import functools
5 from idletest import exec_test, BaseIRCServer
5 from idletest import exec_test, BaseIRCServer, sync_stream
66 from servicetest import (
7 EventPattern, call_async, sync_dbus, make_channel_proxy, assertEquals,
8 assertSameSets, assertContains,
7 EventPattern, call_async, sync_dbus, wrap_channel, assertEquals,
8 assertSameSets, assertContains, assertLength,
99 )
1010 import constants as cs
1111 import dbus
8888 cs.CHANNEL_IFACE_ROOM,
8989 cs.CHANNEL_IFACE_SUBJECT,
9090 cs.CHANNEL_IFACE_ROOM_CONFIG,
91 cs.CHANNEL_IFACE_DESTROYABLE,
9192 ], props[cs.INTERFACES])
9293 assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM
9394 assert props[cs.TARGET_ID] == '#idletest'
121122 assert len(chans) == 1
122123 assert chans[0] == (path, props)
123124
124 chan = make_channel_proxy(conn, path, 'Channel')
125 chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
126 ['Destroyable', 'Messages'])
127
128 # Put an unacknowledged message into the channel
129 stream.sendMessage('PRIVMSG', '#idletest', ':oi oi', prefix='lol')
130 q.expect('dbus-signal', signal='MessageReceived', path=path)
131
132 # Make sure Close()ing the channel makes it respawn. This avoids the old
133 # bug where empathy-chat crashing booted you out of all your channels.
134 patterns = [EventPattern('stream-PART')]
135 q.forbid_events(patterns)
136 chan.Close()
137 q.expect('dbus-signal', signal='Closed', path=chan.object_path)
138 e = q.expect('dbus-signal', signal='NewChannels')
139
140 path, props = e.args[0][0]
141 assertEquals(chan.object_path, path)
142 # We requested the channel originally, but we didn't request it popping
143 # back up.
144 assertEquals(0, props[cs.INITIATOR_HANDLE])
145 assert not props[cs.REQUESTED]
146
147 # The unacknowledged message should still be there and be marked as rescued.
148 messages = chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages')
149 assertLength(1, messages)
150 assert messages[0][0]['rescued'], messages[0]
151
152 # Check that ensuring a respawned channel does what you'd expect.
153 ec_yours, ec_path, ec_props = conn.EnsureChannel(request,
154 dbus_interface=cs.CONN_IFACE_REQUESTS)
155 assert not ec_yours
156 assertEquals(chan.object_path, ec_path)
157 assertEquals(props, ec_props)
158
159 sync_stream(q, stream)
160 q.unforbid_events(patterns)
161
125162 chan.RemoveMembers([self_handle], "bye bye cruel\r\nworld",
126163 dbus_interface=cs.CHANNEL_IFACE_GROUP)
127164
0
1 """
2 Test getting a room-list channel
3 """
4
5 from idletest import exec_test, BaseIRCServer
6 from servicetest import EventPattern, call_async, tp_name_prefix, tp_path_prefix, assertEquals
7 import dbus
8 import constants as cs
9
10 TEST_CHANNELS = (
11 ('#foo', 4, 'discussion about foo'),
12 ('#bar', 8, 'discussion about bar'),
13 ('#baz', 230, '') #empty topic
14 )
15
16 def check_rooms(received_rooms):
17 for room in received_rooms:
18 assert room[1] == tp_name_prefix + '.Channel.Type.Text'
19 info = room[2]
20 found = False
21 for r in TEST_CHANNELS:
22 if r[0] == info['name']:
23 found = True
24 assert r[1] == info['members'] and r[2] == info['subject']
25 break;
26 assert found
27 return True
28
29
30 class RoomListServer(BaseIRCServer):
31 def handleLIST(self, args, prefix):
32 for chan in TEST_CHANNELS:
33 self.sendMessage('322', '%s %s %d :%s' % (self.nick, chan[0], chan[1], chan[2]),
34 prefix="idle.test.server")
35 self.sendMessage('323', '%s :End of /LIST' % self.nick, prefix="idle.test.server")
36 pass
37
38 def test(q, bus, conn, stream):
39 conn.Connect()
40 q.expect_many(
41 EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]),
42 EventPattern('irc-connected'))
43 q.expect('dbus-signal', signal='SelfHandleChanged',
44 args=[1L])
45
46 call_async(q, conn, 'CreateChannel',
47 { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_ROOM_LIST },
48 dbus_interface=cs.CONN_IFACE_REQUESTS)
49 ret = q.expect('dbus-return', method='CreateChannel')
50 path, properties = ret.value
51 assertEquals(cs.CHANNEL_TYPE_ROOM_LIST, properties[cs.CHANNEL_TYPE])
52
53 def looks_like_a_room_list(event):
54 channels, = event.args
55 if len(channels) != 1:
56 return False
57 path, props = channels[0]
58
59 return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_ROOM_LIST and \
60 props[cs.TARGET_HANDLE_TYPE] == cs.HT_NONE and \
61 props[cs.TARGET_ID] == ''
62
63 e = q.expect('dbus-signal', signal='NewChannels',
64 predicate=looks_like_a_room_list)
65
66 chan = bus.get_object(conn.bus_name, path)
67 list_chan = dbus.Interface(chan, tp_name_prefix + u'.Channel.Type.RoomList')
68 list_chan.ListRooms();
69 q.expect('dbus-signal', signal='GotRooms', predicate=lambda x:check_rooms(x.args[0]))
70
71 call_async(q, conn, 'Disconnect')
72 q.expect_many(
73 EventPattern('dbus-return', method='Disconnect'),
74 EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]))
75 return True
76
77 if __name__ == '__main__':
78 exec_test(test, protocol=RoomListServer)
79
0
1 """
2 Test getting multiple room-list channels. telepathy-idle internally only
3 creates a single roomlist channel (since there's not really any reason to have
4 more than one) and passes that channel out whenever somebody asks for it.
5
6 This test just excercises the case where we've already created the channel and
7 we just need to hand it out to the next requestor
8 """
9
10 from idletest import exec_test
11 from servicetest import EventPattern, call_async, assertEquals
12 import constants as cs
13
14 def test(q, bus, conn, stream):
15 conn.Connect()
16 q.expect_many(
17 EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]),
18 EventPattern('irc-connected'))
19 q.expect('dbus-signal', signal='SelfHandleChanged',
20 args=[1L])
21
22 # request a roomlist channel
23 call_async(q, conn, 'CreateChannel',
24 { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_ROOM_LIST },
25 dbus_interface=cs.CONN_IFACE_REQUESTS)
26 ret = q.expect('dbus-return', method='CreateChannel')
27 path, properties = ret.value
28 assertEquals(cs.CHANNEL_TYPE_ROOM_LIST, properties[cs.CHANNEL_TYPE])
29
30 # verify that a new channel was created and signalled
31 def looks_like_a_room_list(event):
32 channels, = event.args
33 if len(channels) != 1:
34 return False
35 path, props = channels[0]
36
37 return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_ROOM_LIST and \
38 props[cs.TARGET_HANDLE_TYPE] == cs.HT_NONE and \
39 props[cs.TARGET_ID] == ''
40
41 e = q.expect('dbus-signal', signal='NewChannels',
42 predicate=looks_like_a_room_list)
43
44 # FIXME: this is pretty questionable.
45
46 # try to request another roomlist channel
47 call_async(q, conn, 'EnsureChannel',
48 { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_ROOM_LIST },
49 dbus_interface=cs.CONN_IFACE_REQUESTS)
50 ret = q.expect('dbus-return', method='EnsureChannel')
51 yours, path2, properties2 = ret.value
52
53 # assert that it returns the same channel
54 assertEquals(path, path2)
55 assert not yours
56
57 call_async(q, conn, 'Disconnect')
58 q.expect_many(
59 EventPattern('dbus-return', method='Disconnect'),
60 EventPattern('dbus-signal', signal='ChannelClosed', args=[path]),
61 EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]))
62 return True
63
64 if __name__ == '__main__':
65 exec_test(test)
66
0 """
1 Test Idle sending PINGs and timing out if it doesn't get a reply.
2 """
3
4 from idletest import exec_test
5 from servicetest import assertLength, EventPattern
6 import constants as cs
7
8 def test(q, bus, conn, stream):
9 conn.Connect()
10 q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
11
12 e = q.expect('stream-PING')
13 assertLength(1, e.data)
14 timestamp = e.data[0]
15 stream.sendMessage('PONG', 'idle.test.server', ':%s' % timestamp,
16 prefix='idle.test.server')
17
18 q.expect('stream-PING')
19 # If we don't answer Idle's ping, after some period of time Idle should
20 # give up and close the connection.
21 q.expect_many(
22 EventPattern('irc-disconnected'),
23 EventPattern('dbus-signal', signal='StatusChanged',
24 args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NETWORK_ERROR]),
25 )
26
27 if __name__ == '__main__':
28 # We expect Idle to blow up the connection after three intervals without a
29 # reply. So the default 5-second test timeout *should* just be enough, but
30 # let's not risk it.
31 exec_test(test, timeout=10, params={
32 'keepalive-interval': 1,
33 })
34
3838 CHANNEL_TYPE_STREAMED_MEDIA = CHANNEL + ".Type.StreamedMedia"
3939 CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text"
4040 CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer"
41 CHANNEL_TYPE_ROOM_LIST = CHANNEL + ".Type.RoomList"
4142 CHANNEL_TYPE_SERVER_AUTHENTICATION = \
4243 CHANNEL + ".Type.ServerAuthentication.DRAFT"
4344 CHANNEL_TYPE_SERVER_TLS_CONNECTION = \
101101 self.rooms.remove(room)
102102 except ValueError:
103103 pass
104 self.sendPart(room, self.nick, args[1])
104
105 try:
106 message = args[1]
107 except IndexError:
108 message = None
109
110 self.sendPart(room, self.nick, message)
105111
106112 def sendJoin(self, room, members=[]):
107113 members.append(self.nick)
0 # coding=utf-8
1 """
2 Test that incoming messages containing well-formed but invalid UTF-8 code
3 points don't make Idle fall off the bus. This is a regression test for
4 <https://bugs.freedesktop.org/show_bug.cgi?id=30741>.
5 """
6
7 from idletest import exec_test
8 from servicetest import assertEquals
9
10 def test(q, bus, conn, stream):
11 conn.Connect()
12 q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
13
14 test_with_message(q, stream, ["I'm no ", " Buddhist"])
15 # Check that valid exotic characters don't get lost
16 test_with_message(q, stream, [u"björk"] * 5)
17
18 test_with_message(q, stream, ["", "lolllllll"])
19 test_with_message(q, stream, ["hello", ""])
20 test_with_message(q, stream, "I am a stabbing robot".split(" "))
21
22 # This is the UTF-8 encoding of U+FDD2, which is not a valid Unicode character.
23 WELL_FORMED_BUT_INVALID_UTF8_BYTES = "\xef\xb7\x92"
24
25 def test_with_message(q, stream, parts):
26 invalid_utf8 = WELL_FORMED_BUT_INVALID_UTF8_BYTES.join(
27 part.encode('utf-8') for part in parts)
28
29 # Idle's default character set is UTF-8. We send it a message which is
30 # basically UTF-8, except that one of its code points is invalid.
31 stream.sendMessage('PRIVMSG', stream.nick, ':%s' % invalid_utf8,
32 prefix='remoteuser')
33
34 # Idle should signal that *something* was received. If it hasn't validated
35 # & sanitized the message properly, the dbus-daemon will kick it off.
36 signal = q.expect('dbus-signal', signal='MessageReceived')
37
38 message_parts = signal.args[0]
39 text_plain = message_parts[1]
40 content = text_plain['content']
41
42 # Don't make any assumption about how many U+FFFD REPLACEMENT CHARACTERs
43 # are used to replace surprising bytes.
44 received_parts = [ part for part in content.split(u"\ufffd")
45 if part != u''
46 ]
47 assertEquals(filter(lambda s: s != u'', parts), received_parts)
48
49 if __name__ == '__main__':
50 exec_test(test)
51