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