Mercurial > pidgin
annotate src/stun.c @ 12116:e75ef7aa913e
[gaim-migrate @ 14416]
" This patch implements a replacement for the queuing
system from 1.x. It also obsoletes a previous patch
[#1338873] I submitted to prioritize the unseen states
in gtk conversations.
The attached envelope.png is ripped from the
msgunread.png already included in gaim. It should be
dropped in the pixmaps directory (Makefile.am is
updated accordingly in this patch).
The two separate queuing preferences from 1.x, queuing
messages while away and queuing all new messages (from
docklet), are replaced with a single 3-way preference
for conversations. The new preference is "Hide new IM
conversations". This preference can be set to never,
away and always.
When a gtk conversation is created, it may be placed in
a hidden conversation window instead of being placed
normally. This decision is based upon the preference
and possibly the away state of the account the
conversation is being created for. This *will* effect
conversations the user explicitly requests to be
created, so in these cases the caller must be sure to
present the conversation to the user, using
gaim_gtkconv_present_conversation(). This is done
already in gtkdialogs.c which handles creating
conversations requested by the user from gaim proper
(menus, double-clicking on budy in blist, etc.).
The main advantage to not queuing messages is that the
conversations exist, the message is written to the
conversation (and logged if appropriate) and the unseen
state is set on the conversation. This means no
additional features are needed to track whether there
are queued messages or not, just use the unseen state
on conversations.
Since conversations may not be visible (messages
"queued"), gaim proper needs some notification that
there are messages waiting. I opted for a menutray icon
that shows up when an im conversation has an unseen
message. Clicking this icon will focus (and show if
hidden) the first conversation with an unseen message.
This is essentially the same behavior of the docklet in
cvs right now, except that the icon is only visible
when there is a conversation with an unread message.
The api that is added is flexible enough to allow
either the docklet or the new blist menutray icon to be
visible for conversations of any/all types and for
unseen messages >= any state. Currently they are set to
only IM conversations and only unseen states >= TEXT
(system messages and no log messages will not trigger
blinking the docklet or showing the blist tray icon),
but these could be made preferences relatively easily
in the future. Other plugins could probably benefit as
well: gaim_gtk_conversations_get_first_unseen().
There is probably some limit to comment size, so I'll
stop rambling now. If anyone has more
questions/comments, catch me in #gaim, here or on
gaim-devel."
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Wed, 16 Nov 2005 18:17:01 +0000 |
parents | 8d019c4bf454 |
children | 5f65a0cca87c |
rev | line source |
---|---|
11225 | 1 /** |
2 * @file stun.c STUN (RFC3489) Implementation | |
3 * @ingroup core | |
4 * | |
5 * gaim | |
6 * | |
11336 | 7 * STUN implementation inspired by jstun [http://jstun.javawi.de/] |
8 * | |
11225 | 9 * Gaim is the legal property of its developers, whose names are too numerous |
10 * to list here. Please refer to the COPYRIGHT file distributed with this | |
11 * source distribution. | |
12 * | |
13 * This program is free software; you can redistribute it and/or modify | |
14 * it under the terms of the GNU General Public License as published by | |
15 * the Free Software Foundation; either version 2 of the License, or | |
16 * (at your option) any later version. | |
17 * | |
18 * This program is distributed in the hope that it will be useful, | |
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 * GNU General Public License for more details. | |
22 * | |
23 * You should have received a copy of the GNU General Public License | |
24 * along with this program; if not, write to the Free Software | |
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
26 * | |
27 */ | |
28 | |
11898 | 29 #include "internal.h" |
30 | |
11364
e1840eb860e7
[gaim-migrate @ 13588]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11354
diff
changeset
|
31 #ifndef _WIN32 |
11225 | 32 #include <net/if.h> |
33 #include <sys/ioctl.h> | |
11364
e1840eb860e7
[gaim-migrate @ 13588]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11354
diff
changeset
|
34 #endif |
11225 | 35 |
36 #include "debug.h" | |
37 #include "account.h" | |
11424 | 38 #include "dnssrv.h" |
11429 | 39 #include "proxy.h" |
11225 | 40 #include "stun.h" |
41 #include "prefs.h" | |
42 | |
11677 | 43 static struct stun_nattype nattype = {-1, 0, "\0"}; |
11225 | 44 |
45 static GSList *callbacks = 0; | |
46 static int fd = -1; | |
11354 | 47 static gint incb = -1; |
48 static gint timeout = -1; | |
49 static struct stun_header *packet; | |
50 static int packetsize = 0; | |
51 static int test = 0; | |
52 static int retry = 0; | |
53 static struct sockaddr_in addr; | |
11225 | 54 |
55 static void do_callbacks() { | |
56 while(callbacks) { | |
57 StunCallback cb = callbacks->data; | |
11336 | 58 if(cb) |
59 cb(&nattype); | |
11225 | 60 callbacks = g_slist_remove(callbacks, cb); |
61 } | |
62 } | |
63 | |
11354 | 64 static gboolean timeoutfunc(void *blah) { |
65 if(retry > 2) { | |
66 if(test == 2) nattype.type = 5; | |
67 /* remove input */ | |
68 gaim_input_remove(incb); | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
69 |
11354 | 70 /* set unknown */ |
71 nattype.status = 0; | |
72 | |
73 /* callbacks */ | |
74 do_callbacks(); | |
75 | |
76 return FALSE; | |
77 } | |
78 retry++; | |
79 sendto(fd, packet, packetsize, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); | |
80 return TRUE; | |
81 } | |
82 | |
83 #ifdef NOTYET | |
84 static void do_test2() { | |
85 struct stun_change data; | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
86 data.hdr.type = htons(0x0001); |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
87 data.hdr.len = 0; |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
88 data.hdr.transid[0] = rand(); |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
89 data.hdr.transid[1] = ntohl(((int)'g' << 24) + ((int)'a' << 16) + ((int)'i' << 8) + (int)'m'); |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
90 data.hdr.transid[2] = rand(); |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
91 data.hdr.transid[3] = rand(); |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
92 data.attrib.type = htons(0x003); |
11354 | 93 data.attrib.len = htons(4); |
94 data.value[3] = 6; | |
95 packet = (struct stun_header*)&data; | |
96 packetsize = sizeof(struct stun_change); | |
97 retry = 0; | |
98 test = 2; | |
99 sendto(fd, packet, packetsize, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
100 timeout = gaim_timeout_add(500, (GSourceFunc)timeoutfunc, NULL); |
11354 | 101 } |
102 #endif | |
103 | |
11225 | 104 static void reply_cb(gpointer data, gint source, GaimInputCondition cond) { |
11336 | 105 char buffer[1024]; |
11225 | 106 char *tmp; |
107 int len; | |
108 struct in_addr in; | |
109 struct stun_attrib *attrib; | |
110 struct stun_header *hdr; | |
11300
dd1a5969b2e5
[gaim-migrate @ 13500]
Richard Laager <rlaager@wiktel.com>
parents:
11229
diff
changeset
|
111 struct ifconf ifc; |
dd1a5969b2e5
[gaim-migrate @ 13500]
Richard Laager <rlaager@wiktel.com>
parents:
11229
diff
changeset
|
112 struct ifreq *ifr; |
dd1a5969b2e5
[gaim-migrate @ 13500]
Richard Laager <rlaager@wiktel.com>
parents:
11229
diff
changeset
|
113 struct sockaddr_in *sinptr; |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
114 |
11336 | 115 len = recv(source, buffer, 1024, 0); |
11225 | 116 |
117 hdr = (struct stun_header*)buffer; | |
11371
6e02e20e3a58
[gaim-migrate @ 13596]
Richard Laager <rlaager@wiktel.com>
parents:
11364
diff
changeset
|
118 if(hdr->transid[0]!=packet->transid[0] || hdr->transid[1]!=packet->transid[1] || hdr->transid[2]!=packet->transid[2] || hdr->transid[3]!=packet->transid[3]) { /* wrong transaction */ |
6e02e20e3a58
[gaim-migrate @ 13596]
Richard Laager <rlaager@wiktel.com>
parents:
11364
diff
changeset
|
119 gaim_debug_info("stun", "got wrong transid\n"); |
11225 | 120 return; |
121 } | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
122 if(test==1) { |
11354 | 123 tmp = buffer + sizeof(struct stun_header); |
124 while(buffer+len > tmp) { | |
11225 | 125 |
11354 | 126 attrib = (struct stun_attrib*) tmp; |
127 if(attrib->type == htons(0x0001) && attrib->len == htons(8)) { | |
128 memcpy(&in.s_addr, tmp+sizeof(struct stun_attrib)+2+2, 4); | |
129 strcpy(nattype.publicip, inet_ntoa(in)); | |
130 } | |
131 tmp += sizeof(struct stun_attrib) + attrib->len; | |
11225 | 132 } |
11371
6e02e20e3a58
[gaim-migrate @ 13596]
Richard Laager <rlaager@wiktel.com>
parents:
11364
diff
changeset
|
133 gaim_debug_info("stun", "got public ip %s\n", nattype.publicip); |
11354 | 134 nattype.status = 2; |
135 nattype.type = 1; | |
136 | |
11371
6e02e20e3a58
[gaim-migrate @ 13596]
Richard Laager <rlaager@wiktel.com>
parents:
11364
diff
changeset
|
137 /* is it a NAT? */ |
11225 | 138 |
11354 | 139 ifc.ifc_len = sizeof(buffer); |
140 ifc.ifc_req = (struct ifreq *) buffer; | |
141 ioctl(source, SIOCGIFCONF, &ifc); | |
11225 | 142 |
11354 | 143 tmp = buffer; |
144 while(tmp < buffer + ifc.ifc_len) { | |
145 ifr = (struct ifreq *) tmp; | |
11225 | 146 |
11364
e1840eb860e7
[gaim-migrate @ 13588]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11354
diff
changeset
|
147 tmp += sizeof(struct ifreq); |
11225 | 148 |
11354 | 149 if(ifr->ifr_addr.sa_family == AF_INET) { |
11371
6e02e20e3a58
[gaim-migrate @ 13596]
Richard Laager <rlaager@wiktel.com>
parents:
11364
diff
changeset
|
150 /* we only care about ipv4 interfaces */ |
11354 | 151 sinptr = (struct sockaddr_in *) &ifr->ifr_addr; |
152 if(sinptr->sin_addr.s_addr == in.s_addr) { | |
11371
6e02e20e3a58
[gaim-migrate @ 13596]
Richard Laager <rlaager@wiktel.com>
parents:
11364
diff
changeset
|
153 /* no NAT */ |
6e02e20e3a58
[gaim-migrate @ 13596]
Richard Laager <rlaager@wiktel.com>
parents:
11364
diff
changeset
|
154 gaim_debug_info("stun", "no nat"); |
11354 | 155 nattype.type = 0; |
156 } | |
11225 | 157 } |
158 } | |
11354 | 159 gaim_timeout_remove(timeout); |
11225 | 160 |
11354 | 161 #ifdef NOTYET |
162 do_test2(); | |
163 #endif | |
164 return; | |
165 } else if(test == 2) { | |
166 do_callbacks(); | |
167 gaim_input_remove(incb); | |
168 gaim_timeout_remove(timeout); | |
169 nattype.type = 2; | |
170 } | |
11336 | 171 } |
11424 | 172 |
11429 | 173 static void hbn_cb(GSList *hosts, gpointer edata, const char *error_message) { |
11354 | 174 static struct stun_header data; |
11424 | 175 int ret; |
11425 | 176 |
11429 | 177 if(!hosts) return; |
178 if(!hosts->data) return; | |
11425 | 179 |
11225 | 180 if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
181 nattype.status = 0; | |
11424 | 182 do_callbacks(); |
183 return; | |
11225 | 184 } |
185 | |
186 addr.sin_family = AF_INET; | |
187 addr.sin_port = htons(12108); | |
188 addr.sin_addr.s_addr = INADDR_ANY; | |
189 while( ((ret = bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in))) < 0 ) && ntohs(addr.sin_port) < 12208) { | |
190 addr.sin_port = htons(ntohs(addr.sin_port)+1); | |
191 } | |
192 if( ret < 0 ) { | |
193 nattype.status = 0; | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
194 do_callbacks(); |
11424 | 195 return; |
11225 | 196 } |
197 incb = gaim_input_add(fd, GAIM_INPUT_READ, reply_cb, NULL); | |
198 | |
11429 | 199 ret = GPOINTER_TO_INT(hosts->data); |
200 hosts = g_slist_remove(hosts, hosts->data); | |
201 memcpy(&addr, hosts->data, sizeof(struct sockaddr_in)); | |
202 g_free(hosts->data); | |
203 hosts = g_slist_remove(hosts, hosts->data); | |
204 while(hosts) { | |
205 hosts = g_slist_remove(hosts, hosts->data); | |
206 g_free(hosts->data); | |
207 hosts = g_slist_remove(hosts, hosts->data); | |
208 } | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
209 |
11225 | 210 data.type = htons(0x0001); |
211 data.len = 0; | |
11354 | 212 data.transid[0] = rand(); |
213 data.transid[1] = ntohl(((int)'g' << 24) + ((int)'a' << 16) + ((int)'i' << 8) + (int)'m'); | |
214 data.transid[2] = rand(); | |
215 data.transid[3] = rand(); | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
216 |
11225 | 217 if( sendto(fd, &data, sizeof(struct stun_header), 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < sizeof(struct stun_header)) { |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
218 nattype.status = 0; |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
219 do_callbacks(); |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
220 return; |
11225 | 221 } |
11354 | 222 test = 1; |
223 packet = &data; | |
224 packetsize = sizeof(struct stun_header); | |
225 timeout = gaim_timeout_add(500, (GSourceFunc)timeoutfunc, NULL); | |
11429 | 226 } |
227 | |
228 static void do_test1(struct srv_response *resp, int results, gpointer sdata) { | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
229 const char *servername = sdata; |
11429 | 230 int port = 3478; |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
231 |
11429 | 232 if(results) { |
233 servername = resp[0].hostname; | |
234 port = resp[0].port; | |
235 } | |
236 gaim_debug_info("stun", "got %d SRV responses, server: %s, port: %d\n", results, servername, port); | |
237 | |
238 gaim_gethostbyname_async(servername, port, hbn_cb, NULL); | |
11424 | 239 g_free(resp); |
11225 | 240 } |
11424 | 241 |
242 struct stun_nattype *gaim_stun_discover(StunCallback cb) { | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
243 const char *servername = gaim_prefs_get_string("/core/network/stun_server"); |
11424 | 244 |
245 gaim_debug_info("stun", "using server %s\n", servername); | |
246 if(nattype.status == 1) { /* currently discovering */ | |
247 if(cb) callbacks = g_slist_append(callbacks, cb); | |
248 return NULL; | |
249 } | |
250 if(nattype.status != -1) { /* already discovered */ | |
251 if(cb) cb(&nattype); | |
252 return &nattype; | |
253 } | |
254 | |
255 if(!servername || (strlen(servername)<2)) { | |
256 nattype.status = 0; | |
257 if(cb) cb(&nattype); | |
258 return &nattype; | |
259 } | |
260 callbacks = g_slist_append(callbacks, cb); | |
11431
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
261 gaim_srv_resolve("stun","udp",servername, do_test1, |
d2e44c8085e0
[gaim-migrate @ 13668]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11429
diff
changeset
|
262 (gpointer) servername); |
11424 | 263 return &nattype; |
264 } | |
265 | |
266 void gaim_stun_init() { | |
267 gaim_prefs_add_string("/core/network/stun_server", ""); | |
268 } |