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 // mail/nntp receiver thread
18 module receiver
is aliced
;
21 import std
.concurrency
;
26 import iv
.timer
: DurTimer
= Timer
;
33 import chibackend
.net
;
38 // ////////////////////////////////////////////////////////////////////////// //
39 __gshared
bool rcDisabled
= false;
40 __gshared
bool rcStarted
= false;
41 __gshared Tid controlThreadId
;
48 struct ControlCommand
{
58 // for CheckDone or CheckError
62 this (Kind atype
) nothrow @safe @nogc { type
= atype
; accid
= 0; }
63 this (Kind atype
, uint aid
) nothrow @safe @nogc { type
= atype
; accid
= aid
; }
72 static stmtAccInfo
= LazyStatement
!"Conf"(`
75 , checktime AS checktime
76 , nosendauth AS nosendauth
77 , debuglog AS debuglog
78 , nntplastindex AS nntplastindex
80 , recvserver AS recvserver
81 , sendserver AS sendserver
85 , nntpgroup AS nntpgroup
92 static stmtSetCheckTime
= LazyStatement
!"Conf"(`
93 INSERT INTO checktimes(accid,lastcheck) VALUES(:accid,:lastcheck)
95 DO UPDATE SET lastcheck=:lastcheck
99 //==========================================================================
103 //==========================================================================
104 void checkerThread (Tid ownerTid
) {
114 ownerTid
.send(ControlCommand(ControlCommand
.Kind
.CheckError
, accid
));
131 foreach (auto arow
; stmtAccInfo
.st
.bind(":accid", accid
).range
) {
134 int upmins
= arow
.checktime
!int;
135 if (upmins
< 1) upmins
= 1; else if (upmins
> 100000) upmins
= 100000;
137 nosendauth
= (arow
.nosendauth
!int > 0);
138 debuglog
= (arow
.debuglog
!int > 0);
139 nntplastindex
= arow
.nntplastindex
!uint;
140 name
= arow
.name
!SQ3Text
;
141 recvserver
= arow
.recvserver
!SQ3Text
;
142 sendserver
= arow
.sendserver
!SQ3Text
;
143 user
= arow
.user
!SQ3Text
;
144 pass
= arow
.pass
!SQ3Text
;
145 inbox
= arow
.inbox
!SQ3Text
;
146 nntpgroup
= arow
.nntpgroup
!SQ3Text
;
150 ownerTid
.send(ControlCommand(ControlCommand
.Kind
.CheckError
, accid
));
154 conwriteln("checking account '", name
, "' (", accid
, ")...");
156 stmtSetCheckTime
.st
.bind(":accid", accid
).bind(":lastcheck", RunningAverageExp
.GetTickCount()+checktime
*60).doAll();
160 conwriteln("done checking account '", name
, "' (", accid
, ")...");
162 if (vbwin
&& !vbwin
.closed
) {
163 vbwin
.postEvent(new UpdatingAccountCompleteEvent(accid
));
166 } catch (Throwable e
) {
167 // here, we are dead and fucked (the exact order doesn't matter)
168 //import core.stdc.stdlib : abort;
169 import core
.stdc
.stdio
: fprintf
, stderr
;
170 //import core.memory : GC;
171 import core
.thread
: thread_suspendAll
;
172 //GC.disable(); // yeah
173 //thread_suspendAll(); // stop right here, you criminal scum!
174 auto s
= e
.toString();
175 fprintf(stderr
, "\n=== FATAL ===\n%.*s\n", cast(uint)s
.length
, s
.ptr
);
176 //abort(); // die, you bitch!
177 ownerTid
.send(ControlCommand(ControlCommand
.Kind
.CheckError
, accid
));
180 //if (vbwin) vbwin.postEvent(evDoConCommands); }
181 ownerTid
.send(ControlCommand(ControlCommand
.Kind
.CheckDone
, accid
));
186 //==========================================================================
190 //==========================================================================
191 void controlThread (Tid ownerTid
) {
195 static struct AccCheck
{
201 AccCheck
[] accidCheckList
;
202 accidCheckList
.reserve(128);
205 CREATE TEMP TABLE IF NOT EXISTS checktimes (
206 accid INTEGER PRIMARY KEY UNIQUE /* unique, never zero */
207 , lastcheck INTEGER NOT NULL DEFAULT 0
208 , checking INTEGER NOT NULL DEFAULT 0
212 static stmtAllAccs
= LazyStatement
!"Conf"(`
215 , checktime AS checktime
217 WHERE nocheck=0 AND inbox<>''
221 static stmtGetCheckTime
= LazyStatement
!"Conf"(`
222 SELECT lastcheck AS lastcheck FROM checktimes WHERE accid=:accid LIMIT 1
226 MonoTime lastCollect
= MonoTime
.currTime
;
227 accidCheckList
~= AccCheck();
230 if (doQuit
&& accidCheckList
.length
== 0) break;
231 bool forceAll
= false;
232 receiveTimeout((doQuit ?
50.msecs
: accidCheckList
.length ?
1.seconds
: 60.seconds
),
233 (ControlCommand cmd
) {
234 final switch (cmd
.type
) {
235 case ControlCommand
.Kind
.ForceUpdateAll
: forceAll
= true; break;
236 case ControlCommand
.Kind
.Ping
: break;
237 case ControlCommand
.Kind
.Quit
: doQuit
= true; break;
238 case ControlCommand
.Kind
.CheckDone
:
239 case ControlCommand
.Kind
.CheckError
:
240 if (accidCheckList
.length
) {
241 foreach (immutable idx
, const ref AccCheck nfo
; accidCheckList
) {
242 if (nfo
.accid
== cmd
.accid
) {
243 //if (!doQuit && vbwin && !vbwin.closed) vbwin.postEvent(new UpdatingAccountCompleteEvent(nfo.accid));
244 foreach (immutable c
; idx
+1..accidCheckList
.length
) accidCheckList
[c
-1] = accidCheckList
[c
];
245 accidCheckList
.length
-= 1;
249 if (!doQuit
&& vbwin
&& !vbwin
.closed
&& accidCheckList
.length
== 0) vbwin
.postEvent(new UpdatingCompleteEvent());
256 for (usize idx
= 0; idx
< accidCheckList
.length
; ) {
257 if (accidCheckList
[idx
].accid
!= 0) {
260 foreach (immutable c
; idx
+1..accidCheckList
.length
) accidCheckList
[c
-1] = accidCheckList
[c
];
261 accidCheckList
.length
-= 1;
266 for (usize idx
= 0; idx
< accidCheckList
.length
; ) {
267 if (accidCheckList
[idx
].inprogress
) {
270 foreach (immutable c
; idx
+1..accidCheckList
.length
) accidCheckList
[c
-1] = accidCheckList
[c
];
271 accidCheckList
.length
-= 1;
278 ulong ctt
= RunningAverageExp
.GetTickCount();
279 foreach (auto arow
; stmtAllAccs
.st
.range
) {
281 foreach (const ref AccCheck nfo
; accidCheckList
) if (nfo
.accid
== arow
.accid
!uint) { found
= true; break; }
285 accidCheckList
~= AccCheck(arow
.accid
!uint);
289 int upmins
= arow
.checktime
!int;
290 if (upmins
< 1) upmins
= 1; else if (upmins
> 100000) upmins
= 100000;
292 foreach (auto crow
; stmtGetCheckTime
.st
.bind(":accid", arow
.accid
!uint).range
) lastcheck
= crow
.lastcheck
!ulong;
293 lastcheck
+= upmins
*60; // next check time
294 if (lastcheck
< ctt
) {
296 accidCheckList
~= AccCheck(arow
.accid
!uint);
300 conwriteln("check for accid ", arow.accid!uint, " in ", (lastcheck-ctt)/60, " minutes...");
307 foreach (ref AccCheck nfo
; accidCheckList
) {
308 if (nfo
.inprogress
) break;
309 if (vbwin
) vbwin
.postEvent(new UpdatingAccountEvent(nfo
.accid
));
310 nfo
.tid
= spawn(&checkerThread
, thisTid
);
311 nfo
.inprogress
= true;
312 nfo
.tid
.send(CheckCommand(nfo
.accid
));
317 immutable ctt
= MonoTime
.currTime
;
318 if ((ctt
-lastCollect
).total
!"minutes" >= 5) {
319 import core
.memory
: GC
;
326 ownerTid
.send(ControlReply
.Quit
);
327 } catch (Throwable e
) {
328 // here, we are dead and fucked (the exact order doesn't matter)
329 import core
.stdc
.stdlib
: abort
;
330 import core
.stdc
.stdio
: fprintf
, stderr
;
331 import core
.memory
: GC
;
332 import core
.thread
: thread_suspendAll
;
333 GC
.disable(); // yeah
334 thread_suspendAll(); // stop right here, you criminal scum!
335 auto s
= e
.toString();
336 fprintf(stderr
, "\n=== FATAL ===\n%.*s\n", cast(uint)s
.length
, s
.ptr
);
337 abort(); // die, you bitch!
342 //==========================================================================
346 //==========================================================================
347 public void receiverDisable () {
352 //==========================================================================
354 // receiverForceUpdateAll
356 //==========================================================================
357 public void receiverForceUpdateAll () {
358 if (!rcStarted
) return;
359 controlThreadId
.send(ControlCommand(ControlCommand
.Kind
.ForceUpdateAll
));
363 //==========================================================================
367 //==========================================================================
368 public void receiverInit () {
369 if (rcStarted
) return;
370 if (rcDisabled
) return;
371 controlThreadId
= spawn(&controlThread
, thisTid
);
376 //==========================================================================
380 //==========================================================================
381 public void receiverDeinit () {
382 if (!rcStarted
) return;
383 controlThreadId
.send(ControlCommand(ControlCommand
.Kind
.Quit
));
387 (ControlReply reply
) {
388 if (reply
== ControlReply
.Quit
) done
= true;