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, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module chibackend
.net
.pop3
/*is aliced*/;
27 import chibackend
: DynStr
;
28 import chibackend
.net
.linesocket
;
36 conwriteln("*** [", name, "]: connecting...");
37 auto pop3 = new SocketPOP3(server);
38 scope(exit) pop3.close();
39 conwriteln("[", name, "]: authenticating...");
40 pop3.auth(user, pass);
41 auto newmsg = pop3.getNewMailCount;
43 conwriteln("[", name, "]: no new messages");
46 conwriteln("[", name, "]: ", newmsg, " new message", (newmsg > 1 ? "s" : ""));
47 foreach (immutable int popidx; 1..newmsg+1) {
48 auto msg = pop3.getMessage(popidx); // full message, with the ending dot
49 //auto msg = pop3.getMessage!true(popidx); // full message, with the ending dot, and exact terminators
51 pop3.deleteMessage(popidx);
57 string from = art.frommail;
58 if (from.length == 0) { conwriteln("SMTP ERROR: no FROM!"); return false; }
60 auto to = extractToMail(art);
61 if (to.length == 0) { conwriteln("SMTP ERROR: no TO!"); return false; }
65 nsk = new SocketSMTP(sendserver);
66 } catch (Exception e) {
67 conwriteln("[", name, "]: connection error: ", e.msg);
70 scope(exit) nsk.close();
73 if (!smtpNoAuth) nsk.auth(email, user, pass);
74 nsk.sendMessage(from, to, art.getTextToSend);
75 } catch (Exception e) {
76 conwriteln("[", name, "]: sending error: ", e.msg);
82 // ////////////////////////////////////////////////////////////////////////// //
83 public final class SocketPOP3
: SocketLine
{
85 this (string server
, bool debugdump
=false) {
86 super(server
, plainport
:110, tlsport
:995, debugdump
:debugdump
);
87 scope(failure
) abort(doshutdown
:false);
88 auto hello
= readLine();
89 if (!isOK(hello
)) throw new Exception("invalid hello (no ok)"); // ("~hello.idup~")");
92 override void quit () {
98 void auth (const(char)[] uname
, const(char)[] upass
) {
99 doSendCmdArg("USER", uname
);
100 auto ln
= readLine();
101 if (!isOK(ln
)) throw new Exception("cannot auth");
102 doSendCmdArg("PASS", upass
);
104 if (!isOK(ln
)) throw new Exception("cannot auth");
107 int getNewMailCount () {
109 auto ln
= readLine();
110 if (!isOK(ln
)) throw new Exception("cannot stat");
112 return getToken
!int(ln
);
115 // returns full unmodified message, with the ending dot line
116 // if `exact` is true, returns line with exact terminators
117 // can return truncated message if it is too big
118 DynStr
getMessage(bool exact
=false) (int idx
, bool useRETR
=true) {
119 if (idx
< 1) throw new Exception("invalid message index");
120 // "RETR" seems to delete messages on some servers so there is a way to use "TOP".
121 // yet the message may contain more lines than we specified in "TOP", therefore
122 // default choice is "RETR". it didn't caused problems so far, but who knows...
124 doSendCmdArg("RETR", idx
);
126 import core
.stdc
.stdio
: snprintf
;
127 char[128] buf
= void;
128 auto len
= snprintf(buf
.ptr
, buf
.length
, "TOP %d 65534", idx
);
129 doSendCmdArg(buf
[0..len
]);
131 auto ln
= readLine();
132 if (!isOK(ln
)) throw new Exception("cannot retr");
136 ln
= readLine
!exact();
139 static if (!exact
) res
~= '\n';
141 if (ln
.length
== 0 || ln
[0] != '.') continue;
142 if (ln
.length
== 1) break;
144 if (ln
.length
== 3 && ln
== ".\r\n") break;
145 if (ln
.length
== 2 && ln
== ".\n") break;
147 if (!toobig
&& res
.length
> 1024*1024*128) {
148 conwriteln("WARNING: mail message too big!");
155 void deleteMessage (int idx
) {
156 if (idx
< 1) throw new Exception("invalid message index");
157 doSendCmdArg("DELE", idx
);
158 auto ln
= readLine();
159 if (!isOK(ln
)) throw new Exception("cannot retr");
163 static bool isOK (const(char)[] s
) {
164 if (s
.length
< 3) return false;
165 if (s
[0] == '+' && (s
[1] == 'O' || s
[1] == 'o') && (s
[2] == 'K' || s
[2] == 'k')) return (s
.length
== 3 || s
[3] <= ' ');
166 if (s
.length
> 8+3) {
167 import iv
.strex
: indexOf
;
168 auto idx
= s
.indexOf("+OK Gpop ready for requests from ");
169 if (idx
>= 0) return true;
176 // ////////////////////////////////////////////////////////////////////////// //
177 public final class SocketSMTP
: SocketLine
{
179 dynstring srv
; // for hello
182 const(char)[] getResponse () {
184 auto ln
= readLine();
185 if (ln
.length
< 4 || ln
[3] <= ' ') return ln
;
190 this (string server
, bool adodump
=true) {
192 if (srv
.length
>= 4 && (srv
[0..4] == "ssl:" || srv
[0..4] == "tls:")) srv
= server
[4..$];
193 super(server
, 25, 465, adodump
);
194 scope(failure
) abort(doshutdown
:false);
195 auto hello
= getResponse();
196 if (hello
.length
< 3 || hello
[0..2] != "22") throw new Exception("invalid hello (not 22)");
197 doSendCmdArg("HELO", srv
);
198 hello
= getResponse();
199 if (hello
.length
< 3 || hello
[0..2] != "25") throw new Exception("invalid hello (not 25)");
202 override void quit () {
205 auto ln
= getResponse();
208 void auth (const(char)[] authority
, const(char)[] user
, const(char)[] pass
) {
209 auto authstr
= base64Encode
!char(authority
~'\0'~user
~'\0'~pass
);
210 doSendCmdArg("AUTH PLAIN", authstr
);
211 auto ln
= getResponse();
212 if (ln
.length
< 3 || ln
[0..2] != "23") throw new Exception("authentication failed");
215 // `dsdata` should be dot-stuffed, and end with proper termination dot line (with CRLF)
216 void sendMessage (const(char)[] from
, const(char)[] to
, const(char)[] dsdata
) {
217 dynstring mf
= "MAIL FROM: <";
221 auto ln
= getResponse();
222 if (ln
.length
< 3 || ln
[0..2] != "25") throw new Exception("sending from failed");
228 if (ln
.length
< 3 || ln
[0..2] != "25") throw new Exception("sending to failed");
231 if (ln
.length
< 3 || ln
[0..2] != "35") throw new Exception("sending data failed");
234 if (ln
.length
< 3 || ln
[0..2] != "25") throw new Exception("sending data failed");