2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module maildb
.net
.linesocket
is aliced
;
26 // ////////////////////////////////////////////////////////////////////////// //
27 public class SocketLine
{
33 bool debugdump
= false;
36 final @property char* rdbuf () nothrow @trusted @nogc { pragma(inline
, true); return (cast(char*)rdbufp
); }
37 final void freeRdBuf () nothrow @trusted @nogc {
39 import core
.stdc
.stdlib
: free
;
40 free(cast(void*)rdbufp
);
47 import core
.stdc
.stdlib
: realloc
;
49 auto nlen
= rdbufsize
+delta
;
50 if (nlen
>= 2*1024*1024) { freeRdBuf(); throw new Exception("line too long"); }
51 auto nb
= cast(char*)realloc(cast(void*)rdbufp
, nlen
);
52 if (nb
is null) { freeRdBuf(); assert(0, "out of memory"); }
53 rdbufp
= cast(usize
)nb
;
58 this (string server
, ushort plainport
, ushort tlsport
, bool adodump
) {
60 if (server
.length
>= 4 && (server
[0..4] == "ssl:" || server
[0..4] == "tls:")) {
61 if (tlsport
== 0) throw new Exception("TLS port is not defined for '"~server
[4..$]~"'");
62 conwriteln("connecting to [", server
[4..$], ":", tlsport
, "]...");
63 auto xsk
= new SSLClientSocket(AddressFamily
.INET
);
64 xsk
.manualHandshake
= false;
65 xsk
.connect(new InternetAddress(server
[4..$], tlsport
));
68 if (plainport
== 0) throw new Exception("PLAIN port is not defined for '"~server
~"'");
69 conwriteln("connecting to [", server
, ":", plainport
, "]...");
70 sk
= new TcpSocket(new InternetAddress(server
, plainport
));
72 scope(failure
) close();
74 sk
.setOption(SocketOptionLevel
.SOCKET
, SocketOption
.SNDTIMEO
, 10.seconds
);
75 sk
.setOption(SocketOptionLevel
.SOCKET
, SocketOption
.RCVTIMEO
, 10.seconds
);
78 ~this () nothrow @trusted @nogc { freeRdBuf(); }
83 if (cast(SSLClientSocket
)sk
) {
86 sk
.shutdown(SocketShutdown
.BOTH
);
89 } catch (Exception e
) {
90 conwriteln("CLOSE ERROR: ", e
.msg
);
98 try { quit(); } catch (Exception e
) {}
99 try { abort(); } catch (Exception e
) {}
104 final @property bool active () { return (sk
!is null && sk
.isAlive
); }
106 final void doSendRaw (const(char)[] data
) {
107 import core
.sys
.posix
.sys
.socket
: MSG_NOSIGNAL
;
109 if (sk
is null ||
!sk
.isAlive
) throw new Exception("can't send to closed socket");
110 if (data
.length
== 0) return;
111 auto dataanchor
= data
; // 'cause we may get data which requires pointer to head
112 while (data
.length
> 1) {
113 auto sd
= sk
.send(data
[0..$-1], cast(SocketFlags
)(MSG_NOSIGNAL|
0x8000/*MSG_MORE*/));
114 if (sd
<= 0) { close(); throw new Exception("sending error"); }
117 while (data
.length
) {
118 auto sd
= sk
.send(data
, cast(SocketFlags
)(MSG_NOSIGNAL
));
119 if (sd
<= 0) { close(); throw new Exception("sending error"); }
124 final void doSend(A
...) (string fmt
, A args
) {
125 import core
.sys
.posix
.sys
.socket
: MSG_NOSIGNAL
;
127 if (sk
is null ||
!sk
.isAlive
) throw new Exception("can't send to closed socket");
128 string s
= fmt
.format(args
)~"\r";
130 if (s
.length
> 4 && s
[0..4] == "PASS") {
131 conwriteln("|>PASS *|");
133 conwriteln("|>", s
[0..$-1], "|");
137 auto sd
= sk
.send(s
, cast(SocketFlags
)(MSG_NOSIGNAL|
0x8000/*MSG_MORE*/));
138 if (sd
<= 0) { close(); throw new Exception("sending error"); }
143 auto sd
= sk
.send(s
, cast(SocketFlags
)(MSG_NOSIGNAL
));
144 if (sd
<= 0) { close(); throw new Exception("sending error"); }
149 //WARNING! result is valid only until next `readLine()` call
150 final const(char)[] readLine () {
151 import core
.stdc
.string
: memmove
;
152 if (sk
is null) return null;
153 // throw away old line
154 //conwriteln("rdbufused=", rdbufused);
156 while (pos
< rdbufused
&& rdbuf
[pos
] != '\n') ++pos
;
157 if (pos
< rdbufused
) {
158 assert(rdbuf
[pos
] == '\n');
162 //conwriteln("removed ", pos, " bytes out of ", rdbufused, " bytes");
163 assert(pos
<= rdbufused
);
164 if (pos
< rdbufused
) memmove(rdbuf
, rdbuf
+pos
, rdbufused
-pos
);
167 // check if we have another line buffered
168 //conwriteln("pos=", pos, "; rdbufused=", rdbufused);
170 while (pos
< rdbufused
&& rdbuf
[pos
] != '\n') ++pos
;
171 if (pos
< rdbufused
) {
172 //conwriteln("GOT: ", pos, " out of ", rdbufused);
173 if (pos
> 0 && rdbuf
[pos
-1] == '\r') --pos
;
174 if (debugdump
) conwriteln("|<", rdbuf
[0..pos
], "|");
175 return rdbuf
[0..pos
];
177 // no buffered line, get more data
179 assert(rdbufused
<= rdbufsize
);
180 if (rdbufused
>= rdbufsize
) growRdBuf();
181 auto rc
= sk
.receive(rdbuf
[rdbufused
..rdbufsize
], SocketFlags
.NONE
);
182 //conwriteln(rc, " bytes read");
183 //if (rc == 0) { return rdbuf[0..rdbufused]; }
184 if (rc
<= 0) throw new Exception("read error");
185 // check for line end
186 uint stpos
= rdbufused
;
188 foreach (immutable idx
; stpos
..stpos
+rc
) {
189 if (rdbuf
[idx
] == '\n') {
190 if (idx
> 0 && rdbuf
[idx
-1] == '\r') {
191 if (debugdump
) conwriteln("|<", rdbuf
[0..idx
-1], "|");
192 return rdbuf
[0..idx
-1];
194 if (debugdump
) conwriteln("|<", rdbuf
[0..idx
], "|");
195 return rdbuf
[0..idx
];
203 const(char)[] getStrToken (ref const(char)[] s
) nothrow @trusted @nogc {
204 while (s
.length
&& s
[0] <= ' ') s
= s
[1..$];
205 if (s
.length
== 0) return null;
207 while (pos
< s
.length
&& s
.ptr
[pos
] > ' ') ++pos
;
208 auto res
= s
[0..pos
];
210 while (s
.length
&& s
[0] <= ' ') s
= s
[1..$];
214 T
getToken(T
) (ref const(char)[] s
) {
215 import std
.conv
: to
;
216 auto tk
= getStrToken(s
);