Define some fcns & vars as static...
[metze/wireshark/wip.git] / epan / dissectors / packet-cups.c
1 /* packet-cups.c
2 * Routines for Common Unix Printing System (CUPS) Browsing Protocol
3 * packet disassembly for the Wireshark network traffic analyzer.
4 *
5 * Charles Levert <charles@comm.polymtl.ca>
6 * Copyright 2001 Charles Levert
7 *
8 * $Id$
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 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <ctype.h>
31
32 #include <glib.h>
33 #include <epan/packet.h>
34 #include <epan/strutil.h>
35
36 /**********************************************************************/
37
38 /* From cups/cups.h, GNU GPL, Copyright 1997-2001 by Easy Software Products. */
39 typedef guint32 cups_ptype_t;           /**** Printer Type/Capability Bits ****/
40 enum                                    /* Not a typedef'd enum so we can OR */
41 {
42   CUPS_PRINTER_LOCAL = 0x0000,          /* Local printer or class */
43   CUPS_PRINTER_CLASS = 0x0001,          /* Printer class */
44   CUPS_PRINTER_REMOTE = 0x0002,         /* Remote printer or class */
45   CUPS_PRINTER_BW = 0x0004,             /* Can do B&W printing */
46   CUPS_PRINTER_COLOR = 0x0008,          /* Can do color printing */
47   CUPS_PRINTER_DUPLEX = 0x0010,         /* Can do duplexing */
48   CUPS_PRINTER_STAPLE = 0x0020,         /* Can staple output */
49   CUPS_PRINTER_COPIES = 0x0040,         /* Can do copies */
50   CUPS_PRINTER_COLLATE = 0x0080,        /* Can collage copies */
51   CUPS_PRINTER_PUNCH = 0x0100,          /* Can punch output */
52   CUPS_PRINTER_COVER = 0x0200,          /* Can cover output */
53   CUPS_PRINTER_BIND = 0x0400,           /* Can bind output */
54   CUPS_PRINTER_SORT = 0x0800,           /* Can sort output */
55   CUPS_PRINTER_SMALL = 0x1000,          /* Can do Letter/Legal/A4 */
56   CUPS_PRINTER_MEDIUM = 0x2000,         /* Can do Tabloid/B/C/A3/A2 */
57   CUPS_PRINTER_LARGE = 0x4000,          /* Can do D/E/A1/A0 */
58   CUPS_PRINTER_VARIABLE = 0x8000,       /* Can do variable sizes */
59   CUPS_PRINTER_IMPLICIT = 0x10000,      /* Implicit class */
60   CUPS_PRINTER_DEFAULT = 0x20000,       /* Default printer on network */
61   CUPS_PRINTER_OPTIONS = 0xfffc         /* ~(CLASS | REMOTE | IMPLICIT) */
62 };
63 /* End insert from cups/cups.h */
64
65 typedef struct {
66         guint32 bit;
67         const char      *on_string;
68         const char      *off_string;
69 } cups_ptype_bit_info;
70
71 static const cups_ptype_bit_info cups_ptype_bits[] = {
72         { CUPS_PRINTER_DEFAULT,
73           "Default printer on network", "Not default printer" },
74         { CUPS_PRINTER_IMPLICIT,
75           "Implicit class", "Explicit class" },
76         { CUPS_PRINTER_VARIABLE,
77           "Can print variable sizes", "Cannot print variable sizes" },
78         { CUPS_PRINTER_LARGE,
79           "Can print up to 36x48 inches", "Cannot print up to 36x48 inches" },
80         { CUPS_PRINTER_MEDIUM,
81           "Can print up to 18x24 inches", "Cannot print up to 18x24 inches" },
82         { CUPS_PRINTER_SMALL,
83           "Can print up to 9x14 inches", "Cannot print up to 9x14 inches" },
84         { CUPS_PRINTER_SORT,
85           "Can sort", "Cannot sort" },
86         { CUPS_PRINTER_BIND,
87           "Can bind", "Cannot bind" },
88         { CUPS_PRINTER_COVER,
89           "Can cover", "Cannot cover" },
90         { CUPS_PRINTER_PUNCH,
91           "Can punch holes", "Cannot punch holes" },
92         { CUPS_PRINTER_COLLATE,
93           "Can do fast collating", "Cannot do fast collating" },
94         { CUPS_PRINTER_COPIES,
95           "Can do fast copies", "Cannot do fast copies" },
96         { CUPS_PRINTER_STAPLE,
97           "Can staple", "Cannot staple" },
98         { CUPS_PRINTER_DUPLEX,
99           "Can duplex", "Cannot duplex" },
100         { CUPS_PRINTER_COLOR,
101           "Can print color", "Cannot print color" },
102         { CUPS_PRINTER_BW,
103           "Can print black", "Cannot print black" },
104         { CUPS_PRINTER_REMOTE,
105           "Remote", "Local (illegal)" },
106         { CUPS_PRINTER_CLASS,
107           "Printer class", "Single printer" }
108 };
109
110 #define N_CUPS_PTYPE_BITS       (sizeof cups_ptype_bits / sizeof cups_ptype_bits[0])
111
112 typedef enum _cups_state {
113         CUPS_IDLE = 3,
114         CUPS_PROCESSING,
115         CUPS_STOPPED
116 } cups_state_t;
117
118 static const value_string cups_state_values[] = {
119         { CUPS_IDLE,            "idle" },
120         { CUPS_PROCESSING,      "processing" },
121         { CUPS_STOPPED,         "stopped" },
122         { 0,                    NULL }
123 };
124
125 static int proto_cups = -1;
126 static int hf_cups_ptype = -1;
127 static int hf_cups_state = -1;
128
129 static gint ett_cups = -1;
130 static gint ett_cups_ptype = -1;
131
132 /* This protocol is heavily related to IPP, but it is CUPS-specific
133    and non-standard. */
134 #define UDP_PORT_CUPS   631
135 #define PROTO_TAG_CUPS  "CUPS"
136
137 static guint get_hex_uint(tvbuff_t *tvb, gint offset,
138     gint *next_offset);
139 static gboolean skip_space(tvbuff_t *tvb, gint offset,
140     gint *next_offset);
141 static const guint8* get_quoted_string(tvbuff_t *tvb, gint offset,
142     gint *next_offset, guint *len);
143 static const guint8* get_unquoted_string(tvbuff_t *tvb, gint offset,
144     gint *next_offset, guint *len);
145
146 /**********************************************************************/
147
148 static void
149 dissect_cups(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
150 {
151         proto_tree      *cups_tree = 0;
152         proto_tree      *ptype_subtree = 0;
153         proto_item      *ti = 0;
154         gint            offset = 0;
155         gint            next_offset;
156         guint           len;
157         unsigned int    u;
158         const guint8    *str;
159         cups_ptype_t    ptype;
160         unsigned int    state;
161
162         col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_CUPS);
163         col_clear(pinfo->cinfo, COL_INFO);
164
165         if (tree) {
166                 ti = proto_tree_add_item(tree, proto_cups, tvb, offset, -1,
167                     FALSE);
168                 cups_tree = proto_item_add_subtree(ti, ett_cups);
169         }
170
171         /* Format (1450 bytes max.):  */
172         /* type state uri ["location" ["info" ["make-and-model"]]]\n */
173
174         ptype = get_hex_uint(tvb, offset, &next_offset);
175         len = next_offset - offset;
176         if (len != 0) {
177                 if (cups_tree) {
178                         ti = proto_tree_add_uint(cups_tree, hf_cups_ptype,
179                             tvb, offset, len, ptype);
180                         ptype_subtree = proto_item_add_subtree(ti,
181                             ett_cups_ptype);
182                         for (u = 0; u < N_CUPS_PTYPE_BITS; u++) {
183                                 proto_tree_add_text(ptype_subtree, tvb,
184                                     offset, len, "%s",
185                                     decode_boolean_bitfield(ptype,
186                                       cups_ptype_bits[u].bit, sizeof (ptype)*8,
187                                       cups_ptype_bits[u].on_string,
188                                       cups_ptype_bits[u].off_string));
189                         }
190                 }
191         }
192         offset = next_offset;
193
194         if (!skip_space(tvb, offset, &next_offset))
195                 return; /* end of packet */
196         offset = next_offset;
197
198         state = get_hex_uint(tvb, offset, &next_offset);
199         len = next_offset - offset;
200         if (len != 0) {
201                 if (cups_tree)
202                         proto_tree_add_uint(cups_tree, hf_cups_state,
203                             tvb, offset, len, state);
204         }
205         offset = next_offset;
206
207         if (!skip_space(tvb, offset, &next_offset))
208                 return; /* end of packet */
209         offset = next_offset;
210
211         str = get_unquoted_string(tvb, offset, &next_offset, &len);
212         if (str == NULL)
213                 return; /* separator/terminator not found */
214         if (cups_tree)
215                 proto_tree_add_text(cups_tree, tvb, offset, len,
216                     "URI: %.*s",
217                     (guint16) len, str);
218         if (check_col(pinfo->cinfo, COL_INFO))
219                 col_add_fstr(pinfo->cinfo, COL_INFO,
220                     "%.*s (%s)",
221                     (guint16) len, str,
222                     val_to_str(state, cups_state_values, "0x%x"));
223         offset = next_offset;
224
225         if (!cups_tree)
226                 return;
227
228         if (!skip_space(tvb, offset, &next_offset))
229                 return; /* end of packet */
230         offset = next_offset;
231
232         str = get_quoted_string(tvb, offset, &next_offset, &len);
233         if (str == NULL)
234                 return; /* separator/terminator not found */
235         proto_tree_add_text(cups_tree, tvb, offset+1, len,
236             "Location: \"%.*s\"",
237             (guint16) len, str);
238         offset = next_offset;
239
240         if (!skip_space(tvb, offset, &next_offset))
241                 return; /* end of packet */
242         offset = next_offset;
243
244         str = get_quoted_string(tvb, offset, &next_offset, &len);
245         if (str == NULL)
246                 return; /* separator/terminator not found */
247         proto_tree_add_text(cups_tree, tvb, offset+1, len,
248             "Information: \"%.*s\"",
249             (guint16) len, str);
250         offset = next_offset;
251
252         if (!skip_space(tvb, offset, &next_offset))
253                 return; /* end of packet */
254         offset = next_offset;
255
256         str = get_quoted_string(tvb, offset, &next_offset, &len);
257         if (str == NULL)
258                 return; /* separator/terminator not found */
259         proto_tree_add_text(cups_tree, tvb, offset+1, len,
260             "Make and model: \"%.*s\"",
261             (guint16) len, str);
262         offset = next_offset;
263
264         return;
265 }
266
267 static guint
268 get_hex_uint(tvbuff_t *tvb, gint offset, gint *next_offset)
269 {
270         int c;
271         guint u = 0;
272
273         while (isxdigit(c = tvb_get_guint8(tvb, offset))) {
274                 if (isdigit(c))
275                         c -= '0';
276                 else if (isupper(c))
277                         c -= 'A' - 10;
278                 else if (islower(c))
279                         c -= 'a' - 10;
280                 else
281                         c = 0; /* This should not happen. */
282
283                 u = 16*u + c;
284
285                 offset++;
286         }
287
288         *next_offset = offset;
289
290         return u;
291 }
292
293 static gboolean
294 skip_space(tvbuff_t *tvb, gint offset, gint *next_offset)
295 {
296         int c;
297
298         while ((c = tvb_get_guint8(tvb, offset)) == ' ')
299                 offset++;
300         if (c == '\r' || c == '\n')
301                 return FALSE;   /* end of packet */
302
303         *next_offset = offset;
304
305         return TRUE;
306 }
307
308 static const guint8*
309 get_quoted_string(tvbuff_t *tvb, gint offset, gint *next_offset, guint *len)
310 {
311         int c;
312         const guint8* s = NULL;
313         guint l = 0;
314         gint o;
315
316         c = tvb_get_guint8(tvb, offset);
317         if (c == '"') {
318                 o = tvb_find_guint8(tvb, offset+1, -1, '"');
319                 if (o != -1) {
320                         offset++;
321                         l = o - offset;
322                         s = tvb_get_ptr(tvb, offset, l);
323                         offset = o + 1;
324                 }
325         }
326
327         *next_offset = offset;
328         *len = l;
329
330         return s;
331 }
332
333 static const guint8*
334 get_unquoted_string(tvbuff_t *tvb, gint offset, gint *next_offset, guint *len)
335 {
336         const guint8* s = NULL;
337         guint l = 0;
338         gint o;
339
340         o = tvb_pbrk_guint8(tvb, offset, -1, (const guint8*)" \t\r\n", NULL);
341         if (o != -1) {
342                 l = o - offset;
343                 s = tvb_get_ptr(tvb, offset, l);
344                 offset = o;
345         }
346
347         *next_offset = offset;
348         *len = l;
349
350         return s;
351 }
352
353 /**********************************************************************/
354
355 void
356 proto_register_cups(void)
357 {
358         static hf_register_info hf[] = {
359                 /* This one could be split in separate fields. */
360                 { &hf_cups_ptype,
361                         { "Type",       "cups.ptype", FT_UINT32, BASE_HEX,
362                           NULL, 0x0, NULL, HFILL }},
363
364                 { &hf_cups_state,
365                         { "State",      "cups.state", FT_UINT8, BASE_HEX,
366                           VALS(cups_state_values), 0x0, NULL, HFILL }}
367         };
368
369         static gint *ett[] = {
370                 &ett_cups,
371                 &ett_cups_ptype
372         };
373
374         proto_cups = proto_register_protocol(
375             "Common Unix Printing System (CUPS) Browsing Protocol",
376             "CUPS", "cups");
377         proto_register_field_array(proto_cups, hf, array_length(hf));
378         proto_register_subtree_array(ett, array_length(ett));
379 }
380
381 void
382 proto_reg_handoff_cups(void)
383 {
384         dissector_handle_t cups_handle;
385
386         cups_handle = create_dissector_handle(dissect_cups, proto_cups);
387         dissector_add("udp.port", UDP_PORT_CUPS, cups_handle);
388 }