root/trunk/nntp/nntp.d

Revision 34, 6.0 kB (checked in by BCS, 1 year ago)

changed e-mail address in my stuff

Line 
1 /*
2 This program is distributed without any warranty of any kind!
3
4 Author: Benjamin Shropshire (shro8822drop_this at vandals dot uidaho edu)
5
6 Please give credit where credit is due. Please don't remove this notice.
7
8 */
9
10 import std.conv;
11 import std.socket;
12 import std.socketstream;
13 import std.stream;
14 import std.date;
15 import inter;
16 debug import std.stdio;
17
18 const uint NNTP_SOCKET = 119;
19 //const uint NNTP_SOCKET = 2049;
20
21 T max(T)(T[] args)
22 {
23     T ret = T.min;
24
25     foreach(t;args)
26         if(t>ret)ret=t;
27    
28     return ret;
29 }
30
31 bool Prefix(char[] sample, char[] test)
32 {
33     return (sample.length >= test.length) && (sample[0..test.length] == test);
34 }
35
36 class WebNNTP : NNTP
37 {
38     struct Post{char[] subj; char[] date; char[] auth; char[] bod; bool MIME;}
39
40     Post[uint] posts;
41
42     char[] server;
43     char[] group;
44     uint last;
45
46     void Add(uint i, char[] s, char[] d, char[] a, char[] b)
47     {
48         debug writef("Trace@"__FILE__":",__LINE__,\n);
49         if(i in posts) return;
50
51         Post t;
52         with(t)
53         {
54             subj = s.dup;
55             date = d.dup;
56             auth = a.dup;
57             bod = b.dup;
58         }
59         if(i > last) last = i;
60         posts[i] = t;
61     }
62
63     void Add(uint i)
64     {
65         debug writef("Trace@"__FILE__":",__LINE__,\n);
66
67         CleanOld(100);
68
69         if(i in posts) return;
70
71
72         auto outgoing = new TcpSocket(AddressFamily.INET);
73         outgoing.connect(new InternetAddress(server, NNTP_SOCKET));
74         scope(exit) outgoing.close;
75         auto outs = new SocketStream(outgoing);
76         scope(exit) outs.close;
77
78         char[] res;
79
80         res = outs.readLine;
81         debug writef("Trace@"__FILE__":",__LINE__,"(ret=%s)\n", res);
82         if((res.length<3) || ('2'!=res[0])) throw new Error("Failed Introduction");
83
84         outs.writeString("group "~group~\r\n);
85
86         res = outs.readLine;
87         debug writef("Trace@"__FILE__":",__LINE__,"(ret=%s)\n", res);
88         if((res.length<3) || ('2'!=res[0])) throw new Error("No Group");
89
90         Post tmp;
91         PullHead(tmp, outs, i);
92         PullBody(tmp, outs, i);
93         posts[i] = tmp;
94
95     }
96
97         // discard posts more than 100 back
98     void CleanOld(int c)
99     {
100         int[] drop;
101         foreach(i;posts.keys)
102             if(i<last-c)
103                 drop~=i;
104         foreach(i;drop)
105             posts.remove(i);
106     }
107
108
109     private void PullHead(inout Post tmp, Stream outs, int i)
110     {
111         debug writef("Trace@"__FILE__":",__LINE__,\n);
112         if(i>getLast) throw new Error("No message");
113
114         outs.writeString("head "~std.string.toString(i)~\r\n);
115
116         char[] res;
117         res = outs.readLine;
118         debug writef("Trace@"__FILE__":",__LINE__,"(ret=%s)\n", res);
119         if((res.length<3) || ('2'!=res[0])) throw new Error("No Post");
120
121         tmp.MIME = false;
122
123         while(!outs.eof)
124         {
125             debug writef("Trace@"__FILE__":",__LINE__,\n);
126             res = outs.readLine;
127             debug writef("Trace@"__FILE__":",__LINE__,\n);
128
129             if(0<res.length && '.' == res[0] && (1==res.length || '.'!=res[1])) break;
130                 if(Prefix(res, "From:"))    tmp.auth = res["From:".length..$].dup;
131             else    if(Prefix(res, "Date:"))    tmp.date = res["Date:".length..$].dup;
132             else    if(Prefix(res, "Subject:")) tmp.subj = res["Subject:".length..$].dup;
133             else    if(Prefix(res, "Content-Type:"))tmp.MIME = !Prefix(res, "Content-Type: text/plain");
134         }
135     }
136
137     private void PullBody(inout Post tmp, Stream outs, int i)
138     {
139         debug writef("Trace@"__FILE__":",__LINE__,\n);
140         if(i>last) throw new Error("No message");
141
142         char[][] bd;
143         int l;
144
145         outs.writeString("body "~std.string.toString(i)~\r\n);
146
147         char[] res;
148         res = outs.readLine;
149         debug writef("Trace@"__FILE__":",__LINE__,"(ret=%s)\n", res);
150         if((res.length<3) || ('2'!=res[0])) throw new Error("No Post");
151
152         while(true)
153         {
154             res = outs.readLine;
155
156             if(0<res.length && '.' == res[0]) break;
157             res ~= \r;
158             bd ~= res;
159             l+= res.length;
160         }
161
162         tmp.bod.length = l;
163
164         l=0;
165         foreach(j,s;bd)
166         {
167             tmp.bod[l..l+s.length] = s;
168             l+=s.length;
169         }
170     }
171
172    
173     this(char[] s, char[] g)
174     {
175         debug writef("Trace@"__FILE__":",__LINE__,"(%s)\n",s);
176         server = s.dup;
177         group = g.dup;
178     }
179
180     char[] getSubject(uint i){Add(i); return posts[i].subj;}
181     char[] getDate(uint i)  {Add(i); return posts[i].date;}
182     char[] getAuthor(uint i){Add(i); return posts[i].auth;}
183     char[] getBody(uint i)  {Add(i); return posts[i].bod;}
184
185     uint getLast()     
186     {
187
188         debug writef("Trace@"__FILE__":",__LINE__,\n);
189
190         auto outgoing = new TcpSocket(AddressFamily.INET);
191         outgoing.connect(new InternetAddress(server, NNTP_SOCKET));
192         debug writef("Trace@"__FILE__":",__LINE__,": connected\n");
193         auto outs = new SocketStream(outgoing);
194         scope(exit) outs.close;
195
196         char[] res;
197         res = outs.readLine;
198         if((0==res.length) || ('2'!=res[0])) throw new Error("Into falure");
199
200         outs.writeString("group "~group~\r\n);
201
202         res = outs.readLine;
203         if((7>=res.length) || ('2'!=res[0])) throw new Error("No Group");
204
205         debug writef("Trace@"__FILE__":",__LINE__,\n);
206
207
208         int at = 4;
209         int start;
210
211         while(at<res.length && ' '==res[at]) at++;  // clear " *"
212         while(at<res.length && ' '!=res[at]) at++;  // clear "[^ ]*"
213         while(at<res.length && ' '==res[at]) at++;  // clear " *"
214         while(at<res.length && ' '!=res[at]) at++;  // clear "[^ ]*"
215         while(at<res.length && ' '==res[at]) at++;  // clear " *"
216         start = at;
217         while(at<res.length && ' '!=res[at]) at++;  // find "[^ ]*"
218
219         last = toInt(res[start..at]);
220
221         CleanOld(100);
222
223         return last;
224     }
225
226     uint getPrev(uint i)
227     {
228         debug writef("Trace@"__FILE__":",__LINE__,\n);
229         return i>0?i-1:0;
230     }
231 }
232
233
234
235
236
237
238 void Populate(WebNNTP from)
239 {
240     auto now = getUTCtime();
241
242     for(int i=1; i<1000; i++)
243     {
244         switch(i%3)
245         {
246             case 0: from.Add(i,"foo",std.date.toString(now-=10),"you ","Boo\r\nBang\r\nSplat"); break;
247             case 1: from.Add(i,"fub",std.date.toString(now-=10),"them","Bwah\r\nha!\r\nha!!!"); break;
248             case 2: from.Add(i,"fig",std.date.toString(now-=10),"me  ","blah\r\nblah\r\nblah"); break;
249             default: assert(0);
250         }
251     }
252
253 }
254
255
256 class GroupDB : GroupGetter
257 {
258     private ushort[char[]] groups;
259
260     void add(ushort p, char[] n)
261     {
262         groups[n] = p;
263     }
264
265     void remove(char[] n)
266     {
267         groups.remove(n);
268     }
269
270     ushort get(char[] n)
271     {
272         ushort* ret = n in groups;
273         if(null is ret) throw new Error("Unknown NewsGroup");
274
275         return *ret;
276     }
277
278 }
Note: See TracBrowser for help on using the browser.