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 import dnetd.dlink; 23 import dnetd.dlistener; 24 import gogga; 25 26 public class DServer : Thread 27 { 28 /* Server configuration */ 29 private DConfig config; 30 31 /** 32 * Connection queue 33 */ 34 private DConnection[] connectionQueue; 35 private Mutex connectionLock; 36 37 /** 38 * Channels 39 */ 40 private DChannel[] channels; 41 private Mutex channelLock; 42 43 /** 44 * Meyer linking subsystem 45 */ 46 private DMeyer meyerSS; 47 48 /** 49 * The listeners attached to this server 50 */ 51 private DListener[] listeners; 52 53 /* TODO: Implement new constructor */ 54 this(DConfig config) 55 { 56 /* Set the function to be called on thread start */ 57 super(&startListeners); 58 59 /* Set the server's config */ 60 this.config = config; 61 62 /* Construct the listeners */ 63 initListeners(config.getGeneral().getAddresses()); 64 65 /* Initialize the server */ 66 init(); 67 68 /* Start the server */ 69 startServer(); 70 } 71 72 /** 73 * Given an array of Address(es) this will construct all 74 * the corresponding listsners (DListener) and append them 75 * to the array 76 */ 77 private void initListeners(Address[] listenAddresses) 78 { 79 gprintln("Constructing "~to!(string)(listenAddresses.length)~" listsners..."); 80 81 foreach(Address listenAddress; listenAddresses) 82 { 83 gprintln("Constructing listener for address '"~to!(string)(listenAddress)~"'"); 84 85 import std.socket : AddressInfo; 86 AddressInfo addrInfo; 87 88 /* Set the address (and port) to the current one along with address family */ 89 addrInfo.address = listenAddress; 90 addrInfo.family = listenAddress.addressFamily; 91 92 /* Set standard (it will always be TCP and in stream access mode) */ 93 addrInfo.protocol = ProtocolType.TCP; 94 addrInfo.type = SocketType.STREAM; 95 96 /* Construct the listener */ 97 listeners ~= new DListener(this, addrInfo); 98 gprintln("Listener for '"~to!(string)(listenAddress)~"' constructed"); 99 } 100 101 gprintln("Listener construction complete."); 102 } 103 104 /** 105 * Starts all the listeners 106 */ 107 private void startListeners() 108 { 109 foreach(DListener listener; listeners) 110 { 111 /* Start the listener */ 112 gprintln("Starting listener "~to!(string)(listener)~"..."); 113 listener.start(); 114 } 115 } 116 117 public DConfig getConfig() 118 { 119 return config; 120 } 121 122 private void init() 123 { 124 /* Setup queues */ 125 initQueues(); 126 127 /* Setup locks */ 128 initLocks(); 129 } 130 131 /** 132 * Creates all needed queues 133 * and their mutexes 134 */ 135 private void initQueues() 136 { 137 /* TODO: Implement me */ 138 } 139 140 private void initLocks() 141 { 142 /* Initialize the connection lock */ 143 connectionLock = new Mutex(); 144 145 /* Initialize the channel lock */ 146 channelLock = new Mutex(); 147 } 148 149 public DMeyer getMeyer() 150 { 151 return meyerSS; 152 } 153 154 private void startServer() 155 { 156 /* Initialize the Meyer linking sub-system */ 157 meyerSS = new DMeyer(this); 158 159 /* Start the listener starter */ 160 start(); 161 } 162 163 public void addChannel(DConnection causer, DChannel channel) 164 { 165 /* Lock the channels list */ 166 // channelLock.lock(); 167 168 channels ~= channel; 169 170 /* TODO: Use causer */ 171 172 /* Unlock the channels list */ 173 // channelLock.unlock(); 174 } 175 176 public void addConnection(DConnection connection) 177 { 178 /* Lock the connections list */ 179 connectionLock.lock(); 180 181 /* Add to the connection queue */ 182 connectionQueue ~= connection; 183 gprintln("Added connection to queue "~to!(string)(connection)); 184 185 /* Unlock the connections list */ 186 connectionLock.unlock(); 187 } 188 189 /* TODO Remove connection */ 190 public void removeConnection(DConnection connection) 191 { 192 /* Lock the connections list */ 193 connectionLock.lock(); 194 195 /* The new connection queue */ 196 DConnection[] connectionQueueNew; 197 198 foreach(DConnection currentConnection; connectionQueue) 199 { 200 if(!(currentConnection is connection)) 201 { 202 connectionQueueNew ~= currentConnection; 203 } 204 } 205 206 /* Set this as the new queue */ 207 connectionQueue = connectionQueueNew; 208 209 gprintln("Removed connection from queue "~to!(string)(connection)); 210 211 /* Unlock the connections list */ 212 connectionLock.unlock(); 213 } 214 215 /* TODO: neew method */ 216 public DChannel getChannel(DConnection causer, string channelName) 217 { 218 DChannel channel = null; 219 220 channelLock.lock(); 221 222 223 foreach(DChannel currentChannel; channels) 224 { 225 if(cmp(currentChannel.getName(), channelName) == 0) 226 { 227 channel = currentChannel; 228 break; 229 } 230 } 231 232 if(channel) 233 { 234 235 } 236 else 237 { 238 channel = new DChannel(channelName); 239 240 this.addChannel(causer, channel); 241 } 242 243 channelLock.unlock(); 244 245 246 return channel; 247 } 248 249 250 public DChannel getChannelByName(string channelName) 251 { 252 /* The channel */ 253 DChannel channel = null; 254 255 /* Lock the channels list */ 256 channelLock.lock(); 257 258 foreach(DChannel currentChannel; channels) 259 { 260 if(cmp(currentChannel.getName(), channelName) == 0) 261 { 262 channel = currentChannel; 263 break; 264 } 265 } 266 267 /* Unlock the channels list */ 268 channelLock.unlock(); 269 270 return channel; 271 } 272 273 /** 274 * Returns the DConnection with the matching 275 * username, null if not found 276 */ 277 public DConnection findUser(string username) 278 { 279 /* The found Connection */ 280 DConnection foundConnection; 281 282 /* Lock the connections list */ 283 connectionLock.lock(); 284 285 /* Find the user with the matching user name */ 286 foreach(DConnection connection; connectionQueue) 287 { 288 /* The connection must be a user (not unspec or server) */ 289 if(connection.getConnectionType() == DConnection.ConnectionType.CLIENT) 290 { 291 /* Match the username */ 292 if(cmp(connection.getUsername(), username) == 0) 293 { 294 foundConnection = connection; 295 } 296 } 297 } 298 299 /* Unlock the connections list */ 300 connectionLock.unlock(); 301 302 return foundConnection; 303 } 304 305 public bool channelExists(string channelName) 306 { 307 /* Whether or not it exists */ 308 bool exists; 309 310 /* Get all channels */ 311 DChannel[] currentChannels = getChannels(); 312 313 foreach(DChannel currentChannel; currentChannels) 314 { 315 if(cmp(currentChannel.getName(), channelName) == 0) 316 { 317 exists = true; 318 break; 319 } 320 } 321 322 return exists; 323 } 324 325 public DChannel[] getChannels() 326 { 327 /* The current channels list */ 328 DChannel[] currentChannels; 329 330 /* Lock the channels list */ 331 channelLock.lock(); 332 333 currentChannels = channels; 334 335 /* Unlock the channels list */ 336 channelLock.unlock(); 337 338 return currentChannels; 339 } 340 341 public string getStatusMessage(string username) 342 { 343 /* Lock the connections list */ 344 connectionLock.lock(); 345 346 /* The matching connection */ 347 DConnection matchedConnection; 348 349 /* Find the connection */ 350 foreach(DConnection connection; connectionQueue) 351 { 352 if(cmp(connection.getUsername(), username) == 0) 353 { 354 matchedConnection = connection; 355 break; 356 } 357 } 358 359 360 /* Unlock the connections list */ 361 connectionLock.unlock(); 362 363 return matchedConnection.getStatusMessage(username); 364 } 365 366 /** 367 * Checks whether the given user has the given 368 * property 369 */ 370 public bool isProperty(string username, string propertyName) 371 { 372 /* Whether or not the user has the given property */ 373 bool status; 374 375 /* Lock the connections list */ 376 connectionLock.lock(); 377 378 /* The matching connection */ 379 DConnection matchedConnection; 380 381 /* Find the connection */ 382 foreach(DConnection connection; connectionQueue) 383 { 384 if(cmp(connection.getUsername(), username) == 0) 385 { 386 matchedConnection = connection; 387 break; 388 } 389 } 390 391 /* Unlock the connections list */ 392 connectionLock.unlock(); 393 394 /* Check for the user's property */ 395 status = matchedConnection.isProperty(propertyName); 396 397 return status; 398 } 399 400 /* TODO: All these functions can really be re-duced, why am I not using getConnection() */ 401 402 /** 403 * Checks whether the given user has the given 404 * property 405 */ 406 public string getProperty(string username, string propertyName) 407 { 408 /* The retrieved property value */ 409 string propertyValue; 410 411 /* Lock the connections list */ 412 connectionLock.lock(); 413 414 /* The matching connection */ 415 DConnection matchedConnection; 416 417 /* Find the connection */ 418 foreach(DConnection connection; connectionQueue) 419 { 420 if(cmp(connection.getUsername(), username) == 0) 421 { 422 matchedConnection = connection; 423 break; 424 } 425 } 426 427 /* Unlock the connections list */ 428 connectionLock.unlock(); 429 430 /* Check for the user's property */ 431 propertyValue = matchedConnection.getProperty(propertyName); 432 433 return propertyValue; 434 } 435 436 /** 437 * Set the property of the given user to the given value 438 */ 439 public void setProperty(string username, string propertyName, string propertyValue) 440 { 441 /* Lock the connections list */ 442 connectionLock.lock(); 443 444 /* The matching connection */ 445 DConnection matchedConnection; 446 447 /* Find the connection */ 448 foreach(DConnection connection; connectionQueue) 449 { 450 if(cmp(connection.getUsername(), username) == 0) 451 { 452 matchedConnection = connection; 453 break; 454 } 455 } 456 457 /* Unlock the connections list */ 458 connectionLock.unlock(); 459 460 /* Set the property's value of the user */ 461 matchedConnection.setProperty(propertyName, propertyValue); 462 } 463 464 465 public string getServerInfo() 466 { 467 /* The server information */ 468 string serverInfo; 469 470 /* TODO: Fetch serverName */ 471 /* TODO: Fetch networkName */ 472 /* TODO: Fetch userCount */ 473 /* TODO: Fetch channelCount */ 474 475 476 return serverInfo; 477 } 478 }