1 /** 2 * NNTP listener (periodically poll server for new messages). 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.net.nntp.listener; 15 16 import ae.net.nntp.client; 17 18 import std.datetime; 19 20 import ae.sys.timing; 21 import ae.sys.log; 22 23 const POLL_PERIOD = 2.seconds; 24 25 class NntpListener 26 { 27 private: 28 NntpClient client; 29 string server; 30 string lastDate; 31 bool[string] oldMessages; 32 TimerTask pollTimer; 33 bool connected, polling; 34 int queued; 35 36 void reconnect() 37 { 38 assert(!connected); 39 client.connect(server, &onConnect); 40 } 41 42 void schedulePoll() 43 { 44 pollTimer = setTimeout(&poll, POLL_PERIOD); 45 } 46 47 void poll() 48 { 49 pollTimer = null; 50 client.getDate(&onDate); 51 client.getNewNews("*", lastDate[0..8] ~ " " ~ lastDate[8..14] ~ " GMT", &onNewNews); 52 } 53 54 void onConnect() 55 { 56 connected = true; 57 queued = 0; 58 59 if (polling) 60 { 61 if (lastDate) 62 poll(); 63 else 64 client.getDate(&onDate); 65 } 66 } 67 68 void onDisconnect(string reason, DisconnectType type) 69 { 70 connected = false; 71 if (polling) 72 { 73 if (pollTimer && pollTimer.isWaiting()) 74 pollTimer.cancel(); 75 if (type != DisconnectType.requested) 76 setTimeout(&reconnect, 10.seconds); 77 } 78 } 79 80 void onDate(string date) 81 { 82 if (polling) 83 { 84 if (lastDate is null) 85 schedulePoll(); 86 lastDate = date; 87 } 88 } 89 90 void onNewNews(string[] reply) 91 { 92 bool[string] messages; 93 foreach (message; reply[1..$]) 94 messages[message] = true; 95 96 assert(queued == 0); 97 foreach (message, b; messages) 98 if (!(message in oldMessages)) 99 { 100 client.getMessage(message, &onMessage); 101 queued++; 102 } 103 oldMessages = messages; 104 if (queued==0) 105 schedulePoll(); 106 } 107 108 void onMessage(string[] lines, string num, string id) 109 { 110 if (handleMessage) 111 handleMessage(lines, num, id); 112 113 if (polling) 114 { 115 queued--; 116 if (queued==0) 117 schedulePoll(); 118 } 119 } 120 121 public: 122 this(Logger log) 123 { 124 client = new NntpClient(log); 125 client.handleDisconnect = &onDisconnect; 126 } 127 128 void connect(string server) 129 { 130 this.server = server; 131 reconnect(); 132 } 133 134 void disconnect() 135 { 136 client.disconnect(); 137 } 138 139 void startPolling(string lastDate = null) 140 { 141 assert(!polling, "Already polling"); 142 polling = true; 143 this.lastDate = lastDate; 144 if (connected) 145 poll(); 146 } 147 148 void delegate(string[] lines, string num, string id) handleMessage; 149 }