1 /**
2 * DServer
3 *
4 * Represents a server instance.
5 *
6 * Holds a list of DConnections,
7 * configuration parameters and
8 * more.
9 */
10 
11 module dnetd.dserver;
12 
13 import core.thread : Thread;
14 import std.socket : Address, Socket, AddressFamily, SocketType, ProtocolType;
15 import dnetd.dconnection;
16 import dnetd.dchannel;
17 import std..string : cmp;
18 import core.sync.mutex : Mutex;
19 import std.stdio;
20 import std.conv : to;
21 import dnetd.dconfig;
22 
23 public class DServer : Thread
24 {
25 	/* The server's socket to bind, listen and accept connections from */
26 	private Socket serverSocket;
27 
28 	/* Bind address */
29 	private Address sockAddress;
30 
31 
32 	/* Server configuration */
33 	private DConfig config;
34 
35 	/**
36 	* Connection queue
37 	*/
38 	private DConnection[] connectionQueue;
39 	private Mutex connectionLock;
40 
41 	/**
42 	* Channels
43 	*/
44 	private DChannel[] channels;
45 	private Mutex channelLock;
46 	
47 	/* TODO: Implement new constructor */
48 	this(DConfig config)
49 	{
50 		/* Set the function to be called on thread start */
51 		super(&dequeueLoop);
52 
53 		/* Set the server's config */
54 		this.config = config;
55 	
56 		/* Set the listening address */
57 		this.sockAddress = config.getGeneral().getAddress();
58 
59 		/* Initialize the server */
60 		init();
61 
62 		/* Start the server */
63 		startServer();
64 	}
65 
66 	public DConfig getConfig()
67 	{
68 		return config;
69 	}
70 
71 	private void init()
72 	{
73 		/* Setup socket */
74 		initNetwork();
75 
76 		/* Setup queues */
77 		initQueues();
78 
79 		/* Setup locks */
80 		initLocks();
81 	}
82 
83 	/**
84 	* Creates the socket, binds it
85 	* to the given address
86 	*/
87 	private void initNetwork()
88 	{
89 		/* Create the socket */
90 		serverSocket = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
91 
92 		/* Bind the socket to the given address */
93 		serverSocket.bind(sockAddress);
94 	}
95 
96 	/**
97 	* Creates all needed queues
98 	* and their mutexes
99 	*/
100 	private void initQueues()
101 	{
102 		/* TODO: Implement me */
103 	}
104 
105 	private void initLocks()
106 	{
107 		/* Initialize the connection lock */
108 		connectionLock = new Mutex();
109 
110 		/* Initialize the channel lock */
111 		channelLock = new Mutex();
112 	}
113 	
114 	private void startServer()
115 	{
116 		/* Start the connection dequeue thread */
117 		start();
118 	}
119 
120 	private void dequeueLoop()
121 	{
122 		/* Start accepting-and-enqueuing connections */
123 		serverSocket.listen(0); /* TODO: Linux be lile, hehahahhahahah who gives one - I give zero */
124 		
125 		while(true)
126 		{
127 			/* Dequeue a connection */
128 			Socket socket = serverSocket.accept();
129 
130 			/* Spawn a connection handler */
131 			DConnection connection = new DConnection(this, socket);
132 
133 			/* Add to the connection queue */
134 			addConnection(connection);
135 		}
136 	}
137 
138 	public void addChannel(DConnection causer, DChannel channel)
139 	{
140 		/* Lock the channels list */
141 		// channelLock.lock();
142 
143 		channels ~= channel;
144 
145 		/* TODO: Use causer */
146 
147 		/* Unlock the channels list */
148 		// channelLock.unlock();
149 	}
150 
151 	public void addConnection(DConnection connection)
152 	{
153 		/* Lock the connections list */
154 		connectionLock.lock();
155 
156 		/* Add to the connection queue */
157 		connectionQueue ~= connection;
158 		writeln("Added connection to queue "~to!(string)(connection));
159 
160 		/* Unlock the connections list */
161 		connectionLock.unlock();
162 	}
163 
164 	/* TODO Remove connection */
165 	public void removeConnection(DConnection connection)
166 	{
167 		/* Lock the connections list */
168 		connectionLock.lock();
169 
170 		/* The new connection queue */
171 		DConnection[] connectionQueueNew;
172 
173 		foreach(DConnection currentConnection; connectionQueue)
174 		{
175 			if(!(currentConnection is connection))
176 			{
177 				connectionQueueNew ~= currentConnection;
178 			}
179 		}
180 
181 		/* Set this as the new queue */
182 		connectionQueue = connectionQueueNew;
183 
184 		writeln("Removed connection from queue "~to!(string)(connection));
185 
186 		/* Unlock the connections list */
187 		connectionLock.unlock();
188 	}
189 
190 	/* TODO: neew method */
191 	public DChannel getChannel(DConnection causer, string channelName)
192 	{
193 		DChannel channel = null;
194 		
195 		channelLock.lock();
196 
197 		
198 		foreach(DChannel currentChannel; channels)
199 		{
200 			if(cmp(currentChannel.getName(), channelName) == 0)
201 			{
202 				channel = currentChannel;
203 				break;
204 			}
205 		}
206 
207 		if(channel)
208 		{
209 			
210 		}
211 		else
212 		{
213 			channel = new DChannel(channelName);
214 								
215 								this.addChannel(causer, channel);
216 		}
217 
218 		channelLock.unlock();
219 
220 
221 		return channel;
222 	}
223 
224 
225 	public DChannel getChannelByName(string channelName)
226 	{
227 		/* The channel */
228 		DChannel channel = null;
229 		
230 		/* Lock the channels list */
231 		channelLock.lock();
232 
233 		foreach(DChannel currentChannel; channels)
234 		{
235 			if(cmp(currentChannel.getName(), channelName) == 0)
236 			{
237 				channel = currentChannel;
238 				break;
239 			}
240 		}
241 
242 		/* Unlock the channels list */
243 		channelLock.unlock();
244 
245 		return channel;
246 	}
247 
248 	/**
249 	* Returns the DConnection with the matching
250 	* username, null if not found
251 	*/
252 	public DConnection findUser(string username)
253 	{
254 		/* Get all the current connections */
255 		DConnection[] connections = getConnections();
256 
257 		/* Find the user with the matching user name */
258 		foreach(DConnection connection; connections)
259 		{
260 			/* The connection must be a user (not unspec or server) */
261 			if(connection.getConnectionType() == DConnection.ConnectionType.CLIENT)
262 			{
263 				/* Match the username */
264 				if(cmp(connection.getUsername(), username) == 0)
265 				{
266 					return connection;
267 				}
268 			}
269 		}
270 
271 		return null;
272 	}
273 
274 	public DConnection[] getConnections()
275 	{
276 		/* The current connections list */
277 		DConnection[] currentConnections;
278 		
279 		/* Lock the connections list */
280 		connectionLock.lock();
281 
282 		currentConnections = connectionQueue;
283 
284 		/* Unlock the connections list */
285 		connectionLock.unlock();
286 		
287 		return currentConnections;
288 	}
289 
290 	public bool channelExists(string channelName)
291 	{
292 		/* Whether or not it exists */
293 		bool exists;
294 
295 		/* Get all channels */
296 		DChannel[] currentChannels = getChannels();
297 
298 		foreach(DChannel currentChannel; currentChannels)
299 		{
300 			if(cmp(currentChannel.getName(), channelName) == 0)
301 			{
302 				exists = true;
303 				break;
304 			}
305 		}
306 
307 		return exists;
308 	}
309 
310 	public DChannel[] getChannels()
311 	{
312 		/* The current channels list */
313 		DChannel[] currentChannels;
314 		
315 		/* Lock the channels list */
316 		channelLock.lock();
317 
318 		currentChannels = channels;
319 
320 		/* Unlock the channels list */
321 		channelLock.unlock();
322 		
323 		return currentChannels;
324 	}
325 
326 	public string getServerInfo()
327 	{
328 		/* The server information */
329 		string serverInfo;
330 
331 		/* TODO: Fetch serverName */
332 		/* TODO: Fetch networkName */
333 		/* TODO: Fetch userCount */
334 		/* TODO: Fetch channelCount */
335 
336 
337 		return serverInfo;
338 	}
339 }