1 module asn1.tools.decoder_mixin;
2 
3 mixin template Decoder(Element)
4 {
5     import asn1.constants;
6     import std.bigint : BigInt;
7     import std.conv : ConvException;
8     import std.stdio : write, writeln, writefln, stdin;
9     import std.utf : UTFException;
10 
11     int main(string[] args)
12     {
13         enum ReturnValue : int
14         {
15             success = 0,
16             elementTerminatedPrematurely = 1,
17             invalidEncodedValue = 2,
18             unexpectedException = int.max
19         }
20 
21         ubyte[] data = stdin.rawRead!ubyte(new ubyte[500000]);
22 
23         if (args.length == 2u && data.length > 0u)
24         {
25             if (args[1] == "-n")
26             {
27                 if (data[$-1] == '\n')
28                 {
29                     data = data[0 .. $-1];
30                     writeln("Removed a trailing LineFeed character from input.");
31                 }
32                 else writeln("There was no trailing LineFeed character to remove from input.");
33             }
34             else if (args[1] == "-r")
35             {
36                 if (data[$-2 .. $] == "\r\n")
37                 {
38                     data = data[0 .. $-2];
39                     writeln("Removed a trailing CarriageReturn+LineFeed from input.");
40                 }
41                 else writeln("There was no trailing CarriageReturn+LineFeed to remove from input.");
42             }
43             else writeln("Ignoring unrecognized option or argument: '" ~ args[1] ~"'.");
44         }
45 
46         Element[] tops;
47 
48         try
49         {
50             while (data.length > 0u)
51                 tops ~= new Element(data);
52         }
53         catch (ASN1ValueSizeException e)
54         {
55             writeln("\n", e.msg, "\n");
56             return ReturnValue.elementTerminatedPrematurely;
57         }
58         catch (ASN1Exception e)
59         {
60             writeln("\n", e.msg, "\n");
61             return ReturnValue.invalidEncodedValue;
62         }
63         catch (Exception e)
64         {
65             writeln("\n", e.msg, "\n");
66             return ReturnValue.unexpectedException;
67         }
68 
69         writeln();
70         foreach (top; tops)
71         {
72             try
73             {
74                 display(top, 0u);
75             }
76             catch (ASN1ValueSizeException e)
77             {
78                 writeln("\n", e.msg, "\n");
79                 return ReturnValue.elementTerminatedPrematurely;
80             }
81             catch (ASN1Exception e)
82             {
83                 writeln("\n", e.msg, "\n");
84                 return ReturnValue.invalidEncodedValue;
85             }
86             catch (Exception e)
87             {
88                 writeln("\n", e.msg, "\n");
89                 return ReturnValue.unexpectedException;
90             }
91         }
92 
93         writeln();
94         return ReturnValue.success;
95     }
96 
97     void display (Element element, ubyte indentation)
98     {
99         string tagClassString = "";
100         bool universal = false;
101         switch (element.tagClass)
102         {
103             case (ASN1TagClass.universal):
104             {
105                 tagClassString = "UNIV";
106                 universal = true;
107                 break;
108             }
109             case (ASN1TagClass.application):
110             {
111                 tagClassString = "APPL";
112                 break;
113             }
114             case (ASN1TagClass.contextSpecific):
115             {
116                 tagClassString = "CTXT";
117                 break;
118             }
119             case (ASN1TagClass.privatelyDefined):
120             {
121                 tagClassString = "PRIV";
122                 break;
123             }
124             default: assert(0, "Impossible tagClass encountered!");
125         }
126 
127         char[] indents;
128         indents.length = indentation;
129         indents[0 .. $] = ' ';
130 
131         if (element.construction == ASN1Construction.primitive)
132         {
133             if (universal)
134                 writefln("%s[ %s %d ] : %s", cast(string) indents, tagClassString, element.tagNumber, stringifyUniversalValue(element));
135             else
136                 writefln("%s[ %s %d ] : %(%02X %)", cast(string) indents, tagClassString, element.tagNumber, element.value);
137         }
138         else
139         {
140             writefln("%s[ %s %d ] :", cast(string) indents, tagClassString, element.tagNumber);
141             indentation += 4;
142             ubyte[] value = element.value.dup;
143             Element[] subs;
144             size_t i = 0u;
145             while (i < value.length)
146                 subs ~= new Element(i, value);
147 
148             foreach (sub; subs)
149             {
150                 display(new Element(element.value), indentation);
151             }
152         }
153         indentation -= 4;
154     }
155 
156     string stringifyBitString (in bool[] value)
157     {
158         char[] ret;
159         ret.length = value.length;
160         foreach(size_t index, bool boolean; value)
161         {
162             if (boolean)
163                 ret[index] = '1';
164             else
165                 ret[index] = '0';
166         }
167         return cast(string) ret;
168     }
169 
170     string stringifyUniversalValue (Element element)
171     {
172         import std.conv : text;
173         import std.format : format;
174 
175         switch (element.tagNumber)
176         {
177             case (0u): return "END OF CONTENT";
178             case (1u): return (element.boolean ? "TRUE" : "FALSE");
179             case (2u): return format("%d", element.integer!BigInt);
180             case (3u): return stringifyBitString(element.bitString);
181             case (4u): return format("%(%02X %)", element.octetString);
182             case (5u): return "NULL";
183             case (6u): return element.objectIdentifier.toString();
184             case (7u): return element.objectDescriptor;
185             case (8u): return "EXTERNAL"; // This should never be executed.
186             case (9u): return text(element.realNumber!real);
187             case (10u): return text(element.enumerated!ptrdiff_t);
188             case (11u): return "EmbeddedPDV"; // This should never be executed.
189             case (12u): return element.utf8String;
190             case (13u): return ("RELATIVE OID: " ~ text(element.value));
191             case (14u): return "!!! INVALID TYPE : RESERVED 14 !!!";
192             case (15u): return "!!! INVALID TYPE : RESERVED 15 !!!";
193             case (16u): return "SEQUENCE"; // This should never be executed.
194             case (17u): return "SET"; // This should never be executed.
195             case (18u): return element.numericString;
196             case (19u): return element.printableString;
197             case (20u): return format("%(%02X %)", element.teletexString);
198             case (21u): return format("%(%02X %)", element.videotexString);
199             case (22u): return element.ia5String;
200             case (23u): return element.utcTime.toISOString();
201             case (24u): return element.generalizedTime.toISOString();
202             case (25u): return element.graphicString;
203             case (26u): return element.visibleString;
204             case (27u): return element.generalString;
205             case (28u): return cast(string) element.universalString;
206             case (29u): return "CharacterString"; // This should never be executed.
207             case (30u): return cast(string) element.bmpString;
208             case (31u): return "!!! INVALID TYPE : UNDEFINED 31 !!!";
209             default: return "!!! INVALID TYPE : tagNumber above 31 !!!";
210         }
211     }
212 }