4904f9b6b9b256c087bce229470affa602215bd1
[obnox/wireshark/wip.git] / ncp2222.py
1 #!/usr/bin/python
2
3 """
4 Creates C code from a table of NCP type 0x2222 packet types.
5 (And 0x3333, which are the replies, but the packets are more commonly
6 refered to as type 0x2222; the 0x3333 replies are understood to be
7 part of the 0x2222 "family")
8
9 Data comes from "Programmer's Guide to the NetWare Core Protocol"
10 by Steve Conner and Dianne Conner.
11
12 Novell provides info at:
13
14 http://developer.novell.com/ndk  (where you can download an *.exe file which
15 installs a PDF)
16
17 or
18
19 http://developer.novell.com/ndk/doc/docui/index.htm#../ncp/ncp__enu/data/
20 for a badly-formatted HTML version of the same PDF.
21
22
23 $Id: ncp2222.py,v 1.13 2002/01/11 14:33:04 gram Exp $
24
25 Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
26
27 This program is free software; you can redistribute it and/or
28 modify it under the terms of the GNU General Public License
29 as published by the Free Software Foundation; either version 2
30 of the License, or (at your option) any later version.
31  
32 This program is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35 GNU General Public License for more details.
36  
37 You should have received a copy of the GNU General Public License
38 along with this program; if not, write to the Free Software
39 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
40 """
41
42 import sys
43 import string
44
45 errors          = {}
46 groups          = {}
47 packets         = None
48 compcode_lists  = None
49 ptvc_lists      = None
50
51 ##############################################################################
52 # Global containers
53 ##############################################################################
54
55 class UniqueCollection:
56         """The UniqueCollection class stores objects which can be compared to other
57         objects of the same class. If two objects in the collection are equivalent,
58         only one is stored."""
59
60         def __init__(self, name):
61                 "Constructor"
62                 self.name = name
63                 self.members = []
64
65         def Add(self, object):
66                 """Add an object to the members lists, if a comparable object
67                 doesn't already exist. The object that is in the member list, that is
68                 either the object that was added or the comparable object that was
69                 already in the member list, is returned."""
70
71                 # Is 'object' a duplicate of some other member?
72                 for member in self.members:
73                         if member == object:
74                                 return member
75
76                 # Store object in our members list.
77                 self.members.append(object)
78                 return object
79
80         def Members(self):
81                 "Returns the list of members."
82                 return self.members
83
84         def HasMember(self, object):
85                 "Does the list of members contain the object?"
86                 for member in self.members:
87                         if member == object:
88                                 return 1
89                 return 0
90
91
92 # This list needs to be defined before the NCP types are defined,
93 # because the NCP types are defined in the global scope, not inside
94 # a function's scope.
95 ptvc_lists      = UniqueCollection('PTVC Lists')
96
97 ##############################################################################
98
99 class NamedList:
100         "NamedList's keep track of PTVC's and Completion Codes"
101         def __init__(self, name, list):
102                 "Constructor"
103                 self.name = name
104                 self.list = list
105
106         def __cmp__(self, other):
107                 "Compare this NamedList to another"
108
109                 # Python will do a deep comparison of lists within lists.
110                 if self.list < other.list:
111                         return -1
112                 elif self.list > other.list:
113                         return 1
114                 else:
115                         return 0
116
117         def __repr__(self):
118                 "String representation"
119                 return "NamedList: " + `self.list`
120
121         def Name(self, new_name = None):
122                 "Get/Set name of list"
123                 if new_name != None:
124                         self.name = new_name
125                 return self.name
126
127         def Records(self):
128                 "Returns record lists"
129                 return self.list
130
131         def Null(self):
132                 "Is there no list (different from an empty list)?"
133                 return self.list == None
134
135         def Empty(self):
136                 "It the list empty (different from a null list)?"
137                 assert(not self.Null())
138
139                 if self.list:
140                         return 0
141                 else:
142                         return 1
143
144
145 class PTVC(NamedList):
146         """ProtoTree TVBuff Cursor List ("PTVC List") Class"""
147
148         def __init__(self, name, records):
149                 "Constructor"
150                 NamedList.__init__(self, name, [])
151
152                 expected_offset = None
153
154                 # Make a PTVCRecord object for each list in 'records'
155                 for record in records:
156                         offset  = record[0]
157                         length  = record[1]
158                         field   = record[2]
159
160                         # Check if an endianness override is given
161                         try:
162                                 endianness = record[3]
163
164                         # If no endianness was given in the record, then
165                         # use the field's default endianness.
166                         except IndexError:
167                                 endianness = field.Endianness()
168
169                         ptvc_rec = PTVCRecord(field, length, endianness)
170
171                         if expected_offset == None:
172                                 expected_offset = offset
173
174                         elif expected_offset == -1:
175                                 pass
176
177                         elif expected_offset != offset:
178                                 sys.stderr.write("Expected offset in %s to be %d\n" % (name,
179                                         expected_offset))
180                                 sys.exit(1)
181
182                         # We can't make a PTVC list from a variable-length
183                         # packet, unless it's FT_UINT_STRING
184                         if type(ptvc_rec.Length()) == type(()):
185                                 if isinstance(ptvc_rec.Field(), nstring8):
186                                         expected_offset = -1
187                                         pass
188                                 else:
189                                         self.list = None
190                                         return
191
192                         elif expected_offset > -1:
193                                 expected_offset = expected_offset + ptvc_rec.Length()
194
195
196                         self.list.append(ptvc_rec)
197
198         def __str__(self):
199                 x =  "static const ptvc_record %s[] = {\n" % (self.Name())
200                 for ptvc_rec in self.list:
201                         x = x +  "\t%s,\n" % (ptvc_rec)
202                 x = x + "\t{ NULL, 0, 0, NULL }\n"
203                 x = x + "};\n"
204                 return x
205
206
207 class PTVCBitfield(PTVC):
208         def __init__(self, name, vars):
209                 NamedList.__init__(self, name, [])
210
211                 for var in vars:
212                         ptvc_rec = PTVCRecord(var, var.Length(), var.Endianness())
213                         self.list.append(ptvc_rec)
214
215         def ETTName(self):
216                 return "ett_%s" % (self.Name(),)
217         def __str__(self):
218                 ett_name = self.ETTName()
219                 x = "static gint %s;\n" % (ett_name,)
220
221                 x = x + "static const ptvc_record ptvc_%s[] = {\n" % (self.Name())
222                 for ptvc_rec in self.list:
223                         x = x +  "\t%s,\n" % (ptvc_rec)
224                 x = x + "\t{ NULL, 0, 0, NULL }\n"
225                 x = x + "};\n"
226
227                 x = x + "static const sub_ptvc_record %s = {\n" % (self.Name(),)
228                 x = x + "\t&%s,\n" % (ett_name,)
229                 x = x + "\tptvc_%s,\n" % (self.Name(),)
230                 x = x + "};\n"
231                 return x
232
233 class PTVCRecord:
234         def __init__(self, field, length, endianness):
235                 "Constructor"
236                 self.field      = field
237                 self.length     = length
238                 self.endianness = endianness
239
240         def __cmp__(self, other):
241                 "Comparison operator"
242                 if self.length < other.length:
243                         return -1
244                 elif self.length > other.length:
245                         return 1
246
247                 if self.field != other.field:
248                         return 1
249                 elif self.endianness != other.endianness:
250                         return 1
251                 else:
252                         return 0
253
254         def __str__(self):
255                 "String representation"
256                 endianness = 'FALSE'
257                 if self.endianness == LE:
258                         endianness = 'TRUE'
259
260                 # Default the length to this value
261                 length = "PTVC_VARIABLE_LENGTH"
262
263                 if type(self.length) == type(0):
264                         length = self.length
265                 else:
266                         var_length = self.field.Length()
267                         if var_length > 0:
268                                 length = var_length
269
270                 sub_ptvc_name = self.field.PTVCName()
271                 if sub_ptvc_name != "NULL":
272                         sub_ptvc_name = "&%s" % (sub_ptvc_name,)
273
274                 return "{ &%s, %s, %s, %s }" % (self.field.HFName(),
275                                 length, endianness, sub_ptvc_name)
276
277         def Offset(self):
278                 return self.offset
279
280         def Length(self):
281                 return self.length
282
283         def Field(self):
284                 return self.field
285
286 ##############################################################################
287
288 class NCP:
289         "NCP Packet class"
290         def __init__(self, func_code, description, group, has_length=1):
291                 "Constructor"
292                 self.func_code          = func_code
293                 self.description        = description
294                 self.group              = group
295                 self.codes              = None
296                 self.request_records    = None
297                 self.reply_records      = None
298                 self.has_length         = has_length
299
300                 if not groups.has_key(group):
301                         sys.stderr.write("NCP 0x%x has invalid group '%s'\n" % (self.func_code, group))
302                         sys.exit(1)
303
304                 if self.HasSubFunction():
305                         # NCP Function with SubFunction
306                         self.start_offset = 10
307                 else:
308                         # Simple NCP Function
309                         self.start_offset = 7
310
311         def FunctionCode(self, part=None):
312                 "Returns the function code for this NCP packet."
313                 if part == None:
314                         return self.func_code
315                 elif part == 'high':
316                         if self.HasSubFunction():
317                                 return (self.func_code & 0xff00) >> 8
318                         else:
319                                 return self.func_code
320                 elif part == 'low':
321                         if self.HasSubFunction():
322                                 return self.func_code & 0x00ff
323                         else:
324                                 return 0x00
325                 else:
326                         sys.stderr.write("Unknown directive '%s' for function_code()\n" % (part))
327                         sys.exit(1)
328
329         def HasSubFunction(self):
330                 "Does this NPC packet require a subfunction field?"
331                 if self.func_code <= 0xff:
332                         return 0
333                 else:
334                         return 1
335
336         def HasLength(self):
337                 return self.has_length
338
339         def Description(self):
340                 return self.description
341
342         def Group(self):
343                 return self.group
344
345         def PTVCRequest(self):
346                 return self.ptvc_request
347
348         def PTVCReply(self):
349                 return self.ptvc_reply
350
351         def Request(self, size, records=[]):
352                 self.request_size = size
353                 self.request_records = records
354                 if self.HasSubFunction():
355                         if self.HasLength():
356                                 self.CheckRecords(size, records, "Request", 10)
357                         else:
358                                 self.CheckRecords(size, records, "Request", 8)
359                 else:
360                         self.CheckRecords(size, records, "Request", 7)
361                 self.ptvc_request = self.MakePTVC(records, "request")
362
363         def Reply(self, size, records=[]):
364                 self.reply_size = size
365                 self.reply_records = records
366                 self.CheckRecords(size, records, "Reply", 8)
367                 self.ptvc_reply = self.MakePTVC(records, "reply")
368
369         def CheckRecords(self, size, records, descr, min_hdr_length):
370                 "Simple sanity check"
371                 min = size
372                 max = size
373                 if type(size) == type(()):
374                         min = size[0]
375                         max = size[1]
376
377                 lower = min_hdr_length
378                 upper = min_hdr_length
379
380                 for record in records:
381                         rec_size = record[1]
382                         rec_lower = rec_size
383                         rec_upper = rec_size
384                         if type(rec_size) == type(()):
385                                 rec_lower = rec_size[0]
386                                 rec_upper = rec_size[1]
387
388                         lower = lower + rec_lower
389                         upper = upper + rec_upper
390
391                 error = 0
392                 if min != lower:
393                         sys.stderr.write("%s records for 2222/0x%x sum to %d bytes minimum, but param1 shows %d\n" \
394                                 % (descr, self.FunctionCode(), lower, min))
395                         error = 1
396                 if max != upper:
397                         sys.stderr.write("%s records for 2222/0x%x sum to %d bytes maximum, but param1 shows %d\n" \
398                                 % (descr, self.FunctionCode(), upper, max))
399                         error = 1
400
401                 if error == 1:
402                         sys.exit(1)
403
404
405         def MakePTVC(self, records, name_suffix):
406                 """Makes a PTVC out of a request or reply record list. Possibly adds
407                 it to the global list of PTVCs (the global list is a UniqueCollection,
408                 so an equivalent PTVC may already be in the global list)."""
409
410                 name = "%s_%s" % (self.CName(), name_suffix)
411                 ptvc = PTVC(name, records)
412                 return ptvc_lists.Add(ptvc)
413
414         def CName(self):
415                 "Returns a C symbol based on the NCP function code"
416                 return "ncp_0x%x" % (self.func_code)
417
418         def Variables(self):
419                 """Returns a list of variables used in the request and reply records.
420                 A variable is listed only once, even if it is used twice (once in
421                 the request, once in the reply)."""
422
423                 variables = {}
424                 if self.request_records:
425                         for record in self.request_records:
426                                 var = record[2]
427                                 variables[var] = 1
428
429                                 sub_vars = var.SubVariables()
430                                 for sv in sub_vars:
431                                         variables[sv] = 1
432
433                 if self.reply_records:
434                         for record in self.reply_records:
435                                 var = record[2]
436                                 variables[var] = 1
437
438                                 sub_vars = var.SubVariables()
439                                 for sv in sub_vars:
440                                         variables[sv] = 1
441
442                 return variables.keys()
443
444
445         def CompletionCodes(self, codes=None):
446                 """Sets or returns the list of completion codes. Internally, a NamedList
447                 is used to store the completion codes, but the caller of this function
448                 never realizes that because Python lists are the input and output."""
449
450                 if codes == None:
451                         return self.codes
452
453                 # Sanity check
454                 okay = 1
455                 for code in codes:
456                         if not errors.has_key(code):
457                                 sys.stderr.write("Errors table does not have key 0x%04x for NCP=0x%x\n" % (code,
458                                         self.func_code))
459                                 okay = 0
460
461                 # Delay the exit until here so that the programmer can get the complete
462                 # list of missing error codes
463                 if not okay:
464                         sys.exit(1)
465
466                 # Create CompletionCode (NamedList) object and possible add it to
467                 # the global list of completion code lists.
468                 name = "%s_errors" % (self.CName())
469                 codes.sort()
470                 codes_list = NamedList(name, codes)
471                 self.codes = compcode_lists.Add(codes_list)
472
473                 self.Finalize()
474
475         def Finalize(self):
476                 """Adds the NCP object to the global collection of NCP objects. This
477                 is done automatically after setting the CompletionCode list. Yes, this
478                 is a shortcut, but it makes our list of NCP packet definitions look
479                 neater, since an explicit "add to global list of packets" is not needed."""
480
481                 # Add packet to global collection of packets
482                 if packets.HasMember(self):
483                         sys.stderr.write("Already have NCP Function Code 0x%x\n" % \
484                                 (self.func_code))
485                         sys.exit(1)
486                 else:
487                         packets.Add(self)
488
489
490
491 ##############################################################################
492
493 LE              = 1             # Little-Endian
494 BE              = 0             # Big-Endian
495 NA              = -1            # Not Applicable
496
497 class Type:
498         " Virtual class for NCP field types"
499         type            = "Type"
500         ftype           = None
501         disp            = "BASE_DEC"
502         endianness      = NA
503         values          = []
504
505         def __init__(self, abbrev, descr, bytes, endianness = NA):
506                 self.abbrev = abbrev
507                 self.descr = descr
508                 self.bytes = bytes
509
510         def Length(self):
511                 return self.bytes
512
513         def Abbreviation(self):
514                 return self.abbrev
515
516         def Description(self):
517                 return self.descr
518
519         def HFName(self):
520                 return "hf_ncp_" + self.abbrev
521
522         def DFilter(self):
523                 return "ncp." + self.abbrev
524
525         def EtherealFType(self):
526                 return self.ftype
527
528         def Display(self, newval=None):
529                 if newval != None:
530                         self.disp = newval
531                 return self.disp
532
533         def ValuesName(self):
534                 return "NULL"
535
536         def Mask(self):
537                 return 0
538
539         def Endianness(self):
540                 return self.endianness
541
542         def SubVariables(self):
543                 return []
544
545         def PTVCName(self):
546                 return "NULL"
547
548 class byte(Type):
549         type    = "byte"
550         ftype   = "FT_UINT8"
551         def __init__(self, abbrev, descr):
552                 Type.__init__(self, abbrev, descr, 1)
553
554 # Same as above. Both are provided for convenience
555 class uint8(Type):
556         type    = "uint8"
557         ftype   = "FT_UINT8"
558         bytes   = 1
559         def __init__(self, abbrev, descr):
560                 Type.__init__(self, abbrev, descr, 1)
561
562 class boolean8(uint8):
563         type    = "boolean8"
564         ftype   = "FT_BOOLEAN"
565
566 class uint16(Type):
567         type    = "uint16"
568         ftype   = "FT_UINT16"
569         def __init__(self, abbrev, descr, endianness = BE):
570                 Type.__init__(self, abbrev, descr, 2, endianness)
571
572 class uint32(Type):
573         type    = "uint32"
574         ftype   = "FT_UINT32"
575         def __init__(self, abbrev, descr, endianness = BE):
576                 Type.__init__(self, abbrev, descr, 4, endianness)
577
578 class nstring8(Type):
579         """A string of up to 255 characters. The first byte
580         gives the string length. Thus, the total length of
581         this data structure is from 1 to 256 bytes, including
582         the first byte."""
583
584         type    = "nstring8"
585         ftype   = "FT_UINT_STRING"
586         def __init__(self, abbrev, descr):
587                 Type.__init__(self, abbrev, descr, 1)
588
589 class fw_string(Type):
590         """A fixed-width string of n bytes."""
591
592         type    = "fw_string"
593         ftype   = "FT_STRING"
594
595         def __init__(self, abbrev, descr, bytes):
596                 Type.__init__(self, abbrev, descr, bytes)
597
598
599 class stringz(Type):
600         "NUL-terminated string, with a maximum length"
601
602         type    = "stringz"
603         ftype   = "FT_STRINGZ"
604         def __init__(self, abbrev, descr):
605                 Type.__init__(self, abbrev, descr, -1)
606
607 class val_string(Type):
608         """Abstract class for val_stringN, where N is number
609         of bits that key takes up."""
610
611         type    = "val_string"
612         disp    = 'BASE_HEX'
613
614         def __init__(self, abbrev, descr, val_string_array, endianness = BE):
615                 Type.__init__(self, abbrev, descr, self.bytes, endianness)
616                 self.values = val_string_array
617
618         def __repr__(self):
619                 result = "static const value_string %s[] = {\n" \
620                                 % (self.ValuesCName())
621                 for val_record in self.values:
622                         value   = val_record[0]
623                         text    = val_record[1]
624                         value_repr = self.value_format % value
625                         result = result + '\t{ %s,\t"%s" },\n' \
626                                         % (value_repr, text)
627
628                 value_repr = self.value_format % 0
629                 result = result + "\t{ %s,\tNULL },\n" % (value_repr)
630                 result = result + "};\n"
631
632                 return result
633
634         def ValuesCName(self):
635                 return "ncp_%s_vals" % (self.abbrev)
636
637         def ValuesName(self):
638                 return "VALS(%s)" % (self.ValuesCName())
639
640 class val_string8(val_string):
641         type            = "val_string8"
642         ftype           = "FT_UINT8"
643         bytes           = 1
644         value_format    = "0x%02x"
645
646 class val_string16(val_string):
647         type            = "val_string16"
648         ftype           = "FT_UINT16"
649         bytes           = 2
650         value_format    = "0x%04x"
651
652 class bytes(Type):
653         type    = 'bytes'
654         ftype   = 'FT_BYTES'
655
656         def __init__(self, abbrev, descr, bytes):
657                 Type.__init__(self, abbrev, descr, bytes, NA)
658
659
660 class bitfield(Type):
661         type    = "bitfield"
662         disp    = 'BASE_HEX'
663
664         def __init__(self, vars):
665                 var_hash = {}
666                 for var in vars:
667                         var_hash[var.bitmask] = var
668
669                 bitmasks = var_hash.keys()
670                 bitmasks.sort()
671                 bitmasks.reverse()
672
673                 ordered_vars = []
674                 for bitmask in bitmasks:
675                         var = var_hash[bitmask]
676                         ordered_vars.append(var)
677
678                 self.vars = ordered_vars
679                 self.sub_ptvc = PTVCBitfield(self.PTVCName(), self.vars)
680
681         def SubVariables(self):
682                 return self.vars
683
684         def SubVariablesPTVC(self):
685                 return self.sub_ptvc
686
687         def PTVCName(self):
688                 return "ncp_%s_bitfield" % (self.abbrev,)
689
690 class bitfield8(bitfield, uint8):
691         type    = "bitfield8"
692         ftype   = "FT_UINT8"
693
694         def __init__(self, abbrev, descr, vars):
695                 uint8.__init__(self, abbrev, descr)
696                 bitfield.__init__(self, vars)
697
698 class bf_uint(Type):
699         type    = "bf_uint"
700         disp    = 'BASE_HEX'
701
702         def __init__(self, bitmask, abbrev, descr):
703                 self.bitmask = bitmask
704                 self.abbrev = abbrev
705                 self.descr = descr
706
707         def Mask(self):
708                 return self.bitmask
709
710 class bf_boolean8(bf_uint, boolean8):
711         type    = "bf_boolean8"
712         ftype   = "FT_BOOLEAN"
713         disp    = "8"
714
715 #class data(Type):
716 #       type    = "data"
717 #       ftype   = "FT_BYTES"
718 #       def __init__(self, abbrev, descr):
719 #               Type.__init__(self, abbrev, descr, -1)
720 #
721 #       def length_var(self, length_var):
722 #               self.length_var = length_var
723
724 ##############################################################################
725 # NCP Field Types. Defined in Appendix A of "Programmer's Guide..."
726 ##############################################################################
727 AcceptedMaxSize = uint16("accepted_max_size", "Accepted Max Size")
728 AcctVersion     = byte("acct_version", "Acct Version")
729 BufferSize      = uint16("buffer_size", "Buffer Size")
730 ConnectionNumber        = uint32("connection_number", "Connection Number")
731 ConnectionsSupportedMax = uint16("connections_supported_max", "Connections Supported Max")
732 ConnectionsInUse        = uint16("connections_in_use", "Connections In Use")
733 ConnectionsMaxUsed      = uint16("connections_max_used", "Connections Max Used")
734 DirHandle       = byte("dir_handle", "Directory Handle")
735
736 EchoSocket      = uint16("echo_socket", "Echo Socket")
737 EchoSocket.Display('BASE_HEX')
738
739 FileHandle      = bytes("file_handle", "File Handle", 6)
740
741 FileLock        = val_string8("file_lock", "File Lock", [
742         [ 0x00, "Not Locked" ],
743         [ 0xfe, "Locked by file lock" ],
744         [ 0xff, "Unknown" ],
745 ])
746
747 FileOffset      = uint32("file_offset", "File Offset")
748 FilePath        = nstring8("file_path", "File Path")
749 FileSize        = uint32("file_size", "File Size")
750 InternetBridgeVersion   = byte("internet_bridge_version", "Internet Bridge Version")
751 JobType         = uint16("job_type", "Job Type")
752
753 LocalLoginInfoCcode     = byte("local_login_info_ccode", "Local Login Info C Code")
754 LogicalLockType = val_string8("logical_lock_type", "Logical Lock Type", [
755         [ 0x00, "Log file" ],
756         [ 0x01, "Log and lock file for exclusive read/write use" ],
757         [ 0x03, "Log and lock with shareable read-only use" ],
758 ])
759
760 LogicalRecordName       = nstring8("logical_record_name", "Logical Record Name")
761 LogLockType     = byte("log_lock_type", "Log Lock Type")
762
763 MaxBytes        = uint16("max_bytes", "Maximum Number of Bytes")
764 MixedModePathFlag       = byte("mixed_mode_path_flag", "Mixed Mode Path Flag")
765 NumBytes        = uint16("num_bytes", "Number of Bytes")
766
767 ObjectFlags     = val_string8("object_flags", "Object Flags", [
768         [ 0x00, "Dynamic object" ],
769         [ 0x01, "Static object" ],
770 ])
771
772 ObjectHasProperties = val_string8("object_has_properites", "Object Has Properties", [
773         [ 0x00, "No properties" ],
774         [ 0xff, "One or more properties" ],
775 ])
776
777 ObjectID        = uint32("object_id", "Object ID")
778 ObjectID.Display('BASE_HEX')
779
780 ObjectName      = nstring8("object_name", "Object Name")
781 ObjectName1     = fw_string("object_name1", "Object Name", 48)
782
783 ObjectSecurity  = val_string8("object_security", "Object Security", [
784         [ 0x00, "Anyone can read or modify the object" ],
785         [ 0x01, "Client logged into the file server can read the object" ],
786         [ 0x02, "Client logged into the file server with the object's name, type and password can read the object" ],
787         [ 0x03, "Client with supervisor equivalence can read the object" ],
788         [ 0x04, "Only the operating system can read the object" ],
789         [ 0x10, "Client logged into the file server can modify the object" ],
790         [ 0x20, "Client logged into the file server with the object's name, type and password can modify the object" ],
791         [ 0x30, "Client with supervisor equivalence can modify the object" ],
792         [ 0x40, "Only the operating system can modify the object" ],
793 ])
794
795 ObjectType      = val_string16("object_type", "Object Type", [
796         [ 0x0000,       "Unknown" ],
797         [ 0x0001,       "User" ],
798         [ 0x0002,       "User group" ],
799         [ 0x0003,       "Print queue" ],
800         [ 0x0004,       "NetWare file server" ],
801         [ 0x0005,       "Job server" ],
802         [ 0x0006,       "Gateway" ],
803         [ 0x0007,       "Print server" ],
804         [ 0x0008,       "Archive queue" ],
805         [ 0x0009,       "Archive server" ],
806         [ 0x000a,       "Job queue" ],
807         [ 0x000b,       "Administration" ],
808         [ 0x0021,       "NAS SNA gateway" ],
809         [ 0x0026,       "Remote bridge server" ],
810         [ 0x0027,       "TCP/IP gateway" ],
811 ])
812
813 OSLanguageID    = byte("os_language_id", "OS Language ID")
814 OSMajorVersion  = byte("os_major_version", "OS Major Version")
815 OSMinorVersion  = byte("os_minor_version", "OS Minor Version")
816 OSRevision      = byte("os_revision", "OS Revision")
817 PingVersion     = uint16("ping_version", "Ping Version", endianness=LE)
818 PingVersion.Display("BASE_HEX")
819
820 PrintServerVersion      = byte("print_server_version", "Print Server Version")
821 ProductMajorVersion     = uint16("product_major_version", "Product Major Version")
822 ProductMinorVersion     = uint16("product_minor_version", "Product Minor Version")
823 ProductRevisionVersion  = byte("product_revision_version", "Product Revision Version")
824
825 PropertyHasMoreSegments = val_string8("property_has_more_segments",
826         "Property Has More Segments", [
827         [ 0x00, "Is last segment" ],
828         [ 0xff, "More segments are available" ],
829 ])
830
831 PropertyName    = nstring8("property_name", "Property Name")
832 PropertyData    = bytes("property_data", "Property Data", 128)
833 PropertySegment = uint8("property_segment", "Property Segment")
834
835 PropertyType    = val_string8("property_type", "Property Type", [
836         [ 0x00, "Static item" ],
837         [ 0x01, "Dynamic item" ],
838         [ 0x02, "Static set" ],
839         [ 0x03, "Dynamic set" ],
840 ])
841
842 ProposedMaxSize = uint16("proposed_max_size", "Proposed Max Size")
843
844 Reserved3       = bytes("reserved3", "Reserved", 3)
845 Reserved51      = bytes("reserved51", "Reserved", 51)
846
847 QMSVersion      = byte("qms_version", "QMS Version")
848
849 # XXX - needs bitfield
850 SecurityFlag    = byte("security_flag", "Security Flag")
851 SecurityRestrictionVersion      = byte("security_restriction_version", "Security Restriction Version")
852
853 ServerName      = stringz("server_name", "Server Name")
854 SFTLevel        = byte("sft_level", "SFT Level")
855
856 TaskNumber      = uint32("task_number", "Task Number")
857 TimeoutLimit    = uint16("timeout_limit", "Timeout Limit")
858 TTSLevel        = byte("tts_level", "TTS Level")
859 UnknownByte     = byte("unknown_byte", "Unknown Byte")
860
861 VAPVersion      = byte("vap_version", "VAP Version")
862 VirtualConsoleVersion   = byte("virtual_console_version", "Virtual Console Version")
863 VolumesSupportedMax     = uint16("volumes_supported_max", "Volumes Supported Max")
864
865 ##############################################################################
866 # NCP Groups
867 ##############################################################################
868 def define_groups():
869         groups['accounting']    = "Accounting"
870         groups['afp']           = "AFP"
871         groups['auditing']      = "Auditing"
872         groups['bindery']       = "Bindery"
873         groups['connection']    = "Connection"
874         groups['directory']     = "Directory"
875         groups['extended']      = "Extended Attribute"
876         groups['file']          = "File"
877         groups['fileserver']    = "File Server"
878         groups['message']       = "Message"
879         groups['migration']     = "Data Migration"
880         groups['misc']          = "Miscellaneous"
881         groups['name']          = "Name Space"
882         groups['nds']           = "NetWare Directory"
883         groups['print']         = "Print"
884         groups['queue']         = "Queue"
885         groups['sync']          = "Synchronization"
886         groups['tss']           = "Transaction Tracking"
887         groups['unknown']       = "Unknown"
888
889 ##############################################################################
890 # NCP Errors
891 ##############################################################################
892 def define_errors():
893         errors[0x0000] = "Ok"
894         errors[0x0001] = "Transaction tracking is available"
895         errors[0x0002] = "Ok. The data has been written"
896
897         errors[0x0100] = "One or more of the ConnectionNumbers in the send list are invalid"
898         errors[0x0101] = "Invalid space limit"
899         errors[0x0102] = "Insufficient disk space"
900         errors[0x0103] = "Queue server cannot add jobs"
901         errors[0x0104] = "Out of disk space"
902         errors[0x0105] = "Semaphore overflow"
903
904         errors[0x0200] = "One or more clients in the send list are not logged in"
905         errors[0x0201] = "Queue server cannot attach"
906
907         errors[0x0300] = "One or more clients in the send list are not accepting messages"
908
909         errors[0x0400] = "Client already has message"
910         errors[0x0401] = "Queue server cannot service job"
911
912         errors[0x7e00] = "NCP failed boundary check"
913
914         errors[0x8000] = "Lock fail"
915         errors[0x8100] = "A file handle could not be allocated by the file server"
916         errors[0x8200] = "Unauthorized to open the file"
917         errors[0x8300] = "Unable to read/write the volume. Possible bad sector on the file server"
918
919         errors[0x8400] = "Unauthorized to create the directory"
920         errors[0x8401] = "Unauthorized to create the file"
921
922         errors[0x8500] = "Unauthorized to delete the specified file"
923         errors[0x8501] = "Unauthorized to overwrite an existing file in this directory"
924
925         errors[0x8700] = "An unexpected character was encountered in the filename"
926         errors[0x8800] = "Invalid file handle"
927         errors[0x8900] = "Unauthorized to search this directory"
928         errors[0x8a00] = "Unauthorized to delete this directory"
929         errors[0x8b00] = "Unauthorized to rename a file in this directory"
930
931         errors[0x8c00] = "No set privileges"
932         errors[0x8c01] = "Unauthorized to modify a file in this directory"
933         errors[0x8c02] = "Unauthorized to change the restriction on this volume"
934
935         errors[0x8d00] = "Some of the affected files are in use by another client"
936         errors[0x8d01] = "The affected file is in use"
937
938         errors[0x8e00] = "All of the affected files are in use by another client"
939         errors[0x8f00] = "Some of the affected files are read-only"
940
941         errors[0x9000] = "An attempt to modify a read-only volume occurred"
942         errors[0x9001] = "All of the affected files are read-only"
943
944         errors[0x9100] = "Some of the affected files already exist"
945
946         errors[0x9200] = "Directory with the new name already exists"
947         errors[0x9201] = "All of the affected files already exist"
948
949         errors[0x9300] = "Unauthorized to read from this file"
950         errors[0x9400] = "Unauthorized to write to this file"
951         errors[0x9500] = "The affected file is detached"
952
953         errors[0x9600] = "The file server has run out of memory to service this request"
954         errors[0x9601] = "No alloc space for message"
955
956         errors[0x9800] = "The affected volume is not mounted"
957         errors[0x9801] = "The volume associated with VolumeNumber is not mounted"
958         errors[0x9802] = "The resulting voume does not exist"
959         errors[0x9803] = "The destination volume is not mounted"
960
961         errors[0x9900] = "The file server has run out of directory space on the affected volume"
962         errors[0x9a00] = "The request attempted to rename the affected file to another volume"
963
964         errors[0x9b00] = "DirHandle is not associated with a valid directory path"
965         errors[0x9b01] = "A resulting directory handle is not associated with a valid directory path"
966         errors[0x9b02] = "The directory associated with DirHandle does not exist"
967         errors[0x9b03] = "Bad directory handle"
968
969         errors[0x9c00] = "The resulting path is not valid"
970         errors[0x9c01] = "The resulting file path is not valid"
971         errors[0x9c02] = "The resulting directory path is not valid"
972         errors[0x9c03] = "Invalid path"
973
974         errors[0x9d00] = "A directory handle was not available for allocation"
975
976         errors[0x9e00] = "The name of the directory does not conform to a legal name for this name space"
977         errors[0x9e01] = "The new directory name does not conform to a legal name for this name space"
978
979         errors[0x9f00] = "The request attempted to delete a directory that is in use by another client"
980
981         errors[0xa000] = "The request attempted to delete a directory that is not empty"
982         errors[0xa100] = "An unrecoverable error occured on the affected directory"
983         errors[0xa200] = "The request attempted to read from a file region that is physically locked"
984         errors[0xa400] = "Invalid directory rename attempted"
985
986         errors[0xbf00] = "Requests for this name space are not valid on this volume"
987
988         errors[0xc000] = "Unauthorized to retrieve accounting data"
989         errors[0xc100] = "The ACCOUNT_BALANCE property does not exist"
990         errors[0xc200] = "The object has exceeded its credit limit"
991         errors[0xc300] = "Too many holds have been placed against this account"
992         errors[0xc400] = "The client account has been disabled"
993
994         errors[0xc500] = "Access to the account has been denied because of intruder detection"
995         errors[0xc501] = "Login lockout"
996
997         errors[0xc600] = "The caller does not have operator priviliges"
998         errors[0xc601] = "The client does not have operator priviliges"
999
1000         errors[0xd000] = "Queue error"
1001         errors[0xd100] = "The queue does not exist"
1002
1003         errors[0xd200] = "A queue server is not associated with this queue"
1004         errors[0xd201] = "A queue server is not associated with the selected queue"
1005         errors[0xd202] = "No queue server"
1006
1007         errors[0xd300] = "No queue rights"
1008
1009         errors[0xd400] = "The queue is full and cannot accept another request"
1010         errors[0xd401] = "The queue associated with ObjectId is full and cannot accept another request"
1011
1012         errors[0xd500] = "A job does not exist in this queue"
1013         errors[0xd501] = "No queue job"
1014         errors[0xd502] = "The job associated with JobNumber does not exist in this queue"
1015
1016         errors[0xd600] = "The file server does not allow unencrypted passwords"
1017         errors[0xd601] = "No job right"
1018
1019         errors[0xd700] = "Bad account"
1020         errors[0xd701] = "The old and new password strings are identical"
1021         errors[0xd702] = "The job is currently being serviced"
1022         errors[0xd703] = "The queue is currently servicing a job"
1023         errors[0xd704] = "Queue servicing"
1024
1025         errors[0xd800] = "Queue not active"
1026
1027         errors[0xd900] = "The file server cannot accept another connection as it has reached its limit"
1028         errors[0xd901] = "The client is not security equivalent to one of the objects in the Q_SERVERS group property of the target queue"
1029         errors[0xd902] = "Station is not a server"
1030
1031         errors[0xda00] = "Attempted to login to the file server during a restricted time period"
1032         errors[0xda01] = "Queue halted"
1033
1034         errors[0xdb00] = "Attempted to login to the file server from an unauthorized workstation or network"
1035         errors[0xdb01] = "The queue cannot attach another queue server"
1036         errors[0xdb02] = "Maximum queue servers"
1037
1038         errors[0xde00] = "Attempted to login to the file server with an incorrect password"
1039         errors[0xdf00] = "Attempted to login to the file server with a password that has expired"
1040
1041         errors[0xe700] = "No disk track"
1042         errors[0xe800] = "Write to group"
1043         errors[0xe900] = "The object is already a member of the group property"
1044
1045         errors[0xea00] = "No such member"
1046         errors[0xea01] = "The bindery object is not a member of the set"
1047         errors[0xea02] = "Non-existent member"
1048
1049         errors[0xeb00] = "The property is not a set property"
1050
1051         errors[0xec00] = "No such set"
1052         errors[0xec01] = "The set property does not exist"
1053
1054         errors[0xed00] = "Property exists"
1055         errors[0xed01] = "The property already exists"
1056         errors[0xed02] = "An attempt was made to create a bindery object property that already exists"
1057
1058         errors[0xee00] = "The object already exists"
1059         errors[0xee01] = "The bindery object already exists"
1060
1061         errors[0xef00] = "Illegal name"
1062         errors[0xef01] = "Illegal characters in ObjectName field"
1063         errors[0xef02] = "Invalid name"
1064
1065         errors[0xf000] = "A wildcard was detected in a field that does not support wildcards"
1066         errors[0xf001] = "An illegal wildcard was detected in ObjectName"
1067
1068         errors[0xf100] = "The client does not have the rights to access this bindery object"
1069         errors[0xf101] = "Bindery security"
1070         errors[0xf102] = "Invalid bindery security"
1071
1072         errors[0xf200] = "Unauthorized to read from this object"
1073         errors[0xf300] = "Unauthorized to rename this object"
1074
1075         errors[0xf400] = "Unauthorized to delete this object"
1076         errors[0xf401] = "No object delete privileges"
1077         errors[0xf402] = "Unauthorized to delete this queue"
1078
1079         errors[0xf500] = "Unauthorized to create this object"
1080         errors[0xf501] = "No object create"
1081
1082         errors[0xf600] = "No property delete"
1083         errors[0xf601] = "Unauthorized to delete the property of this object"
1084         errors[0xf602] = "Unauthorized to delete this property"
1085
1086         errors[0xf700] = "Unauthorized to create this property"
1087         errors[0xf701] = "No property create privilege"
1088
1089         errors[0xf800] = "Unauthorized to write to this property"
1090         errors[0xf900] = "Unauthorized to read this property"
1091         errors[0xfa00] = "Temporary remap error"
1092
1093         errors[0xfb00] = "No such property"
1094         errors[0xfb01] = "The file server does not support this request"
1095         errors[0xfb02] = "The specified property does not exist"
1096         errors[0xfb03] = "The PASSWORD property does not exist for this bindery object"
1097         errors[0xfb04] = "NDS NCP not available"
1098
1099         errors[0xfc00] = "The message queue cannot accept another message"
1100         errors[0xfc01] = "The trustee associated with ObjectId does not exist"
1101         errors[0xfc02] = "The specified bindery object does not exist"
1102         errors[0xfc03] = "The bindery object associated with ObjectID does not exist"
1103         errors[0xfc04] = "A bindery object does not exist that matches"
1104         errors[0xfc05] = "The specified queue does not exist"
1105         errors[0xfc06] = "No such object"
1106         errors[0xfc07] = "The queue associated with ObjectID does not exist"
1107
1108         errors[0xfd00] = "Bad station number"
1109         errors[0xfd01] = "The connection associated with ConnectionNumber is not active"
1110         errors[0xfd02] = "Lock collision"
1111         errors[0xfd03] = "Transacktion tracking is disabled"
1112
1113         errors[0xfe00] = "I/O failure"
1114         errors[0xfe01] = "The files containing the bindery on the file server are locked"
1115         errors[0xfe02] = "A file with the specified name already exists in this directory"
1116         errors[0xfe03] = "No more restrictions were found"
1117         errors[0xfe04] = "The file server was unable to lock the file within the specified time limit"
1118         errors[0xfe05] = "The file server was unable to lock all files within the specified time limit"
1119         errors[0xfe06] = "The bindery object associated with ObjectID is not a valid trustee"
1120         errors[0xfe07] = "Directory locked"
1121         errors[0xfe08] = "Bindery locked"
1122         errors[0xfe09] = "Invalid semaphore name length"
1123         errors[0xfe0a] = "The file server was unable to complete the operation within the specified time limit"
1124         errors[0xfe0b] = "Transaction restart"
1125         errors[0xfe0c] = "Bad packet"
1126
1127         errors[0xff00] = "Failure"
1128         errors[0xff01] = "Lock error"
1129         errors[0xff02] = "File not found"
1130         errors[0xff03] = "The file not found or cannot be unlocked"
1131         errors[0xff04] = "Record not found"
1132         errors[0xff05] = "The logical record was not found"
1133         errors[0xff06] = "The printer associated with PrinterNumber does not exist"
1134         errors[0xff07] = "No such printer"
1135         errors[0xff08] = "Unable to complete the request"
1136         errors[0xff09] = "Unauthorized to change privileges of this trustee"
1137         errors[0xff0a] = "No files matching the search criteria were found"
1138         errors[0xff0b] = "A file matching the search criteria was not found"
1139         errors[0xff0c] = "Verification failed"
1140         errors[0xff0d] = "Object associated with ObjectID is not a manager"
1141         errors[0xff0e] = "Invalid initial semaphore value"
1142         errors[0xff0f] = "The semaphore handle is not valid"
1143         errors[0xff10] = "SemaphoreHandle is not associated with a valid sempahore"
1144         errors[0xff11] = "Invalid semaphore handle"
1145         errors[0xff12] = "Transaction tracking is not available"
1146         errors[0xff13] = "The transaction has not yet been written to disk"
1147         errors[0xff14] = "Directory already exists"
1148         errors[0xff15] = "The file already exists and the deletion flag was not set"
1149         errors[0xff16] = "No matching files or directories were found"
1150         errors[0xff17] = "A file or directory matching the search criteria was not found"
1151         errors[0xff18] = "The file already exists"
1152         errors[0xff19] = "No files found"
1153
1154
1155 ##############################################################################
1156 # Produce C code
1157 ##############################################################################
1158 def produce_code():
1159
1160         global errors
1161
1162         print "/*"
1163         print " * Generated automatically from %s" % (sys.argv[0])
1164         print " * Do not edit this file manually, as all changes will be lost."
1165         print " */\n"
1166
1167         print """
1168 /*
1169  * This program is free software; you can redistribute it and/or
1170  * modify it under the terms of the GNU General Public License
1171  * as published by the Free Software Foundation; either version 2
1172  * of the License, or (at your option) any later version.
1173  * 
1174  * This program is distributed in the hope that it will be useful,
1175  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1176  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1177  * GNU General Public License for more details.
1178  * 
1179  * You should have received a copy of the GNU General Public License
1180  * along with this program; if not, write to the Free Software
1181  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
1182  */
1183
1184 #ifdef HAVE_CONFIG_H
1185 # include "config.h"
1186 #endif
1187
1188 #include <glib.h>
1189 #include "packet.h"
1190 #include "conversation.h"
1191 #include "ptvcursor.h"
1192 #include "packet-ncp-int.h"
1193     
1194 static int hf_ncp_func = -1;
1195 static int hf_ncp_length = -1;
1196 static int hf_ncp_subfunc = -1;
1197 static int hf_ncp_completion_code = -1;
1198 static int hf_ncp_connection_status = -1;
1199         """
1200
1201         # Look at all packet types in the packets collection, and cull information
1202         # from them.
1203         packet_keys = []
1204         for packet in packets.Members():
1205                 packet_keys.append(packet.FunctionCode())
1206         packet_keys.sort()
1207
1208         errors_used_list = []
1209         errors_used_hash = {}
1210         groups_used_list = []
1211         groups_used_hash = {}
1212         variables_used_hash = {}
1213
1214         for pkt in packets.Members():
1215                 # Determine which error codes are used.
1216                 codes = pkt.CompletionCodes()
1217                 for code in codes.Records():
1218                         if not errors_used_hash.has_key(code):
1219                                 errors_used_hash[code] = len(errors_used_list)
1220                                 errors_used_list.append(code)
1221
1222                 # Determine which groups are used.
1223                 group = pkt.Group()
1224                 if not groups_used_hash.has_key(group):
1225                         groups_used_hash[group] = len(groups_used_list)
1226                         groups_used_list.append(group)
1227
1228                 # Determine which variables are used.
1229                 vars = pkt.Variables()
1230                 for var in vars:
1231                         variables_used_hash[var] = 1
1232
1233
1234
1235         # Print the hf variable declarations
1236         for var in variables_used_hash.keys():
1237                 print "static int " + var.HFName() + " = -1;"
1238
1239
1240         # Print the value_string's
1241         for var in variables_used_hash.keys():
1242                 if isinstance(var, val_string):
1243                         print ""
1244                         print `var`
1245
1246
1247         # Determine which error codes are not used
1248         errors_not_used = {}
1249         # Copy the keys from the error list...
1250         for code in errors.keys():
1251                 errors_not_used[code] = 1
1252         # ... and remove the ones that *were* used.
1253         for code in errors_used_list:
1254                 del errors_not_used[code]
1255
1256         # Print a remark showing errors not used
1257         list_errors_not_used = errors_not_used.keys()
1258         list_errors_not_used.sort()
1259         for code in list_errors_not_used:
1260                 print "/* Error 0x%04x not used: %s */" % (code, errors[code])
1261         print "\n"
1262
1263         # Print the errors table
1264         print "/* Error strings. */"
1265         print "static const char *ncp_errors[] = {"
1266         for code in errors_used_list:
1267                 print '\t/* %02d (0x%04x) */ "%s",' % (errors_used_hash[code], code, errors[code])
1268         print "};\n"
1269
1270
1271
1272
1273         # Determine which groups are not used
1274         groups_not_used = {}
1275         # Copy the keys from the group list...
1276         for group in groups.keys():
1277                 groups_not_used[group] = 1
1278         # ... and remove the ones that *were* used.
1279         for group in groups_used_list:
1280                 del groups_not_used[group]
1281
1282         # Print a remark showing groups not used
1283         list_groups_not_used = groups_not_used.keys()
1284         list_groups_not_used.sort()
1285         for group in list_groups_not_used:
1286                 print "/* Group not used: %s = %s */" % (group, groups[group])
1287         print "\n"
1288
1289         # Print the groups table
1290         print "/* Group strings. */"
1291         print "static const char *ncp_groups[] = {"
1292         for group in groups_used_list:
1293                 print '\t/* %02d (%s) */ "%s",' % (groups_used_hash[group], group, groups[group])
1294         print "};\n"
1295
1296         # Print the group macros
1297         for group in groups_used_list:
1298                 name = string.upper(group)
1299                 print "#define NCP_GROUP_%s\t%d" % (name, groups_used_hash[group])
1300         print "\n"
1301
1302         # Print PTVC's for bitfields
1303         ett_list = []
1304         print "/* PTVC records for bit-fields. */"
1305         for var in variables_used_hash.keys():
1306                 if isinstance(var, bitfield):
1307                         sub_vars_ptvc = var.SubVariablesPTVC()
1308                         print "/* %s */" % (sub_vars_ptvc.Name())
1309                         print sub_vars_ptvc
1310                         ett_list.append(sub_vars_ptvc.ETTName())
1311
1312         # Print PTVC's not already printed for bitfields
1313         print "/* PTVC records. These are re-used to save space. */"
1314         for ptvc in ptvc_lists.Members():
1315                 if not ptvc.Null() and not ptvc.Empty():
1316                         print ptvc
1317
1318         # Print error_equivalency tables
1319         print "/* Error-Equivalency Tables. These are re-used to save space. */"
1320         for compcodes in compcode_lists.Members():
1321                 errors = compcodes.Records()
1322                 # Make sure the record for error = 0x00 comes last.
1323                 print "static const error_equivalency %s[] = {" % (compcodes.Name())
1324                 for error in errors:
1325                         error_in_packet = error >> 8;
1326                         ncp_error_index = errors_used_hash[error]
1327                         print "\t{ 0x%02x, %d }, /* 0x%04x */" % (error_in_packet,
1328                                 ncp_error_index, error)
1329                 print "\t{ 0x00, -1 }\n};\n"
1330
1331
1332         # Functions without length parameter
1333         funcs_without_length = {}
1334
1335
1336         # Print ncp_record packet records
1337         print "#define SUBFUNC_WITH_LENGTH      0x02"
1338         print "#define SUBFUNC_NO_LENGTH        0x01"
1339         print "#define NO_SUBFUNC               0x00"
1340
1341         print "/* ncp_record structs for packets */"
1342         print "static const ncp_record ncp_packets[] = {"
1343         for pkt in packets.Members():
1344                 if pkt.HasSubFunction():
1345                         func = pkt.FunctionCode('high')
1346                         if pkt.HasLength():
1347                                 subfunc_string = "SUBFUNC_WITH_LENGTH"
1348                                 # Ensure that the function either has a length param or not
1349                                 if funcs_without_length.has_key(func):
1350                                         sys.exit("Function 0x%04x sometimes has length param, sometimes not." \
1351                                                 % (pkt.FunctionCode(),))
1352                         else:
1353                                 subfunc_string = "SUBFUNC_NO_LENGTH"
1354                                 funcs_without_length[func] = 1
1355                 else:
1356                         subfunc_string = "NO_SUBFUNC"
1357                 print '\t{ 0x%02x, 0x%02x, %s, "%s",' % (pkt.FunctionCode('high'),
1358                         pkt.FunctionCode('low'), subfunc_string, pkt.Description()),
1359
1360                 print '\t%d /* %s */,' % (groups_used_hash[pkt.Group()], pkt.Group())
1361
1362                 ptvc = pkt.PTVCRequest()
1363                 if not ptvc.Null() and not ptvc.Empty():
1364                         ptvc_request = ptvc.Name()
1365                 else:
1366                         ptvc_request = 'NULL'
1367
1368                 ptvc = pkt.PTVCReply()
1369                 if not ptvc.Null() and not ptvc.Empty():
1370                         ptvc_reply = ptvc.Name()
1371                 else:
1372                         ptvc_reply = 'NULL'
1373
1374                 errors = pkt.CompletionCodes()
1375                 print '\t\t%s, NULL, %s, NULL,' % (ptvc_request, ptvc_reply)
1376                 print '\t\t%s },\n' % (errors.Name())
1377
1378         print '\t{ 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL }'
1379         print "};\n"
1380
1381         print "/* ncp funcs that require a subfunc */"
1382         print "static const guint8 ncp_func_requires_subfunc[] = {"
1383         hi_seen = {}
1384         for pkt in packets.Members():
1385                 if pkt.HasSubFunction():
1386                         hi_func = pkt.FunctionCode('high')
1387                         if not hi_seen.has_key(hi_func):
1388                                 print "\t0x%02x," % (hi_func)
1389                                 hi_seen[hi_func] = 1
1390         print "\t0"
1391         print "};\n"
1392
1393
1394         print "/* ncp funcs that have no length parameter */"
1395         print "static const guint8 ncp_func_has_no_length_parameter[] = {"
1396         funcs = funcs_without_length.keys()
1397         funcs.sort()
1398         for func in funcs:
1399                 print "\t0x%02x," % (func,)
1400         print "\t0"
1401         print "};\n"
1402
1403         # proto_register_ncp2222()
1404         print """
1405 void
1406 proto_register_ncp2222(void)
1407 {
1408
1409         static hf_register_info hf[] = {
1410         { &hf_ncp_func,
1411         { "Function", "ncp.func", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
1412
1413         { &hf_ncp_length,
1414         { "Packet Length", "ncp.length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1415
1416         { &hf_ncp_subfunc,
1417         { "SubFunction", "ncp.subfunc", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
1418
1419         { &hf_ncp_completion_code,
1420         { "Completion Code", "ncp.completion_code", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
1421
1422         { &hf_ncp_connection_status,
1423         { "Connection Status", "ncp.connection_status", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
1424         """
1425
1426         # Print the registration code for the hf variables
1427         for var in variables_used_hash.keys():
1428                 print "\t{ &%s," % (var.HFName())
1429                 print "\t{ \"%s\", \"%s\", %s, %s, %s, 0x%x, \"\", HFILL }},\n" % \
1430                         (var.Description(), var.DFilter(),
1431                         var.EtherealFType(), var.Display(), var.ValuesName(),
1432                         var.Mask())
1433
1434         print """
1435         };
1436         """
1437
1438         # ETT's ?
1439         if ett_list:
1440                 print """
1441         static gint *ett[] = {
1442                 """
1443
1444                 for ett in ett_list:
1445                         print "\t\t&%s," % (ett,)
1446
1447                 print """
1448         };
1449                 """
1450
1451         print """
1452         proto_register_field_array(proto_ncp, hf, array_length(hf));
1453         """
1454
1455         if ett_list:
1456                 print """
1457         proto_register_subtree_array(ett, array_length(ett));
1458         """
1459
1460         print "}\n"
1461         print '#include "packet-ncp2222.inc"'
1462
1463
1464 def main():
1465         global packets
1466         global compcode_lists
1467         global ptvc_lists
1468
1469         packets         = UniqueCollection('NCP Packet Descriptions')
1470         compcode_lists  = UniqueCollection('Completion Code Lists')
1471
1472         define_errors()
1473         define_groups()
1474         define_ncp2222()
1475
1476         produce_code()
1477
1478 def define_ncp2222():
1479         ##############################################################################
1480         # NCP Packets. Here I list functions and subfunctions in hexadecimal like the
1481         # NCP book (and I believe LanAlyzer does this too).
1482         # However, Novell lists these in decimal in their on-line documentation.
1483         ##############################################################################
1484         # 2222/02
1485         pkt = NCP(0x02, "File Release Lock", 'sync')
1486         pkt.Request(7)
1487         pkt.Reply(8)
1488         pkt.CompletionCodes([0x0000, 0xff00])
1489
1490         #
1491         # Untested
1492         #
1493         # 2222/03
1494         #pkt = NCP(0x03, "Log File", 'sync')
1495         #pkt.request( (12, 267), [
1496         #       [ 7, 1, DirHandle ],
1497         #       [ 8, 1, LogLockType ],
1498         #       [ 9, 2, TimeoutLimit, LE ],
1499         #       [ 11, (1, 256), FilePath ],
1500         #       ])
1501         #pkt.completion_codes([0x0000, 0x8200, 0x9600, 0xfe00, 0xff01])
1502         #
1503         ## 2222/04
1504         #pkt = NCP(0x04, "Lock File Set", 'sync')
1505         #pkt.request([
1506         #       [ 7, TimeoutLimit ],
1507         #       ])
1508         #pkt.completion_codes([0xfe, 0xff01])
1509         #
1510         ## 2222/05
1511         #pkt = NCP(0x05, "Release File", 'sync')
1512         #pkt.request([
1513         #       [ 7, DirHandle ],
1514         #       [ 8, FilePath ],
1515         #       ])
1516         #pkt.completion_codes([0x7e, 0x98, 0x9b, 0x9c, 0xff02])
1517         #
1518         ## 2222/06
1519         #pkt = NCP(0x06, "Release File Set", 'sync')
1520         #pkt.request([
1521         #       [ 7, UnknownByte ],
1522         #       ])
1523         #pkt.completion_codes()
1524         #
1525         ## 2222/07
1526         #pkt = NCP(0x07, "Clear File", 'sync')
1527         #pkt.request([
1528         #       [ 7, DirHandle ],
1529         #       [ 8, FilePath ],
1530         #       ])
1531         #pkt.completion_codes([0x7e, 0x96, 0x98, 0x9b, 0x9c,
1532         #       0xa1, 0xfd, 0xff])
1533         #
1534         ## 2222/08
1535         #pkt = NCP(0x08, "Clear File Set", 'sync')
1536         #pkt.request([
1537         #       [ 7, FileLock ],
1538         #       ])
1539         #pkt.completion_codes([0x7e])
1540         #
1541         ## 2222/09
1542         #pkt = NCP(0x09, "Log Logical Record", 'sync')
1543         #pkt.request([
1544         #       [ 7, LogicalLockType ],
1545         #       [ 8, TimeoutLimit_be ],
1546         #       [ 10, LogicalRecordName ],
1547         #       ])
1548         #pkt.completion_codes([0x96, 0xfe, 0xff])
1549         #
1550         ## 2222/0a
1551         #pkt = NCP(0x0a, "Lock Logical Record Set", 'sync')
1552         #pkt.request([
1553         #       [ 7, LogicalLockType ],
1554         #       [ 8, TimeoutLimit_le ],
1555         #       ])
1556         #pkt.completion_codes([0xfe, 0xff])
1557         #
1558         ## 2222/0b
1559         #pkt = NCP(0x0b, "Clear Logical Record", 'sync')
1560         #pkt.request([
1561         #       [7, LogicalRecordName ],
1562         #       ])
1563         #pkt.completion_codes([0xff]
1564         ## 2222/0c
1565         ## 2222/0d
1566         ## 2222/0e
1567         ## 2222/0f
1568         ## 2222/11
1569         #
1570         ## 2222/1100
1571         #pkt = NCP(0x1100, "Lock Logical Record Set", 'sync')
1572         #pkt.request([
1573         #       [ 10, var_length_data("data").length_var("packetlength") ]
1574         #       ])
1575         #pkt.completion_codes()
1576         #
1577
1578         # 2222/1711
1579         pkt = NCP(0x1711, "Get File Server Information", 'fileserver')
1580         pkt.Request(10)
1581         pkt.Reply(136, [
1582                 [ 8, 48, ServerName ],
1583                 [ 56, 1, OSMajorVersion ],
1584                 [ 57, 1, OSMinorVersion ],
1585                 [ 58, 2, ConnectionsSupportedMax ],
1586                 [ 60, 2, ConnectionsInUse ],
1587                 [ 62, 2, VolumesSupportedMax ],
1588                 [ 64, 1, OSRevision ],
1589                 [ 65, 1, SFTLevel ],
1590                 [ 66, 1, TTSLevel ],
1591                 [ 67, 2, ConnectionsMaxUsed ],
1592                 [ 69, 1, AcctVersion ],
1593                 [ 70, 1, VAPVersion ],
1594                 [ 71, 1, QMSVersion ],
1595                 [ 72, 1, PrintServerVersion ],
1596                 [ 73, 1, VirtualConsoleVersion ],
1597                 [ 74, 1, SecurityRestrictionVersion ],
1598                 [ 75, 1, InternetBridgeVersion ],
1599                 [ 76, 1, MixedModePathFlag ],
1600                 [ 77, 1, LocalLoginInfoCcode ],
1601                 [ 78, 2, ProductMajorVersion ],
1602                 [ 80, 2, ProductMinorVersion ],
1603                 [ 82, 2, ProductRevisionVersion ],
1604                 [ 84, 1, OSLanguageID ],
1605                 [ 85, 51, Reserved51 ],
1606         ])
1607         pkt.CompletionCodes([0x0000, 0x9600])
1608
1609
1610         # 2222/1735
1611         pkt = NCP(0x1735, "Get Bindery Object ID", 'bindery')
1612         pkt.Request((13,60), [
1613                 [ 10, 2, ObjectType ],
1614                 [ 12, (1,48), ObjectName ],
1615         ])
1616         pkt.Reply(62, [
1617                 [ 8, 4, ObjectID ],
1618                 [ 12, 2, ObjectType ],
1619                 [ 14, 48, ObjectName1 ],
1620         ])
1621         pkt.CompletionCodes([0x0000, 0x9600, 0xef01, 0xf000, 0xfc02,
1622                 0xfe01, 0xff00])
1623
1624         # 2222/1737
1625         pkt = NCP(0x1737, "Scan Bindery Object", 'bindery')
1626         pkt.Request((17,64), [
1627                 [ 10, 4, ObjectID ],
1628                 [ 14, 2, ObjectType ],
1629                 [ 16, (1,48), ObjectName ],
1630         ])
1631         pkt.Reply(65, [
1632                 [ 8, 4, ObjectID ],
1633                 [ 12, 2, ObjectType ],
1634                 [ 14, 48, ObjectName1 ],
1635                 [ 62, 1, ObjectFlags ],
1636                 [ 63, 1, ObjectSecurity ],
1637                 [ 64, 1, ObjectHasProperties ],
1638         ])
1639         pkt.CompletionCodes([0x0000, 0x9600, 0xef01, 0xfc02,
1640                 0xfe01, 0xff00])
1641
1642         # 2222/173D
1643         pkt = NCP(0x173D, "Read Property Value", 'bindery')
1644         pkt.Request((15,77), [
1645                 [ 10, 2, ObjectType ],
1646                 [ 12, (1,48), ObjectName ],
1647                 [ -1, 1, PropertySegment ],
1648                 [ -1, (1,16), PropertyName ],
1649         ])
1650         pkt.Reply(138, [
1651                 [ 8, 128, PropertyData ],
1652                 [ 136, 1, PropertyHasMoreSegments ],
1653                 [ 137, 1, PropertyType ],
1654         ])
1655         pkt.CompletionCodes([0x0000, 0x8800, 0x9300, 0x9600, 0xec01,
1656                 0xf000, 0xf100, 0xf900, 0xfb02, 0xfc02, 0xfe01, 0xff00 ])
1657
1658         # 2222/177C
1659         pkt = NCP(0x177C, "Service Queue Job", 'queue')
1660         pkt.Request(16, [
1661                 [ 10, 4, ObjectID ],
1662                 [ 14, 2, JobType ],
1663         ])
1664         pkt.Reply(24, [ # XXX - 76, [
1665                 [ 8, 4, ConnectionNumber ],
1666                 [ 12, 4, TaskNumber ],
1667                 [ 16, 4, ObjectID ],
1668                 [ 20, 4, ObjectID ],
1669                 # XXX - DateTime
1670         ])
1671         # These completion codes are not documented, but guessed.
1672         pkt.CompletionCodes([0x0000, 0x9900, 0xd000, 0xd100, 0xd201, 0xd300,
1673                 0xd401, 0xd502, 0xd601, 0xd704, 0xd800, 0xd901, 0xda01, 0xdb01,
1674                 0xff00 ])
1675
1676         # 2222/18
1677         pkt = NCP(0x18, "End of Job", 'connection')
1678         pkt.Request(7)
1679         pkt.Reply(8)
1680         pkt.CompletionCodes([0x0000])
1681
1682         # 2222/19
1683         pkt = NCP(0x19, "Logout", 'connection')
1684         pkt.Request(7)
1685         pkt.Reply(8)
1686         pkt.CompletionCodes([0x0000])
1687
1688         # 2222/21
1689         pkt = NCP(0x21, "Negotiate Buffer Size", 'connection')
1690         pkt.Request(9, [
1691                 [ 7, 2, BufferSize ],
1692         ])
1693         pkt.Reply(10, [
1694                 [ 8, 2, BufferSize ],
1695         ])
1696         pkt.CompletionCodes([0x0000])
1697
1698         # 2222/42
1699         pkt = NCP(0x42, "Close File", 'file')
1700         pkt.Request(13, [
1701                 [ 7, 6, FileHandle ],
1702         ])
1703         pkt.Reply(8)
1704         pkt.CompletionCodes([0x0000, 0xff00])
1705
1706         # 2222/47
1707         pkt = NCP(0x47, "Get Current Size of File", 'file')
1708         pkt.Request(13, [
1709                 [ 7, 6, FileHandle ],
1710         ])
1711         pkt.Reply(12, [
1712                 [ 8, 4, FileSize ],
1713         ])
1714         pkt.CompletionCodes([0x0000, 0x8800])
1715
1716         # 2222/48
1717         pkt = NCP(0x48, "Read From A File", 'file')
1718         pkt.Request(20, [
1719                 [ 7, 1, UnknownByte ],
1720                 [ 8, 6, FileHandle ],
1721                 [ 14, 4, FileOffset ],  # my nomenclature
1722                 [ 18, 2, MaxBytes ],    # my nomenclature
1723         ])
1724         pkt.Reply(10, [ # XXX - (10,-1), [
1725                 [ 8, 2, NumBytes ],     # my nomenclature
1726                 # XXX
1727         ])
1728         pkt.CompletionCodes([0x0000, 0x8800, 0x9300, 0xff00])
1729
1730         # 2222/5701     - no info
1731         # 2222/5702     - no info
1732         # 2222/5706     - no info
1733         # 2222/5714     - no info
1734         # 2222/72       - no info
1735
1736         # 2222/61
1737         pkt = NCP(0x61, "Get Big Packet NCP Max Packet Size", 'unknown')
1738         pkt.Request(10, [
1739                 [ 7, 2, ProposedMaxSize ],
1740                 [ 9, 1, SecurityFlag ],
1741         ])
1742         pkt.Reply(13, [
1743                 [ 8, 2, AcceptedMaxSize ],
1744                 [ 10, 2, EchoSocket ],
1745                 [ 12, 1, SecurityFlag ],
1746         ])
1747         pkt.CompletionCodes([0x0000])
1748
1749         # 2222/6801
1750         pkt = NCP(0x6801, "Ping for NDS NCP", "nds", has_length=0)
1751         pkt.Request(11, [
1752                 [ 8, 3, Reserved3 ]
1753         ])
1754         # XXX - expand, Unicode
1755         pkt.Reply(10, [
1756                 [ 8, 2, PingVersion ],
1757         ])
1758         pkt.CompletionCodes([0x0000, 0xfb04, 0xfe0c])
1759
1760
1761 if __name__ == '__main__':
1762         main()