1 module buffer.message;
2 
3 import std.traits;
4 import std.typecons;
5 import std.variant;
6 import std.conv : to;
7 import std.exception;
8 
9 import crypto.rsa;
10 
11 public import buffer.compiler;
12 public import buffer.packet;
13 import buffer.utils;
14 
15 ///
16 abstract class Message
17 {
18 public:
19 
20     alias int8     = byte;
21     alias uint8    = ubyte;
22     alias int16    = short;
23     alias uint16   = ushort;
24     alias int32    = int;
25     alias uint32   = uint;
26     alias int64    = long;
27     alias uint64   = ulong;
28     alias float32  = float;
29     alias float64  = double;
30     alias float128 = real;
31     //bool
32     //char
33     //string
34 
35     ///
36     static void settings(const ushort magic, const CryptType crypt = CryptType.NONE, const string key = string.init)
37     {
38         assert((crypt == CryptType.NONE) || (crypt != CryptType.NONE && key != string.init),
39                 "Must specify key when specifying the type of CryptType.");
40 
41         _magic = magic;
42         _crypt = crypt;
43         _key   = key;
44 
45         if ((_crypt == CryptType.RSA) || (_crypt == CryptType.RSA_XTEA_MIXIN))
46         {
47             _rsaKey = RSA.decodeKey(Message._key);
48 
49             enforce(!_rsaKey.isNull, "Rsakey is incorrect.");
50         }
51     }
52 
53     ///
54     static void settings(const ushort magic, RSAKeyInfo rsaKey, const bool mixinXteaMode = false)
55     {
56         _magic = magic;
57         _crypt = mixinXteaMode ? CryptType.RSA_XTEA_MIXIN : CryptType.RSA;
58         _rsaKey = rsaKey;
59     }
60 
61     ///
62     static ubyte[] serialize_without_msginfo(Params...)(const string method, Params params)
63     {
64         return serialize_without_msginfo!(Params)(_magic, _crypt, _key, _rsaKey, method, params);
65     }
66 
67     ///
68     static ubyte[] serialize_without_msginfo(Params...)(const ushort magic, const CryptType crypt, const string key, Nullable!RSAKeyInfo rsaKey,
69         string method, Params params)
70     {
71         Variant[] t_params;
72 
73         foreach(p; params)
74         {
75             t_params ~= Variant(p);
76         }
77 
78         return Packet.build(magic, crypt, key, rsaKey, string.init, method, t_params);
79     }
80 
81     static void getMessageInfo(const ubyte[] buffer, out string name, out string method)
82     {
83         Packet.parseInfo(buffer, name, method);
84     }
85 
86     ///
87     static Variant[] deserialize(const ubyte[] buffer, out string name, out string method)
88     {
89         return deserializeEx(_magic, _crypt, _key, _rsaKey, buffer, name, method);
90     }
91 
92     ///
93     static Variant[] deserializeEx(const ushort magic, const CryptType crypt, const string key, Nullable!RSAKeyInfo rsaKey,
94         const ubyte[] buffer, out string name, out string method)
95     {
96         return Packet.parse(buffer, magic, crypt, key, rsaKey, name, method);
97     }
98 
99     ///
100     static T deserialize(T)(const ubyte[] buffer)
101     if ((BaseTypeTuple!T.length > 0) && is(BaseTypeTuple!T[0] == Message))
102     {
103         string method;
104 
105         return deserialize!T(buffer, method);
106     }
107 
108     ///
109     static T deserialize(T)(const ubyte[] buffer, out string method)
110     if ((BaseTypeTuple!T.length > 0) && is(BaseTypeTuple!T[0] == Message))
111     {
112         string name;
113         const Variant[] params = deserialize(buffer, name, method);
114 
115         if (name == string.init || params == null)
116         {
117             return null;
118         }
119 
120         T message = new T();
121         if (getClassSimpleName(T.classinfo.name) != name)
122         {
123             assert(0, "The type T(" ~ T.classinfo.name ~ ") of the incoming template is incorrect. It should be " ~ name);
124         }
125 
126         static foreach (i, type; FieldTypeTuple!(T))
127         {
128             mixin(`
129                 message.` ~ FieldNameTuple!T[i] ~ ` = params[` ~ i.to!string ~ `].get!` ~ type.stringof ~ `;
130             `);
131         }
132 
133         return message;
134     }
135 
136 protected:
137 
138     ubyte[] serialize(T)(const T message, const string method = string.init)
139     if ((BaseTypeTuple!T.length > 0) && is(BaseTypeTuple!T[0] == Message))
140     {
141         assert(message !is null, "The object to serialize cannot be null.");
142 
143         Variant[] params;
144 
145         static foreach (i, type; FieldTypeTuple!T)
146         {
147             mixin(`
148                 params ~= Variant(message.` ~ FieldNameTuple!T[i] ~ `);
149             `);
150         }
151 
152         return Packet.build(_magic, _crypt, _key, _rsaKey, getClassSimpleName(T.classinfo.name), method, params);
153     }
154 
155 package:
156 
157     __gshared ushort              _magic;
158     __gshared CryptType           _crypt;
159     __gshared string              _key;
160     __gshared Nullable!RSAKeyInfo _rsaKey;
161 }