oops! fixed invalid binding name (rare case, never happened before ;-)
[chiroptera.git] / chibackend / net / nntp.d
blob6e3bb24f6366310d2555b6a0d2d41fa50b03a163
1 /* DigitalMars NNTP reader
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.nntp /*is aliced*/;
19 import std.socket;
21 import iv.alice;
22 import iv.cmdcon;
24 import chibackend : DynStr;
25 import chibackend.net.linesocket;
29 usage:
31 receiving
32 =========
34 auto nsk = new SocketNNTP(server);
35 scope(exit) nsk.close();
37 nsk.selectGroup(group);
38 if (nsk.emptyGroup) {
39 conwriteln("[", name, ":", group, "]: no new articles");
40 return;
43 uint stnum = inbox.maxNntpIndex+1;
44 if (stnum == 0) stnum = (nsk.hiwater > 1023 ? nsk.hiwater-1023 : 0);
45 if (stnum > nsk.hiwater) {
46 conwriteln("[", name, ":", group, "]: no new articles");
47 return;
50 conwriteln("[", name, ":", group, "]: ", nsk.hiwater+1-stnum, " (possible) new articles");
52 // download new articles
53 foreach (immutable uint anum; stnum..nsk.hiwater+1) {
54 auto msg = nsk.getArticle(anum);
57 sending
58 ========
59 SocketNNTP nsk;
60 try {
61 nsk = new SocketNNTP(server);
62 } catch (Exception e) {
63 conwriteln("[", name, ":", group, "]: connection error: ", e.msg);
64 return false;
66 scope(exit) nsk.close();
68 try {
69 nsk.selectGroup(group);
70 nsk.doSend("POST");
71 nsk.doSendRaw(art.getTextToSend);
72 auto ln = nsk.readLine;
73 conwriteln(ln); // 340 Ok, recommended message-ID <o7dq4o$mpm$1@digitalmars.com>
74 if (ln.length == 0 || ln[0] != '3') throw new Exception(ln.idup);
75 } catch (Exception e) {
76 conwriteln("[", name, ":", group, "]: sending error: ", e.msg);
77 return false;
82 // ////////////////////////////////////////////////////////////////////////// //
83 public final class SocketNNTP : SocketLine {
84 private:
85 bool canPost;
87 public: //WARNING! DO NOT CHANGE!
88 dynstring group;
89 uint lowater, hiwater;
90 bool emptyGroup = true;
92 public:
93 this (string server, bool debugdump=false) {
94 super(server, plainport:119, tlsport:563, debugdump:debugdump);
95 scope(failure) abort(doshutdown:false);
96 auto hello = readLine();
97 if (hello.length < 4 && hello[0] != '2' || hello[1] != '0' || hello[3] != ' ') throw new Exception("invalid hello");
98 if (hello[2] == '0') canPost = true;
99 else if (hello[2] == '1') canPost = false;
100 else throw new Exception("invalid hello");
103 @property bool postingAllowed () const nothrow @safe @nogc { return canPost; }
105 override void quit () {
106 if (!active) return;
107 doSend("QUIT");
108 auto ln = readLine();
111 void selectGroup (const(char)[] name) {
112 doSendCmdArg("GROUP", name);
113 const(char)[] ln;
114 try {
115 ln = readLine();
116 } catch (Throwable e) {
117 abort(doshutdown:false);
118 throw e;
120 scope(failure) { group.clear(); lowater = hiwater = 0; emptyGroup = true; }
121 auto origln = ln;
122 if (getStrToken(ln) != "211") {
123 conwriteln("*ERROR: NNTP server GROUP reply: ", origln);
124 throw new Exception("invalid GROUP reply");
126 auto cnt = getToken!uint(ln);
127 lowater = getToken!uint(ln);
128 hiwater = getToken!uint(ln);
129 if (cnt == 0 || lowater > hiwater || (lowater == 0 && hiwater == 0)) {
130 lowater = hiwater = 0;
131 emptyGroup = true;
132 } else {
133 emptyGroup = false;
135 group = name;
138 // if `exact` is true, returns line with exact terminators
139 // returns empty string if there's no such article (this is not a error!)
140 // can return truncated message if it is too big
141 DynStr getArticle(bool exact=false) (uint num) {
142 DynStr res;
143 doSendCmdArg("ARTICLE", num);
144 auto ln = readLine();
145 //conwriteln("|", ln, "|");
146 auto rescode = getToken!uint(ln);
147 if (rescode != 220) {
148 if (rescode == 420 || rescode == 423) {
149 //throw new Exception("no such article");
150 return res;
152 close();
153 throw new Exception("ARTICLE failed");
155 if (getToken!uint(ln) != num) { close(); throw new Exception("invalid replied article number"); }
156 uint number = num;
157 //string msgid = getToken(ln).idup;
158 // add index header
159 static if (exact) bool needAddEOL = false;
160 if (number) {
161 res ~= "NNTP-Index: ";
162 res.appendNum(number);
163 static if (exact) needAddEOL = true; else res.append('\n');
164 if (mDebugDump) conwriteln("+", number);
166 // read text
167 bool toobig = false;
168 for (;;) {
169 ln = readLine!exact;
170 static if (exact) {
171 if (needAddEOL && ln.length) {
172 if (ln.length >= 2 && ln[$-2..$] == "\r\n") res.append("\r\n"); else res.append("\n");
173 needAddEOL = false;
176 if (!toobig) {
177 res ~= ln;
178 static if (!exact) res ~= '\n';
180 if (ln.length == 0 || ln[0] != '.') continue;
181 if (ln.length == 1) break;
182 static if (exact) {
183 if (ln.length == 3 && ln == ".\r\n") break;
184 if (ln.length == 2 && ln == ".\n") break;
186 if (!toobig && res.length > 1024*1024*128) {
187 conwriteln("WARNING: mail message too big!");
188 toobig = true;
191 static if (exact) {
192 if (needAddEOL) res.append('\n');
194 return res;