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