Imported Upstream version 0.1.13
Sjoerd Simons
11 years ago
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 | ||
0 | 290 | commit 81cffc1bd9d0e1e8778a392e23dd4f100e09a761 |
1 | 291 | Author: Will Thompson <will.thompson@collabora.co.uk> |
2 | 292 | Date: 2012-08-02 14:05:41 +0100 |
457 | 747 | Set G_MESSAGES_DEBUG during testing |
458 | 748 | |
459 | 749 | 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. | |
460 | 826 | |
461 | 827 | commit 2479ef3d6052ec03eb998496cb32f41f54f5799c |
462 | 828 | 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 | ||
0 | 39 | telepathy-idle 0.1.12 (2012-08-02) |
1 | 40 | ================================== |
2 | 41 |
0 | 0 | #! /bin/sh |
1 | 1 | # 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. | |
3 | 3 | # |
4 | 4 | # Report bugs to <https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=idle>. |
5 | 5 | # |
590 | 590 | # Identity of this package. |
591 | 591 | PACKAGE_NAME='telepathy-idle' |
592 | 592 | 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' | |
595 | 595 | PACKAGE_BUGREPORT='https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=idle' |
596 | 596 | PACKAGE_URL='' |
597 | 597 | |
1357 | 1357 | # Omit some internal or obsolete options to make the list less imposing. |
1358 | 1358 | # This message is too long to be a string in the A/UX 3.1 sh. |
1359 | 1359 | 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. | |
1361 | 1361 | |
1362 | 1362 | Usage: $0 [OPTION]... [VAR=VALUE]... |
1363 | 1363 | |
1427 | 1427 | |
1428 | 1428 | if test -n "$ac_init_help"; then |
1429 | 1429 | 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:";; | |
1431 | 1431 | esac |
1432 | 1432 | cat <<\_ACEOF |
1433 | 1433 | |
1555 | 1555 | test -n "$ac_init_help" && exit $ac_status |
1556 | 1556 | if $ac_init_version; then |
1557 | 1557 | cat <<\_ACEOF |
1558 | telepathy-idle configure 0.1.12 | |
1558 | telepathy-idle configure 0.1.13 | |
1559 | 1559 | generated by GNU Autoconf 2.69 |
1560 | 1560 | |
1561 | 1561 | Copyright (C) 2012 Free Software Foundation, Inc. |
1833 | 1833 | This file contains any messages produced by compilers while |
1834 | 1834 | running configure, to aid debugging if configure makes a mistake. |
1835 | 1835 | |
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 | |
1837 | 1837 | generated by GNU Autoconf 2.69. Invocation command line was |
1838 | 1838 | |
1839 | 1839 | $ $0 $@ |
2694 | 2694 | |
2695 | 2695 | # Define the identity of the package. |
2696 | 2696 | PACKAGE='telepathy-idle' |
2697 | VERSION='0.1.12' | |
2697 | VERSION='0.1.13' | |
2698 | 2698 | |
2699 | 2699 | |
2700 | 2700 | cat >>confdefs.h <<_ACEOF |
14241 | 14241 | # report actual input values of CONFIG_FILES etc. instead of their |
14242 | 14242 | # values after options handling. |
14243 | 14243 | 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 | |
14245 | 14245 | generated by GNU Autoconf 2.69. Invocation command line was |
14246 | 14246 | |
14247 | 14247 | CONFIG_FILES = $CONFIG_FILES |
14307 | 14307 | cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 |
14308 | 14308 | ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" |
14309 | 14309 | ac_cs_version="\\ |
14310 | telepathy-idle config.status 0.1.12 | |
14310 | telepathy-idle config.status 0.1.13 | |
14311 | 14311 | configured by $0, generated by GNU Autoconf 2.69, |
14312 | 14312 | with options \\"\$ac_cs_config\\" |
14313 | 14313 |
10 | 10 | |
11 | 11 | m4_define([idle_major_version], [0]) |
12 | 12 | m4_define([idle_minor_version], [1]) |
13 | m4_define([idle_micro_version], [12]) | |
13 | m4_define([idle_micro_version], [13]) | |
14 | 14 | m4_define([idle_nano_version], [0]) |
15 | 15 | |
16 | 16 | m4_define([idle_base_version], |
41 | 41 | $(tools_dir)/doc-generator.xsl \ |
42 | 42 | $< > $@ |
43 | 43 | |
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 \ | |
45 | 48 | $(tools_dir)/glib-ginterface-gen.py |
46 | 49 | $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ |
47 | 50 | --filename=_gen/svc --signal-marshal-prefix=_idle_ext \ |
589 | 589 | $(tools_dir)/doc-generator.xsl \ |
590 | 590 | $< > $@ |
591 | 591 | |
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 \ | |
593 | 596 | $(tools_dir)/glib-ginterface-gen.py |
594 | 597 | $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ |
595 | 598 | --filename=_gen/svc --signal-marshal-prefix=_idle_ext \ |
29 | 29 | idle-parser.h \ |
30 | 30 | protocol.c \ |
31 | 31 | protocol.h \ |
32 | idle-roomlist-channel.h \ | |
33 | idle-roomlist-channel.c \ | |
34 | idle-roomlist-manager.h \ | |
35 | idle-roomlist-manager.c \ | |
32 | 36 | idle-server-connection.c \ |
33 | 37 | idle-server-connection.h \ |
34 | 38 | idle-text.h \ |
76 | 76 | idle-debug.lo idle-handles.lo idle-im-channel.lo \ |
77 | 77 | idle-im-manager.lo idle-muc-channel.lo idle-muc-manager.lo \ |
78 | 78 | room-config.lo idle-parser.lo protocol.lo \ |
79 | idle-roomlist-channel.lo idle-roomlist-manager.lo \ | |
79 | 80 | idle-server-connection.lo idle-text.lo |
80 | 81 | nodist_libidle_convenience_la_OBJECTS = |
81 | 82 | libidle_convenience_la_OBJECTS = $(am_libidle_convenience_la_OBJECTS) \ |
331 | 332 | idle-parser.h \ |
332 | 333 | protocol.c \ |
333 | 334 | protocol.h \ |
335 | idle-roomlist-channel.h \ | |
336 | idle-roomlist-channel.c \ | |
337 | idle-roomlist-manager.h \ | |
338 | idle-roomlist-manager.c \ | |
334 | 339 | idle-server-connection.c \ |
335 | 340 | idle-server-connection.h \ |
336 | 341 | idle-text.h \ |
485 | 490 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-muc-channel.Plo@am__quote@ |
486 | 491 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-muc-manager.Plo@am__quote@ |
487 | 492 | @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@ | |
488 | 495 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-server-connection.Plo@am__quote@ |
489 | 496 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle-text.Plo@am__quote@ |
490 | 497 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle.Po@am__quote@ |
44 | 44 | #include "idle-handles.h" |
45 | 45 | #include "idle-im-manager.h" |
46 | 46 | #include "idle-muc-manager.h" |
47 | #include "idle-roomlist-manager.h" | |
47 | 48 | #include "idle-parser.h" |
48 | 49 | #include "idle-server-connection.h" |
49 | 50 | |
50 | 51 | #include "extensions/extensions.h" /* Renaming */ |
52 | ||
53 | #define DEFAULT_KEEPALIVE_INTERVAL 30 /* sec */ | |
54 | #define MISSED_KEEPALIVES_BEFORE_DISCONNECTING 3 | |
51 | 55 | |
52 | 56 | /* From RFC 2813 : |
53 | 57 | * This in essence means that the client may send one (1) message every |
54 | 58 | * two (2) seconds without being adversely affected. Services MAY also |
55 | 59 | * be subject to this mechanism. |
56 | 60 | */ |
57 | ||
58 | #define DEFAULT_KEEPALIVE_INTERVAL 30 /* sec */ | |
59 | ||
60 | 61 | #define MSG_QUEUE_TIMEOUT 2 |
61 | 62 | static gboolean flush_queue_faster = FALSE; |
62 | 63 | |
138 | 139 | LAST_PROPERTY_ENUM |
139 | 140 | }; |
140 | 141 | |
141 | /* private structure */ | |
142 | typedef struct _IdleConnectionPrivate IdleConnectionPrivate; | |
143 | 142 | struct _IdleConnectionPrivate { |
144 | 143 | /* |
145 | 144 | * network connection |
146 | 145 | */ |
147 | ||
148 | 146 | IdleServerConnection *conn; |
149 | 147 | 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; | |
150 | 153 | |
151 | 154 | /* IRC connection properties */ |
152 | 155 | char *nickname; |
197 | 200 | TpSimplePasswordManager *password_manager; |
198 | 201 | }; |
199 | 202 | |
200 | #define IDLE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDLE_TYPE_CONNECTION, IdleConnectionPrivate)) | |
201 | ||
202 | 203 | static void _iface_create_handle_repos(TpBaseConnection *self, TpHandleRepoIface **repos); |
203 | 204 | static GPtrArray *_iface_create_channel_managers(TpBaseConnection *self); |
204 | 205 | static gchar *_iface_get_unique_connection_name(TpBaseConnection *self); |
211 | 212 | static IdleParserHandlerResult _nick_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); |
212 | 213 | static IdleParserHandlerResult _nickname_in_use_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); |
213 | 214 | 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); | |
214 | 217 | static IdleParserHandlerResult _version_privmsg_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); |
215 | 218 | static IdleParserHandlerResult _welcome_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); |
216 | 219 | static IdleParserHandlerResult _whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data); |
223 | 226 | static void connection_connect_cb(IdleConnection *conn, gboolean success, TpConnectionStatusReason fail_reason); |
224 | 227 | static void connection_disconnect_cb(IdleConnection *conn, TpConnectionStatusReason reason); |
225 | 228 | 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); | |
227 | 230 | |
228 | 231 | static void idle_connection_add_queue_timeout (IdleConnection *self); |
229 | 232 | static void idle_connection_clear_queue_timeout (IdleConnection *self); |
235 | 238 | GHashTable *attributes_hash); |
236 | 239 | |
237 | 240 | 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; | |
240 | 244 | priv->sconn_status = SERVER_CONNECTION_STATE_NOT_CONNECTED; |
241 | 245 | priv->msg_queue = g_queue_new(); |
242 | 246 | |
257 | 261 | } |
258 | 262 | |
259 | 263 | 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; | |
261 | 266 | |
262 | 267 | switch (prop_id) { |
263 | 268 | case PROP_NICKNAME: |
318 | 323 | } |
319 | 324 | |
320 | 325 | 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; | |
322 | 328 | |
323 | 329 | switch (prop_id) { |
324 | 330 | case PROP_NICKNAME: |
373 | 379 | |
374 | 380 | static void idle_connection_dispose (GObject *object) { |
375 | 381 | IdleConnection *self = IDLE_CONNECTION(object); |
376 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(self); | |
382 | IdleConnectionPrivate *priv = self->priv; | |
377 | 383 | |
378 | 384 | if (priv->dispose_has_run) |
379 | 385 | return; |
406 | 412 | } |
407 | 413 | |
408 | 414 | 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; | |
410 | 417 | IdleOutputPendingMsg *msg; |
411 | 418 | |
412 | 419 | idle_contact_info_finalize(object); |
507 | 514 | flush_queue_faster = TRUE; |
508 | 515 | } |
509 | 516 | |
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; | |
512 | 520 | GPtrArray *managers = g_ptr_array_sized_new(1); |
513 | 521 | GObject *manager; |
514 | 522 | |
518 | 526 | manager = g_object_new(IDLE_TYPE_MUC_MANAGER, "connection", self, NULL); |
519 | 527 | g_ptr_array_add(managers, manager); |
520 | 528 | |
521 | priv->password_manager = tp_simple_password_manager_new(self); | |
529 | priv->password_manager = tp_simple_password_manager_new(base); | |
522 | 530 | 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); | |
523 | 534 | |
524 | 535 | return managers; |
525 | 536 | } |
531 | 542 | idle_handle_repos_init(repos); |
532 | 543 | } |
533 | 544 | |
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; | |
536 | 548 | |
537 | 549 | return g_strdup_printf("%s@%s%p", priv->nickname, priv->server, self); |
538 | 550 | } |
539 | 551 | |
540 | 552 | static gboolean _finish_shutdown_idle_func(gpointer data) { |
541 | 553 | 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; | |
543 | 556 | if (priv->force_disconnect_id != 0) { |
544 | 557 | g_source_remove(priv->force_disconnect_id); |
545 | 558 | } |
553 | 566 | _force_disconnect (gpointer data) |
554 | 567 | { |
555 | 568 | 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); | |
558 | 573 | return FALSE; |
559 | 574 | } |
560 | 575 | |
561 | 576 | static void _iface_disconnected(TpBaseConnection *self) { |
562 | 577 | IdleConnection *conn = IDLE_CONNECTION(self); |
563 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
578 | IdleConnectionPrivate *priv = conn->priv; | |
564 | 579 | |
565 | 580 | /* we never got around to actually creating the connection |
566 | 581 | * iface object because we were still trying to connect, so |
579 | 594 | priv->force_disconnect_id = g_timeout_add_seconds(2, _force_disconnect, conn); |
580 | 595 | } |
581 | 596 | |
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; | |
584 | 600 | |
585 | 601 | if (priv->quitting) |
586 | 602 | return; |
589 | 605 | * iface object because we were still trying to connect, so |
590 | 606 | * don't try to send any traffic down it */ |
591 | 607 | if (priv->conn == NULL) { |
592 | g_idle_add(_finish_shutdown_idle_func, self);; | |
608 | g_idle_add(_finish_shutdown_idle_func, self); | |
593 | 609 | } else { |
594 | 610 | idle_server_connection_disconnect_async(priv->conn, NULL, NULL, NULL); |
595 | 611 | } |
623 | 639 | static void _password_prompt_cb(GObject *source, GAsyncResult *result, gpointer user_data) { |
624 | 640 | IdleConnection *conn = user_data; |
625 | 641 | TpBaseConnection *base_conn = TP_BASE_CONNECTION(conn); |
626 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
642 | IdleConnectionPrivate *priv = conn->priv; | |
627 | 643 | const GString *password; |
628 | 644 | GError *error = NULL; |
629 | 645 | |
650 | 666 | |
651 | 667 | static gboolean _iface_start_connecting(TpBaseConnection *self, GError **error) { |
652 | 668 | IdleConnection *conn = IDLE_CONNECTION(self); |
653 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
669 | IdleConnectionPrivate *priv = conn->priv; | |
654 | 670 | |
655 | 671 | g_assert(priv->nickname != NULL); |
656 | 672 | g_assert(priv->server != NULL); |
674 | 690 | static void _connection_connect_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) { |
675 | 691 | IdleServerConnection *sconn = IDLE_SERVER_CONNECTION(source_object); |
676 | 692 | IdleConnection *conn = IDLE_CONNECTION(user_data); |
677 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
693 | IdleConnectionPrivate *priv = conn->priv; | |
678 | 694 | GError *error = NULL; |
679 | 695 | |
680 | 696 | if (!idle_server_connection_connect_finish(sconn, res, &error)) { |
696 | 712 | idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_WHOISUSER, _whois_user_handler, conn); |
697 | 713 | |
698 | 714 | 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); | |
699 | 717 | |
700 | 718 | idle_parser_add_handler_with_priority(conn->parser, IDLE_PARSER_PREFIXCMD_NICK, _nick_handler, conn, IDLE_PARSER_HANDLER_PRIORITY_FIRST); |
701 | 719 | idle_parser_add_handler(conn->parser, IDLE_PARSER_PREFIXCMD_PRIVMSG_USER, _version_privmsg_handler, conn); |
704 | 722 | } |
705 | 723 | |
706 | 724 | static void _start_connecting_continue(IdleConnection *conn) { |
707 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
725 | IdleConnectionPrivate *priv = conn->priv; | |
708 | 726 | IdleServerConnection *sconn; |
709 | 727 | |
710 | 728 | if (tp_str_empty(priv->realname)) { |
735 | 753 | static gboolean keepalive_timeout_cb(gpointer user_data); |
736 | 754 | |
737 | 755 | 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; | |
739 | 757 | TpConnectionStatusReason tp_reason; |
740 | 758 | |
741 | 759 | /* cancel scheduled forced disconnect since we are now disconnected */ |
791 | 809 | } |
792 | 810 | |
793 | 811 | 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); | |
797 | 813 | idle_parser_receive(conn->parser, converted); |
798 | 814 | |
799 | 815 | g_free(converted); |
801 | 817 | |
802 | 818 | static gboolean keepalive_timeout_cb(gpointer user_data) { |
803 | 819 | IdleConnection *conn = IDLE_CONNECTION(user_data); |
804 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
820 | IdleConnectionPrivate *priv = conn->priv; | |
805 | 821 | 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) { | |
809 | 826 | priv->keepalive_timeout = 0; |
810 | 827 | return FALSE; |
811 | 828 | } |
812 | 829 | |
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); | |
815 | 855 | _send_with_priority(conn, cmd, SERVER_CMD_MIN_PRIORITY); |
816 | 856 | |
817 | 857 | return TRUE; |
820 | 860 | static void _msg_queue_timeout_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) { |
821 | 861 | IdleServerConnection *sconn = IDLE_SERVER_CONNECTION(source_object); |
822 | 862 | IdleConnection *conn = IDLE_CONNECTION (user_data); |
823 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
863 | IdleConnectionPrivate *priv = conn->priv; | |
824 | 864 | GError *error = NULL; |
825 | 865 | |
826 | 866 | priv->msg_sending = FALSE; |
836 | 876 | |
837 | 877 | static gboolean msg_queue_timeout_cb(gpointer user_data) { |
838 | 878 | IdleConnection *conn = IDLE_CONNECTION(user_data); |
839 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
879 | IdleConnectionPrivate *priv = conn->priv; | |
840 | 880 | IdleOutputPendingMsg *output_msg; |
841 | 881 | |
842 | 882 | IDLE_DEBUG("called"); |
859 | 899 | return FALSE; |
860 | 900 | } |
861 | 901 | |
902 | priv->msg_sending = TRUE; | |
862 | 903 | idle_server_connection_send_async(priv->conn, output_msg->message, NULL, _msg_queue_timeout_ready, conn); |
863 | 904 | idle_output_pending_msg_free (output_msg); |
864 | priv->msg_sending = TRUE; | |
865 | 905 | |
866 | 906 | return TRUE; |
867 | 907 | } |
869 | 909 | static void |
870 | 910 | idle_connection_add_queue_timeout (IdleConnection *self) |
871 | 911 | { |
872 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self); | |
912 | IdleConnectionPrivate *priv = self->priv; | |
873 | 913 | |
874 | 914 | if (priv->msg_queue_timeout == 0) |
875 | 915 | { |
893 | 933 | static void |
894 | 934 | idle_connection_clear_queue_timeout (IdleConnection *self) |
895 | 935 | { |
896 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE (self); | |
936 | IdleConnectionPrivate *priv = self->priv; | |
897 | 937 | |
898 | 938 | if (priv->msg_queue_timeout != 0) |
899 | 939 | { |
906 | 946 | * Queue a IRC command for sending, clipping it to IRC_MSG_MAXLEN bytes and appending the required <CR><LF> to it |
907 | 947 | */ |
908 | 948 | 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; | |
910 | 950 | gchar cmd[IRC_MSG_MAXLEN + 3]; |
911 | 951 | int len; |
912 | 952 | gchar *converted; |
946 | 986 | gsize |
947 | 987 | idle_connection_get_max_message_length(IdleConnection *conn) |
948 | 988 | { |
949 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
989 | IdleConnectionPrivate *priv = conn->priv; | |
950 | 990 | if (priv->relay_prefix != NULL) { |
951 | 991 | /* server will add ':<relay_prefix> ' to all messages it relays on to |
952 | 992 | * other users. the +2 is for the initial : and the trailing space */ |
1055 | 1095 | return IDLE_PARSER_HANDLER_RESULT_HANDLED; |
1056 | 1096 | } |
1057 | 1097 | |
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 | ||
1058 | 1127 | static IdleParserHandlerResult _version_privmsg_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) { |
1059 | 1128 | IdleConnection *conn = IDLE_CONNECTION(user_data); |
1060 | 1129 | const gchar *msg = g_value_get_string(g_value_array_get_nth(args, 2)); |
1091 | 1160 | _whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) |
1092 | 1161 | { |
1093 | 1162 | IdleConnection *conn = IDLE_CONNECTION(user_data); |
1094 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
1163 | IdleConnectionPrivate *priv = conn->priv; | |
1095 | 1164 | |
1096 | 1165 | /* message format: <nick> <user> <host> * :<real name> */ |
1097 | 1166 | TpHandle handle = g_value_get_uint(g_value_array_get_nth(args, 0)); |
1119 | 1188 | g_assert(conn != NULL); |
1120 | 1189 | g_assert(IDLE_IS_CONNECTION(conn)); |
1121 | 1190 | |
1122 | priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
1191 | priv = conn->priv; | |
1123 | 1192 | |
1124 | 1193 | if ((priv->password != NULL) && (priv->password[0] != '\0')) { |
1125 | 1194 | g_snprintf(msg, IRC_MSG_MAXLEN + 1, "PASS %s", priv->password); |
1138 | 1207 | } |
1139 | 1208 | |
1140 | 1209 | static void send_quit_request(IdleConnection *conn) { |
1141 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
1210 | IdleConnectionPrivate *priv = conn->priv; | |
1142 | 1211 | gchar cmd[IRC_MSG_MAXLEN + 1] = "QUIT"; |
1143 | 1212 | |
1144 | 1213 | if (priv->quit_message != NULL) |
1169 | 1238 | |
1170 | 1239 | static void |
1171 | 1240 | _queue_alias_changed(IdleConnection *conn, TpHandle handle, const gchar *alias) { |
1172 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
1241 | IdleConnectionPrivate *priv = conn->priv; | |
1173 | 1242 | |
1174 | 1243 | if (!priv->queued_aliases_owners) { |
1175 | 1244 | TpHandleRepoIface *handles = tp_base_connection_get_handles(TP_BASE_CONNECTION(conn), TP_HANDLE_TYPE_CONTACT); |
1214 | 1283 | } |
1215 | 1284 | |
1216 | 1285 | void idle_connection_emit_queued_aliases_changed(IdleConnection *conn) { |
1217 | IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(conn); | |
1286 | IdleConnectionPrivate *priv = conn->priv; | |
1218 | 1287 | |
1219 | 1288 | if (!priv->queued_aliases) |
1220 | 1289 | return; |
1368 | 1437 | } |
1369 | 1438 | |
1370 | 1439 | 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; | |
1372 | 1441 | GError *error = NULL; |
1373 | 1442 | gsize bytes_written; |
1374 | 1443 | gchar *ret; |
1392 | 1461 | return TRUE; |
1393 | 1462 | } |
1394 | 1463 | |
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; | |
1397 | 1498 | GError *error = NULL; |
1398 | 1499 | gsize bytes_written; |
1399 | 1500 | gchar *ret; |
1400 | 1501 | gchar *p; |
1401 | 1502 | |
1402 | 1503 | if (input == NULL) { |
1403 | *output = NULL; | |
1404 | return; | |
1504 | return NULL; | |
1405 | 1505 | } |
1406 | 1506 | |
1407 | 1507 | ret = g_convert(input, -1, "UTF-8", priv->charset, NULL, &bytes_written, &error); |
1416 | 1516 | if (*p & (1 << 7)) |
1417 | 1517 | *p = '?'; |
1418 | 1518 | } |
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; | |
1423 | 1532 | } |
1424 | 1533 | |
1425 | 1534 | static void _aliasing_iface_init(gpointer g_iface, gpointer iface_data) { |
33 | 33 | |
34 | 34 | typedef struct _IdleConnection IdleConnection; |
35 | 35 | typedef struct _IdleConnectionClass IdleConnectionClass; |
36 | typedef struct _IdleConnectionPrivate IdleConnectionPrivate; | |
36 | 37 | |
37 | 38 | struct _IdleConnectionClass { |
38 | 39 | TpBaseConnectionClass parent_class; |
44 | 45 | TpContactsMixin contacts; |
45 | 46 | IdleParser *parser; |
46 | 47 | GQueue *contact_info_requests; |
48 | IdleConnectionPrivate *priv; | |
47 | 49 | }; |
48 | 50 | |
49 | 51 | GType idle_connection_get_type(void); |
29 | 29 | IDLE_DEBUG_NETWORK = (1 << 4), |
30 | 30 | IDLE_DEBUG_PARSER = (1 << 5), |
31 | 31 | IDLE_DEBUG_TEXT = (1 << 6), |
32 | IDLE_DEBUG_ROOMLIST = (1 << 7), | |
32 | 33 | } IdleDebugFlags; |
33 | 34 | |
34 | 35 | void idle_debug_init (void); |
170 | 170 | static GPtrArray * |
171 | 171 | idle_im_channel_get_interfaces (TpBaseChannel *channel) |
172 | 172 | { |
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); | |
174 | 176 | |
175 | 177 | g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_MESSAGES); |
176 | 178 | g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE); |
386 | 386 | { |
387 | 387 | IdleIMManager *self = IDLE_IM_MANAGER (user_data); |
388 | 388 | IdleIMManagerPrivate *priv = IDLE_IM_MANAGER_GET_PRIVATE (self); |
389 | TpHandle handle; | |
389 | TpBaseChannel *base = TP_BASE_CHANNEL (chan); | |
390 | 390 | |
391 | 391 | tp_channel_manager_emit_channel_closed_for_object (self, |
392 | 392 | TP_EXPORTABLE_CHANNEL (chan)); |
393 | 393 | |
394 | 394 | if (priv->channels) |
395 | 395 | { |
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)) | |
404 | 399 | { |
405 | 400 | IDLE_DEBUG ("removing channel with handle %u", handle); |
406 | 401 | g_hash_table_remove (priv->channels, GUINT_TO_POINTER (handle)); |
45 | 45 | |
46 | 46 | static void _password_iface_init(gpointer, gpointer); |
47 | 47 | static void subject_iface_init(gpointer, gpointer); |
48 | static void destroyable_iface_init(gpointer, gpointer); | |
48 | 49 | |
49 | 50 | static void idle_muc_channel_send (GObject *obj, TpMessage *message, TpMessageSendingFlags flags); |
50 | 51 | static void idle_muc_channel_close (TpBaseChannel *base); |
58 | 59 | G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_SUBJECT, subject_iface_init); |
59 | 60 | G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_ROOM_CONFIG, |
60 | 61 | tp_base_room_config_iface_init); |
62 | G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE, destroyable_iface_init); | |
61 | 63 | ) |
62 | 64 | |
63 | 65 | /* property enum */ |
130 | 132 | TP_IFACE_CHANNEL_INTERFACE_ROOM, |
131 | 133 | TP_IFACE_CHANNEL_INTERFACE_SUBJECT, |
132 | 134 | TP_IFACE_CHANNEL_INTERFACE_ROOM_CONFIG, |
135 | TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE, | |
133 | 136 | NULL |
134 | 137 | }; |
135 | 138 | |
308 | 311 | NULL); |
309 | 312 | } |
310 | 313 | |
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 | ||
311 | 328 | static void idle_muc_channel_class_init (IdleMUCChannelClass *idle_muc_channel_class) { |
312 | 329 | GObjectClass *object_class = G_OBJECT_CLASS (idle_muc_channel_class); |
313 | 330 | TpBaseChannelClass *base_channel_class = TP_BASE_CHANNEL_CLASS (idle_muc_channel_class); |
335 | 352 | |
336 | 353 | base_channel_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT; |
337 | 354 | base_channel_class->target_handle_type = TP_HANDLE_TYPE_ROOM; |
338 | base_channel_class->interfaces = muc_channel_interfaces; | |
339 | 355 | |
340 | 356 | base_channel_class->close = idle_muc_channel_close; |
341 | 357 | base_channel_class->fill_immutable_properties = idle_muc_channel_fill_immutable_properties; |
342 | 358 | base_channel_class->get_object_path_suffix = idle_muc_channel_get_path_suffix; |
359 | base_channel_class->get_interfaces = idle_muc_channel_get_interfaces; | |
343 | 360 | |
344 | 361 | param_spec = g_param_spec_string ( |
345 | 362 | "server", "Room.Server", "always empty", |
1324 | 1341 | |
1325 | 1342 | IDLE_DEBUG ("called on %p", self); |
1326 | 1343 | |
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 | ||
1327 | 1365 | if (priv->state == MUC_STATE_JOINED) |
1328 | 1366 | part_from_channel (self, NULL); |
1329 | 1367 | |
1330 | 1368 | /* FIXME: this is wrong if called while JOIN is in flight. */ |
1331 | 1369 | if (priv->state < MUC_STATE_JOINED) |
1332 | 1370 | 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 | ||
1347 | 1376 | static void idle_muc_channel_get_password_flags (TpSvcChannelInterfacePassword *iface, DBusGMethodInvocation *context) { |
1348 | 1377 | IdleMUCChannel *obj = IDLE_MUC_CHANNEL(iface); |
1349 | 1378 | IdleMUCChannelPrivate *priv; |
1357 | 1386 | } |
1358 | 1387 | |
1359 | 1388 | |
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 | */ | |
1372 | 1389 | static void idle_muc_channel_provide_password (TpSvcChannelInterfacePassword *iface, const gchar * password, DBusGMethodInvocation *context) { |
1373 | 1390 | IdleMUCChannel *obj = IDLE_MUC_CHANNEL(iface); |
1374 | 1391 | IdleMUCChannelPrivate *priv; |
1522 | 1539 | IMPLEMENT (set_subject); |
1523 | 1540 | #undef IMPLEMENT |
1524 | 1541 | } |
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 | } |
675 | 675 | static void _channel_closed_cb(IdleMUCChannel *chan, gpointer user_data) { |
676 | 676 | IdleMUCManager *manager = IDLE_MUC_MANAGER(user_data); |
677 | 677 | IdleMUCManagerPrivate *priv = IDLE_MUC_MANAGER_GET_PRIVATE(manager); |
678 | TpBaseChannel *base = TP_BASE_CHANNEL (chan); | |
678 | 679 | GSList *reqs = take_request_tokens(user_data, chan); |
679 | 680 | |
680 | 681 | /* If there are any tokens for this channel when it closes, the request |
688 | 689 | |
689 | 690 | g_slist_free(reqs); |
690 | 691 | |
692 | tp_channel_manager_emit_channel_closed_for_object (manager, | |
693 | TP_EXPORTABLE_CHANNEL (chan)); | |
694 | ||
691 | 695 | 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); | |
695 | 703 | } |
696 | 704 | } |
697 | 705 |
84 | 84 | {"NOTICE", "cIr:", IDLE_PARSER_PREFIXCMD_NOTICE_CHANNEL}, |
85 | 85 | {"NOTICE", "cIc:", IDLE_PARSER_PREFIXCMD_NOTICE_USER}, |
86 | 86 | {"PART", "cIr.", IDLE_PARSER_PREFIXCMD_PART}, |
87 | {"PONG", "IIs:", IDLE_PARSER_PREFIXCMD_PONG}, | |
87 | 88 | {"PRIVMSG", "cIr:", IDLE_PARSER_PREFIXCMD_PRIVMSG_CHANNEL}, |
88 | 89 | {"PRIVMSG", "cIc:", IDLE_PARSER_PREFIXCMD_PRIVMSG_USER}, |
89 | 90 | {"QUIT", "cI.", IDLE_PARSER_PREFIXCMD_QUIT}, |
118 | 119 | {"312", "IIIcs:", IDLE_PARSER_NUMERIC_WHOISSERVER}, |
119 | 120 | {"311", "IIIcssI:", IDLE_PARSER_NUMERIC_WHOISUSER}, |
120 | 121 | {"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}, | |
121 | 125 | |
122 | 126 | {NULL, NULL, IDLE_PARSER_LAST_MESSAGE_CODE} |
123 | 127 | }; |
349 | 353 | for (int i = 0; i < IDLE_PARSER_LAST_MESSAGE_CODE; i++) { |
350 | 354 | const MessageSpec *spec = &(message_specs[i]); |
351 | 355 | |
352 | if ((split_msg[0] != ':') && (i <= IDLE_PARSER_CMD_PING)) { | |
356 | if ((split_msg[0] != ':') && (i <= IDLE_PARSER_LAST_NON_PREFIX_CMD)) { | |
353 | 357 | if (!g_ascii_strcasecmp(tokens[0], spec->str)) |
354 | 358 | _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) { | |
356 | 360 | if (!g_ascii_strcasecmp(tokens[2], spec->str)) |
357 | 361 | _parse_and_forward_one(parser, tokens, spec->code, spec->format); |
358 | 362 | } |
48 | 48 | IDLE_PARSER_CMD_ERROR = 0, |
49 | 49 | IDLE_PARSER_CMD_PING, |
50 | 50 | |
51 | IDLE_PARSER_LAST_NON_PREFIX_CMD = IDLE_PARSER_CMD_PING, | |
52 | ||
51 | 53 | IDLE_PARSER_PREFIXCMD_INVITE, |
52 | 54 | IDLE_PARSER_PREFIXCMD_JOIN, |
53 | 55 | IDLE_PARSER_PREFIXCMD_KICK, |
57 | 59 | IDLE_PARSER_PREFIXCMD_NOTICE_CHANNEL, |
58 | 60 | IDLE_PARSER_PREFIXCMD_NOTICE_USER, |
59 | 61 | IDLE_PARSER_PREFIXCMD_PART, |
62 | IDLE_PARSER_PREFIXCMD_PONG, | |
60 | 63 | IDLE_PARSER_PREFIXCMD_PRIVMSG_CHANNEL, |
61 | 64 | IDLE_PARSER_PREFIXCMD_PRIVMSG_USER, |
62 | 65 | IDLE_PARSER_PREFIXCMD_QUIT, |
91 | 94 | IDLE_PARSER_NUMERIC_WHOISSERVER, |
92 | 95 | IDLE_PARSER_NUMERIC_WHOISUSER, |
93 | 96 | IDLE_PARSER_NUMERIC_WHOISIDLE, |
97 | IDLE_PARSER_NUMERIC_LIST, | |
98 | IDLE_PARSER_NUMERIC_LISTEND, | |
99 | IDLE_PARSER_NUMERIC_UNKNOWNCOMMAND, | |
94 | 100 | |
95 | 101 | IDLE_PARSER_LAST_MESSAGE_CODE |
96 | 102 | } 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 |
361 | 361 | user_data); |
362 | 362 | } |
363 | 363 | |
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 | ||
364 | 381 | void idle_server_connection_disconnect_full_async(IdleServerConnection *conn, guint reason, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { |
365 | 382 | IdleServerConnectionPrivate *priv = IDLE_SERVER_CONNECTION_GET_PRIVATE(conn); |
366 | 383 | GSimpleAsyncResult *result; |
72 | 72 | gboolean idle_server_connection_connect_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error); |
73 | 73 | void idle_server_connection_disconnect_async(IdleServerConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); |
74 | 74 | 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); | |
75 | 76 | gboolean idle_server_connection_disconnect_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error); |
76 | 77 | void idle_server_connection_send_async(IdleServerConnection *conn, const gchar *cmd, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); |
77 | 78 | gboolean idle_server_connection_send_finish(IdleServerConnection *conn, GAsyncResult *result, GError **error); |
3 | 3 | connect/connect-success-ssl.py \ |
4 | 4 | connect/connect-fail.py \ |
5 | 5 | connect/connect-fail-ssl.py \ |
6 | connect/ping.py \ | |
6 | 7 | connect/server-quit-ignore.py \ |
7 | 8 | connect/server-quit-noclose.py \ |
8 | 9 | connect/socket-closed-after-handshake.py \ |
14 | 15 | channels/requests-create.py \ |
15 | 16 | channels/requests-muc.py \ |
16 | 17 | channels/muc-channel-topic.py \ |
18 | channels/muc-destroy.py \ | |
19 | channels/room-list-channel.py \ | |
20 | channels/room-list-multiple.py \ | |
17 | 21 | messages/accept-invalid-nicks.py \ |
18 | 22 | messages/contactinfo-request.py \ |
23 | messages/invalid-utf8.py \ | |
19 | 24 | messages/messages-iface.py \ |
20 | 25 | messages/message-order.py \ |
21 | 26 | messages/leading-space.py \ |
270 | 270 | connect/connect-success-ssl.py \ |
271 | 271 | connect/connect-fail.py \ |
272 | 272 | connect/connect-fail-ssl.py \ |
273 | connect/ping.py \ | |
273 | 274 | connect/server-quit-ignore.py \ |
274 | 275 | connect/server-quit-noclose.py \ |
275 | 276 | connect/socket-closed-after-handshake.py \ |
281 | 282 | channels/requests-create.py \ |
282 | 283 | channels/requests-muc.py \ |
283 | 284 | channels/muc-channel-topic.py \ |
285 | channels/muc-destroy.py \ | |
286 | channels/room-list-channel.py \ | |
287 | channels/room-list-multiple.py \ | |
284 | 288 | messages/accept-invalid-nicks.py \ |
285 | 289 | messages/contactinfo-request.py \ |
290 | messages/invalid-utf8.py \ | |
286 | 291 | messages/messages-iface.py \ |
287 | 292 | messages/message-order.py \ |
288 | 293 | 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) |
2 | 2 | """ |
3 | 3 | |
4 | 4 | import functools |
5 | from idletest import exec_test, BaseIRCServer | |
5 | from idletest import exec_test, BaseIRCServer, sync_stream | |
6 | 6 | 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, | |
9 | 9 | ) |
10 | 10 | import constants as cs |
11 | 11 | import dbus |
88 | 88 | cs.CHANNEL_IFACE_ROOM, |
89 | 89 | cs.CHANNEL_IFACE_SUBJECT, |
90 | 90 | cs.CHANNEL_IFACE_ROOM_CONFIG, |
91 | cs.CHANNEL_IFACE_DESTROYABLE, | |
91 | 92 | ], props[cs.INTERFACES]) |
92 | 93 | assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM |
93 | 94 | assert props[cs.TARGET_ID] == '#idletest' |
121 | 122 | assert len(chans) == 1 |
122 | 123 | assert chans[0] == (path, props) |
123 | 124 | |
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 | ||
125 | 162 | chan.RemoveMembers([self_handle], "bye bye cruel\r\nworld", |
126 | 163 | dbus_interface=cs.CHANNEL_IFACE_GROUP) |
127 | 164 |
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 |
38 | 38 | CHANNEL_TYPE_STREAMED_MEDIA = CHANNEL + ".Type.StreamedMedia" |
39 | 39 | CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" |
40 | 40 | CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer" |
41 | CHANNEL_TYPE_ROOM_LIST = CHANNEL + ".Type.RoomList" | |
41 | 42 | CHANNEL_TYPE_SERVER_AUTHENTICATION = \ |
42 | 43 | CHANNEL + ".Type.ServerAuthentication.DRAFT" |
43 | 44 | CHANNEL_TYPE_SERVER_TLS_CONNECTION = \ |
101 | 101 | self.rooms.remove(room) |
102 | 102 | except ValueError: |
103 | 103 | 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) | |
105 | 111 | |
106 | 112 | def sendJoin(self, room, members=[]): |
107 | 113 | 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 |