1 module mail.utils;
2 
3 import std.algorithm;
4 import std.conv : to;
5 import std.string;
6 import std.datetime;
7 
8 enum months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
9 
10 extern (C) pure nothrow
11 {
12     alias void* iconv_t;
13 
14     iconv_t iconv_open(const char* tocode, const char* fromcode);
15     size_t iconv(iconv_t cd, const char** inbuf, size_t* inbytesleft,
16             char** outbuf, size_t* outbytesleft);
17     int iconv_close(iconv_t cd);
18 }
19 
20 string recode(string f, string t, char[] src)
21 {
22     auto cd = iconv_open(toStringz(t.toUpper), toStringz(f.toUpper));
23     if (cast(size_t)(cd) == -1)
24     {
25         return to!string(src);
26     }
27     char[] dst;
28     dst.length = src.length * 6;
29 
30     auto src_ptr = cast(char*) src.ptr;
31     auto dst_ptr = cast(char*) dst.ptr;
32     size_t src_len = src.length;
33     size_t dst_len = dst.length;
34 
35     auto r = iconv(cd, &src_ptr, &src_len, &dst_ptr, &dst_len);
36     iconv_close(cd);
37 
38     if (r == -1)
39     {
40         return to!string(src);
41     }
42     return dst[0 .. $ - dst_len].to!string;
43 }
44 
45 ubyte[] fromPercentEncoding(ref ubyte[] src, ubyte chr = '%')
46 {
47     ubyte[] dst;
48     if (!src.length)
49     {
50         return dst;
51     }
52     size_t i = 0;
53     size_t len = src.length;
54     int outLen = 0;
55     int a, b;
56     ubyte c;
57     while (i < len)
58     {
59         c = src[i];
60         if (c == chr && i + 2 < len)
61         {
62             a = src[++i];
63             b = src[++i];
64             if (a >= '0' && a <= '9')
65                 a -= '0';
66             else if (a >= 'a' && a <= 'f')
67                 a = a - 'a' + 10;
68             else if (a >= 'A' && a <= 'F')
69                 a = a - 'A' + 10;
70 
71             if (b >= '0' && b <= '9')
72                 b -= '0';
73             else if (b >= 'a' && b <= 'f')
74                 b = b - 'a' + 10;
75             else if (b >= 'A' && b <= 'F')
76                 b = b - 'A' + 10;
77             dst ~= cast(ubyte)((a << 4) | b);
78         }
79         else
80         {
81             dst ~= c;
82         }
83         ++i;
84         ++outLen;
85     }
86     if (outLen != len)
87     {
88         dst.length = outLen;
89     }
90     return dst;
91 }
92 
93 ubyte[] removeAll(ubyte[] src, ubyte[] val)
94 {
95     ubyte[] dst = src;
96     if (!val.length)
97     {
98         return src;
99     }
100     ptrdiff_t i = -1;
101     do
102     {
103         i = dst.countUntil(val);
104         if (i >= 0)
105         {
106             foreach (j; 0 .. val.length)
107             {
108                 dst = dst.remove(i);
109             }
110         }
111     }
112     while (i >= 0);
113     return dst;
114 }
115 
116 ubyte[] removeAll(ubyte[] src, ubyte val)
117 {
118     ubyte[] dst = src;
119     ptrdiff_t i = -1;
120     do
121     {
122         i = dst.countUntil(val);
123         if (i >= 0)
124         {
125             dst = dst.remove(i);
126         }
127     }
128     while (i >= 0);
129     return dst;
130 }
131 
132 SysTime parseDate(in string src, in SysTime fail = Clock.currTime)
133 {
134     import std.ascii : isDigit;
135 
136     DateTime dt;
137     auto tz = new immutable SimpleTimeZone(0.minutes);
138 
139     scope (failure)
140     {
141         return fail;
142     }
143 
144     auto l = src.findSplitAfter(",")[1].strip().toUpper;
145 
146     uint x = l[1].isDigit ? 2 : 1;
147 
148     dt.day = l[0 .. x].to!int;
149 
150     l = l[++x .. $];
151 
152     dt.month  = cast(Month)(months.countUntil(l[0 .. 3]) + 1);
153     dt.year   = l[ 4 .. 8].to!int;
154     dt.hour   = l[ 9 .. 11].to!int;
155     dt.minute = l[12 .. 14].to!int;
156     dt.second = l[15 .. 17].to!int;
157     int z = 0;
158 
159     if (l.length > 18)
160     {
161         auto tmp = l[18 .. $];
162 
163         switch (tmp)
164         {
165         case "UT", "UTC", "GMT":
166             break;
167         default:
168             auto sign = +1;
169             if (tmp[0] == '-')
170             {
171                 sign = -1;
172             }
173 
174             if (tmp[0] == '-' || tmp[0] == '+')
175             {
176                 tmp = tmp[1 .. $];
177             }
178 
179             if (tmp.length == 4)
180             {
181                 auto tzH = tmp[0 .. 2].to!int;
182                 auto tzM = tmp[2 .. 4].to!int;
183 
184                 return SysTime(dt, new immutable SimpleTimeZone(((tzH * 60 + tzM) * sign).minutes));
185             }
186             break;
187         }
188     }
189 
190     return SysTime(dt, tz);
191 }
192 ///
193 unittest
194 {
195     assert(parseDate("8 Feb 2017 15:21:13 +0000").toISOExtString == "2017-02-08T15:21:13+00:00");
196     assert(parseDate("8 Feb 2017 15:21:13 +0530").toISOExtString == "2017-02-08T15:21:13+05:30");
197     assert(parseDate("18 Feb 2017 15:21:13 +0000").toISOExtString == "2017-02-18T15:21:13+00:00");
198     assert(parseDate("18 Feb 2017 15:21:13 +0530").toISOExtString == "2017-02-18T15:21:13+05:30");
199 }