Don't guard col_set_str (COL_PROTOCOL) with col_check
[obnox/wireshark/wip.git] / epan / dissectors / packet-icq.c
1 /* packet-icq.c
2  * Routines for ICQ packet disassembly
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /*
26  * This file: by Kojak <kojak@bigwig.net>
27  *
28  * Decoding code ripped, reference to the original author at the
29  * appropriate place with the code itself.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #ifdef HAVE_STDDEF_H
37 #include <stddef.h>
38 #endif
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <assert.h>
43 #include <ctype.h>
44 #include <time.h>
45 #include <string.h>
46 #include <glib.h>
47
48 #include <epan/packet.h>
49 #include <epan/addr_resolv.h>
50 #include <epan/expert.h>
51
52 static int proto_icq = -1;
53 static int hf_icq_uin = -1;
54 static int hf_icq_client_cmd = -1;
55 static int hf_icq_server_cmd = -1;
56 static int hf_icq_sessionid = -1;
57 static int hf_icq_checkcode = -1;
58 static int hf_icq_decode = -1;
59 static int hf_icq_type = -1;
60
61 static gint ett_icq = -1;
62 static gint ett_icq_header = -1;
63 static gint ett_icq_decode = -1;
64 static gint ett_icq_body = -1;
65 static gint ett_icq_body_parts = -1;
66
67 /* This is not IANA registered */
68 #define UDP_PORT_ICQ    4000
69
70 enum { ICQ5_client, ICQ5_server};
71
72 static void dissect_icqv5(tvbuff_t *tvb,
73                    packet_info *pinfo,
74                    proto_tree *tree);
75
76 static void
77 dissect_icqv5Server(tvbuff_t *tvb,
78                     int offset,
79                     packet_info *pinfo,
80                     proto_tree *tree,
81                     int pktsize);
82
83 /* Offsets of fields in the ICQ headers */
84 /* Can be 0x0002 or 0x0005 */
85 #define ICQ_VERSION             0x00
86 /* Is either one (server) or four (client) bytes long */
87 /* Client header offsets */
88 #define ICQ5_UNKNOWN            0x02
89 #define ICQ5_CL_UIN             0x06
90 #define ICQ5_CL_SESSIONID       0x0a
91 #define ICQ5_CL_CMD             0x0e
92 #define ICQ5_CL_SEQNUM1         0x10
93 #define ICQ5_CL_SEQNUM2         0x12
94 #define ICQ5_CL_CHECKCODE       0x14
95 #define ICQ5_CL_PARAM           0x18
96 #define ICQ5_CL_HDRSIZE         0x18
97
98 /* Server header offsets */
99 #define ICQ5_SRV_SESSIONID      0x03
100 #define ICQ5_SRV_CMD            0x07
101 #define ICQ5_SRV_SEQNUM1        0x09
102 #define ICQ5_SRV_SEQNUM2        0x0b
103 #define ICQ5_SRV_UIN            0x0d
104 #define ICQ5_SRV_CHECKCODE      0x11
105 #define ICQ5_SRV_PARAM          0x15
106 #define ICQ5_SRV_HDRSIZE        0x15
107
108 #define SRV_ACK                 0x000a
109
110 #define SRV_SILENT_TOO_LONG     0x001e
111
112 #define SRV_GO_AWAY             0x0028
113
114 #define SRV_NEW_UIN             0x0046
115
116 /* LOGIN_REPLY is very scary. It has a lot of fields that are undocumented
117  * Only the IP field makes sense */
118 #define SRV_LOGIN_REPLY         0x005a
119 #define SRV_LOGIN_REPLY_IP      0x000c
120
121 #define SRV_BAD_PASS            0x0064
122
123 #define SRV_USER_ONLINE         0x006e
124 #define SRV_USER_ONL_UIN        0x0000
125 #define SRV_USER_ONL_IP         0x0004
126 #define SRV_USER_ONL_PORT       0x0008
127 #define SRV_USER_ONL_REALIP     0x000c
128 #define SRV_USER_ONL_X1         0x0010
129 #define SRV_USER_ONL_STATUS     0x0013
130 #define SRV_USER_ONL_X2         0x0015
131
132 #define SRV_USER_OFFLINE        0x0078
133 #define SRV_USER_OFFLINE_UIN    0x0000
134
135 #define SRV_MULTI               0x0212
136 #define SRV_MULTI_NUM           0x0000
137
138 #define SRV_META_USER           0x03de
139 #define SRV_META_USER_SUBCMD    0x0000
140 #define SRV_META_USER_RESULT    0x0002
141 #define SRV_META_USER_DATA      0x0003
142
143 #define SRV_UPDATE_SUCCESS      0x01e0
144
145 #define SRV_UPDATE_FAIL         0x01ea
146
147 /*
148  * ICQv5 SRV_META_USER subcommands
149  */
150 #define META_EX_USER_FOUND      0x0190
151 #define META_USER_FOUND         0x019a
152 #define META_ABOUT              0x00e6
153 #define META_USER_INFO          0x00c8
154
155 #define SRV_RECV_MESSAGE        0x00dc
156 #define SRV_RECV_MSG_UIN        0x0000
157 #define SRV_RECV_MSG_YEAR       0x0004
158 #define SRV_RECV_MSG_MONTH      0x0006
159 #define SRV_RECV_MSG_DAY        0x0007
160 #define SRV_RECV_MSG_HOUR       0x0008
161 #define SRV_RECV_MSG_MINUTE     0x0009
162 #define SRV_RECV_MSG_MSG_TYPE   0x000a
163
164 #define SRV_RAND_USER           0x024e
165 #define SRV_RAND_USER_UIN       0x0000
166 #define SRV_RAND_USER_IP        0x0004
167 #define SRV_RAND_USER_PORT      0x0008
168 #define SRV_RAND_USER_REAL_IP   0x000c
169 #define SRV_RAND_USER_CLASS     0x0010
170 #define SRV_RAND_USER_X1        0x0011
171 #define SRV_RAND_USER_STATUS    0x0015
172 #define SRV_RAND_USER_TCP_VER   0x0019
173
174 /* This message has the same structure as cmd_send_msg */
175 #define SRV_SYS_DELIVERED_MESS  0x0104
176
177 static const value_string serverMetaSubCmdCode[] = {
178     { META_USER_FOUND, "META_USER_FOUND" },
179     { META_EX_USER_FOUND, "META_EX_USER_FOUND"  },
180     { META_ABOUT, "META_ABOUT" },
181     { META_USER_INFO, "META_USER_INFO" },
182     { 0, NULL }
183 };
184
185 static const value_string serverCmdCode[] = {
186     { SRV_ACK, "SRV_ACK" },
187     { SRV_SILENT_TOO_LONG, "SRV_SILENT_TOO_LONG" },
188     { SRV_GO_AWAY, "SRV_GO_AWAY" },
189     { SRV_NEW_UIN, "SRV_NEW_UIN" },
190     { SRV_LOGIN_REPLY, "SRV_LOGIN_REPLY" },
191     { SRV_BAD_PASS, "SRV_BAD_PASS" },
192     { SRV_USER_ONLINE, "SRV_USER_ONLINE" },
193     { SRV_USER_OFFLINE, "SRV_USER_OFFLINE" },
194     { 130, "SRV_QUERY" },
195     { 140, "SRV_USER_FOUND" },
196     { 160, "SRV_END_OF_SEARCH" },
197     { 180, "SRV_NEW_USER" },
198     { 200, "SRV_UPDATE_EXT" },
199     { SRV_RECV_MESSAGE, "SRV_RECV_MESSAGE" },
200     { 230, "SRV_END_OFFLINE_MESSAGES" },
201     { 240, "SRV_NOT_CONNECTED" },
202     { 250, "SRV_TRY_AGAIN" },
203     { SRV_SYS_DELIVERED_MESS, "SRV_SYS_DELIVERED_MESS" },
204     { 280, "SRV_INFO_REPLY" },
205     { 290, "SRV_EXT_INFO_REPLY" },
206     { 420, "SRV_STATUS_UPDATE" },
207     { 450, "SRV_SYSTEM_MESSAGE" },
208     { SRV_UPDATE_SUCCESS, "SRV_UPDATE_SUCCESS" },
209     { SRV_UPDATE_FAIL, "SRV_UPDATE_FAIL" },
210     { 500, "SRV_AUTH_UPDATE" },
211     { SRV_MULTI, "SRV_MULTI_PACKET" },
212     { 540, "SRV_END_CONTACTLIST_STATUS" },
213     { SRV_RAND_USER, "SRV_RAND_USER" },
214     { SRV_META_USER, "SRV_META_USER" },
215     { 0, NULL }
216 };
217
218 #define MSG_TEXT                0x0001
219 #define MSG_URL                 0x0004
220 #define MSG_AUTH_REQ            0x0006
221 #define MSG_AUTH                0x0008
222 #define MSG_USER_ADDED          0x000c
223 #define MSG_EMAIL               0x000e
224 #define MSG_CONTACTS            0x0013
225
226 #define STATUS_ONLINE           0x00000000
227 #define STATUS_AWAY             0x00000001
228 #define STATUS_DND              0x00000013
229 #define STATUS_INVISIBLE        0x00000100
230 #define STATUS_OCCUPIED         0x00000010
231 #define STATUS_NA               0x00000004
232 #define STATUS_CHAT             0x00000020
233
234 /* Offsets for all packets measured from the start of the payload; i.e.
235  * with the ICQ header removed
236  */
237 #define CMD_ACK                 0x000a
238 #define CMD_ACK_RANDOM          0x0000
239
240 #define CMD_SEND_MSG            0x010E
241 #define CMD_SEND_MSG_RECV_UIN   0x0000
242 #define CMD_SEND_MSG_MSG_TYPE   0x0004
243 #define CMD_SEND_MSG_MSG_LEN    0x0006
244 #define CMD_SEND_MSG_MSG_TEXT   0x0008
245 /* The rest of the packet should be a null-term string */
246
247 #define CMD_LOGIN               0x03E8
248 #define CMD_LOGIN_TIME          0x0000
249 #define CMD_LOGIN_PORT          0x0004
250 #define CMD_LOGIN_PASSLEN       0x0008
251 #define CMD_LOGIN_PASSWD        0x000A
252 /* The password is variable length; so when we've decoded the passwd,
253  * the structure starts counting at 0 again.
254  */
255 #define CMD_LOGIN_IP            0x0004
256 #define CMD_LOGIN_STATUS        0x0009
257
258 #define CMD_CONTACT_LIST        0x0406
259 #define CMD_CONTACT_LIST_NUM    0x0000
260
261 #define CMD_USER_META           0x064a
262
263 #define CMD_REG_NEW_USER        0x03fc
264
265 #define CMD_ACK_MESSAGES        0x0442
266 #define CMD_ACK_MESSAGES_RANDOM 0x0000
267
268 #define CMD_KEEP_ALIVE          0x042e
269 #define CMD_KEEP_ALIVE_RANDOM   0x0000
270
271 #define CMD_SEND_TEXT_CODE      0x0438
272 #define CMD_SEND_TEXT_CODE_LEN  0x0000
273 #define CMD_SEND_TEXT_CODE_TEXT 0x0002
274
275 #define CMD_MSG_TO_NEW_USER     0x0456
276
277 #define CMD_QUERY_SERVERS       0x04ba
278
279 #define CMD_QUERY_ADDONS        0x04c4
280
281 #define CMD_STATUS_CHANGE       0x04d8
282 #define CMD_STATUS_CHANGE_STATUS        0x0000
283
284 #define CMD_ADD_TO_LIST         0x053c
285 #define CMD_ADD_TO_LIST_UIN     0x0000
286
287 #define CMD_RAND_SEARCH         0x056e
288 #define CMD_RAND_SEARCH_GROUP   0x0000
289
290 #define CMD_META_USER           0x064a
291
292 static const value_string msgTypeCode[] = {
293     { MSG_TEXT, "MSG_TEXT" },
294     { MSG_URL, "MSG_URL" },
295     { MSG_AUTH_REQ, "MSG_AUTH_REQ" },
296     { MSG_AUTH, "MSG_AUTH" },
297     { MSG_USER_ADDED, "MSG_USER_ADDED" },
298     { MSG_EMAIL, "MSG_EMAIL" },
299     { MSG_CONTACTS, "MSG_CONTACTS" },
300     { 0, NULL }
301 };
302
303 static const value_string statusCode[] = {
304     { STATUS_ONLINE, "ONLINE" },
305     { STATUS_AWAY, "AWAY" },
306     { STATUS_DND, "DND" },
307     { STATUS_INVISIBLE, "INVISIBLE" },
308     { STATUS_OCCUPIED, "OCCUPIED" },
309     { STATUS_NA, "NA" },
310     { STATUS_CHAT, "Free for Chat" },
311     { 0, NULL }
312 };
313
314 static const value_string clientCmdCode[] = {
315     { CMD_ACK, "CMD_ACK" },
316     { CMD_SEND_MSG, "CMD_SEND_MESSAGE" },
317     { CMD_LOGIN, "CMD_LOGIN" },
318     { CMD_REG_NEW_USER, "CMD_REG_NEW_USER" },
319     { 1030, "CMD_CONTACT_LIST" },
320     { 1050, "CMD_SEARCH_UIN" },
321     { 1060, "CMD_SEARCH_USER" },
322     { 1070, "CMD_KEEP_ALIVE" },
323     { CMD_SEND_TEXT_CODE, "CMD_SEND_TEXT_CODE" },
324     { CMD_ACK_MESSAGES, "CMD_ACK_MESSAGES" },
325     { 1100, "CMD_LOGIN_1" },
326     { CMD_MSG_TO_NEW_USER, "CMD_MSG_TO_NEW_USER" },
327     { 1120, "CMD_INFO_REQ" },
328     { 1130, "CMD_EXT_INFO_REQ" },
329     { 1180, "CMD_CHANGE_PW" },
330     { 1190, "CMD_NEW_USER_INFO" },
331     { 1200, "CMD_UPDATE_EXT_INFO" },
332     { CMD_QUERY_SERVERS, "CMD_QUERY_SERVERS" },
333     { CMD_QUERY_ADDONS, "CMD_QUERY_ADDONS" },
334     { CMD_STATUS_CHANGE, "CMD_STATUS_CHANGE" },
335     { 1260, "CMD_NEW_USER_1" },
336     { 1290, "CMD_UPDATE_INFO" },
337     { 1300, "CMD_AUTH_UPDATE" },
338     { 1310, "CMD_KEEP_ALIVE2" },
339     { 1320, "CMD_LOGIN_2" },
340     { CMD_ADD_TO_LIST, "CMD_ADD_TO_LIST" },
341     { 1380, "CMD_RAND_SET" },
342     { CMD_RAND_SEARCH, "CMD_RAND_SEARCH" },
343     { CMD_META_USER, "CMD_META_USER" },
344     { 1700, "CMD_INVIS_LIST" },
345     { 1710, "CMD_VIS_LIST" },
346     { 1720, "CMD_UPDATE_LIST" },
347     { 0, NULL }
348 };
349
350 /*
351  * All ICQv5 decryption code thanx to Sebastien Dault (daus01@gel.usherb.ca)
352  */
353 static const guchar
354 table_v5 [] = {
355  0x59, 0x60, 0x37, 0x6B, 0x65, 0x62, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x60, 0x57, 0x5B, 0x3D,
356  0x5E, 0x34, 0x6D, 0x36, 0x50, 0x3F, 0x6F, 0x67, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x47, 0x63, 0x39,
357  0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x43, 0x69, 0x48, 0x33, 0x31, 0x64, 0x35, 0x5A, 0x4A, 0x42,
358  0x56, 0x40, 0x67, 0x53, 0x41, 0x07, 0x6C, 0x49, 0x58, 0x3B, 0x4D, 0x46, 0x68, 0x43, 0x69, 0x48,
359  0x33, 0x31, 0x44, 0x65, 0x62, 0x46, 0x48, 0x53, 0x41, 0x07, 0x6C, 0x69, 0x48, 0x33, 0x51, 0x54,
360  0x5D, 0x4E, 0x6C, 0x49, 0x38, 0x4B, 0x55, 0x4A, 0x62, 0x46, 0x48, 0x33, 0x51, 0x34, 0x6D, 0x36,
361  0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x63, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64, 0x35, 0x5A,
362  0x6A, 0x52, 0x6E, 0x3C, 0x51, 0x34, 0x6D, 0x36, 0x50, 0x5F, 0x5F, 0x3F, 0x4F, 0x37, 0x4B, 0x35,
363  0x5A, 0x4A, 0x62, 0x66, 0x58, 0x3B, 0x4D, 0x66, 0x58, 0x5B, 0x5D, 0x4E, 0x6C, 0x49, 0x58, 0x3B,
364  0x4D, 0x66, 0x58, 0x3B, 0x4D, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64,
365  0x55, 0x6A, 0x32, 0x3E, 0x44, 0x45, 0x52, 0x6E, 0x3C, 0x31, 0x64, 0x55, 0x6A, 0x52, 0x4E, 0x6C,
366  0x69, 0x48, 0x53, 0x61, 0x4C, 0x39, 0x30, 0x6F, 0x47, 0x63, 0x59, 0x60, 0x57, 0x5B, 0x3D, 0x3E,
367  0x64, 0x35, 0x3A, 0x3A, 0x5A, 0x6A, 0x52, 0x4E, 0x6C, 0x69, 0x48, 0x53, 0x61, 0x6C, 0x49, 0x58,
368  0x3B, 0x4D, 0x46, 0x68, 0x63, 0x39, 0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x67, 0x53, 0x41, 0x25, 0x41,
369  0x3C, 0x51, 0x54, 0x3D, 0x5E, 0x54, 0x5D, 0x4E, 0x4C, 0x39, 0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F,
370  0x47, 0x43, 0x69, 0x48, 0x33, 0x51, 0x54, 0x5D, 0x6E, 0x3C, 0x31, 0x64, 0x35, 0x5A, 0x00, 0x00 };
371
372 static const char*
373 findMsgType(int num)
374 {
375     return val_to_str(num, msgTypeCode, "Unknown");
376 }
377
378 static const char*
379 findSubCmd(int num)
380 {
381     return val_to_str(num, serverMetaSubCmdCode, "Unknown (0x%04x)");
382 }
383
384 static const char*
385 findClientCmd(int num)
386 {
387     return val_to_str(num, clientCmdCode, "Unknown (%u)");
388 }
389
390 static const char*
391 findServerCmd(int num)
392 {
393     return val_to_str(num, serverCmdCode, "Unknown (%u)");
394 }
395
396 static const char*
397 findStatus(int num)
398 {
399     return val_to_str(num, statusCode, "Unknown (0x%08x)");
400 }
401
402 static guint32
403 get_v5key(tvbuff_t *tvb, int len)
404 {
405     guint32 a1, a2, a3, a4, a5;
406     guint32 code, check, key;
407
408     code = tvb_get_letohl(tvb, ICQ5_CL_CHECKCODE);
409
410     a1 = code & 0x0001f000;
411     a2 = code & 0x07c007c0;
412     a3 = code & 0x003e0001;
413     a4 = code & 0xf8000000;
414     a5 = code & 0x0000083e;
415
416     a1 = a1 >> 0x0c;
417     a2 = a2 >> 0x01;
418     a3 = a3 << 0x0a;
419     a4 = a4 >> 0x10;
420     a5 = a5 << 0x0f;
421
422     check = a5 + a1 + a2 + a3 + a4;
423     key = len * 0x68656C6C;
424     key += check;
425     return key;
426 }
427
428 static void
429 decrypt_v5(guchar *bfr, guint32 size,guint32 key)
430 {
431     guint32 i;
432     guint32 k;
433
434     for (i=ICQ5_CL_SESSIONID; i < size; i+=4 ) {
435         k = key+table_v5[i&0xff];
436         if ( i != 0x16 ) {
437             bfr[i] ^= (guchar)(k & 0xff);
438             bfr[i+1] ^= (guchar)((k & 0xff00)>>8);
439         }
440         if ( i != 0x12 ) {
441             bfr[i+2] ^= (guchar)((k & 0xff0000)>>16);
442             bfr[i+3] ^= (guchar)((k & 0xff000000)>>24);
443         }
444     }
445 }
446
447 static void
448 dissect_icqv4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
449 {
450     proto_tree *icq_tree = NULL;
451     proto_item *ti = NULL;
452
453     /* Not really implemented yet */
454     if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
455         col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQv4 (UDP)");
456     }
457     if (check_col(pinfo->cinfo, COL_INFO)) {
458         col_set_str(pinfo->cinfo, COL_INFO, "ICQ Version 4 protocol");
459     }
460     if (tree) {
461         ti = proto_tree_add_protocol_format(tree, proto_icq, tvb, 0, -1,
462                                             "ICQv4");
463         icq_tree = proto_item_add_subtree(ti, ett_icq);
464
465         proto_tree_add_text(icq_tree, tvb, ICQ_VERSION, 2, "Version: %u",
466                             tvb_get_letohs(tvb, ICQ_VERSION));
467     }
468 }
469
470 static void
471 dissect_icqv3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
472 {
473     proto_tree *icq_tree = NULL;
474     proto_item *ti = NULL;
475
476     /* Not really implemented yet */
477     if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
478         col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQv3 (UDP)");
479     }
480     if (check_col(pinfo->cinfo, COL_INFO)) {
481         col_set_str(pinfo->cinfo, COL_INFO, "ICQ Version 3 protocol");
482     }
483     if (tree) {
484         ti = proto_tree_add_protocol_format(tree, proto_icq, tvb, 0, -1,
485                                             "ICQv3");
486         icq_tree = proto_item_add_subtree(ti, ett_icq);
487
488         proto_tree_add_text(icq_tree, tvb, ICQ_VERSION, 2, "Version: %u",
489                             tvb_get_letohs(tvb, ICQ_VERSION));
490     }
491 }
492
493 static void
494 dissect_icqv2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
495 {
496     proto_tree *icq_tree = NULL;
497     proto_item *ti = NULL;
498
499     /* Not really implemented yet */
500     if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
501         col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQv2 (UDP)");
502     }
503     if (check_col(pinfo->cinfo, COL_INFO)) {
504         col_set_str(pinfo->cinfo, COL_INFO, "ICQ Version 2 protocol");
505     }
506     if (tree) {
507         ti = proto_tree_add_protocol_format(tree, proto_icq, tvb, 0, -1,
508                                             "ICQv2");
509         icq_tree = proto_item_add_subtree(ti, ett_icq);
510
511         proto_tree_add_text(icq_tree, tvb, ICQ_VERSION, 2, "Version: %u",
512                             tvb_get_letohs(tvb, ICQ_VERSION));
513     }
514 }
515
516 /*
517  * The packet has, at offset "offset" a (len, string) pair.
518  * Display the length and string in the tree.
519  *
520  * If anything is wrong, return -1, since -1 is not a valid string
521  * length. Else, return the number of chars processed.
522  */
523 static guint16
524 proto_add_icq_attr(proto_tree* tree, /* The tree to add to */
525                    tvbuff_t *tvb,    /* Tvbuff with packet */
526                    const int offset, /* Offset from the start of packet of field */
527                    const char* descr)   /* The description to use in the tree */
528 {
529     guint16 len;
530
531     len = tvb_get_letohs(tvb, offset);
532     if (len > tvb_reported_length_remaining(tvb, offset))
533         return -1;      /* length goes past end of packet */
534     proto_tree_add_text(tree, tvb, offset, sizeof(guint16) + len,
535                         "%s[%u]: %.*s", descr, len, len,
536                         tvb_get_ptr(tvb, offset + sizeof(guint16), len));
537     return len + sizeof(guint16);
538 }
539
540 static void
541 icqv5_decode_msgType(proto_tree* tree, tvbuff_t *tvb, int offset, int size,
542                      packet_info *pinfo)
543 {
544     proto_item* ti = NULL;
545     proto_tree* subtree = NULL;
546     int left = size;
547     guint16 msgType;
548     gint sep_offset;
549     int sz;            /* Size of the current element */
550     unsigned int n;
551     static const char* url_field_descr[] = {
552         "Description",
553         "URL",
554     };
555 #define N_URL_FIELDS    (sizeof url_field_descr / sizeof url_field_descr[0])
556     static const char* email_field_descr[] = {
557         "Nickname",
558         "First name",
559         "Last name",
560         "Email address",
561         "Unknown",
562         "Text"
563     };
564 #define N_EMAIL_FIELDS  (sizeof email_field_descr / sizeof email_field_descr[0])
565     static const char* auth_req_field_descr[] = {
566         "Nickname",
567         "First name",
568         "Last name",
569         "Email address",
570         "Unknown",
571         "Reason"
572     };
573 #define N_AUTH_REQ_FIELDS       (sizeof auth_req_field_descr / sizeof auth_req_field_descr[0])
574     static const char* user_added_field_descr[] = {
575         "Nickname",
576         "First name",
577         "Last name",
578         "Email address",
579     };
580 #define N_USER_ADDED_FIELDS     (sizeof user_added_field_descr / sizeof user_added_field_descr[0])
581
582     msgType = tvb_get_letohs(tvb, offset);
583     ti = proto_tree_add_text(tree, tvb, offset, size,
584                              "Message: Type = %u (%s)", msgType, findMsgType(msgType));
585     /* Create a new subtree */
586     subtree = proto_item_add_subtree(ti, ett_icq_body_parts);
587
588     proto_tree_add_text(subtree, tvb, offset, 2,
589                         "Type: %u (%s)", msgType, findMsgType(msgType));
590     offset += 2;
591     left -= 2;
592     if (msgType != MSG_AUTH) {
593         /*
594          * XXX - does a MSG_AUTH message really have 3 bytes of information
595          * rather than a length field?
596          */
597         proto_tree_add_text(subtree, tvb, offset, 2, "Length: %u",
598                             tvb_get_letohs(tvb, offset));
599         offset += 2;
600         left -= 2;
601     }
602
603     switch(msgType) {
604     case 0xffff:           /* Field unknown */
605         break;
606     default:
607         expert_add_info_format(pinfo, NULL, PI_UNDECODED, PI_WARN,
608                                "Unknown msgType: %u (0x%x)", msgType,
609                                msgType);
610         break;
611     case MSG_TEXT:
612         proto_tree_add_text(subtree, tvb, offset, left, "Msg: %.*s", left-1,
613                             tvb_get_ptr(tvb, offset, left));
614         break;
615     case MSG_URL:
616         for (n = 0; n < N_URL_FIELDS; n++) {
617             if (n != N_URL_FIELDS - 1) {
618                 sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
619                 sz = sep_offset - offset + 1;
620             } else
621                 sz = left;
622             if (sz != 0) {
623                 proto_tree_add_text(subtree, tvb, offset, sz, "%s: %.*s",
624                                     url_field_descr[n],
625                                     sz - 1,
626                                     tvb_get_ptr(tvb, offset, sz));
627             } else {
628                 proto_tree_add_text(subtree, tvb, offset, 0,
629                                     "%s: %s", url_field_descr[n], "(empty)");
630             }
631             offset += sz;
632             left -= sz;
633         }
634         break;
635     case MSG_EMAIL:
636         for (n = 0; n < N_EMAIL_FIELDS; n++) {
637             if (n != N_EMAIL_FIELDS - 1) {
638                 sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
639                 sz = sep_offset - offset + 1;
640             } else
641                 sz = left;
642             if (sz != 0) {
643                 proto_tree_add_text(subtree, tvb, offset, sz, "%s: %.*s",
644                                     email_field_descr[n],
645                                     sz - 1,
646                                     tvb_get_ptr(tvb, offset, sz));
647             } else {
648                 proto_tree_add_text(subtree, tvb, offset, 0, "%s: %s",
649                                     email_field_descr[n], "(empty)");
650             }
651             offset += sz;
652             left -= sz;
653         }
654         break;
655
656     case MSG_AUTH:
657     {
658         /* Three bytes, first is a char signifying success */
659         unsigned char auth_suc;
660
661         auth_suc = tvb_get_guint8(tvb, offset);
662         proto_tree_add_text(subtree, tvb, offset, 1,
663                             "Authorization: (%u) %s",auth_suc,
664                             (auth_suc==0)?"Denied":"Allowed");
665         offset++;
666         proto_tree_add_text(subtree, tvb, offset, sizeof(guint16), "x1: 0x%04x",
667                             tvb_get_letohs(tvb, offset));
668         break;
669     }
670     case MSG_AUTH_REQ:
671         for (n = 0; n < N_AUTH_REQ_FIELDS; n++) {
672             if (n != N_AUTH_REQ_FIELDS - 1) {
673                 sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
674                 sz = sep_offset - offset + 1;
675             } else
676                 sz = left;
677             if (sz != 0) {
678                 proto_tree_add_text(subtree, tvb, offset, sz, "%s: %.*s",
679                                     auth_req_field_descr[n], sz - 1,
680                                     tvb_get_ptr(tvb, offset, sz));
681             } else {
682                 proto_tree_add_text(subtree, tvb, offset, 0, "%s: %s",
683                                     auth_req_field_descr[n], "(empty)");
684             }
685             offset += sz;
686             left -= sz;
687         }
688         break;
689     case MSG_USER_ADDED:
690         for (n = 0; n < N_USER_ADDED_FIELDS; n++) {
691             if (n != N_USER_ADDED_FIELDS - 1) {
692                 sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
693                 sz = sep_offset - offset + 1;
694             } else
695                 sz = left;
696             if (sz != 0) {
697                 proto_tree_add_text(subtree, tvb, offset, sz, "%s: %.*s",
698                                     user_added_field_descr[n], sz - 1,
699                                     tvb_get_ptr(tvb, offset, sz));
700             } else {
701                 proto_tree_add_text(subtree, tvb, offset, 0, "%s: %s",
702                                     user_added_field_descr[n], "(empty)");
703             }
704             offset += sz;
705             left -= sz;
706         }
707         break;
708     case MSG_CONTACTS:
709     {
710         gint sep_offset_prev;
711         int sz_local = 0;            /* Size of the current element */
712         int n_local = 0;             /* The nth element */
713         gboolean last = FALSE;
714
715         while (!last) {
716             sep_offset = tvb_find_guint8(tvb, offset, left, 0xfe);
717             if (sep_offset != -1)
718                 sz_local = sep_offset - offset + 1;
719             else {
720                 sz_local = left;
721                 last = TRUE;
722             }
723             if (n_local == 0) {
724                 /* The first element is the number of Nick/UIN pairs follow */
725                 proto_tree_add_text(subtree, tvb, offset, sz_local,
726                                     "Number of pairs: %.*s", sz_local - 1,
727                                     tvb_get_ptr(tvb, offset, sz_local));
728                 n_local++;
729             } else if (!last) {
730                 int svsz = sz_local;
731
732                 left -= sz_local;
733                 sep_offset_prev = sep_offset;
734                 sep_offset = tvb_find_guint8(tvb, sep_offset_prev, left, 0xfe);
735                 if (sep_offset != -1)
736                     sz_local = sep_offset - offset + 1;
737                 else {
738                     sz_local = left;
739                     last = TRUE;
740                 }
741                 proto_tree_add_text(subtree, tvb, offset, sz_local + svsz,
742                                     "%.*s: %.*s", svsz - 1,
743                                     tvb_get_ptr(tvb, offset, svsz), sz_local - 1,
744                                     tvb_get_ptr(tvb, sep_offset_prev + 1, sz_local));
745                 n_local += 2;
746             }
747
748             left -= (sz_local+1);
749             offset = sep_offset + 1;
750         }
751         break;
752     }
753     }
754 }
755
756 /*********************************
757  *
758  * Client commands
759  *
760  *********************************/
761 static void
762 icqv5_cmd_ack(proto_tree* tree,/* Tree to put the data in */
763               tvbuff_t *tvb, /* Tvbuff with decrypted packet data */
764               int offset) /* Offset from the start of the packet to the content */
765 {
766     proto_tree* subtree;
767     proto_item* ti;
768
769     if (tree){
770         ti = proto_tree_add_text(tree, tvb, offset, 4, "Body");
771         subtree = proto_item_add_subtree(ti, ett_icq_body);
772         proto_tree_add_text(subtree, tvb, offset + CMD_ACK_RANDOM, 4,
773                             "Random: 0x%08x",
774                             tvb_get_letohl(tvb, offset + CMD_ACK_RANDOM));
775     }
776 }
777
778 static void
779 icqv5_cmd_rand_search(proto_tree* tree, /* Tree to put the data in */
780                       tvbuff_t *tvb,    /* Decrypted packet content */
781                       int offset,       /* Offset from the start of the packet to the content */
782                       int size)         /* Number of chars left to do */
783 {
784     guint16 group;
785     proto_tree* subtree;
786     proto_item* ti;
787
788     static const char* groups[] = {
789         "Name",
790         "General",
791         "Romance",
792         "Games",
793         "Students",
794         "20 Something",
795         "30 Something",
796         "40 Something",
797         "50 or worse",
798         "Man want women",
799         "Women want men"
800     };
801
802     if (tree){
803         if (size < 4) {
804             ti = proto_tree_add_text(tree, tvb, offset, size,
805                                      "Body (%d bytes, should be 4)", size);
806             return;
807         }
808         ti = proto_tree_add_text(tree, tvb, offset, 4, "Body");
809         subtree = proto_item_add_subtree(ti, ett_icq_body);
810         group = tvb_get_letohs(tvb, offset + CMD_RAND_SEARCH_GROUP);
811         if (group>0 && (group<=sizeof(groups)/sizeof(const char*)))
812             proto_tree_add_text(subtree, tvb, offset + CMD_RAND_SEARCH_GROUP,
813                                 4, "Group: (%u) %s", group, groups[group-1]);
814         else
815             proto_tree_add_text(subtree, tvb, offset + CMD_RAND_SEARCH_GROUP,
816                                 4, "Group: (%u)", group);
817     }
818 }
819
820 static void
821 icqv5_cmd_ack_messages(proto_tree* tree, /* Tree to put the data in */
822                        tvbuff_t *tvb,    /* Decrypted packet content */
823                        int offset)       /* Offset from the start of the packet to the content */
824 {
825     proto_tree* subtree;
826     proto_item* ti;
827
828     if (tree){
829         ti = proto_tree_add_text(tree, tvb, offset, 4, "Body");
830         subtree = proto_item_add_subtree(ti, ett_icq_body);
831         proto_tree_add_text(subtree, tvb, offset + CMD_ACK_MESSAGES_RANDOM,
832                             4, "Random: 0x%08x",
833                             tvb_get_letohl(tvb, offset + CMD_ACK_MESSAGES_RANDOM));
834     }
835 }
836
837 static void
838 icqv5_cmd_keep_alive(proto_tree* tree, /* Tree to put the data in */
839                      tvbuff_t *tvb,    /* Decrypted packet content */
840                      int offset)       /* Offset from the start of the packet to the content */
841 {
842     guint32 randomx;
843     proto_tree* subtree;
844     proto_item* ti;
845
846     if (tree){
847         ti = proto_tree_add_text(tree, tvb, offset, 4, "Body");
848         subtree = proto_item_add_subtree(ti, ett_icq_body);
849         randomx = tvb_get_letohl(tvb, offset + CMD_KEEP_ALIVE_RANDOM);
850         proto_tree_add_text(subtree, tvb, offset + CMD_KEEP_ALIVE_RANDOM,
851                             4, "Random: 0x%08x", randomx);
852     }
853 }
854
855 static void
856 icqv5_cmd_send_text_code(proto_tree* tree, /* Tree to put the data in */
857                          tvbuff_t *tvb,    /* Decrypted packet content */
858                          int offset,       /* Offset from the start of the packet to the content */
859                          int size)         /* Number of chars left to do */
860 {
861     proto_tree* subtree = NULL;
862     proto_item* ti = NULL;
863     guint16 len;
864     guint16 x1;
865
866     if (tree){
867         ti = proto_tree_add_text(tree, tvb, offset, size, "Body");
868     }
869
870     len = tvb_get_letohs(tvb, offset+CMD_SEND_TEXT_CODE_LEN);
871     if (tree){
872         subtree = proto_item_add_subtree(ti, ett_icq_body);
873         proto_tree_add_text(subtree, tvb, offset + CMD_SEND_TEXT_CODE_LEN,
874                             2, "Length: %d", len);
875     }
876
877     if (len>0) {
878         if (tree){
879             proto_tree_add_text(subtree, tvb, offset + CMD_SEND_TEXT_CODE_TEXT,
880                             len, "Text: %.*s", len,
881                             tvb_get_ptr(tvb, offset + CMD_SEND_TEXT_CODE_TEXT,
882                                         len));
883         }
884     }
885
886     x1 = tvb_get_letohs(tvb, offset + CMD_SEND_TEXT_CODE_TEXT + len);
887     if (tree){
888         proto_tree_add_text(subtree, tvb,
889                             offset + CMD_SEND_TEXT_CODE_TEXT + len,
890                             2, "X1: 0x%04x", x1);
891     }
892 }
893
894 static void
895 icqv5_cmd_add_to_list(proto_tree* tree, /* Tree to put the data in */
896                       tvbuff_t *tvb,    /* Decrypted packet content */
897                       int offset)       /* Offset from the start of the packet to the content */
898 {
899     guint32 uin;
900     proto_tree* subtree;
901     proto_item* ti;
902
903     if (tree){
904         ti = proto_tree_add_text(tree, tvb, offset, 4, "Body");
905         subtree = proto_item_add_subtree(ti, ett_icq_body);
906         uin = tvb_get_letohl(tvb, offset + CMD_ADD_TO_LIST);
907         proto_tree_add_text(subtree, tvb, offset + CMD_ADD_TO_LIST_UIN, 4,
908                             "UIN: %u", uin);
909     }
910 }
911
912 static void
913 icqv5_cmd_status_change(proto_tree* tree, /* Tree to put the data in */
914                         tvbuff_t *tvb,    /* Decrypted packet content */
915                         int offset)       /* Offset from the start of the packet to the content */
916 {
917     guint32 status;
918     proto_tree* subtree;
919     proto_item* ti;
920
921     if (tree){
922         ti = proto_tree_add_text(tree, tvb, offset, 4, "Body");
923         subtree = proto_item_add_subtree(ti, ett_icq_body);
924         status = tvb_get_letohl(tvb, offset + CMD_STATUS_CHANGE_STATUS);
925         proto_tree_add_text(subtree, tvb, offset + CMD_STATUS_CHANGE_STATUS,
926                             4, "Status: %s", findStatus(status));
927     }
928 }
929
930 static void
931 icqv5_cmd_send_msg(proto_tree* tree, tvbuff_t *tvb, int offset, int size,
932                    packet_info *pinfo)
933 {
934     proto_tree* subtree;
935     proto_item* ti;
936     int left = size;            /* left chars to do */
937
938     if (tree) {
939         if (size < 4) {
940             ti = proto_tree_add_text(tree, tvb, offset, size,
941                                      "Body (%d bytes, should be >= 4)", size);
942             return;
943         }
944         ti = proto_tree_add_text(tree, tvb, offset, size, "Body");
945         subtree = proto_item_add_subtree(ti, ett_icq_body);
946         proto_tree_add_text(subtree, tvb, offset + CMD_SEND_MSG_RECV_UIN, 4,
947                             "Receiver UIN: %u",
948                             tvb_get_letohl(tvb, offset + CMD_SEND_MSG_RECV_UIN));
949         left -= 4;
950
951         icqv5_decode_msgType(subtree, tvb, offset + CMD_SEND_MSG_MSG_TYPE,
952                              left, pinfo);
953     }
954 }
955
956 static void
957 icqv5_cmd_login(proto_tree* tree, tvbuff_t *tvb, int offset, int size)
958 {
959     proto_item* ti;
960     proto_tree* subtree;
961     time_t theTime;
962     char *aTime;
963     guint32 port;
964     guint32 passwdLen;
965     const guchar *ipAddrp;
966     guint32 status;
967
968     if (tree) {
969         ti = proto_tree_add_text(tree, tvb, offset, size, "Body");
970         subtree = proto_item_add_subtree(ti, ett_icq_body);
971         theTime = tvb_get_letohl(tvb, offset + CMD_LOGIN_TIME);
972         aTime = ctime(&theTime);
973         aTime[strlen(aTime)-1] = '\0';
974         proto_tree_add_text(subtree, tvb, offset + CMD_LOGIN_TIME, 4,
975                             "Time: %ld = %s", (long)theTime, aTime);
976         port = tvb_get_letohl(tvb, offset + CMD_LOGIN_PORT);
977         proto_tree_add_text(subtree, tvb, offset + CMD_LOGIN_PORT, 4,
978                             "Port: %u", port);
979         passwdLen = tvb_get_letohs(tvb, offset + CMD_LOGIN_PASSLEN);
980         proto_tree_add_text(subtree, tvb, offset + CMD_LOGIN_PASSLEN,
981                             2 + passwdLen, "Passwd: %.*s", (int)passwdLen,
982                             tvb_get_ptr(tvb, offset + CMD_LOGIN_PASSWD,
983                                         passwdLen));
984         ipAddrp = tvb_get_ptr(tvb,
985                         offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_IP,
986                         4);
987         proto_tree_add_text(subtree, tvb,
988                             offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_IP,
989                             4, "IP: %s", ip_to_str(ipAddrp));
990         status = tvb_get_letohs(tvb,
991                                 offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_STATUS);
992         proto_tree_add_text(subtree, tvb,
993                             offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_STATUS,
994                             4, "Status: %s", findStatus(status));
995     }
996 }
997
998 static void
999 icqv5_cmd_contact_list(proto_tree* tree, tvbuff_t *tvb, int offset, int size)
1000 {
1001     proto_tree* subtree;
1002     proto_item* ti;
1003     unsigned char num;
1004     int i;
1005     guint32 uin;
1006
1007     if (tree) {
1008         ti = proto_tree_add_text(tree, tvb, offset, size, "Body");
1009         subtree = proto_item_add_subtree(ti, ett_icq_body);
1010         num = tvb_get_guint8(tvb, offset + CMD_CONTACT_LIST_NUM);
1011         proto_tree_add_text(subtree, tvb, offset + CMD_CONTACT_LIST,
1012                             1, "Number of uins: %u", num);
1013         /*
1014          * A sequence of num times UIN follows
1015          */
1016         offset += (CMD_CONTACT_LIST_NUM + 1);
1017         for (i = 0; i < num; i++) {
1018             uin = tvb_get_letohl(tvb, offset);
1019             proto_tree_add_text(subtree, tvb, offset, 4,
1020                                 "UIN[%d]: %u", i ,uin);
1021             offset += 4;
1022         }
1023     }
1024 }
1025
1026 static void
1027 icqv5_cmd_no_params(proto_tree* tree, /* Tree to put the data in */
1028                     tvbuff_t *tvb,    /* Decrypted packet content */
1029                     int offset)       /* Offset from the start of the packet to the content */
1030 {
1031     proto_tree* subtree;
1032     proto_item* ti;
1033
1034     if (tree){
1035         ti = proto_tree_add_text(tree, tvb, offset, 0, "Body");
1036         subtree = proto_item_add_subtree(ti, ett_icq_body);
1037         proto_tree_add_text(subtree, tvb, offset, 0, "No parameters");
1038     }
1039 }
1040
1041 /**********************
1042  *
1043  * Server commands
1044  *
1045  **********************
1046  */
1047 static void
1048 icqv5_srv_no_params(proto_tree* tree, /* Tree to put the data in */
1049                     tvbuff_t *tvb,    /* Packet content */
1050                     int offset)       /* Offset from the start of the packet to the content */
1051 {
1052     proto_tree* subtree;
1053     proto_item* ti;
1054
1055     if (tree){
1056         ti = proto_tree_add_text(tree, tvb, offset, 0, "Body");
1057         subtree = proto_item_add_subtree(ti, ett_icq_body);
1058         proto_tree_add_text(subtree, tvb, offset, 0, "No Parameters");
1059     }
1060 }
1061
1062 static void
1063 icqv5_srv_login_reply(proto_tree* tree,/* Tree to put the data in */
1064                       tvbuff_t *tvb,   /* Tvbuff with packet */
1065                       int offset,      /* Offset from the start of the packet to the content */
1066                       int size)        /* Number of chars left to do */
1067 {
1068     proto_tree* subtree;
1069     proto_item* ti;
1070     const guchar *ipAddrp;
1071
1072     if (tree) {
1073         if (size < SRV_LOGIN_REPLY_IP + 8) {
1074             ti = proto_tree_add_text(tree, tvb, offset, size,
1075                                      "Body (%d bytes, should be %d)", size,
1076                                      SRV_LOGIN_REPLY_IP + 8);
1077             return;
1078         }
1079         ti = proto_tree_add_text(tree, tvb, offset, SRV_LOGIN_REPLY_IP + 8,
1080                                  "Body");
1081         subtree = proto_item_add_subtree(ti, ett_icq_body);
1082         ipAddrp = tvb_get_ptr(tvb, offset + SRV_LOGIN_REPLY_IP, 4);
1083         proto_tree_add_text(subtree, tvb, offset + SRV_LOGIN_REPLY_IP, 4,
1084                             "IP: %s", ip_to_str(ipAddrp));
1085     }
1086 }
1087
1088 static void
1089 icqv5_srv_user_online(proto_tree* tree,/* Tree to put the data in */
1090                       tvbuff_t *tvb,   /* Tvbuff with packet */
1091                       int offset,      /* Offset from the start of the packet to the content */
1092                       int size)        /* Number of chars left to do */
1093 {
1094     proto_tree* subtree;
1095     proto_item* ti;
1096     const guchar *ipAddrp;
1097     const guchar *realipAddrp;
1098     guint32 status;
1099
1100     if (tree) {
1101         if (size < SRV_LOGIN_REPLY_IP + 8) {
1102             ti = proto_tree_add_text(tree, tvb, offset, size,
1103                                      "Body (%d bytes, should be %d)", size,
1104                                      SRV_LOGIN_REPLY_IP + 8);
1105             return;
1106         }
1107         ti = proto_tree_add_text(tree, tvb, offset, SRV_LOGIN_REPLY_IP + 8,
1108                                  "Body");
1109         subtree = proto_item_add_subtree(ti, ett_icq_body);
1110         proto_tree_add_text(subtree, tvb, offset + SRV_USER_ONL_UIN, 4,
1111                             "UIN: %u",
1112                             tvb_get_letohl(tvb, offset + SRV_USER_ONL_UIN));
1113         ipAddrp = tvb_get_ptr(tvb, offset + SRV_USER_ONL_IP, 4);
1114         proto_tree_add_text(subtree, tvb, offset + SRV_USER_ONL_IP, 4,
1115                             "IP: %s", ip_to_str(ipAddrp));
1116         proto_tree_add_text(subtree, tvb, offset + SRV_USER_ONL_PORT, 4,
1117                             "Port: %u",
1118                             tvb_get_letohl(tvb, offset + SRV_USER_ONL_PORT));
1119         realipAddrp = tvb_get_ptr(tvb, offset + SRV_USER_ONL_REALIP, 4);
1120         proto_tree_add_text(subtree, tvb, offset + SRV_USER_ONL_REALIP, 4,
1121                             "RealIP: %s", ip_to_str(realipAddrp));
1122         status = tvb_get_letohs(tvb, offset + SRV_USER_ONL_STATUS);
1123         proto_tree_add_text(subtree, tvb, offset + SRV_USER_ONL_STATUS, 2,
1124                             "Status: %s", findStatus(status));
1125         /*
1126          * Kojak: Hypothesis is that this field might be an encoding for the
1127          * version used by the UIN that changed. To test this, I included
1128          * this line to the code.
1129          */
1130         proto_tree_add_text(subtree, tvb, offset + SRV_USER_ONL_X2, 4,
1131                             "Version: %08x",
1132                             tvb_get_letohl(tvb, offset + SRV_USER_ONL_X2));
1133     }
1134 }
1135
1136 static void
1137 icqv5_srv_user_offline(proto_tree* tree,/* Tree to put the data in */
1138                       tvbuff_t *tvb,    /* Tvbuff with packet */
1139                       int offset,       /* Offset from the start of the packet to the content */
1140                       int size)         /* Number of chars left to do */
1141 {
1142     proto_tree* subtree;
1143     proto_item* ti;
1144
1145     if (tree) {
1146         if (size < SRV_USER_OFFLINE_UIN + 4) {
1147             ti = proto_tree_add_text(tree, tvb, offset, size,
1148                                      "Body (%d bytes, should be %d)", size,
1149                                      SRV_USER_OFFLINE_UIN + 4);
1150             return;
1151         }
1152         ti = proto_tree_add_text(tree, tvb, offset, SRV_USER_OFFLINE_UIN + 4,
1153                                  "Body");
1154         subtree = proto_item_add_subtree(ti, ett_icq_body);
1155         proto_tree_add_text(subtree, tvb, offset + SRV_USER_OFFLINE_UIN, 4,
1156                             "UIN: %u",
1157                             tvb_get_letohl(tvb, offset + SRV_USER_OFFLINE));
1158     }
1159 }
1160
1161 static void
1162 icqv5_srv_multi(proto_tree* tree, /* Tree to put the data in */
1163                 tvbuff_t *tvb,    /* Packet content */
1164                 int offset,       /* Offset from the start of the packet to the content */
1165                 int size,         /* Number of chars left to do */
1166                 packet_info* pinfo)
1167 {
1168     proto_tree* subtree;
1169     proto_item* ti;
1170     guint8 num;
1171     guint16 pktSz;
1172     int i;
1173
1174     if (tree) {
1175         ti = proto_tree_add_text(tree, tvb, offset, size, "Body");
1176         subtree = proto_item_add_subtree(ti, ett_icq_body);
1177         num = tvb_get_guint8(tvb, offset + SRV_MULTI_NUM);
1178         proto_tree_add_text(subtree, tvb, offset + SRV_MULTI_NUM, 1,
1179                             "Number of pkts: %u", num);
1180         /*
1181          * A sequence of num times ( pktsize, packetData) follows
1182          */
1183         offset += (SRV_MULTI_NUM + 1);
1184         for (i = 0; i < num; i++) {
1185             pktSz = tvb_get_letohs(tvb, offset);
1186             offset += 2;
1187             dissect_icqv5Server(tvb, offset, pinfo, subtree, pktSz);
1188             offset += pktSz;
1189         }
1190     }
1191 }
1192
1193 static void
1194 icqv5_srv_meta_user(proto_tree* tree, /* Tree to put the data in */
1195                     tvbuff_t *tvb,    /* Tvbuff with packet */
1196                     int offset,       /* Offset from the start of the packet to the content */
1197                     int size,         /* Number of chars left to do */
1198                     packet_info *pinfo)
1199 {
1200 #if 0
1201     proto_tree* subtree = NULL;
1202 #endif
1203     proto_tree* sstree = NULL;
1204     proto_item* ti = NULL;
1205     int left = size;
1206     guint16 subcmd;
1207     unsigned char result;
1208
1209     if (tree) {
1210 #if 0
1211         ti = proto_tree_add_text(tree, tvb, offset, size, "Body");
1212         subtree = proto_item_add_subtree(ti, ett_icq_body);
1213         subcmd = tvb_get_letohs(tvb, offset + SRV_META_USER_SUBCMD);
1214         ti = proto_tree_add_text(subtree, tvb, offset + SRV_META_USER_SUBCMD,
1215                                  2, "%s", findSubCmd(subcmd));
1216         result = tvb_get_guint8(tvb, offset + SRV_META_USER_RESULT);
1217         proto_tree_add_text(subtree, tvb, offset + SRV_META_USER_RESULT, 1,
1218                             "%s", (result==0x0a)?"Success":"Failure");
1219         sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
1220 #else
1221         subcmd = tvb_get_letohs(tvb, offset + SRV_META_USER_SUBCMD);
1222         ti = proto_tree_add_text(tree, tvb, offset + SRV_META_USER_SUBCMD,
1223                                  2, "%s", findSubCmd(subcmd));
1224         sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
1225         result = tvb_get_guint8(tvb, offset + SRV_META_USER_RESULT);
1226         proto_tree_add_text(sstree, tvb, offset + SRV_META_USER_RESULT,
1227                             1, "%s", (result==0x0a)?"Success":"Failure");
1228 #endif
1229
1230         /* Skip the META_USER header */
1231         left -= 3;
1232         offset += 3;
1233
1234         switch(subcmd) {
1235         case META_EX_USER_FOUND:
1236         {
1237             /* This is almost the same as META_USER_FOUND,
1238              * however, there's an extra length field
1239              */
1240             guint16 pktLen;
1241
1242             /* Read the length field */
1243             pktLen = tvb_get_letohs(tvb, offset);
1244             proto_tree_add_text(sstree, tvb, offset, sizeof(guint16),
1245                                 "Length: %u", pktLen);
1246
1247             offset += sizeof(guint16); left -= sizeof(guint16);
1248         }
1249         case META_USER_FOUND:
1250         {
1251             /* The goto mentioned in this block should be local to this
1252              * block if C'd allow it.
1253              *
1254              * They are used to "implement" a poorman's exception handling
1255              */
1256             int len = 0;
1257             const char *descr[] = {
1258                 "Nick",
1259                 "First name",
1260                 "Last name",
1261                 "Email",
1262                 NULL};
1263             const char** d = descr;
1264             unsigned char auth;
1265             /*
1266              * Read UIN
1267              */
1268             proto_tree_add_text(sstree, tvb, offset, sizeof(guint32),
1269                                 "UIN: %u", tvb_get_letohl(tvb, offset));
1270             offset+=sizeof(guint32);left-=sizeof(guint32);
1271
1272             for ( ; *d!=NULL; d++) {
1273                 len = proto_add_icq_attr(sstree, tvb, offset, *d);
1274                 if (len == -1)
1275                     return;
1276                 offset += len; left -= len;
1277             }
1278             /* Get the authorize setting */
1279             auth = tvb_get_guint8(tvb, offset);
1280             proto_tree_add_text(sstree, tvb, offset, 1,
1281                                 "authorization: %s", (auth==0x01)?"Necessary":"Who needs it");
1282             offset++; left--;
1283             /* Get x2 */
1284             proto_tree_add_text(sstree, tvb, offset, sizeof(guint16),
1285                                 "x2: 0x%04x", tvb_get_letohs(tvb, offset));
1286             offset+=sizeof(guint16);left-=sizeof(guint16);
1287             /* Get x3 */
1288             proto_tree_add_text(sstree, tvb, offset, sizeof(guint32),
1289                                 "x3: 0x%08x", tvb_get_letohl(tvb, offset));
1290             offset+=sizeof(guint32);left-=sizeof(guint32);
1291             break;
1292         }
1293         case META_ABOUT:
1294         {
1295             int len;
1296
1297             /* Get the about information */
1298             len = tvb_get_letohs(tvb, offset);
1299             offset+=sizeof(guint16);left-=sizeof(guint16);
1300             proto_tree_add_text(sstree, tvb, offset - sizeof(guint16),
1301                                 sizeof(guint16)+len, "About(%d): %.*s", len,
1302                                 len, tvb_get_ptr(tvb, offset, len));
1303             offset+=len;left-=len;
1304             break;
1305         }
1306         case META_USER_INFO:
1307         {
1308             /* The goto mentioned in this block should be local to this
1309              * block if C'd allow it.
1310              *
1311              * They are used to "implement" a poorman's exception handling
1312              */
1313             static const char* descr[] = {
1314                 "Nick",
1315                 "First name",
1316                 "Last name",
1317                 "Primary email",
1318                 "Secondary email",
1319                 "Old email",
1320                 "City",
1321                 "State",
1322                 "Phone",
1323                 "Fax",
1324                 "Street",
1325                 "Cellphone",
1326                 "Zip",
1327                 NULL};
1328             const char** d = descr;
1329             guint16 country;
1330             guint8 user_timezone;
1331             guint8 auth;
1332             int len = 0;
1333 #if 0
1334             /* Get the uin */
1335             uin = tvb_get_letohl(tvb, offset);
1336             proto_tree_add_text(sstree, tvb, offset, sizeof(guint32),
1337                                 "UIN: %u", uin);
1338             offset+=sizeof(guint32);left-=sizeof(guint32);
1339 #endif
1340
1341             /*
1342              * Get every field from the description
1343              */
1344             while ((*d)!=NULL) {
1345                 len = tvb_get_letohs(tvb, offset);
1346                 offset+=sizeof(guint16);left-=sizeof(guint16);
1347                 if (len>0) {
1348                     proto_tree_add_text(sstree, tvb, offset - sizeof(guint16),
1349                                         sizeof(guint16)+len, "%s(%d): %.*s",
1350                                         *d, len, len - 1,
1351                                         tvb_get_ptr(tvb, offset, len - 1));
1352                     offset+=len;left-=len;
1353                 }
1354                 d++;
1355             }
1356             /* Get country code */
1357             country = tvb_get_letohs(tvb, offset);
1358             proto_tree_add_text(sstree, tvb, offset, sizeof(guint16),
1359                                 "Countrycode: %u", country);
1360             offset+=sizeof(guint16); left-=sizeof(guint16);
1361             /* Get the timezone setting */
1362             user_timezone = tvb_get_guint8(tvb, offset);
1363             proto_tree_add_text(sstree, tvb, offset, sizeof(unsigned char),
1364                                 "Timezone: %u", user_timezone);
1365             offset++; left--;
1366             /* Get the authorize setting */
1367             auth = tvb_get_guint8(tvb, offset);
1368             proto_tree_add_text(sstree, tvb, offset, sizeof(unsigned char),
1369                                 "Authorization: (%u) %s", auth,
1370                                 (auth==0)?"No":"Yes");
1371             offset++; left--;
1372             /* Get the webaware setting */
1373             auth = tvb_get_guint8(tvb, offset);
1374             proto_tree_add_text(sstree, tvb, offset, sizeof(unsigned char),
1375                                 "Webaware: (%u) %s", auth,
1376                                 (auth==0)?"No":"Yes");
1377             offset++; left--;
1378             /* Get the authorize setting */
1379             auth = tvb_get_guint8(tvb, offset);
1380             proto_tree_add_text(sstree, tvb, offset, sizeof(unsigned char),
1381                                 "HideIP: (%u) %s", auth, (auth==0)?"No":"Yes");
1382             offset++; left--;
1383             break;
1384         }
1385         default:
1386             /* This information is already printed in the tree */
1387             expert_add_info_format(pinfo, NULL, PI_UNDECODED, PI_WARN,
1388                                    "Meta subcmd: 0x%x", subcmd);
1389             break;
1390         }
1391     }
1392 }
1393
1394 static void
1395 icqv5_srv_recv_message(proto_tree* tree, /* Tree to put the data in */
1396                        tvbuff_t* tvb,    /* Packet content */
1397                        int offset,       /* Offset from the start of the packet to the content */
1398                        int size,         /* Number of chars left to do */
1399                        packet_info *pinfo)
1400 {
1401     proto_tree* subtree = NULL;
1402     proto_item* ti = NULL;
1403     int left = size;
1404     guint16 year;
1405     guint8 month;
1406     guint8 day;
1407     guint8 hour;
1408     guint8 minute;
1409
1410     if (tree) {
1411         ti = proto_tree_add_text(tree, tvb, offset, 4, "Body");
1412         subtree = proto_item_add_subtree(ti, ett_icq_body);
1413         proto_tree_add_item(subtree, hf_icq_uin, tvb, offset + SRV_RECV_MSG_UIN,
1414                             sizeof(guint32), TRUE);
1415         year = tvb_get_letohs(tvb, offset + SRV_RECV_MSG_YEAR);
1416         month = tvb_get_guint8(tvb, offset + SRV_RECV_MSG_MONTH);
1417         day = tvb_get_guint8(tvb, offset + SRV_RECV_MSG_DAY);
1418         hour = tvb_get_guint8(tvb, offset + SRV_RECV_MSG_HOUR);
1419         minute = tvb_get_guint8(tvb, offset + SRV_RECV_MSG_MINUTE);
1420
1421         proto_tree_add_text(subtree, tvb, offset + SRV_RECV_MSG_YEAR,
1422                             sizeof(guint16) + 4*sizeof(unsigned char),
1423                             "Time: %u-%u-%u %02u:%02u",
1424                             day, month, year, hour, minute);
1425         icqv5_decode_msgType(subtree, tvb, offset + SRV_RECV_MSG_MSG_TYPE,
1426                              left, pinfo);
1427     }
1428 }
1429
1430 static void
1431 icqv5_srv_rand_user(proto_tree* tree,      /* Tree to put the data in */
1432                     tvbuff_t *tvb,         /* Tvbuff with packet */
1433                     int offset)            /* Offset from the start of the packet to the content */
1434 {
1435     proto_tree* subtree = NULL;
1436     proto_item* ti = NULL;
1437     guint32 uin;
1438     const unsigned char* IP = NULL;
1439     guint32 port;
1440     const unsigned char* realIP = NULL;
1441     guint8 commClass;
1442     guint32 status;
1443     guint16 tcpVer;
1444
1445     if (tree) {
1446         ti = proto_tree_add_text(tree, tvb, offset, SRV_RAND_USER_TCP_VER + 2,
1447                                  "Body");
1448         subtree = proto_item_add_subtree(ti, ett_icq_body);
1449         /* guint32 UIN */
1450         uin = tvb_get_letohl(tvb, offset + SRV_RAND_USER_UIN);
1451         proto_tree_add_text(subtree, tvb, offset + SRV_RAND_USER_UIN,
1452                             sizeof(guint32), "UIN: %u", uin);
1453         /* guint32 IP */
1454         IP = tvb_get_ptr(tvb, offset + SRV_RAND_USER_IP, 4);
1455         proto_tree_add_text(subtree, tvb, offset + SRV_RAND_USER_IP,
1456                             sizeof(guint32), "IP: %s", ip_to_str(IP));
1457         /* guint16 portNum */
1458         /* XXX - 16 bits, or 32 bits? */
1459         port = tvb_get_letohs(tvb, offset + SRV_RAND_USER_PORT);
1460         proto_tree_add_text(subtree, tvb, offset + SRV_RAND_USER_UIN,
1461                             sizeof(guint32), "Port: %u", port);
1462         /* guint32 realIP */
1463         realIP = tvb_get_ptr(tvb, offset + SRV_RAND_USER_REAL_IP, 4);
1464         proto_tree_add_text(subtree, tvb, offset + SRV_RAND_USER_REAL_IP,
1465                             sizeof(guint32), "RealIP: %s", ip_to_str(realIP));
1466         /* guint8 Communication Class */
1467         commClass = tvb_get_guint8(tvb, offset + SRV_RAND_USER_CLASS);
1468         proto_tree_add_text(subtree, tvb, offset + SRV_RAND_USER_CLASS,
1469                             sizeof(guint8), "Class: %s",
1470                             (commClass!=4)?"User to User":"Through Server");
1471         /* guint32 status */
1472         /* XXX - 16 bits, or 32 bits? */
1473         status = tvb_get_letohs(tvb, offset + SRV_RAND_USER_STATUS);
1474         proto_tree_add_text(subtree, tvb, offset + SRV_RAND_USER_STATUS,
1475                             sizeof(guint32), "Status: %s", findStatus(status));
1476         /* guint16 tcpVersion */
1477         tcpVer = tvb_get_letohs(tvb, offset + SRV_RAND_USER_TCP_VER);
1478         proto_tree_add_text(subtree, tvb, offset + SRV_RAND_USER_TCP_VER,
1479                             sizeof(guint16), "TCPVersion: %u", tcpVer);
1480     }
1481 }
1482
1483 /*
1484  * Dissect all the v5 client traffic. This is encrypted, so be careful.
1485  */
1486 static void
1487 dissect_icqv5Client(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1488 {
1489     proto_tree *icq_tree = NULL;
1490     proto_tree *icq_header_tree = NULL;
1491     proto_item *ti = NULL;
1492
1493     int pktsize;                /* The actual size of the ICQ content */
1494     int capturedsize;           /* The captured size of the ICQ content */
1495     guint32 rounded_size;
1496     guint32 key;
1497     guint16 cmd;
1498     guint8 *decr_pd;            /* Decrypted content */
1499     tvbuff_t *decr_tvb;
1500
1501     pktsize = tvb_reported_length(tvb);
1502     capturedsize = tvb_length(tvb);
1503
1504     /* Get the encryption key */
1505     key = get_v5key(tvb, pktsize);
1506
1507     /*
1508      * Make a copy of the packet data, and decrypt it.
1509      * The decryption processes 4 bytes at a time, and starts at
1510      * an offset of ICQ5_CL_SESSIONID (which isn't a multiple of 4),
1511      * so we make sure that there are
1512      *
1513      *  (ICQ5_CL_SESSIONID + a multiple of 4)
1514      *
1515      * bytes in the buffer.
1516      */
1517     rounded_size = ((((capturedsize - ICQ5_CL_SESSIONID) + 3)/4)*4) + ICQ5_CL_SESSIONID;
1518     decr_pd = g_malloc(rounded_size);
1519     tvb_memcpy(tvb, decr_pd, 0, capturedsize);
1520     decrypt_v5(decr_pd, rounded_size, key);
1521
1522     /* Allocate a new tvbuff, referring to the decrypted data. */
1523     decr_tvb = tvb_new_child_real_data(tvb, decr_pd, capturedsize, pktsize);
1524
1525     /* Arrange that the allocated packet data copy be freed when the
1526        tvbuff is freed. */
1527     tvb_set_free_cb(decr_tvb, g_free);
1528
1529     /* Add the decrypted data to the data source list. */
1530     add_new_data_source(pinfo, decr_tvb, "Decrypted");
1531
1532     cmd = tvb_get_letohs(decr_tvb, ICQ5_CL_CMD);
1533
1534     if (check_col(pinfo->cinfo, COL_INFO))
1535         col_add_fstr(pinfo->cinfo, COL_INFO, "ICQv5 %s", findClientCmd(cmd));
1536
1537     if (tree) {
1538         ti = proto_tree_add_protocol_format(tree, proto_icq, tvb, 0, -1,
1539                                             "ICQv5 %s (len %u)",
1540                                             findClientCmd(cmd), pktsize);
1541         icq_tree = proto_item_add_subtree(ti, ett_icq);
1542         ti = proto_tree_add_uint_format(icq_tree, hf_icq_type, tvb, 0,
1543                                         ICQ5_CL_HDRSIZE, ICQ5_client, "Header");
1544         icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
1545
1546         proto_tree_add_text(icq_header_tree, tvb, ICQ_VERSION, 2, "Version: %u",
1547                             tvb_get_letohs(tvb, ICQ_VERSION));
1548         proto_tree_add_item(icq_header_tree, hf_icq_uin, tvb, ICQ5_CL_UIN, 4,
1549                             TRUE);
1550         proto_tree_add_item(icq_header_tree, hf_icq_sessionid, decr_tvb,
1551                             ICQ5_CL_SESSIONID, 4, TRUE);
1552         proto_tree_add_uint_format(icq_header_tree, hf_icq_client_cmd,
1553                                    decr_tvb, ICQ5_CL_CMD, 2, cmd,
1554                                    "Command: %s (%u)",
1555                                    val_to_str(cmd, clientCmdCode, "Unknown"), cmd);
1556         proto_tree_add_text(icq_header_tree, decr_tvb, ICQ5_CL_SEQNUM1, 2,
1557                             "Seq Number 1: 0x%04x",
1558                             tvb_get_letohs(decr_tvb, ICQ5_CL_SEQNUM1));
1559         proto_tree_add_text(icq_header_tree, decr_tvb, ICQ5_CL_SEQNUM2, 2,
1560                             "Seq Number 2: 0x%04x",
1561                             tvb_get_letohs(decr_tvb, ICQ5_CL_SEQNUM2));
1562         proto_tree_add_uint_format(icq_header_tree, hf_icq_checkcode, tvb,
1563                                    ICQ5_CL_CHECKCODE, 4, key, "Key: 0x%08x",
1564                                    key);
1565         switch(cmd) {
1566         case CMD_ACK:
1567             icqv5_cmd_ack(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE);
1568             break;
1569         case CMD_SEND_MSG:
1570         case CMD_MSG_TO_NEW_USER:
1571             icqv5_cmd_send_msg(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE,
1572                                pktsize - ICQ5_CL_HDRSIZE, pinfo);
1573             break;
1574         case CMD_RAND_SEARCH:
1575             icqv5_cmd_rand_search(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE,
1576                                   pktsize - ICQ5_CL_HDRSIZE);
1577             break;
1578         case CMD_LOGIN:
1579             icqv5_cmd_login(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE,
1580                             pktsize - ICQ5_CL_HDRSIZE);
1581             break;
1582         case CMD_SEND_TEXT_CODE:
1583             icqv5_cmd_send_text_code(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE,
1584                                      pktsize - ICQ5_CL_HDRSIZE);
1585             break;
1586         case CMD_STATUS_CHANGE:
1587             icqv5_cmd_status_change(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE);
1588             break;
1589         case CMD_ACK_MESSAGES:
1590             icqv5_cmd_ack_messages(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE);
1591             break;
1592         case CMD_KEEP_ALIVE:
1593             icqv5_cmd_keep_alive(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE);
1594             break;
1595         case CMD_ADD_TO_LIST:
1596             icqv5_cmd_add_to_list(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE);
1597             break;
1598         case CMD_CONTACT_LIST:
1599             icqv5_cmd_contact_list(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE,
1600                                    pktsize - ICQ5_CL_HDRSIZE);
1601             break;
1602         case CMD_META_USER:
1603         case CMD_REG_NEW_USER:
1604         case CMD_QUERY_SERVERS:
1605         case CMD_QUERY_ADDONS:
1606             icqv5_cmd_no_params(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE);
1607             break;
1608         default:
1609             proto_tree_add_text(icq_tree, decr_tvb, ICQ5_CL_HDRSIZE,
1610                                 pktsize - ICQ5_CL_HDRSIZE, "Body");
1611             expert_add_info_format(pinfo, NULL, PI_UNDECODED, PI_WARN,
1612                                    "Missing: %s", findClientCmd(cmd));
1613             break;
1614         }
1615     }
1616 }
1617
1618 static void
1619 dissect_icqv5Server(tvbuff_t *tvb, int offset, packet_info *pinfo,
1620                     proto_tree *tree, int pktsize)
1621 {
1622     /* Server traffic is easy, not encrypted */
1623     proto_tree *icq_tree = NULL;
1624     proto_tree *icq_header_tree = NULL;
1625     proto_item *ti = NULL;
1626     int changeCol = (pktsize==-1);
1627
1628     guint16 cmd;
1629
1630     cmd = tvb_get_letohs(tvb, offset + ICQ5_SRV_CMD);
1631     if (changeCol && check_col(pinfo->cinfo, COL_INFO))
1632         col_add_fstr(pinfo->cinfo, COL_INFO, "ICQv5 %s", findServerCmd(cmd));
1633
1634     if (pktsize == -1)
1635         pktsize = tvb_reported_length(tvb);
1636
1637     if (tree) {
1638         ti = proto_tree_add_protocol_format(tree, proto_icq, tvb, offset,
1639                                             pktsize, "ICQv5 %s (len %u)",
1640                                             findServerCmd(cmd), pktsize);
1641
1642         icq_tree = proto_item_add_subtree(ti, ett_icq);
1643
1644         ti = proto_tree_add_uint_format(icq_tree, hf_icq_type, tvb, offset,
1645                                         ICQ5_SRV_HDRSIZE, ICQ5_server,
1646                                         "Header");
1647         icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
1648
1649         proto_tree_add_text(icq_header_tree, tvb, offset + ICQ_VERSION, 2,
1650                             "Version: %u", tvb_get_letohs(tvb, ICQ_VERSION));
1651         proto_tree_add_item(icq_header_tree, hf_icq_sessionid, tvb,
1652                             offset + ICQ5_SRV_SESSIONID, 4, TRUE);
1653         proto_tree_add_uint_format(icq_header_tree, hf_icq_server_cmd, tvb,
1654                             offset + ICQ5_SRV_CMD, 2, cmd, "Command: %s (%u)",
1655                             val_to_str(cmd, serverCmdCode, "Unknown"), cmd);
1656         proto_tree_add_text(icq_header_tree, tvb, offset + ICQ5_SRV_SEQNUM1, 2,
1657                             "Seq Number 1: 0x%04x",
1658                             tvb_get_letohs(tvb, offset + ICQ5_SRV_SEQNUM1));
1659         proto_tree_add_text(icq_header_tree, tvb, offset + ICQ5_SRV_SEQNUM2, 2,
1660                             "Seq Number 2: 0x%04x",
1661                             tvb_get_letohs(tvb, offset + ICQ5_SRV_SEQNUM2));
1662         proto_tree_add_item(icq_header_tree, hf_icq_uin, tvb,
1663                             offset + ICQ5_SRV_UIN, 4, TRUE);
1664         proto_tree_add_item(icq_header_tree, hf_icq_checkcode, tvb,
1665                             offset + ICQ5_SRV_CHECKCODE, 4, TRUE);
1666         switch (cmd) {
1667         case SRV_RAND_USER:
1668             icqv5_srv_rand_user(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE);
1669             break;
1670         case SRV_SYS_DELIVERED_MESS:
1671             /* The message structures are all the same. Why not run
1672              * the same routine? */
1673             icqv5_cmd_send_msg(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE,
1674                                pktsize - ICQ5_SRV_HDRSIZE, pinfo);
1675             break;
1676         case SRV_USER_ONLINE:
1677             icqv5_srv_user_online(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE,
1678                                   pktsize - ICQ5_SRV_HDRSIZE);
1679             break;
1680         case SRV_USER_OFFLINE:
1681             icqv5_srv_user_offline(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE,
1682                                    pktsize - ICQ5_SRV_HDRSIZE);
1683             break;
1684         case SRV_LOGIN_REPLY:
1685             icqv5_srv_login_reply(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE,
1686                                   pktsize - ICQ5_SRV_HDRSIZE);
1687             break;
1688         case SRV_META_USER:
1689             icqv5_srv_meta_user(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE,
1690                                 pktsize - ICQ5_SRV_HDRSIZE, pinfo);
1691             break;
1692         case SRV_RECV_MESSAGE:
1693             icqv5_srv_recv_message(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE,
1694                                    pktsize - ICQ5_SRV_HDRSIZE, pinfo);
1695             break;
1696         case SRV_MULTI:
1697             icqv5_srv_multi(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE,
1698                             pktsize - ICQ5_SRV_HDRSIZE, pinfo);
1699             break;
1700         case SRV_ACK:
1701         case SRV_SILENT_TOO_LONG:
1702         case SRV_GO_AWAY:
1703         case SRV_NEW_UIN:
1704         case SRV_BAD_PASS:
1705         case SRV_UPDATE_SUCCESS:
1706             icqv5_srv_no_params(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE);
1707             break;
1708         default:
1709             proto_tree_add_text(icq_tree, tvb, offset + ICQ5_SRV_HDRSIZE,
1710                                 pktsize - ICQ5_SRV_HDRSIZE, "Body");
1711             expert_add_info_format(pinfo, NULL, PI_UNDECODED, PI_WARN,
1712                                    "Missing: %s", findClientCmd(cmd));
1713             break;
1714         }
1715     }
1716 }
1717
1718 static void dissect_icqv5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1719 {
1720   guint32 unknown;
1721
1722   col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQv5 (UDP)");
1723   if (check_col(pinfo->cinfo, COL_INFO))
1724       col_set_str(pinfo->cinfo, COL_INFO, "ICQv5 packet");
1725
1726   unknown = tvb_get_letohl(tvb, ICQ5_UNKNOWN);
1727
1728   if (unknown == 0x0L) {
1729       dissect_icqv5Client(tvb, pinfo, tree);
1730   } else {
1731       dissect_icqv5Server(tvb, 0, pinfo, tree, -1);
1732   }
1733 }
1734
1735 static int
1736 dissect_icq(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1737 {
1738   int version;
1739
1740   version = tvb_get_letohs(tvb, ICQ_VERSION);
1741   if (version < 2 || version > 5)
1742     return 0;   /* This is not a (recognized) ICQ packet */
1743
1744   if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
1745     col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICQ");
1746   }
1747   if (check_col(pinfo->cinfo, COL_INFO)) {
1748     col_clear(pinfo->cinfo, COL_INFO);
1749   }
1750
1751   version = tvb_get_letohs(tvb, ICQ_VERSION);
1752   switch (version) {
1753   case 0x0005:
1754       dissect_icqv5(tvb, pinfo, tree);
1755       break;
1756   case 0x0004:
1757       dissect_icqv4(tvb, pinfo, tree);
1758       break;
1759   case 0x0003:
1760       dissect_icqv3(tvb, pinfo, tree);
1761       break;
1762   case 0x0002:
1763       dissect_icqv2(tvb, pinfo, tree);
1764       break;
1765   default:
1766       expert_add_info_format(pinfo, NULL, PI_UNDECODED, PI_WARN,
1767                              "Unknown version (0x%x)", version);
1768       break;
1769   }
1770
1771   return (tvb_length(tvb));
1772 }
1773
1774 /* registration with the filtering engine */
1775 void
1776 proto_register_icq(void)
1777 {
1778     static hf_register_info hf[] = {
1779         { &hf_icq_type,
1780           {"Type", "icq.type", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1781         { &hf_icq_uin,
1782           {"UIN", "icq.uin", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1783         { &hf_icq_sessionid,
1784           {"Session ID", "icq.sessionid", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }},
1785         { &hf_icq_client_cmd,
1786           {"Client command", "icq.client_cmd", FT_UINT16, BASE_HEX, VALS(clientCmdCode), 0x0, NULL, HFILL }},
1787         { &hf_icq_server_cmd,
1788           {"Server command", "icq.server_cmd", FT_UINT16, BASE_DEC, VALS(serverCmdCode), 0x0, NULL, HFILL }},
1789         { &hf_icq_checkcode,
1790           {"Checkcode", "icq.checkcode", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }},
1791         { &hf_icq_decode,
1792           {"Decode", "icq.decode", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}
1793     };
1794     static gint *ett[] = {
1795         &ett_icq,
1796         &ett_icq_header,
1797         &ett_icq_decode,
1798         &ett_icq_body,
1799         &ett_icq_body_parts,
1800     };
1801
1802     proto_icq = proto_register_protocol("ICQ Protocol", "ICQ", "icq");
1803
1804     proto_register_field_array(proto_icq, hf, array_length(hf));
1805
1806     proto_register_subtree_array(ett, array_length(ett));
1807 }
1808
1809 void
1810 proto_reg_handoff_icq(void)
1811 {
1812     dissector_handle_t icq_handle;
1813
1814     icq_handle = new_create_dissector_handle(dissect_icq, proto_icq);
1815     dissector_add("udp.port", UDP_PORT_ICQ, icq_handle);
1816 }