Add new string proto encoding for windows-1250 (ENC_WINDOWS_1250)
[metze/wireshark/wip.git] / epan / dissectors / packet-gadu-gadu.c
1 /* packet-gadu-gadu.c
2  * Routines for Gadu-Gadu dissection
3  * Copyright 2011,2012, Jakub Zawadzki <darkjames-ws@darkjames.pl>
4  *
5  * $Id$
6  *
7  * Protocol documentation available at http://toxygen.net/libgadu/protocol/
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #define NEW_PROTO_TREE_API
29
30 #include "config.h"
31
32 #include <glib.h>
33
34 #include <epan/packet.h>
35 #include <epan/prefs.h>
36 #include <epan/wmem/wmem.h>
37
38 #include <epan/dissectors/packet-tcp.h>
39
40 #define TCP_PORT_GADU_GADU 8074 /* assigned by IANA */
41
42 /* desegmentation of Gadu-Gadu over TCP */
43 static gboolean gadu_gadu_desegment = TRUE;
44
45 static dissector_handle_t gadu_gadu_handle;
46
47 static int ett_gadu_gadu = -1;
48 static int ett_gadu_gadu_contact = -1;
49
50 #define GG_ERA_OMNIX_MASK 0x04000000
51 #define GG_HAS_AUDIO_MASK 0x40000000
52
53 #define GG_WELCOME                  0x01
54 #define GG_STATUS                   0x02
55 #define GG_LOGIN_OK                 0x03
56 #define GG_SEND_MSG_ACK             0x05
57 #define GG_PONG                     0x07
58 #define GG_PING                     0x08
59 #define GG_LOGIN_FAILED             0x09
60 #define GG_RECV_MSG                 0x0a
61 #define GG_DISCONNECTING            0x0b
62 #define GG_NOTIFY_REPLY             0x0c
63 #define GG_DISCONNECT_ACK           0x0d
64 #define GG_PUBDIR50_REPLY           0x0e
65 #define GG_STATUS60                 0x0f
66 #define GG_USERLIST_REPLY           0x10
67 #define GG_NOTIFY_REPLY60           0x11
68 #define GG_NEED_EMAIL               0x14
69 #define GG_LOGIN_HASH_TYPE_INVALID  0x16
70 #define GG_STATUS77                 0x17
71 #define GG_NOTIFY_REPLY77           0x18
72 #define GG_DCC7_INFO                0x1f
73 #define GG_DCC7_NEW                 0x20
74 #define GG_DCC7_ACCEPT              0x21
75 #define GG_DCC7_REJECT              0x22
76 #define GG_DCC7_ID_REPLY            0x23
77 #define GG_DCC7_ID_ABORTED          0x25
78 #define GG_XML_EVENT                0x27
79 #define GG_STATUS80BETA             0x2a
80 #define GG_NOTIFY_REPLY80BETA       0x2b
81 #define GG_XML_ACTION               0x2c
82 #define GG_RECV_MSG80               0x2e
83 #define GG_USERLIST_REPLY80         0x30
84 #define GG_LOGIN_OK80               0x35
85 #define GG_STATUS80                 0x36
86 #define GG_NOTIFY_REPLY80           0x37
87 #define GG_USERLIST100_REPLY        0x41
88 #define GG_LOGIN80_FAILED           0x43
89 #define GG_USER_DATA                0x44
90 #define GG_TYPING_NOTIFY            0x59
91 #define GG_OWN_MESSAGE              0x5A
92 #define GG_OWN_RESOURCE_INFO        0x5B
93 #define GG_USERLIST100_VERSION      0x5C
94
95
96 #define GG_TYPE_VS(x) { x, #x }
97
98 /* original (GG_*) names likes in documentation (http://toxygen.net/libgadu/protocol/#ch1.16) */
99 static const value_string gadu_gadu_packets_type_recv[] = {
100         GG_TYPE_VS(GG_WELCOME),
101         GG_TYPE_VS(GG_STATUS),
102         GG_TYPE_VS(GG_LOGIN_OK),
103         GG_TYPE_VS(GG_SEND_MSG_ACK),
104         GG_TYPE_VS(GG_PONG),
105         GG_TYPE_VS(GG_PING),
106         GG_TYPE_VS(GG_LOGIN_FAILED),
107         GG_TYPE_VS(GG_RECV_MSG),
108         GG_TYPE_VS(GG_DISCONNECTING),
109         GG_TYPE_VS(GG_NOTIFY_REPLY),
110         GG_TYPE_VS(GG_DISCONNECT_ACK),
111         GG_TYPE_VS(GG_PUBDIR50_REPLY),
112         GG_TYPE_VS(GG_STATUS60),
113         GG_TYPE_VS(GG_USERLIST_REPLY),
114         GG_TYPE_VS(GG_NOTIFY_REPLY60),
115         GG_TYPE_VS(GG_NEED_EMAIL),
116         GG_TYPE_VS(GG_LOGIN_HASH_TYPE_INVALID),
117         GG_TYPE_VS(GG_STATUS77),
118         GG_TYPE_VS(GG_NOTIFY_REPLY77),
119         GG_TYPE_VS(GG_DCC7_INFO),
120         GG_TYPE_VS(GG_DCC7_NEW),
121         GG_TYPE_VS(GG_DCC7_ACCEPT),
122         GG_TYPE_VS(GG_DCC7_REJECT),
123         GG_TYPE_VS(GG_DCC7_ID_REPLY),
124         GG_TYPE_VS(GG_DCC7_ID_ABORTED),
125         GG_TYPE_VS(GG_XML_EVENT),
126         GG_TYPE_VS(GG_STATUS80BETA),
127         GG_TYPE_VS(GG_NOTIFY_REPLY80BETA),
128         GG_TYPE_VS(GG_XML_ACTION),
129         GG_TYPE_VS(GG_RECV_MSG80),
130         GG_TYPE_VS(GG_USERLIST_REPLY80),
131         GG_TYPE_VS(GG_LOGIN_OK80),
132         GG_TYPE_VS(GG_STATUS80),
133         GG_TYPE_VS(GG_NOTIFY_REPLY80),
134         GG_TYPE_VS(GG_USERLIST100_REPLY),
135         GG_TYPE_VS(GG_LOGIN80_FAILED),
136         GG_TYPE_VS(GG_USER_DATA),
137         GG_TYPE_VS(GG_TYPING_NOTIFY),
138         GG_TYPE_VS(GG_OWN_MESSAGE),
139         GG_TYPE_VS(GG_OWN_RESOURCE_INFO),
140         GG_TYPE_VS(GG_USERLIST100_VERSION),
141         { 0, NULL }
142 };
143
144 #define GG_NEW_STATUS           0x02
145 #define GG_PONG                 0x07
146 #define GG_PING                 0x08
147 #define GG_SEND_MSG             0x0b
148 #define GG_LOGIN                0x0c
149 #define GG_ADD_NOTIFY           0x0d
150 #define GG_REMOVE_NOTIFY        0x0e
151 #define GG_NOTIFY_FIRST         0x0f
152 #define GG_NOTIFY_LAST          0x10
153 #define GG_LIST_EMPTY           0x12
154 #define GG_LOGIN_EXT            0x13
155 #define GG_PUBDIR50_REQUEST     0x14
156 #define GG_LOGIN60              0x15
157 #define GG_USERLIST_REQUEST     0x16
158 #define GG_LOGIN70              0x19
159 #define GG_DCC7_INFO            0x1f
160 #define GG_DCC7_NEW             0x20
161 #define GG_DCC7_ACCEPT          0x21
162 #define GG_DCC7_REJECT          0x22
163 #define GG_DCC7_ID_REQUEST      0x23
164 #define GG_DCC7_ID_DUNNO1       0x24
165 #define GG_DCC7_ID_ABORT        0x25
166 #define GG_NEW_STATUS80BETA     0x28
167 #define GG_LOGIN80BETA          0x29
168 #define GG_SEND_MSG80           0x2d
169 #define GG_USERLIST_REQUEST80   0x2f
170 #define GG_LOGIN80              0x31
171 #define GG_NEW_STATUS80         0x38
172 #define GG_USERLIST100_REQUEST  0x40
173 #define GG_RECV_MSG_ACK         0x46
174 #define GG_TYPING_NOTIFY        0x59
175 #define GG_OWN_DISCONNECT       0x62
176 #define GG_NEW_STATUS105        0x63
177 #define GG_NOTIFY105            0x78
178 #define GG_ADD_NOTIFY105        0x7b
179 #define GG_REMOVE_NOTIFY105     0x7c
180 #define GG_LOGIN105             0x83
181
182 static const value_string gadu_gadu_packets_type_send[] = {
183         GG_TYPE_VS(GG_NEW_STATUS),
184         GG_TYPE_VS(GG_PONG),
185         GG_TYPE_VS(GG_PING),
186         GG_TYPE_VS(GG_SEND_MSG),
187         GG_TYPE_VS(GG_LOGIN),
188         GG_TYPE_VS(GG_ADD_NOTIFY),
189         GG_TYPE_VS(GG_REMOVE_NOTIFY),
190         GG_TYPE_VS(GG_NOTIFY_FIRST),
191         GG_TYPE_VS(GG_NOTIFY_LAST),
192         GG_TYPE_VS(GG_LIST_EMPTY),
193         GG_TYPE_VS(GG_LOGIN_EXT),
194         GG_TYPE_VS(GG_PUBDIR50_REQUEST),
195         GG_TYPE_VS(GG_LOGIN60),
196         GG_TYPE_VS(GG_USERLIST_REQUEST),
197         GG_TYPE_VS(GG_LOGIN70),
198         GG_TYPE_VS(GG_DCC7_INFO),
199         GG_TYPE_VS(GG_DCC7_NEW),
200         GG_TYPE_VS(GG_DCC7_ACCEPT),
201         GG_TYPE_VS(GG_DCC7_REJECT),
202         GG_TYPE_VS(GG_DCC7_ID_REQUEST),
203         GG_TYPE_VS(GG_DCC7_ID_DUNNO1),
204         GG_TYPE_VS(GG_DCC7_ID_ABORT),
205         GG_TYPE_VS(GG_NEW_STATUS80BETA),
206         GG_TYPE_VS(GG_LOGIN80BETA),
207         GG_TYPE_VS(GG_SEND_MSG80),
208         GG_TYPE_VS(GG_USERLIST_REQUEST80),
209         GG_TYPE_VS(GG_LOGIN80),
210         GG_TYPE_VS(GG_NEW_STATUS80),
211         GG_TYPE_VS(GG_USERLIST100_REQUEST),
212         GG_TYPE_VS(GG_RECV_MSG_ACK),
213         GG_TYPE_VS(GG_TYPING_NOTIFY),
214         GG_TYPE_VS(GG_OWN_DISCONNECT),
215         GG_TYPE_VS(GG_NEW_STATUS105),
216         GG_TYPE_VS(GG_NOTIFY105),
217         GG_TYPE_VS(GG_ADD_NOTIFY105),
218         GG_TYPE_VS(GG_REMOVE_NOTIFY105),
219         GG_TYPE_VS(GG_LOGIN105),
220         { 0, NULL }
221 };
222
223 static const value_string gadu_gadu_msg_ack_status_vals[] = {
224         { 0x01, "Message blocked" },
225         { 0x02, "Message delivered" },
226         { 0x03, "Message queued" },
227         { 0x04, "Message not delivered (queue full)" },
228         { 0x06, "CTCP Message not delivered" },
229         { 0, NULL }
230 };
231
232 #define GG_STATUS_NOT_AVAIL         0x01
233 #define GG_STATUS_NOT_AVAIL_DESCR   0x15
234 #define GG_STATUS_FFC               0x17
235 #define GG_STATUS_FFC_DESCR         0x18
236 #define GG_STATUS_AVAIL             0x02
237 #define GG_STATUS_AVAIL_DESCR       0x04
238 #define GG_STATUS_BUSY              0x03
239 #define GG_STATUS_BUSY_DESCR        0x05
240 #define GG_STATUS_DND               0x21
241 #define GG_STATUS_DND_DESCR         0x22
242 #define GG_STATUS_INVISIBLE         0x14
243 #define GG_STATUS_INVISIBLE_DESCR   0x16
244 #define GG_STATUS_BLOCKED           0x06
245
246 #define GG_LOGIN_HASH_GG32 0x01
247 #define GG_LOGIN_HASH_SHA1 0x02
248
249 static const value_string gadu_gadu_hash_type_vals[] = {
250         { GG_LOGIN_HASH_GG32, "GG32 hash" },
251         { GG_LOGIN_HASH_SHA1, "SHA1 hash" },
252         { 0, NULL }
253 };
254
255 #define GG_USERLIST_PUT 0x00
256 #define GG_USERLIST_PUT_MORE 0x01
257 #define GG_USERLIST_GET 0x02
258
259 static const value_string gadu_gadu_userlist_request_type_vals[] = {
260         { GG_USERLIST_PUT, "Userlist put" },
261         { GG_USERLIST_PUT_MORE, "Userlist put (more)" },
262         { GG_USERLIST_GET, "Userlist get" },
263         { 0, NULL }
264 };
265
266 #define GG_USERLIST_PUT_REPLY 0x00
267 #define GG_USERLIST_PUT_MORE_REPLY 0x02
268 #define GG_USERLIST_GET_REPLY 0x06
269 #define GG_USERLIST_GET_MORE_REPLY 0x04
270
271 static const value_string gadu_gadu_userlist_reply_type_vals[] = {
272         { GG_USERLIST_PUT_REPLY, "Userlist put" },
273         { GG_USERLIST_PUT_MORE_REPLY, "Userlist put (more)" },
274         { GG_USERLIST_GET_REPLY, "Userlist get" },
275         { GG_USERLIST_GET_MORE_REPLY, "Userlist get (more)" },
276         { 0, NULL }
277 };
278
279 #define GG_USERLIST100_FORMAT_TYPE_NONE 0x00
280 #define GG_USERLIST100_FORMAT_TYPE_GG70 0x01
281 #define GG_USERLIST100_FORMAT_TYPE_GG100 0x02
282
283 static const value_string gadu_gadu_userlist_request_format_vals[] = {
284         { GG_USERLIST100_FORMAT_TYPE_NONE, "None" },
285         { GG_USERLIST100_FORMAT_TYPE_GG70, "Classic (7.0)" },
286         { GG_USERLIST100_FORMAT_TYPE_GG100, "XML (10.0)" },
287         { 0, NULL }
288 };
289
290 /* XXX, add compatible libgadu versions? */
291 static const value_string gadu_gadu_version_vals[] = {
292         { 0x2e, "Gadu-Gadu 8.0 (build 8283)" },
293         { 0x2d, "Gadu-Gadu 8.0 (build 4881)" },
294         { 0x2a, "Gadu-Gadu 7.7 (build 3315)" },
295         { 0x29, "Gadu-Gadu 7.6 (build 1688)" },
296         { 0x28, "Gadu-Gadu 7.5.0 (build 2201)" },
297         { 0x27, "Gadu-Gadu 7.0 (build 22)" },
298         { 0x26, "Gadu-Gadu 7.0 (build 20)" },
299         { 0x25, "Gadu-Gadu 7.0 (build 1)" },
300         { 0x24, "Gadu-Gadu 6.1 (build 155) or 7.6 (build 1359)" },
301         { 0x22, "Gadu-Gadu 6.0 (build 140)" },
302         { 0x21, "Gadu-Gadu 6.0 (build 133)" },
303         { 0x20, "Gadu-Gadu 6.0" },
304         { 0x1e, "Gadu-Gadu 5.7 beta (build 121)" },
305         { 0x1c, "Gadu_Gadu 5.7 beta" },
306         { 0x1b, "Gadu-Gadu 5.0.5" },
307         { 0x19, "Gadu-Gadu 5.0.3" },
308         { 0x18, "Gadu-Gadu 5.0.1, 5.0.0, 4.9.3" },
309         { 0x17, "Gadu-Gadu 4.9.2" },
310         { 0x16, "Gadu-Gadu 4.9.1" },
311         { 0x15, "Gadu-Gadu 4.8.9" },
312         { 0x14, "Gadu-Gadu 4.8.3, 4.8.1" },
313         { 0x11, "Gadu-Gadu 4.6.10, 4.6.1" },
314         { 0x10, "Gadu-Gadu 4.5.22, 4.5.21, 4.5.19, 4.5.17, 4.5.15" },
315         { 0x0f, "Gadu-Gadu 4.5.12" },
316         { 0x0b, "Gadu-Gadu 4.0.30, 4.0.29, 4.0.28, 4.0.25" },
317         { 0, NULL }
318 };
319
320 static const value_string gadu_gadu_dcc_type_vals[] = {
321         { 1, "Voice transmission" },
322         { 4, "File transmission" },
323         { 0, NULL }
324 };
325
326 static const value_string gadu_gadu_typing_notify_type_vals[] = {
327         { 1, "Typing started" },
328         { 0, "Typing stopped" },
329         { 0, NULL }
330 };
331
332 static const value_string gadu_gadu_pubdir_type_vals[] = {
333         { 1, "Public directory write" },
334         { 2, "Public directory read" },
335         { 3, "Public directory search" },
336         { 0, NULL }
337 };
338
339 static header_field_info *hfi_gadu_gadu = NULL;
340
341 #define GADU_GADU_HFI_INIT HFI_INIT(proto_gadu_gadu)
342
343 /* Header */
344 static header_field_info hfi_gadu_gadu_header_type_recv GADU_GADU_HFI_INIT =
345         { "Packet Type", "gadu-gadu.recv", FT_UINT32, BASE_HEX, VALS(gadu_gadu_packets_type_recv), 0x0, "Packet Type (recv)", HFILL };
346
347 static header_field_info hfi_gadu_gadu_header_type_send GADU_GADU_HFI_INIT =
348         { "Packet Type", "gadu-gadu.send", FT_UINT32, BASE_HEX, VALS(gadu_gadu_packets_type_send), 0x0, "Packet Type (send)", HFILL };
349
350 static header_field_info hfi_gadu_gadu_header_length GADU_GADU_HFI_INIT =
351         { "Packet Length", "gadu-gadu.len", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL };
352
353 /* Login common (gadu-gadu.login.*) */
354 static header_field_info hfi_gadu_gadu_login_uin GADU_GADU_HFI_INIT =
355         { "Client UIN", "gadu-gadu.login.uin", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
356
357 static header_field_info hfi_gadu_gadu_login_hash_type GADU_GADU_HFI_INIT =
358         { "Login hash type", "gadu-gadu.login.hash_type", FT_UINT8, BASE_HEX, gadu_gadu_hash_type_vals, 0x00, NULL, HFILL };
359
360 static header_field_info hfi_gadu_gadu_login_hash GADU_GADU_HFI_INIT =
361         { "Login hash", "gadu-gadu.login.hash", FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL };
362
363 static header_field_info hfi_gadu_gadu_login_status GADU_GADU_HFI_INIT =
364         { "Client status", "gadu-gadu.login.status", FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL };
365
366 static header_field_info hfi_gadu_gadu_login_protocol GADU_GADU_HFI_INIT =
367         { "Client protocol", "gadu-gadu.login.protocol", FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL };
368
369 static header_field_info hfi_gadu_gadu_login_version GADU_GADU_HFI_INIT =
370         { "Client version", "gadu-gadu.login.version", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL };
371
372 static header_field_info hfi_gadu_gadu_login_local_ip GADU_GADU_HFI_INIT =
373         { "Client local IP", "gadu-gadu.login.local_ip", FT_IPv4, BASE_NONE, NULL, 0x00, NULL, HFILL };
374
375 static header_field_info hfi_gadu_gadu_login_local_port GADU_GADU_HFI_INIT =
376         { "Client local port", "gadu-gadu.login.local_port", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL };
377
378 /* GG_LOGIN80 (gadu-gadu.login80.*) */
379 static header_field_info hfi_gadu_gadu_login80_lang GADU_GADU_HFI_INIT =
380         { "Client language", "gadu-gadu.login80.lang", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL };
381
382 /* Contacts details (gadu-gadu.user_data.*) */
383 static header_field_info hfi_gadu_gadu_userdata_uin GADU_GADU_HFI_INIT =
384         { "UIN", "gadu-gadu.user_data.uin", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
385
386 static header_field_info hfi_gadu_gadu_userdata_attr_name GADU_GADU_HFI_INIT =
387         { "Attribute name", "gadu-gadu.user_data.attr_name", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL };
388
389 static header_field_info hfi_gadu_gadu_userdata_attr_type GADU_GADU_HFI_INIT =
390         { "Attribute type", "gadu-gadu.user_data.attr_type", FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL };
391
392 static header_field_info hfi_gadu_gadu_userdata_attr_value GADU_GADU_HFI_INIT =
393         { "Attribute value", "gadu-gadu.user_data.attr_val", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL };
394
395 /* Typing notify (gadu-gadu.typing_notify.*) */
396 static header_field_info hfi_gadu_gadu_typing_notify_type GADU_GADU_HFI_INIT =
397         { "Typing notify type", "gadu-gadu.typing_notify.type", FT_UINT16, BASE_HEX, gadu_gadu_typing_notify_type_vals, 0x00, NULL, HFILL };
398
399 static header_field_info hfi_gadu_gadu_typing_notify_uin GADU_GADU_HFI_INIT =
400         { "Typing notify recipient", "gadu-gadu.typing_notify.uin", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
401
402 /* Message common (gadu-gadu.msg.*) */
403 static header_field_info hfi_gadu_gadu_msg_uin GADU_GADU_HFI_INIT =
404         { "Message sender or recipient", "gadu-gadu.msg.uin", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
405
406 static header_field_info hfi_gadu_gadu_msg_sender GADU_GADU_HFI_INIT =
407         { "Message sender", "gadu-gadu.msg.sender", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
408
409 static header_field_info hfi_gadu_gadu_msg_recipient GADU_GADU_HFI_INIT =
410         { "Message recipient", "gadu-gadu.msg.recipient", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
411
412 static header_field_info hfi_gadu_gadu_msg_seq GADU_GADU_HFI_INIT =
413         { "Message sequence number", "gadu-gadu.msg.seq", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
414
415 static header_field_info hfi_gadu_gadu_msg_time GADU_GADU_HFI_INIT =
416         { "Message time", "gadu-gadu.msg.time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL };
417
418 static header_field_info hfi_gadu_gadu_msg_class GADU_GADU_HFI_INIT =
419         { "Message class", "gadu-gadu.msg.class", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL };
420
421 static header_field_info hfi_gadu_gadu_msg_text GADU_GADU_HFI_INIT =
422         { "Message text", "gadu-gadu.msg.text", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL };
423
424 /* GG_RECV_MSG80, GG_SEND_MSG80 (gadu-gadu.msg80.*) */
425 static header_field_info hfi_gadu_gadu_msg80_offset_plain GADU_GADU_HFI_INIT =
426         { "Message plaintext offset", "gadu-gadu.msg80.offset_plain", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
427
428 static header_field_info hfi_gadu_gadu_msg80_offset_attr GADU_GADU_HFI_INIT =
429         { "Message attribute offset", "gadu-gadu.msg80.offset_attributes", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
430
431 /* GG_SEND_MSG_ACK (gadu-gadu.msg_ack.*) */
432 static header_field_info hfi_gadu_gadu_msg_ack_status GADU_GADU_HFI_INIT =
433         { "Message status", "gadu-gadu.msg_ack.status", FT_UINT32, BASE_HEX, gadu_gadu_msg_ack_status_vals, 0x00, NULL, HFILL };
434
435 static header_field_info hfi_gadu_gadu_msg_ack_recipient GADU_GADU_HFI_INIT =
436         { "Message recipient", "gadu-gadu.msg_ack.recipient", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
437
438 static header_field_info hfi_gadu_gadu_msg_ack_seq GADU_GADU_HFI_INIT =
439         { "Message sequence number", "gadu-gadu.msg_ack.seq", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
440
441 /* Status common (gadu-gadu.status.*) */
442 static header_field_info hfi_gadu_gadu_status_uin GADU_GADU_HFI_INIT =
443         { "UIN", "gadu-gadu.status.uin", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
444
445 static header_field_info hfi_gadu_gadu_status_status GADU_GADU_HFI_INIT =
446         { "Status", "gadu-gadu.status.status", FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL };
447
448 static header_field_info hfi_gadu_gadu_status_ip GADU_GADU_HFI_INIT =
449         { "IP", "gadu-gadu.status.remote_ip", FT_IPv4, BASE_NONE, NULL, 0x00, NULL, HFILL };
450
451 static header_field_info hfi_gadu_gadu_status_port GADU_GADU_HFI_INIT =
452         { "Port", "gadu-gadu.status.remote_port", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL };
453
454 static header_field_info hfi_gadu_gadu_status_version GADU_GADU_HFI_INIT =
455         { "Version", "gadu-gadu.status.version", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL };
456
457 static header_field_info hfi_gadu_gadu_status_img_size GADU_GADU_HFI_INIT =
458         { "Image size", "gadu-gadu.status.image_size", FT_UINT8, BASE_DEC, NULL, 0x00, "Maximum image size in KB", HFILL };
459
460 static header_field_info hfi_gadu_gadu_status_descr GADU_GADU_HFI_INIT =
461         { "Description", "gadu-gadu.status.description", FT_STRINGZ, BASE_NONE, NULL, 0x00, NULL, HFILL };
462
463 /* Direct Connection (gadu-gadu.dcc.*) */
464 static header_field_info hfi_dcc_type GADU_GADU_HFI_INIT =
465         { "Direct connection type", "gadu-gadu.dcc.type", FT_UINT32, BASE_HEX, gadu_gadu_dcc_type_vals, 0x00, NULL, HFILL };
466
467 static header_field_info hfi_dcc_id GADU_GADU_HFI_INIT =
468         { "Direct connection id", "gadu-gadu.dcc.id", FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL };
469
470 static header_field_info hfi_dcc_uin_to GADU_GADU_HFI_INIT =
471         { "Direct connection UIN target", "gadu-gadu.dcc.uin_to", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
472
473 static header_field_info hfi_dcc_uin_from GADU_GADU_HFI_INIT =
474         { "Direct connection UIN initiator", "gadu-gadu.dcc.uin_from", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
475
476 static header_field_info hfi_dcc_filename GADU_GADU_HFI_INIT =
477         { "Direct connection filename", "gadu-gadu.dcc.filename", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL };
478
479 /* New status (setting status) common (gadu-gadu.new_status.*) */
480 static header_field_info hfi_gadu_gadu_new_status_status GADU_GADU_HFI_INIT =
481         { "Status", "gadu-gadu.new_status.status", FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL };
482
483 static header_field_info hfi_gadu_gadu_new_status_desc GADU_GADU_HFI_INIT =
484         { "Description", "gadu-gadu.new_status.description", FT_STRINGZ, BASE_NONE, NULL, 0x00, NULL, HFILL };
485
486 /* Userlist (gadu-gadu.userlist.*) */
487 static header_field_info hfi_gadu_gadu_userlist_request_type GADU_GADU_HFI_INIT =
488         { "Request type", "gadu-gadu.userlist.request_type", FT_UINT32, BASE_HEX, gadu_gadu_userlist_request_type_vals, 0x00, NULL, HFILL };
489
490 static header_field_info hfi_gadu_gadu_userlist_version GADU_GADU_HFI_INIT =
491         { "Userlist version", "gadu-gadu.userlist.version", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
492
493 static header_field_info hfi_gadu_gadu_userlist_format GADU_GADU_HFI_INIT =
494         { "Userlist format", "gadu-gadu.userlist.format", FT_UINT8, BASE_HEX, gadu_gadu_userlist_request_format_vals, 0x00, NULL, HFILL };
495
496 static header_field_info hfi_gadu_gadu_userlist_reply_type GADU_GADU_HFI_INIT =
497         { "Reply type", "gadu-gadu.userlist.reply_type", FT_UINT32, BASE_HEX, gadu_gadu_userlist_reply_type_vals, 0x00, NULL, HFILL };
498
499 /* Public Directory (gadu-gadu.pubdir.*) */
500 static header_field_info hfi_gadu_gadu_pubdir_request_type GADU_GADU_HFI_INIT =
501         { "Request type", "gadu-gadu.pubdir.request_type", FT_UINT8, BASE_HEX, gadu_gadu_pubdir_type_vals, 0x00, NULL, HFILL };
502
503 static header_field_info hfi_gadu_gadu_pubdir_request_seq GADU_GADU_HFI_INIT =
504         { "Request sequence", "gadu-gadu.pubdir.request_seq", FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL };
505
506 static header_field_info hfi_gadu_gadu_pubdir_request_str GADU_GADU_HFI_INIT =
507         { "Request string", "gadu-gadu.pubdir.request_str", FT_STRINGZ, BASE_NONE, NULL, 0x00, NULL, HFILL };
508
509 static header_field_info hfi_gadu_gadu_pubdir_reply_type GADU_GADU_HFI_INIT =
510         { "Reply type", "gadu-gadu.pubdir.reply_type", FT_UINT8, BASE_HEX, gadu_gadu_pubdir_type_vals, 0x00, NULL, HFILL };
511
512 static header_field_info hfi_gadu_gadu_pubdir_reply_seq GADU_GADU_HFI_INIT =
513         { "Reply sequence", "gadu-gadu.pubdir.reply_seq", FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL };
514
515 static header_field_info hfi_gadu_gadu_pubdir_reply_str GADU_GADU_HFI_INIT =
516         { "Reply string", "gadu-gadu.pubdir.request_str", FT_STRINGZ, BASE_NONE, NULL, 0x00, NULL, HFILL };
517
518 /* Contact (notify) common (gadu-gadu.contact.*) */
519 static header_field_info hfi_gadu_gadu_contact_uin GADU_GADU_HFI_INIT =
520         { "UIN", "gadu-gadu.contact.uin", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL };
521
522 static header_field_info hfi_gadu_gadu_contact_uin_str GADU_GADU_HFI_INIT =
523         { "UIN", "gadu-gadu.contact.uin_str", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL };
524
525 static header_field_info hfi_gadu_gadu_contact_type GADU_GADU_HFI_INIT =
526         { "Type", "gadu-gadu.contact.type", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL };
527
528 /* GG_WELCOME */
529 static header_field_info hfi_gadu_gadu_welcome_seed GADU_GADU_HFI_INIT =
530         { "Seed", "gadu-gadu.welcome.seed", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL };
531
532 /* Not dissected data */
533 static header_field_info hfi_gadu_gadu_data GADU_GADU_HFI_INIT =
534         { "Packet Data", "gadu-gadu.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL };
535
536
537 static dissector_handle_t xml_handle;
538
539 struct gadu_gadu_conv_data {
540         guint32 uin;    /* uin from login packet */
541 };
542
543 static struct gadu_gadu_conv_data *
544 gadu_gadu_create_conversation(packet_info *pinfo, guint32 uin)
545 {
546         conversation_t *conv;
547         struct gadu_gadu_conv_data *gg_conv;
548
549         conv = find_or_create_conversation(pinfo);
550         gg_conv = (struct gadu_gadu_conv_data *)conversation_get_proto_data(conv, hfi_gadu_gadu->id);
551         if (!gg_conv) {
552                 gg_conv = wmem_new(wmem_file_scope(), struct gadu_gadu_conv_data);
553                 gg_conv->uin = uin;
554
555                 conversation_add_proto_data(conv, hfi_gadu_gadu->id, gg_conv);
556         }
557         /* assert(gg_conv->uin == uin); */
558         return gg_conv;
559 }
560
561 static struct gadu_gadu_conv_data *
562 gadu_gadu_get_conversation_data(packet_info *pinfo)
563 {
564         conversation_t *conv;
565
566         conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
567         if (conv)
568                 return (struct gadu_gadu_conv_data *)conversation_get_proto_data(conv, hfi_gadu_gadu->id);
569         return NULL;
570 }
571
572 static gboolean
573 gadu_gadu_status_has_descr(int status)
574 {
575         return
576                 (status == GG_STATUS_NOT_AVAIL_DESCR) ||
577                 (status == GG_STATUS_FFC_DESCR) ||
578                 (status == GG_STATUS_AVAIL_DESCR) ||
579                 (status == GG_STATUS_BUSY_DESCR) ||
580                 (status == GG_STATUS_DND_DESCR) ||
581                 (status == GG_STATUS_INVISIBLE_DESCR);
582 }
583
584 static int
585 dissect_gadu_gadu_stringz_cp1250(tvbuff_t *tvb, const header_field_info *hfi, proto_tree *tree, const int offset)
586 {
587         const char *str;
588         int len;
589
590         /* XXX, new code is throwing exception if string is not NUL terminated */
591         str = (const char *) tvb_get_stringz_enc(wmem_packet_scope(), tvb, offset, &len, ENC_NA | ENC_WINDOWS_1250);
592
593         proto_tree_add_unicode_string(tree, hfi->id, tvb, offset, len, str);
594
595         return offset + len;
596 }
597
598 static int
599 dissect_gadu_gadu_uint32_string_utf8(tvbuff_t *tvb, const header_field_info *hfi, proto_tree *tree, int offset)
600 {
601         const int org_offset = offset;
602
603         const char *str;
604         guint32 len;
605
606         len = tvb_get_letohl(tvb, offset);
607         offset += 4;
608
609         if (len > 0) {
610                 /* proto_item_fill_label() is broken for UTF-8 strings.
611                  * It's using internally format_text() which doesn't support UTF-8
612                  */
613                 /* proto_tree_add_item(tree, hfindex, tvb, offset, len, ENC_UTF_8|ENC_NA); */
614
615                 /* Use workaround */
616                 str = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, len, ENC_UTF_8|ENC_NA);
617
618         } else
619                 str = "";
620
621         offset += len;
622
623         proto_tree_add_unicode_string(tree, hfi->id, tvb, org_offset, offset - org_offset, str);
624         return offset;
625 }
626
627
628 static int
629 dissect_gadu_gadu_disconnecting(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_, int offset)
630 {
631         col_set_str(pinfo->cinfo, COL_INFO, "Disconnecting");
632
633         /* empty packet */
634
635         return offset;
636 }
637
638
639 static int
640 dissect_gadu_gadu_disconnect_ack(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_, int offset)
641 {
642         col_set_str(pinfo->cinfo, COL_INFO, "Disconnect acknowledge (< 10.0)");
643
644         /* empty packet */
645
646         return offset;
647 }
648
649 static void *
650 _tvb_memcpy_reverse(tvbuff_t *tvb, void *target, gint offset, size_t length)
651 {
652         guint8 *t = (guint8 *) target;
653
654         while (length > 0) {
655                 length--;
656                 t[length] = tvb_get_guint8(tvb, offset);
657                 offset++;
658         }
659         return target;
660 }
661
662 static int
663 dissect_gadu_gadu_login_protocol(tvbuff_t *tvb, proto_tree *tree, int offset)
664 {
665         proto_item *ti;
666
667         guint32 protocol;
668
669         protocol = tvb_get_letohl(tvb, offset) & 0xff;
670         proto_tree_add_item(tree, &hfi_gadu_gadu_login_protocol, tvb, offset, 4, ENC_LITTLE_ENDIAN);
671         ti = proto_tree_add_string(tree, &hfi_gadu_gadu_login_version, tvb, offset, 4, val_to_str(protocol, gadu_gadu_version_vals, "Unknown (0x%x)"));
672         PROTO_ITEM_SET_GENERATED(ti);
673         offset += 4;
674
675         return offset;
676 }
677
678 static int
679 dissect_gadu_gadu_login(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
680 {
681         proto_item *ti;
682
683         guint32 uin;
684         guint8 hash[4];
685
686         col_set_str(pinfo->cinfo, COL_INFO, "Login request (< 6.0)");
687
688         uin = tvb_get_letohl(tvb, offset);
689         gadu_gadu_create_conversation(pinfo, uin);
690
691         proto_tree_add_uint(tree, &hfi_gadu_gadu_login_uin, tvb, offset, 4, uin);
692         offset += 4;
693
694         ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_login_hash_type, tvb, 0, 0, GG_LOGIN_HASH_GG32);
695         PROTO_ITEM_SET_GENERATED(ti);
696
697         /* hash is 32-bit number written in LE */
698         _tvb_memcpy_reverse(tvb, hash, offset, 4);
699         proto_tree_add_bytes_format_value(tree, hfi_gadu_gadu_login_hash.id, tvb, offset, 4, hash, "0x%.8x", tvb_get_letohl(tvb, offset));
700         offset += 4;
701
702         proto_tree_add_item(tree, &hfi_gadu_gadu_login_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
703         offset += 4;
704
705         offset = dissect_gadu_gadu_login_protocol(tvb, tree, offset);
706
707         proto_tree_add_item(tree, &hfi_gadu_gadu_login_local_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
708         offset += 4;
709
710         proto_tree_add_item(tree, &hfi_gadu_gadu_login_local_port, tvb, offset, 2, ENC_LITTLE_ENDIAN);
711         offset += 2;
712
713         return offset;
714 }
715
716 static int
717 dissect_gadu_gadu_login_hash(tvbuff_t *tvb, proto_tree *tree, int offset)
718 {
719         guint8 hash_type;
720
721         guint8 hash[4];
722         int i;
723
724         hash_type = tvb_get_guint8(tvb, offset);
725         proto_tree_add_item(tree, &hfi_gadu_gadu_login_hash_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
726         offset += 1;
727
728         switch (hash_type) {
729                 case GG_LOGIN_HASH_GG32:
730                         /* hash is 32-bit number written in LE */
731                         _tvb_memcpy_reverse(tvb, hash, offset, 4);
732                         proto_tree_add_bytes_format_value(tree, hfi_gadu_gadu_login_hash.id, tvb, offset, 4, hash, "0x%.8x", tvb_get_letohl(tvb, offset));
733                         for (i = 4; i < 64; i++) {
734                                 if (tvb_get_guint8(tvb, offset+i)) {
735                                         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset + 4, 64-4, ENC_NA);
736                                         break;
737                                 }
738                         }
739                         break;
740
741                 case GG_LOGIN_HASH_SHA1:
742                         proto_tree_add_item(tree, &hfi_gadu_gadu_login_hash, tvb, offset, 20, ENC_NA);
743                         for (i = 20; i < 64; i++) {
744                                 if (tvb_get_guint8(tvb, offset+i)) {
745                                         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset + 20, 64-20, ENC_NA);
746                                         break;
747                                 }
748                         }
749                         break;
750
751                 default:
752                         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 64, ENC_NA);
753                         break;
754         }
755         offset += 64;
756
757         return offset;
758 }
759
760 static int
761 dissect_gadu_gadu_login70(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
762 {
763         guint32 uin;
764
765         col_set_str(pinfo->cinfo, COL_INFO, "Login request (7.0)");
766
767         uin = tvb_get_letohl(tvb, offset) & ~(GG_ERA_OMNIX_MASK | GG_HAS_AUDIO_MASK);
768         gadu_gadu_create_conversation(pinfo, uin);
769
770         proto_tree_add_uint(tree, &hfi_gadu_gadu_login_uin, tvb, offset, 4, uin);
771         offset += 4;
772
773         offset = dissect_gadu_gadu_login_hash(tvb, tree, offset);
774
775         proto_tree_add_item(tree, &hfi_gadu_gadu_login_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
776         offset += 4;
777
778         offset = dissect_gadu_gadu_login_protocol(tvb, tree, offset);
779
780         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 1, ENC_NA); /* 00 */
781         offset += 1;
782
783         proto_tree_add_item(tree, &hfi_gadu_gadu_login_local_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
784         offset += 4;
785
786         proto_tree_add_item(tree, &hfi_gadu_gadu_login_local_port, tvb, offset, 2, ENC_LITTLE_ENDIAN);
787         offset += 2;
788
789         /* XXX packet not fully dissected */
790
791         return offset;
792 }
793
794 static int
795 dissect_gadu_gadu_login80(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
796 {
797         guint32 uin;
798
799         col_set_str(pinfo->cinfo, COL_INFO, "Login request (8.0)");
800
801         uin = tvb_get_letohl(tvb, offset);
802         gadu_gadu_create_conversation(pinfo, uin);
803
804         proto_tree_add_item(tree, &hfi_gadu_gadu_login_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
805         offset += 4;
806
807         proto_tree_add_item(tree, &hfi_gadu_gadu_login80_lang, tvb, offset, 2, ENC_ASCII | ENC_NA);
808         offset += 2;
809
810         offset = dissect_gadu_gadu_login_hash(tvb, tree, offset);
811
812         proto_tree_add_item(tree, &hfi_gadu_gadu_login_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
813         offset += 4;
814
815         /* XXX packet not fully dissected */
816
817         return offset;
818 }
819
820 static int
821 dissect_gadu_gadu_login_ok(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_, int offset)
822 {
823         col_set_str(pinfo->cinfo, COL_INFO, "Login success (< 8.0)");
824
825         /* not empty packet, but content unknown */
826
827         return offset;
828 }
829
830 static int
831 dissect_gadu_gadu_login_failed(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_, int offset)
832 {
833         col_set_str(pinfo->cinfo, COL_INFO, "Login fail (< 8.0)");
834
835         /* empty packet */
836
837         return offset;
838 }
839
840 static int
841 dissect_gadu_gadu_login_ok80(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_, int offset)
842 {
843         col_set_str(pinfo->cinfo, COL_INFO, "Login success (8.0)");
844
845         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 4, ENC_NA); /* 01 00 00 00 */
846         offset += 4;
847
848         return offset;
849 }
850
851 static int
852 dissect_gadu_gadu_login80_failed(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
853 {
854         col_set_str(pinfo->cinfo, COL_INFO, "Login fail (8.0)");
855
856         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 4, ENC_NA); /* 01 00 00 00 */
857         offset += 4;
858
859         return offset;
860 }
861
862 static int
863 dissect_gadu_gadu_user_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
864 {
865         guint32 users_num;
866
867         col_set_str(pinfo->cinfo, COL_INFO, "Contact details");
868
869         /* XXX, add subtrees */
870
871         offset += 4;
872
873         users_num = tvb_get_letohl(tvb, offset);
874         offset += 4;
875
876         while (users_num--) {
877                 guint32 attr_num;
878
879                 proto_tree_add_item(tree, &hfi_gadu_gadu_userdata_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
880                 offset += 4;
881
882                 attr_num = tvb_get_letohl(tvb, offset);
883                 offset += 4;
884
885                 while (attr_num--) {
886                         guint32 name_size, val_size;
887                         char *name, *val;
888         /* name */
889                         name_size = tvb_get_letohl(tvb, offset);
890                         offset += 4;
891
892                         name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, name_size, ENC_ASCII | ENC_NA);
893                         proto_tree_add_string(tree, &hfi_gadu_gadu_userdata_attr_name, tvb, offset - 4, 4 + name_size, name);
894                         offset += name_size;
895         /* type */
896                         proto_tree_add_item(tree, &hfi_gadu_gadu_userdata_attr_type, tvb, offset, 4, ENC_LITTLE_ENDIAN);
897                         offset += 4;
898         /* value */
899                         val_size = tvb_get_letohl(tvb, offset);
900                         offset += 4;
901
902                         val = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, val_size, ENC_ASCII | ENC_NA);
903                         proto_tree_add_string(tree, &hfi_gadu_gadu_userdata_attr_value, tvb, offset - 4, 4 + val_size, val);
904                         offset += val_size;
905                 }
906         }
907
908         return offset;
909 }
910
911 static int
912 dissect_gadu_gadu_typing_notify(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
913 {
914         col_set_str(pinfo->cinfo, COL_INFO, "Typing notify");
915
916         /* XXX, when type > 1, it's length not type ! */
917         proto_tree_add_item(tree, &hfi_gadu_gadu_typing_notify_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
918         offset += 2;
919
920         proto_tree_add_item(tree, &hfi_gadu_gadu_typing_notify_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
921         offset += 4;
922
923         return offset;
924 }
925
926 static int
927 dissect_gadu_gadu_msg_attr(tvbuff_t *tvb _U_, proto_tree *tree _U_, int offset)
928 {
929         /* XXX, stub */
930
931         return offset;
932 }
933
934 static int
935 dissect_gadu_gadu_recv_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
936 {
937         struct gadu_gadu_conv_data *conv;
938         proto_item *ti;
939
940         col_set_str(pinfo->cinfo, COL_INFO, "Receive message (< 8.0)");
941
942         if ((conv = gadu_gadu_get_conversation_data(pinfo))) {
943                 ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_msg_recipient, tvb, 0, 0, conv->uin);
944                 PROTO_ITEM_SET_GENERATED(ti);
945
946                 ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_msg_uin, tvb, 0, 0, conv->uin);
947                 PROTO_ITEM_SET_GENERATED(ti);
948                 PROTO_ITEM_SET_HIDDEN(ti);
949         }
950
951         ti = proto_tree_add_item(tree, &hfi_gadu_gadu_msg_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
952         PROTO_ITEM_SET_HIDDEN(ti);
953         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_sender, tvb, offset, 4, ENC_LITTLE_ENDIAN);
954         offset += 4;
955
956         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN);
957         offset += 4;
958
959         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_time, tvb, offset, 4, ENC_LITTLE_ENDIAN);
960         offset += 4;
961
962         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_class, tvb, offset, 4, ENC_LITTLE_ENDIAN);
963         offset += 4;
964
965         offset = dissect_gadu_gadu_stringz_cp1250(tvb, &hfi_gadu_gadu_msg_text, tree, offset);
966
967         offset = dissect_gadu_gadu_msg_attr(tvb, tree, offset);
968
969         return offset;
970 }
971
972 static int
973 dissect_gadu_gadu_send_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
974 {
975         struct gadu_gadu_conv_data *conv;
976         proto_item *ti;
977
978         col_set_str(pinfo->cinfo, COL_INFO, "Send message (< 8.0)");
979
980         ti = proto_tree_add_item(tree, &hfi_gadu_gadu_msg_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
981         PROTO_ITEM_SET_HIDDEN(ti);
982         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_recipient, tvb, offset, 4, ENC_LITTLE_ENDIAN);
983         offset += 4;
984
985         if ((conv = gadu_gadu_get_conversation_data(pinfo))) {
986                 ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_msg_sender, tvb, 0, 0, conv->uin);
987                 PROTO_ITEM_SET_GENERATED(ti);
988
989                 ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_msg_uin, tvb, 0, 0, conv->uin);
990                 PROTO_ITEM_SET_GENERATED(ti);
991                 PROTO_ITEM_SET_HIDDEN(ti);
992         }
993
994         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN);
995         offset += 4;
996
997         ti = proto_tree_add_time(tree, &hfi_gadu_gadu_msg_time, tvb, 0, 0, &(pinfo->fd->abs_ts));
998         PROTO_ITEM_SET_GENERATED(ti);
999
1000         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_class, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1001         offset += 4;
1002
1003         offset = dissect_gadu_gadu_stringz_cp1250(tvb, &hfi_gadu_gadu_msg_text, tree, offset);
1004
1005         offset = dissect_gadu_gadu_msg_attr(tvb, tree, offset);
1006
1007         return offset;
1008 }
1009
1010 static int
1011 dissect_gadu_gadu_recv_msg80(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1012 {
1013         struct gadu_gadu_conv_data *conv;
1014         proto_item *ti;
1015
1016         col_set_str(pinfo->cinfo, COL_INFO, "Receive message (8.0)");
1017
1018         if ((conv = gadu_gadu_get_conversation_data(pinfo))) {
1019                 ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_msg_recipient, tvb, 0, 0, conv->uin);
1020                 PROTO_ITEM_SET_GENERATED(ti);
1021
1022                 ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_msg_uin, tvb, 0, 0, conv->uin);
1023                 PROTO_ITEM_SET_GENERATED(ti);
1024                 PROTO_ITEM_SET_HIDDEN(ti);
1025         }
1026
1027         ti = proto_tree_add_item(tree, &hfi_gadu_gadu_msg_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1028         PROTO_ITEM_SET_HIDDEN(ti);
1029         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_sender, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1030         offset += 4;
1031
1032         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1033         offset += 4;
1034
1035         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_time, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1036         offset += 4;
1037
1038         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_class, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1039         offset += 4;
1040
1041         proto_tree_add_item(tree, &hfi_gadu_gadu_msg80_offset_plain, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1042         offset += 4;
1043
1044         proto_tree_add_item(tree, &hfi_gadu_gadu_msg80_offset_attr, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1045         offset += 4;
1046
1047         /* XXX packet not fully dissected */
1048
1049         return offset;
1050 }
1051
1052 static int
1053 dissect_gadu_gadu_send_msg80(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1054 {
1055         struct gadu_gadu_conv_data *conv;
1056         proto_item *ti;
1057
1058         col_set_str(pinfo->cinfo, COL_INFO, "Send message (8.0)");
1059
1060         ti = proto_tree_add_item(tree, &hfi_gadu_gadu_msg_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1061         PROTO_ITEM_SET_HIDDEN(ti);
1062         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_recipient, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1063         offset += 4;
1064
1065         if ((conv = gadu_gadu_get_conversation_data(pinfo))) {
1066                 ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_msg_sender, tvb, 0, 0, conv->uin);
1067                 PROTO_ITEM_SET_GENERATED(ti);
1068
1069                 ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_msg_uin, tvb, 0, 0, conv->uin);
1070                 PROTO_ITEM_SET_GENERATED(ti);
1071                 PROTO_ITEM_SET_HIDDEN(ti);
1072         }
1073
1074         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1075         offset += 4;
1076
1077         ti = proto_tree_add_time(tree, &hfi_gadu_gadu_msg_time, tvb, 0, 0, &(pinfo->fd->abs_ts));
1078         PROTO_ITEM_SET_GENERATED(ti);
1079
1080         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_class, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1081         offset += 4;
1082
1083         proto_tree_add_item(tree, &hfi_gadu_gadu_msg80_offset_plain, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1084         offset += 4;
1085
1086         proto_tree_add_item(tree, &hfi_gadu_gadu_msg80_offset_attr, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1087         offset += 4;
1088
1089         /* XXX packet not fully dissected */
1090
1091         return offset;
1092 }
1093
1094 static int
1095 dissect_gadu_gadu_send_msg_ack(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1096 {
1097         col_set_str(pinfo->cinfo, COL_INFO, "Message acknowledge (server)");
1098
1099         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_ack_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1100         offset += 4;
1101
1102         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_ack_recipient, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1103         offset += 4;
1104
1105         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_ack_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1106         offset += 4;
1107
1108         return offset;
1109 }
1110
1111 static int
1112 dissect_gadu_gadu_recv_msg_ack(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1113 {
1114         col_set_str(pinfo->cinfo, COL_INFO, "Message acknowledge (client)");
1115
1116         proto_tree_add_item(tree, &hfi_gadu_gadu_msg_ack_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1117         offset += 4;
1118
1119         return offset;
1120 }
1121
1122 static int
1123 dissect_gadu_gadu_status60(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1124 {
1125         guint32 uin;
1126         guint8 status;
1127
1128         col_set_str(pinfo->cinfo, COL_INFO, "Receive status (6.0)");
1129
1130         uin = tvb_get_letohl(tvb, offset) & ~(GG_ERA_OMNIX_MASK | GG_HAS_AUDIO_MASK);
1131         proto_tree_add_uint(tree, &hfi_gadu_gadu_status_uin, tvb, offset, 4, uin);
1132         offset += 4;
1133
1134         status = tvb_get_guint8(tvb, offset);
1135         proto_tree_add_item(tree, &hfi_gadu_gadu_status_status, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1136         offset += 1;
1137
1138         proto_tree_add_item(tree, &hfi_gadu_gadu_status_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
1139         offset += 4;
1140
1141         proto_tree_add_item(tree, &hfi_gadu_gadu_status_port, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1142         offset += 2;
1143
1144         proto_tree_add_item(tree, &hfi_gadu_gadu_status_version, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1145         offset += 1;
1146
1147         proto_tree_add_item(tree, &hfi_gadu_gadu_status_img_size, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1148         offset += 1;
1149
1150         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 1, ENC_NA); /* 00 */
1151         offset += 1;
1152
1153         if (gadu_gadu_status_has_descr(status))
1154                 offset = dissect_gadu_gadu_stringz_cp1250(tvb, &hfi_gadu_gadu_status_descr, tree, offset);
1155
1156         return offset;
1157 }
1158
1159 static int
1160 dissect_gadu_gadu_status77(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1161 {
1162         guint32 uin;
1163         guint8 status;
1164
1165         col_set_str(pinfo->cinfo, COL_INFO, "Receive status (7.7)");
1166
1167         uin = tvb_get_letohl(tvb, offset) & ~(GG_ERA_OMNIX_MASK | GG_HAS_AUDIO_MASK);
1168         proto_tree_add_uint(tree, &hfi_gadu_gadu_status_uin, tvb, offset, 4, uin);
1169         offset += 4;
1170
1171         status = tvb_get_guint8(tvb, offset);
1172         proto_tree_add_item(tree, &hfi_gadu_gadu_status_status, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1173         offset += 1;
1174
1175         proto_tree_add_item(tree, &hfi_gadu_gadu_status_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
1176         offset += 4;
1177
1178         proto_tree_add_item(tree, &hfi_gadu_gadu_status_port, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1179         offset += 2;
1180
1181         proto_tree_add_item(tree, &hfi_gadu_gadu_status_version, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1182         offset += 1;
1183
1184         proto_tree_add_item(tree, &hfi_gadu_gadu_status_img_size, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1185         offset += 1;
1186
1187         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 1, ENC_NA); /* 00 */
1188         offset += 1;
1189
1190         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 4, ENC_NA);
1191         offset += 4;
1192
1193         if (gadu_gadu_status_has_descr(status))
1194                 offset = dissect_gadu_gadu_stringz_cp1250(tvb, &hfi_gadu_gadu_status_descr, tree, offset);
1195
1196         return offset;
1197 }
1198
1199 static int
1200 dissect_gadu_gadu_status80(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1201 {
1202         col_set_str(pinfo->cinfo, COL_INFO, "Receive status (8.0)");
1203
1204         proto_tree_add_item(tree, &hfi_gadu_gadu_status_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1205         offset += 4;
1206
1207         proto_tree_add_item(tree, &hfi_gadu_gadu_status_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1208         offset += 4;
1209
1210         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 4, ENC_NA);
1211         offset += 4;
1212
1213         proto_tree_add_item(tree, &hfi_gadu_gadu_status_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
1214         offset += 4;
1215
1216         proto_tree_add_item(tree, &hfi_gadu_gadu_status_port, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1217         offset += 2;
1218
1219         proto_tree_add_item(tree, &hfi_gadu_gadu_status_img_size, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1220         offset += 1;
1221
1222         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 1, ENC_NA);
1223         offset += 1;
1224
1225         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 4, ENC_NA);
1226         offset += 4;
1227
1228         offset = dissect_gadu_gadu_uint32_string_utf8(tvb, &hfi_gadu_gadu_status_descr, tree, offset);
1229
1230         return offset;
1231 }
1232
1233 static int
1234 dissect_gadu_gadu_notify_reply80(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_, int offset)
1235 {
1236         col_set_str(pinfo->cinfo, COL_INFO, "Receive status list (8.0)");
1237
1238         /* XXX packet not fully dissected */
1239
1240         return offset;
1241 }
1242
1243 static int
1244 dissect_gadu_gadu_new_status(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1245 {
1246         guint32 status;
1247
1248         col_set_str(pinfo->cinfo, COL_INFO, "New status (< 8.0)");
1249
1250         status = tvb_get_letohl(tvb, offset);
1251         proto_tree_add_item(tree, &hfi_gadu_gadu_new_status_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1252         offset += 4;
1253
1254         if (gadu_gadu_status_has_descr(status & 0xff))
1255                 offset = dissect_gadu_gadu_stringz_cp1250(tvb, &hfi_gadu_gadu_status_descr, tree, offset);
1256
1257         return offset;
1258 }
1259
1260 static int
1261 dissect_gadu_gadu_new_status80(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1262 {
1263         col_set_str(pinfo->cinfo, COL_INFO, "New status (8.0)");
1264
1265         proto_tree_add_item(tree, &hfi_gadu_gadu_new_status_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1266         offset += 4;
1267
1268         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 4, ENC_NA);
1269         offset += 4;
1270
1271         offset = dissect_gadu_gadu_uint32_string_utf8(tvb, &hfi_gadu_gadu_new_status_desc, tree, offset);
1272
1273         return offset;
1274 }
1275
1276 static int
1277 dissect_gadu_gadu_list_empty(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_, int offset)
1278 {
1279         col_set_str(pinfo->cinfo, COL_INFO, "Notify list (empty)");
1280
1281         /* empty packet */
1282
1283         return offset;
1284 }
1285
1286 static int
1287 dissect_gadu_gadu_add_notify(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1288 {
1289         col_set_str(pinfo->cinfo, COL_INFO, "Notify list add");
1290
1291         proto_tree_add_item(tree, &hfi_gadu_gadu_contact_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1292         offset += 4;
1293
1294         proto_tree_add_item(tree, &hfi_gadu_gadu_contact_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1295         offset += 1;
1296
1297         return offset;
1298 }
1299
1300 static int
1301 dissect_gadu_gadu_notify105_common(tvbuff_t *tvb, proto_tree *tree, int offset, char **puin)
1302 {
1303         guint16 uin_len;
1304         char *uin;
1305
1306         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 1, ENC_NA); /* unknown 00 */
1307         offset += 1;
1308
1309         uin_len = tvb_get_guint8(tvb, offset);
1310         offset += 1;
1311         uin = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, uin_len, ENC_ASCII | ENC_NA);
1312         proto_tree_add_string(tree, &hfi_gadu_gadu_contact_uin_str, tvb, offset - 1, 1 + uin_len, uin);
1313         offset += uin_len;
1314         if (puin)
1315                 *puin = uin;
1316
1317         proto_tree_add_item(tree, &hfi_gadu_gadu_contact_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1318         offset += 1;
1319
1320         return offset;
1321 }
1322
1323 static int
1324 dissect_gadu_gadu_add_notify105(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1325 {
1326         col_set_str(pinfo->cinfo, COL_INFO, "Notify list add (10.5)");
1327
1328         return dissect_gadu_gadu_notify105_common(tvb, tree, offset, NULL);
1329 }
1330
1331 static int
1332 dissect_gadu_gadu_remove_notify(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1333 {
1334         col_set_str(pinfo->cinfo, COL_INFO, "Notify list remove");
1335
1336         proto_tree_add_item(tree, &hfi_gadu_gadu_contact_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1337         offset += 4;
1338
1339         proto_tree_add_item(tree, &hfi_gadu_gadu_contact_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1340         offset += 1;
1341
1342         return offset;
1343 }
1344
1345 static int
1346 dissect_gadu_gadu_remove_notify105(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1347 {
1348         col_set_str(pinfo->cinfo, COL_INFO, "Notify list remove (10.5)");
1349
1350         return dissect_gadu_gadu_notify105_common(tvb, tree, offset, NULL);
1351 }
1352
1353 static int
1354 dissect_gadu_gadu_notify_common(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset)
1355 {
1356         proto_tree *contact_tree;
1357         proto_item *ti;
1358
1359         while (tvb_reported_length_remaining(tvb, offset) >= 4+1) {
1360                 guint32 uin = tvb_get_letohl(tvb, offset);
1361
1362                 ti = proto_tree_add_text(tree, tvb, offset, 5, "Contact: %u", uin);
1363                 contact_tree = proto_item_add_subtree(ti, ett_gadu_gadu_contact);
1364
1365                 proto_tree_add_item(contact_tree, &hfi_gadu_gadu_contact_uin, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1366                 offset += 4;
1367
1368                 proto_tree_add_item(contact_tree, &hfi_gadu_gadu_contact_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1369                 offset += 1;
1370         }
1371
1372         return offset;
1373 }
1374
1375 static int
1376 dissect_gadu_gadu_notify_first(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1377 {
1378         col_set_str(pinfo->cinfo, COL_INFO, "Notify list");
1379
1380         return dissect_gadu_gadu_notify_common(tvb, pinfo, tree, offset);
1381 }
1382
1383 static int
1384 dissect_gadu_gadu_notify_last(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1385 {
1386         col_set_str(pinfo->cinfo, COL_INFO, "Notify list (last)");
1387
1388         return dissect_gadu_gadu_notify_common(tvb, pinfo, tree, offset);
1389 }
1390
1391 static int
1392 dissect_gadu_gadu_notify105(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1393 {
1394         col_set_str(pinfo->cinfo, COL_INFO, "Notify list (10.5)");
1395
1396         while (tvb_reported_length_remaining(tvb, offset) >= 2) {
1397                 const int org_offset = offset;
1398
1399                 proto_tree *contact_tree;
1400                 proto_item *ti;
1401
1402                 char *uin;
1403
1404                 ti = proto_tree_add_text(tree, tvb, offset, 0, "Contact: ");
1405                 contact_tree = proto_item_add_subtree(ti, ett_gadu_gadu_contact);
1406
1407                 offset = dissect_gadu_gadu_notify105_common(tvb, contact_tree, offset, &uin);
1408                 proto_item_append_text(ti, "%s", uin);
1409
1410                 proto_item_set_len(ti, offset - org_offset);
1411         }
1412
1413         return offset;
1414 }
1415
1416 static int
1417 dissect_gadu_gadu_ping(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_, int offset)
1418 {
1419         col_set_str(pinfo->cinfo, COL_INFO, "Ping");
1420
1421         /* empty packet */
1422
1423         return offset;
1424 }
1425
1426 static int
1427 dissect_gadu_gadu_welcome(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1428 {
1429         col_set_str(pinfo->cinfo, COL_INFO, "Welcome");
1430
1431         proto_tree_add_item(tree, &hfi_gadu_gadu_welcome_seed, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1432         offset += 4;
1433
1434         return offset;
1435 }
1436
1437 static int
1438 dissect_gadu_gadu_userlist_xml_compressed(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1439 {
1440         int remain = tvb_reported_length_remaining(tvb, offset);
1441         tvbuff_t *uncomp_tvb;
1442
1443         if (remain <= 0)
1444                 return offset;
1445
1446         if ((uncomp_tvb = tvb_child_uncompress(tvb, tvb, offset, remain))) {
1447                 proto_tree_add_text(tree, tvb, offset, remain, "Userlist XML data: [Decompression succeeded]");
1448
1449                 add_new_data_source(pinfo, uncomp_tvb, "Uncompressed userlist");
1450
1451                 /* XXX add DTD (pinfo->match_string) */
1452                 call_dissector_only(xml_handle, uncomp_tvb, pinfo, tree, NULL);
1453         } else
1454                 proto_tree_add_text(tree, tvb, offset, remain, "Userlist XML data: [Error: Decompression failed] (or no libz)");
1455
1456         offset += remain;
1457
1458         return offset;
1459 }
1460
1461 static int
1462 dissect_gadu_gadu_userlist_request80(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1463 {
1464         guint8 type;
1465         proto_item *ti;
1466
1467         col_set_str(pinfo->cinfo, COL_INFO, "Userlist request (8.0)");
1468
1469         type = tvb_get_guint8(tvb, offset);
1470         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_request_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1471         offset += 1;
1472
1473         ti = proto_tree_add_uint(tree, &hfi_gadu_gadu_userlist_format, tvb, 0, 0, GG_USERLIST100_FORMAT_TYPE_GG100);
1474         PROTO_ITEM_SET_GENERATED(ti);
1475
1476         switch (type) {
1477                 case GG_USERLIST_PUT:
1478                         offset = dissect_gadu_gadu_userlist_xml_compressed(tvb, pinfo, tree, offset);
1479                         break;
1480         }
1481
1482         return offset;
1483 }
1484
1485 static int
1486 dissect_gadu_gadu_userlist_request100(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1487 {
1488         guint8 type, format;
1489
1490         col_set_str(pinfo->cinfo, COL_INFO, "Userlist request (10.0)");
1491
1492         type = tvb_get_guint8(tvb, offset);
1493         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_request_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1494         offset += 1;
1495
1496         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1497         offset += 4;
1498
1499         format = tvb_get_guint8(tvb, offset);
1500         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1501         offset += 1;
1502
1503         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 1, ENC_NA); /* 01 */
1504         offset += 1;
1505
1506         switch (type) {
1507                 case GG_USERLIST_PUT:
1508                         if (format == GG_USERLIST100_FORMAT_TYPE_GG100)
1509                                 offset = dissect_gadu_gadu_userlist_xml_compressed(tvb, pinfo, tree, offset);
1510                         break;
1511         }
1512
1513         return offset;
1514 }
1515
1516 static int
1517 dissect_gadu_gadu_userlist_reply80(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1518 {
1519         guint8 type;
1520
1521         col_set_str(pinfo->cinfo, COL_INFO, "Userlist reply (8.0)");
1522
1523         type = tvb_get_guint8(tvb, offset);
1524         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_reply_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1525         offset += 1;
1526
1527         switch (type) {
1528                 case GG_USERLIST_GET_REPLY:
1529                         offset = dissect_gadu_gadu_userlist_xml_compressed(tvb, pinfo, tree, offset);
1530                         break;
1531         }
1532
1533         return offset;
1534 }
1535
1536 static int
1537 dissect_gadu_gadu_userlist_reply100(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1538 {
1539         guint8 type, format;
1540
1541         col_set_str(pinfo->cinfo, COL_INFO, "Userlist reply (10.0)");
1542
1543         type = tvb_get_guint8(tvb, offset);
1544         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_reply_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1545         offset += 1;
1546
1547         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1548         offset += 4;
1549
1550         format = tvb_get_guint8(tvb, offset);
1551         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1552         offset += 1;
1553
1554         proto_tree_add_item(tree, &hfi_gadu_gadu_data, tvb, offset, 1, ENC_NA); /* 01 */
1555         offset += 1;
1556
1557         switch (type) {
1558                 case GG_USERLIST_GET_REPLY:
1559                         if (format == GG_USERLIST100_FORMAT_TYPE_GG100)
1560                                 offset = dissect_gadu_gadu_userlist_xml_compressed(tvb, pinfo, tree, offset);
1561                         break;
1562         }
1563
1564         return offset;
1565 }
1566
1567 static int
1568 dissect_gadu_gadu_userlist_version100(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1569 {
1570         col_set_str(pinfo->cinfo, COL_INFO, "Userlist version (10.0)");
1571
1572         proto_tree_add_item(tree, &hfi_gadu_gadu_userlist_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1573         offset += 4;
1574
1575         return offset;
1576 }
1577
1578 static int
1579 dissect_gadu_gadu_dcc7_id_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1580 {
1581         col_set_str(pinfo->cinfo, COL_INFO, "Direct connection id request");
1582
1583         proto_tree_add_item(tree, &hfi_dcc_type, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1584         offset += 4;
1585
1586         return offset;
1587 }
1588
1589 static int
1590 dissect_gadu_gadu_dcc7_id_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1591 {
1592         col_set_str(pinfo->cinfo, COL_INFO, "Direct connection id reply");
1593
1594         proto_tree_add_item(tree, &hfi_dcc_type, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1595         offset += 4;
1596
1597         proto_tree_add_item(tree, &hfi_dcc_id, tvb, offset, 8, ENC_NA);
1598         offset += 8;
1599
1600         return offset;
1601 }
1602
1603 static int
1604 dissect_gadu_gadu_dcc7_new(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1605 {
1606         col_set_str(pinfo->cinfo, COL_INFO, "Direct connection new");
1607
1608         proto_tree_add_item(tree, &hfi_dcc_id, tvb, offset, 8, ENC_NA);
1609         offset += 8;
1610
1611         proto_tree_add_item(tree, &hfi_dcc_uin_from, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1612         offset += 4;
1613
1614         proto_tree_add_item(tree, &hfi_dcc_uin_to, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1615         offset += 4;
1616
1617         proto_tree_add_item(tree, &hfi_dcc_type, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1618         offset += 4;
1619
1620         proto_tree_add_item(tree, &hfi_dcc_filename, tvb, offset, 255, ENC_ASCII | ENC_NA);
1621         offset += 255;
1622
1623         return offset;
1624 }
1625
1626 static int
1627 dissect_gadu_gadu_dcc7_id_abort(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1628 {
1629         col_set_str(pinfo->cinfo, COL_INFO, "Direct connection abort");
1630
1631         proto_tree_add_item(tree, &hfi_dcc_id, tvb, offset, 8, ENC_NA);
1632         offset += 8;
1633
1634         proto_tree_add_item(tree, &hfi_dcc_uin_from, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1635         offset += 4;
1636
1637         proto_tree_add_item(tree, &hfi_dcc_uin_to, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1638         offset += 4;
1639
1640         return offset;
1641 }
1642
1643 static int
1644 dissect_gadu_gadu_pubdir50_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1645 {
1646         int pos;
1647
1648         col_set_str(pinfo->cinfo, COL_INFO, "Public directory request");
1649
1650         proto_tree_add_item(tree, &hfi_gadu_gadu_pubdir_request_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1651         offset += 1;
1652
1653         proto_tree_add_item(tree, &hfi_gadu_gadu_pubdir_request_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1654         offset += 4;
1655         /* XXX, link request sequence with reply sequence */
1656
1657         while ((pos = tvb_find_guint8(tvb, offset, -1, '\0')) > 0) {
1658                 /* XXX, display it better, field=value */
1659                 dissect_gadu_gadu_stringz_cp1250(tvb, &hfi_gadu_gadu_pubdir_request_str, tree, offset);
1660                 offset = pos + 1;
1661         }
1662
1663         return offset;
1664 }
1665
1666 static int
1667 dissect_gadu_gadu_pubdir50_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1668 {
1669         int pos;
1670
1671         col_set_str(pinfo->cinfo, COL_INFO, "Public directory reply");
1672
1673         proto_tree_add_item(tree, &hfi_gadu_gadu_pubdir_reply_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1674         offset += 1;
1675
1676         proto_tree_add_item(tree, &hfi_gadu_gadu_pubdir_reply_seq, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1677         offset += 4;
1678         /* XXX, link reply sequence with request sequence */
1679
1680         while ((pos = tvb_find_guint8(tvb, offset, -1, '\0')) > 0) {
1681                 /* XXX, display it better, field=value */
1682                 dissect_gadu_gadu_stringz_cp1250(tvb, &hfi_gadu_gadu_pubdir_reply_str, tree, offset);
1683                 offset = pos + 1;
1684         }
1685
1686         return offset;
1687 }
1688
1689 static int
1690 dissect_gadu_gadu_xml_action(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1691 {
1692         tvbuff_t *xml_tvb;
1693         int ret;
1694
1695         col_set_str(pinfo->cinfo, COL_INFO, "XML action message");
1696
1697         xml_tvb = tvb_new_subset_remaining(tvb, offset);
1698
1699         /* XXX add DTD (pinfo->match_string) */
1700         ret = call_dissector_only(xml_handle, xml_tvb, pinfo, tree, NULL);
1701
1702         return offset + ret;
1703 }
1704
1705 static int
1706 dissect_gadu_gadu_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
1707 {
1708         proto_tree *gadu_gadu_tree = NULL;
1709
1710         int offset = 0;
1711         guint32 pkt_type;
1712
1713         col_clear(pinfo->cinfo, COL_INFO); /* XXX, remove, add separator when multiple PDU */
1714
1715         if (tree) {
1716                 proto_item *ti = proto_tree_add_item(tree, hfi_gadu_gadu, tvb, 0, -1, ENC_NA);
1717                 gadu_gadu_tree = proto_item_add_subtree(ti, ett_gadu_gadu);
1718         }
1719
1720         pkt_type = tvb_get_letohl(tvb, offset);
1721         proto_tree_add_item(gadu_gadu_tree, (pinfo->p2p_dir == P2P_DIR_RECV) ? &hfi_gadu_gadu_header_type_recv : &hfi_gadu_gadu_header_type_send, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1722         offset += 4;
1723         proto_tree_add_item(gadu_gadu_tree, &hfi_gadu_gadu_header_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1724         offset += 4;
1725
1726         if (pinfo->p2p_dir == P2P_DIR_RECV) {
1727                 switch (pkt_type) {
1728                         case GG_DISCONNECTING:
1729                                 offset = dissect_gadu_gadu_disconnecting(tvb, pinfo, gadu_gadu_tree, offset);
1730                                 break;
1731
1732                         case GG_DISCONNECT_ACK:
1733                                 offset = dissect_gadu_gadu_disconnect_ack(tvb, pinfo, gadu_gadu_tree, offset);
1734                                 break;
1735
1736                         case GG_LOGIN_OK:
1737                                 offset = dissect_gadu_gadu_login_ok(tvb, pinfo, gadu_gadu_tree, offset);
1738                                 break;
1739
1740                         case GG_LOGIN_OK80:
1741                                 offset = dissect_gadu_gadu_login_ok80(tvb, pinfo, gadu_gadu_tree, offset);
1742                                 break;
1743
1744                         case GG_LOGIN_FAILED:
1745                                 offset = dissect_gadu_gadu_login_failed(tvb, pinfo, gadu_gadu_tree, offset);
1746                                 break;
1747
1748                         case GG_LOGIN80_FAILED:
1749                                 offset = dissect_gadu_gadu_login80_failed(tvb, pinfo, gadu_gadu_tree, offset);
1750                                 break;
1751
1752                         case GG_USER_DATA:
1753                                 offset = dissect_gadu_gadu_user_data(tvb, pinfo, gadu_gadu_tree, offset);
1754                                 break;
1755
1756                         case GG_TYPING_NOTIFY:
1757                                 offset = dissect_gadu_gadu_typing_notify(tvb, pinfo, gadu_gadu_tree, offset);
1758                                 break;
1759
1760                         case GG_RECV_MSG:
1761                                 offset = dissect_gadu_gadu_recv_msg(tvb, pinfo, gadu_gadu_tree, offset);
1762                                 break;
1763
1764                         case GG_RECV_MSG80:
1765                                 offset = dissect_gadu_gadu_recv_msg80(tvb, pinfo, gadu_gadu_tree, offset);
1766                                 break;
1767
1768                         case GG_SEND_MSG_ACK:
1769                                 /* GG_SEND_MSG_ACK is received by client */
1770                                 offset = dissect_gadu_gadu_send_msg_ack(tvb, pinfo, gadu_gadu_tree, offset);
1771                                 break;
1772
1773                         case GG_STATUS60:
1774                                 offset = dissect_gadu_gadu_status60(tvb, pinfo, gadu_gadu_tree, offset);
1775                                 break;
1776
1777                         case GG_STATUS77:
1778                                 offset = dissect_gadu_gadu_status77(tvb, pinfo, gadu_gadu_tree, offset);
1779                                 break;
1780
1781                         case GG_STATUS80:
1782                                 offset = dissect_gadu_gadu_status80(tvb, pinfo, gadu_gadu_tree, offset);
1783                                 break;
1784
1785                         case GG_NOTIFY_REPLY80:
1786                                 offset = dissect_gadu_gadu_notify_reply80(tvb, pinfo, gadu_gadu_tree, offset);
1787                                 break;
1788
1789                         case GG_DCC7_ID_REPLY:
1790                                 offset = dissect_gadu_gadu_dcc7_id_reply(tvb, pinfo, gadu_gadu_tree, offset);
1791                                 break;
1792
1793                         case GG_WELCOME:
1794                                 offset = dissect_gadu_gadu_welcome(tvb, pinfo, gadu_gadu_tree, offset);
1795                                 break;
1796
1797                         case GG_USERLIST_REPLY80:
1798                                 offset = dissect_gadu_gadu_userlist_reply80(tvb, pinfo, gadu_gadu_tree, offset);
1799                                 break;
1800
1801                         case GG_USERLIST100_REPLY:
1802                                 offset = dissect_gadu_gadu_userlist_reply100(tvb, pinfo, gadu_gadu_tree, offset);
1803                                 break;
1804
1805                         case GG_USERLIST100_VERSION:
1806                                 offset = dissect_gadu_gadu_userlist_version100(tvb, pinfo, gadu_gadu_tree, offset);
1807                                 break;
1808
1809                         case GG_PUBDIR50_REPLY:
1810                                 offset = dissect_gadu_gadu_pubdir50_reply(tvb, pinfo, gadu_gadu_tree, offset);
1811                                 break;
1812
1813                         case GG_XML_ACTION:
1814                                 offset = dissect_gadu_gadu_xml_action(tvb, pinfo, gadu_gadu_tree, offset);
1815                                 break;
1816
1817                         case GG_STATUS:
1818                         case GG_PONG:
1819                         case GG_PING:
1820                         case GG_NOTIFY_REPLY:
1821                         case GG_USERLIST_REPLY:
1822                         case GG_NOTIFY_REPLY60:
1823                         case GG_NEED_EMAIL:
1824                         case GG_LOGIN_HASH_TYPE_INVALID:
1825                         case GG_NOTIFY_REPLY77:
1826                         case GG_DCC7_INFO:
1827                         case GG_DCC7_NEW:
1828                         case GG_DCC7_ACCEPT:
1829                         case GG_DCC7_REJECT:
1830                         case GG_DCC7_ID_ABORTED:
1831                         case GG_XML_EVENT:
1832                         case GG_STATUS80BETA:
1833                         case GG_NOTIFY_REPLY80BETA:
1834                         case GG_OWN_MESSAGE:
1835                         case GG_OWN_RESOURCE_INFO:
1836                         default:
1837                         {
1838                                 const char *pkt_name = try_val_to_str(pkt_type, gadu_gadu_packets_type_recv);
1839
1840                                 if (pkt_name)
1841                                         col_set_str(pinfo->cinfo, COL_INFO, pkt_name);
1842                                 else
1843                                         col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown recv packet: %.2x", pkt_type);
1844                                 break;
1845                         }
1846                 }
1847
1848         } else {
1849                 switch (pkt_type) {
1850                         case GG_LOGIN:
1851                                 offset = dissect_gadu_gadu_login(tvb, pinfo, gadu_gadu_tree, offset);
1852                                 break;
1853
1854                         case GG_LOGIN70:
1855                                 offset = dissect_gadu_gadu_login70(tvb, pinfo, gadu_gadu_tree, offset);
1856                                 break;
1857
1858                         case GG_LOGIN80:
1859                                 offset = dissect_gadu_gadu_login80(tvb, pinfo, gadu_gadu_tree, offset);
1860                                 break;
1861
1862                         case GG_LIST_EMPTY:
1863                                 offset = dissect_gadu_gadu_list_empty(tvb, pinfo, gadu_gadu_tree, offset);
1864                                 break;
1865
1866                         case GG_NOTIFY_FIRST:
1867                                 offset = dissect_gadu_gadu_notify_first(tvb, pinfo, gadu_gadu_tree, offset);
1868                                 break;
1869
1870                         case GG_NOTIFY_LAST:
1871                                 offset = dissect_gadu_gadu_notify_last(tvb, pinfo, gadu_gadu_tree, offset);
1872                                 break;
1873
1874                         case GG_NOTIFY105:
1875                                 offset = dissect_gadu_gadu_notify105(tvb, pinfo, gadu_gadu_tree, offset);
1876                                 break;
1877
1878                         case GG_ADD_NOTIFY:
1879                                 offset = dissect_gadu_gadu_add_notify(tvb, pinfo, gadu_gadu_tree, offset);
1880                                 break;
1881
1882                         case GG_ADD_NOTIFY105:
1883                                 offset = dissect_gadu_gadu_add_notify105(tvb, pinfo, gadu_gadu_tree, offset);
1884                                 break;
1885
1886                         case GG_REMOVE_NOTIFY:
1887                                 offset = dissect_gadu_gadu_remove_notify(tvb, pinfo, gadu_gadu_tree, offset);
1888                                 break;
1889
1890                         case GG_REMOVE_NOTIFY105:
1891                                 offset = dissect_gadu_gadu_remove_notify105(tvb, pinfo, gadu_gadu_tree, offset);
1892                                 break;
1893
1894                         case GG_PING:
1895                                 offset = dissect_gadu_gadu_ping(tvb, pinfo, gadu_gadu_tree, offset);
1896                                 break;
1897
1898                         case GG_TYPING_NOTIFY:
1899                                 offset = dissect_gadu_gadu_typing_notify(tvb, pinfo, gadu_gadu_tree, offset);
1900                                 break;
1901
1902                         case GG_SEND_MSG:
1903                                 offset = dissect_gadu_gadu_send_msg(tvb, pinfo, gadu_gadu_tree, offset);
1904                                 break;
1905
1906                         case GG_SEND_MSG80:
1907                                 offset = dissect_gadu_gadu_send_msg80(tvb, pinfo, gadu_gadu_tree, offset);
1908                                 break;
1909
1910                         case GG_RECV_MSG_ACK:
1911                                 /* GG_RECV_MSG_ACK is send by client */
1912                                 offset = dissect_gadu_gadu_recv_msg_ack(tvb, pinfo, gadu_gadu_tree, offset);
1913                                 break;
1914
1915                         case GG_NEW_STATUS:
1916                                 offset = dissect_gadu_gadu_new_status(tvb, pinfo, gadu_gadu_tree, offset);
1917                                 break;
1918
1919                         case GG_NEW_STATUS80:
1920                                 offset = dissect_gadu_gadu_new_status80(tvb, pinfo, gadu_gadu_tree, offset);
1921                                 break;
1922
1923                         case GG_DCC7_ID_REQUEST:
1924                                 offset = dissect_gadu_gadu_dcc7_id_request(tvb, pinfo, gadu_gadu_tree, offset);
1925                                 break;
1926
1927                         case GG_DCC7_NEW:
1928                                 offset = dissect_gadu_gadu_dcc7_new(tvb, pinfo, gadu_gadu_tree, offset);
1929                                 break;
1930
1931                         case GG_DCC7_ID_ABORT:
1932                                 offset = dissect_gadu_gadu_dcc7_id_abort(tvb, pinfo, gadu_gadu_tree, offset);
1933                                 break;
1934
1935                         case GG_USERLIST_REQUEST80:
1936                                 offset = dissect_gadu_gadu_userlist_request80(tvb, pinfo, gadu_gadu_tree, offset);
1937                                 break;
1938
1939                         case GG_USERLIST100_REQUEST:
1940                                 offset = dissect_gadu_gadu_userlist_request100(tvb, pinfo, gadu_gadu_tree, offset);
1941                                 break;
1942
1943                         case GG_PUBDIR50_REQUEST:
1944                                 offset = dissect_gadu_gadu_pubdir50_request(tvb, pinfo, gadu_gadu_tree, offset);
1945                                 break;
1946
1947                         case GG_PONG:
1948                         case GG_LOGIN_EXT:
1949                         case GG_LOGIN60:
1950                         case GG_USERLIST_REQUEST:
1951                         case GG_DCC7_INFO:
1952                         case GG_DCC7_ACCEPT:
1953                         case GG_DCC7_REJECT:
1954                         case GG_DCC7_ID_DUNNO1:
1955                         case GG_NEW_STATUS80BETA:
1956                         case GG_LOGIN80BETA:
1957                         case GG_OWN_DISCONNECT:
1958                         case GG_NEW_STATUS105:
1959                         case GG_LOGIN105:
1960                         default:
1961                         {
1962                                 const char *pkt_name = try_val_to_str(pkt_type, gadu_gadu_packets_type_send);
1963
1964                                 if (pkt_name)
1965                                         col_set_str(pinfo->cinfo, COL_INFO, pkt_name);
1966                                 else
1967                                         col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown send packet: %.2x", pkt_type);
1968                                 break;
1969                         }
1970                 }
1971         }
1972
1973         /* for now display rest of data as FT_BYTES. */
1974         if (tvb_reported_length_remaining(tvb, offset) > 0) {
1975                 proto_tree_add_item(gadu_gadu_tree, &hfi_gadu_gadu_data, tvb, offset, -1, ENC_NA);
1976         }
1977
1978         return tvb_length(tvb);
1979 }
1980
1981 static guint
1982 get_gadu_gadu_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
1983 {
1984         guint32 len = tvb_get_letohl(tvb, offset + 4);
1985
1986         return len + 8;
1987 }
1988
1989 static int
1990 dissect_gadu_gadu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1991 {
1992         if (pinfo->srcport == pinfo->match_uint && pinfo->destport != pinfo->match_uint)
1993                 pinfo->p2p_dir = P2P_DIR_RECV;
1994         else if (pinfo->srcport != pinfo->match_uint && pinfo->destport == pinfo->match_uint)
1995                 pinfo->p2p_dir = P2P_DIR_SENT;
1996         else
1997                 return 0;
1998
1999         col_set_str(pinfo->cinfo, COL_PROTOCOL, "Gadu-Gadu");
2000         col_clear(pinfo->cinfo, COL_INFO);
2001
2002         tcp_dissect_pdus(tvb, pinfo, tree, gadu_gadu_desegment, 8, get_gadu_gadu_pdu_len, dissect_gadu_gadu_pdu, data);
2003         return tvb_length(tvb);
2004 }
2005
2006 void
2007 proto_register_gadu_gadu(void)
2008 {
2009 #ifndef HAVE_HFI_SECTION_INIT
2010         static header_field_info *hfi[] = {
2011         /* Header */
2012                 &hfi_gadu_gadu_header_type_recv,
2013                 &hfi_gadu_gadu_header_type_send,
2014                 &hfi_gadu_gadu_header_length,
2015         /* Login common */
2016                 &hfi_gadu_gadu_login_uin,
2017                 &hfi_gadu_gadu_login_hash_type,
2018                 &hfi_gadu_gadu_login_hash,
2019                 &hfi_gadu_gadu_login_status,
2020                 &hfi_gadu_gadu_login_protocol,
2021                 &hfi_gadu_gadu_login_version,
2022                 &hfi_gadu_gadu_login_local_ip,
2023                 &hfi_gadu_gadu_login_local_port,
2024         /* GG_LOGIN80 */
2025                 &hfi_gadu_gadu_login80_lang,
2026         /* Contacts details */
2027                 &hfi_gadu_gadu_userdata_uin,
2028                 &hfi_gadu_gadu_userdata_attr_name,
2029                 &hfi_gadu_gadu_userdata_attr_type,
2030                 &hfi_gadu_gadu_userdata_attr_value,
2031                 &hfi_gadu_gadu_typing_notify_type,
2032                 &hfi_gadu_gadu_typing_notify_uin,
2033         /* Message common */
2034                 &hfi_gadu_gadu_msg_uin,
2035                 &hfi_gadu_gadu_msg_sender,
2036                 &hfi_gadu_gadu_msg_recipient,
2037                 &hfi_gadu_gadu_msg_seq,
2038                 &hfi_gadu_gadu_msg_time,
2039                 &hfi_gadu_gadu_msg_class,
2040                 &hfi_gadu_gadu_msg_text,
2041         /* GG_RECV_MSG80, GG_SEND_MSG80 */
2042                 &hfi_gadu_gadu_msg80_offset_plain,
2043                 &hfi_gadu_gadu_msg80_offset_attr,
2044         /* Contact (notify) common */
2045                 &hfi_gadu_gadu_contact_uin,
2046                 &hfi_gadu_gadu_contact_uin_str,
2047                 &hfi_gadu_gadu_contact_type,
2048         /* Status common */
2049                 &hfi_gadu_gadu_status_uin,
2050                 &hfi_gadu_gadu_status_status,
2051                 &hfi_gadu_gadu_status_ip,
2052                 &hfi_gadu_gadu_status_port,
2053                 &hfi_gadu_gadu_status_version,
2054                 &hfi_gadu_gadu_status_img_size,
2055                 &hfi_gadu_gadu_status_descr,
2056         /* New status (setting status) common */
2057                 &hfi_gadu_gadu_new_status_status,
2058                 &hfi_gadu_gadu_new_status_desc,
2059         /* Userlist */
2060                 &hfi_gadu_gadu_userlist_request_type,
2061                 &hfi_gadu_gadu_userlist_version,
2062                 &hfi_gadu_gadu_userlist_format,
2063                 &hfi_gadu_gadu_userlist_reply_type,
2064         /* Direct Connection */
2065                 &hfi_dcc_type,
2066                 &hfi_dcc_id,
2067                 &hfi_dcc_uin_to,
2068                 &hfi_dcc_uin_from,
2069                 &hfi_dcc_filename,
2070         /* Public Directory */
2071                 &hfi_gadu_gadu_pubdir_request_type,
2072                 &hfi_gadu_gadu_pubdir_request_seq,
2073                 &hfi_gadu_gadu_pubdir_request_str,
2074                 &hfi_gadu_gadu_pubdir_reply_type,
2075                 &hfi_gadu_gadu_pubdir_reply_seq,
2076                 &hfi_gadu_gadu_pubdir_reply_str,
2077         /* GG_WELCOME */
2078                 &hfi_gadu_gadu_welcome_seed,
2079         /* GG_SEND_MSG_ACK */
2080                 &hfi_gadu_gadu_msg_ack_status,
2081                 &hfi_gadu_gadu_msg_ack_recipient,
2082                 &hfi_gadu_gadu_msg_ack_seq,
2083         /* Not dissected data */
2084                 &hfi_gadu_gadu_data,
2085         };
2086 #endif /* HAVE_HFI_SECTION_INIT */
2087
2088         static gint *ett[] = {
2089                 &ett_gadu_gadu,
2090                 &ett_gadu_gadu_contact
2091         };
2092
2093         module_t *gadu_gadu_module;
2094
2095         int proto_gadu_gadu;
2096
2097         proto_gadu_gadu = proto_register_protocol("Gadu-Gadu Protocol", "Gadu-Gadu", "gadu-gadu");
2098         hfi_gadu_gadu = proto_registrar_get_nth(proto_gadu_gadu);
2099
2100         gadu_gadu_module = prefs_register_protocol(proto_gadu_gadu, NULL);
2101         prefs_register_bool_preference(gadu_gadu_module, "desegment",
2102                         "Reassemble Gadu-Gadu messages spanning multiple TCP segments",
2103                         "Whether the Gadu-Gadu dissector should reassemble messages spanning multiple TCP segments."
2104                         "To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
2105                         &gadu_gadu_desegment);
2106
2107         proto_register_fields(proto_gadu_gadu, hfi, array_length(hfi));
2108         proto_register_subtree_array(ett, array_length(ett));
2109
2110         gadu_gadu_handle = new_create_dissector_handle(dissect_gadu_gadu, proto_gadu_gadu);
2111 }
2112
2113 void
2114 proto_reg_handoff_gadu_gadu(void)
2115 {
2116         dissector_add_uint("tcp.port", TCP_PORT_GADU_GADU, gadu_gadu_handle);
2117
2118         xml_handle = find_dissector("xml");
2119 }
2120