1 /** 2 * dconnection 3 * 4 * Client/server connection handler spawned 5 * by socket connection dequeue loop. 6 * 7 * Handles all interactions between 8 * the server and the specific client/server. 9 */ 10 11 module dnetd.dconnection; 12 13 import core.thread : Thread; 14 import std.socket : Socket; 15 import bmessage; 16 import tristanable.encoding : DataMessage; 17 import core.sync.mutex : Mutex; 18 import dnetd.dserver : DServer; 19 import std.string : split; 20 import dnetd.dchannel : DChannel; 21 import std.conv : to; 22 import std.stdio : writeln; 23 import std.algorithm : reverse; 24 import gogga; 25 26 public class DConnection : Thread 27 { 28 /* The connection type */ 29 public enum ConnectionType 30 { 31 CLIENT, SERVER, UNSPEC 32 } 33 34 /* Command types */ 35 public enum Command 36 { 37 JOIN, 38 PART, 39 AUTH, 40 LINK, 41 REGISTER, 42 LIST, 43 MSG, 44 MEMBER_COUNT, 45 MEMBER_LIST, 46 SERVER_INFO, 47 MOTD, 48 MEMBER_INFO, 49 STATUS, 50 CHAN_PROP, 51 SET_PROP, 52 53 54 /* User property commands */ 55 GET_USER_PROPS, 56 GET_USER_PROP, 57 SET_USER_PROP, 58 DELETE_USER_PROP, 59 IS_USER_PROP, 60 61 62 UNKNOWN 63 } 64 65 /** 66 * Connection information 67 */ 68 private DServer server; 69 private Socket socket; 70 private bool hasAuthed; 71 private ConnectionType connType; 72 private string username; 73 private string currentStatus; 74 75 /** 76 * User property support 77 * 78 * `properties` - the property store 79 * `propertiesMutex` - the mutex 80 */ 81 private string[string] properties; /* TODO: New, replace old status mechanism */ 82 private Mutex propertiesLock; 83 84 /* Write lock for socket */ 85 /* TODO: Forgot how bmessage works, might need, might not, if multipel calls 86 * then yes, if single then no as it is based off (well glibc's write) 87 * thread safe code 88 */ 89 private Mutex writeLock; 90 91 /** 92 * Mutex to provide safe access to the status message 93 */ 94 private Mutex statusMessageLock; 95 96 this(DServer server, Socket socket) 97 { 98 /* Set the function to be called on thread start */ 99 super(&worker); 100 101 /* Set the associated server */ 102 this.server = server; 103 104 /* Set the socket */ 105 this.socket = socket; 106 107 /* Set the default state */ 108 connType = ConnectionType.UNSPEC; 109 110 /* Initialize locks */ 111 initLocks(); 112 113 /* Initialize status (TODO: Remove, we use props now) */ 114 currentStatus = "available,Hey there I'm using DNET!"; 115 116 /* Start the connection handler */ 117 start(); 118 } 119 120 /** 121 * Initializes mutexes 122 */ 123 private void initLocks() 124 { 125 /* Initialize the socket write lock */ 126 writeLock = new Mutex(); 127 128 /* Initialize the status message lock */ 129 statusMessageLock = new Mutex(); 130 131 /* Initialize the properties lock */ 132 propertiesLock = new Mutex(); 133 } 134 135 /** 136 * Byte dequeue loop 137 */ 138 private void worker() 139 { 140 /* Received bytes (for bformat) */ 141 byte[] receivedBytes; 142 143 /* Received message */ 144 DataMessage receivedMessage; 145 146 while(true) 147 { 148 /** 149 * Block to receive a bformat message 150 * 151 * (Does decoding for bformat too) 152 */ 153 bool status = receiveMessage(socket, receivedBytes); 154 155 /* TODO: Check status */ 156 if(status) 157 { 158 /* Decode the tristanable message (tagged message) */ 159 receivedMessage = DataMessage.decode(receivedBytes); 160 161 /* Process the message */ 162 process(receivedMessage); 163 } 164 else 165 { 166 /* TODO: Error handling */ 167 gprintln("Error with receive: "~to!(string)(this), DebugType.ERROR); 168 break; 169 } 170 } 171 172 /* Clean up */ 173 cleanUp(); 174 } 175 176 private void cleanUp() 177 { 178 gprintln(to!(string)(this)~" Cleaning up connection..."); 179 180 /* Remove this user from all channels he is in */ 181 DChannel[] channels = server.getChannels(); 182 183 /* Loop through each channel */ 184 foreach(DChannel currentChannel; channels) 185 { 186 /* Check if you are a member of it */ 187 if(currentChannel.isMember(this)) 188 { 189 /* Leave the channel */ 190 currentChannel.leave(this); 191 gprintln(to!(string)(this)~" Leaving '"~currentChannel.getName()~"'..."); 192 } 193 } 194 195 /* Remove this user from the connection queue */ 196 server.removeConnection(this); 197 198 gprintln(to!(string)(this)~" Connection cleaned up"); 199 } 200 201 /* TODO: add mutex for writing with message and funciton for doing so */ 202 203 /** 204 * Write to socket 205 * 206 * Encodes the byte array as a tristanable tagged 207 * message and then encodes that as a bformat 208 * message 209 * 210 * Locks the writeLock mutex, sends it over the 211 * socket to the client/server, and unlocks the 212 * mutex 213 */ 214 public bool writeSocket(long tag, byte[] data) 215 { 216 /* Send status */ 217 bool status; 218 219 /* Create the tagged message */ 220 DataMessage message = new DataMessage(tag, data); 221 222 gprintln("writeSocket: mutex lock"); 223 /* Lock the write mutex */ 224 writeLock.lock(); 225 226 /* Send the message */ 227 gprintln("writeSocket: Data: "~to!(string)(data)~" Tag: "~to!(string)(tag)); 228 status = sendMessage(socket, message.encode()); 229 230 /* Unlock the write mutex */ 231 writeLock.unlock(); 232 233 gprintln("writeSocket: mutex unlock"); 234 235 return status; 236 } 237 238 private Command getCommand(ubyte commandByte) 239 { 240 Command command = Command.UNKNOWN; 241 242 if(commandByte == 0) 243 { 244 command = Command.AUTH; 245 } 246 else if(commandByte == 1) 247 { 248 command = Command.LINK; 249 } 250 else if(commandByte == 2) 251 { 252 command = Command.REGISTER; 253 } 254 else if(commandByte == 3) 255 { 256 command = Command.JOIN; 257 } 258 else if(commandByte == 4) 259 { 260 command = Command.PART; 261 } 262 else if(commandByte == 5) 263 { 264 command = Command.MSG; 265 } 266 else if(commandByte == 6) 267 { 268 command = Command.LIST; 269 } 270 else if(commandByte == 7) 271 { 272 command = Command.MSG; 273 } 274 else if(commandByte == 8) 275 { 276 command = Command.MEMBER_COUNT; 277 } 278 else if(commandByte == 9) 279 { 280 command = Command.MEMBER_LIST; 281 } 282 else if(commandByte == 10) 283 { 284 command = Command.SERVER_INFO; 285 } 286 else if(commandByte == 11) 287 { 288 command = Command.MOTD; 289 } 290 else if(commandByte == 12) 291 { 292 command = Command.MEMBER_INFO; 293 } 294 else if(commandByte == 13) 295 { 296 command = Command.STATUS; 297 } 298 else if(commandByte == 14) 299 { 300 command = Command.CHAN_PROP; 301 } 302 else if(commandByte == 15) 303 { 304 command = Command.GET_USER_PROPS; 305 } 306 else if(commandByte == 16) 307 { 308 command = Command.GET_USER_PROP; 309 } 310 else if(commandByte == 17) 311 { 312 command = Command.SET_USER_PROP; 313 } 314 else if(commandByte == 18) 315 { 316 command = Command.DELETE_USER_PROP; 317 } 318 else if(commandByte == 19) 319 { 320 command = Command.IS_USER_PROP; 321 } 322 323 324 325 326 return command; 327 } 328 329 /** 330 * Process the received message 331 */ 332 private void process(DataMessage message) 333 { 334 /* Get the tag */ 335 /** 336 * TODO: Client side will always do 1, because we don't have 337 * multi-thread job processing, only need this to differentiate 338 * between commands and async notifications 339 */ 340 long tag = message.tag; 341 gprintln("tag: "~ to!(string)(tag)); 342 343 /* The reply */ 344 byte[] reply; 345 346 /* Get the command */ 347 byte commandByte = message.data[0]; 348 Command command = getCommand(commandByte); 349 gprintln(to!(string)(this)~" ~> "~to!(string)(command)); 350 351 /* If `auth` command (requires: unauthed) */ 352 if(command == Command.AUTH && !hasAuthed) 353 { 354 /* Get the length of the username */ 355 ubyte usernameLength = message.data[1]; 356 357 /* Get the username and password */ 358 string username = cast(string)message.data[2..2+cast(uint)usernameLength]; 359 string password = cast(string)message.data[2+cast(uint)usernameLength..message.data.length]; 360 361 /* Authenticate */ 362 bool status = authenticate(username, password); 363 364 /* TODO: What to do on bad authetication? */ 365 366 /* Set the username */ 367 this.username = username; 368 369 /* Set the type of this connection to `client` */ 370 connType = ConnectionType.CLIENT; 371 hasAuthed = true; 372 373 /* Encode the reply */ 374 reply = [status]; 375 } 376 /* If `link` command (requires: unauthed) */ 377 else if(command == Command.LINK && !hasAuthed) 378 { 379 /* TODO: Implement me later */ 380 381 /* Check if this connection is a DLink'd one */ 382 //server.getMeyer().get 383 384 385 /* Set the type of this connection to `server` */ 386 connType = ConnectionType.SERVER; 387 hasAuthed = true; 388 } 389 /* If `register` command (requires: unauthed, client) */ 390 else if(command == Command.REGISTER && !hasAuthed && connType == ConnectionType.CLIENT) 391 { 392 393 } 394 /* If `join` command (requires: authed, client) */ 395 else if(command == Command.JOIN && hasAuthed && connType == ConnectionType.CLIENT) 396 { 397 /* Get the channel names */ 398 string channelList = cast(string)message.data[1..message.data.length]; 399 string[] channels = split(channelList, ","); 400 401 /** 402 * Loop through each channel, check if it 403 * exists, if so join it, else create it 404 * and then join it 405 */ 406 bool isPresentInfo = false; 407 foreach(string channelName; channels) 408 { 409 /** 410 * Finds the channel, if it exists then it returns it, 411 * if it does not exist then it will create it and then 412 * return it 413 */ 414 DChannel channel = server.getChannel(this, channelName); 415 416 /* Join the channel */ 417 isPresentInfo = channel.join(this); 418 } 419 420 /* TODO: Do reply */ 421 /* Encode the reply */ 422 reply = [isPresentInfo]; 423 } 424 /* If `part` command (requires: authed, client) */ 425 else if(command == Command.PART && hasAuthed && connType == ConnectionType.CLIENT) 426 { 427 /* Get the channel names */ 428 string channelList = cast(string)message.data[1..message.data.length]; 429 string[] channels = split(channelList, ","); 430 431 /** 432 * Loop through each channel, check if it 433 * exists, if so leave it 434 */ 435 foreach(string channelName; channels) 436 { 437 /* Attempt to find the channel */ 438 DChannel channel = server.getChannelByName(channelName); 439 440 /* Leave a channel the channel only if it exists */ 441 if(!(channel is null)) 442 { 443 channel.leave(this); 444 } 445 } 446 447 /* TODO: Do reply */ 448 /* Encode the reply */ 449 reply = [true]; 450 } 451 /* If `list` command (requires: authed, client) */ 452 else if(command == Command.LIST && hasAuthed && connType == ConnectionType.CLIENT) 453 { 454 /* Get all channels */ 455 DChannel[] channels = server.getChannels(); 456 457 /* Generate a list of channel names (CSV) */ 458 string channelList; 459 for(ulong i = 0; i < channels.length; i++) 460 { 461 if(i == channels.length-1) 462 { 463 channelList ~= channels[i].getName(); 464 } 465 else 466 { 467 channelList ~= channels[i].getName()~","; 468 } 469 } 470 471 /* TODO: Reply */ 472 /* Encode the reply */ 473 reply = [true]; 474 reply ~= channelList; 475 } 476 /* If `msg` command (requires: authed, client) */ 477 else if(command == Command.MSG && hasAuthed && connType == ConnectionType.CLIENT) 478 { 479 /* Status */ 480 bool status = true; 481 482 /* Get the type of message */ 483 ubyte messageType = message.data[1]; 484 485 /* Get the location length */ 486 ubyte locationLength = message.data[2]; 487 488 /* Get the channel/person name */ 489 string destination = cast(string)message.data[3..3+cast(uint)locationLength]; 490 491 /* Get the message */ 492 string msg = cast(string)message.data[3+cast(uint)locationLength..message.data.length]; 493 494 /* Send status */ 495 bool sendStatus; 496 497 /* If we are sending to a user */ 498 if(messageType == 0) 499 { 500 /* Send the message to the user */ 501 sendStatus = sendUserMessage(destination, msg); 502 } 503 /* If we are sending to a channel */ 504 else if(messageType == 1) 505 { 506 /* The channel wanting to send to */ 507 DChannel channel = server.getChannelByName(destination); 508 509 /* If the channel exists */ 510 if(channel) 511 { 512 /* TODO Implemet me */ 513 sendStatus = channel.sendMessage(this, msg); 514 } 515 /* If the channel does not exist */ 516 else 517 { 518 status = false; 519 } 520 } 521 /* Unknown destination type */ 522 else 523 { 524 status = false; 525 } 526 527 /* TODO: Handling here, should we make the user wait? */ 528 529 /* Encode the reply */ 530 /* TODO: */ 531 reply = [status]; 532 } 533 /* If `membercount` command (requires: authed, client) */ 534 else if(command == Command.MEMBER_COUNT && hasAuthed && connType == ConnectionType.CLIENT) 535 { 536 /* Status */ 537 bool status = true; 538 539 /* Get the channel name */ 540 string channelName = cast(string)message.data[1..message.data.length]; 541 542 /* The memebr count */ 543 long memberCount; 544 545 /* Get the member count */ 546 status = getMemberCount(channelName, memberCount); 547 548 /* Encode the status */ 549 reply = [status]; 550 551 /* If there was no error fetching the member count */ 552 if(status) 553 { 554 /* Data bytes */ 555 byte[] numberBytes; 556 numberBytes.length = 8; 557 558 /* Encode the length (Big Endian) from Little Endian */ 559 numberBytes[0] = *((cast(byte*)&memberCount)+7); 560 numberBytes[1] = *((cast(byte*)&memberCount)+6); 561 numberBytes[2] = *((cast(byte*)&memberCount)+5); 562 numberBytes[3] = *((cast(byte*)&memberCount)+4); 563 numberBytes[4] = *((cast(byte*)&memberCount)+3); 564 numberBytes[5] = *((cast(byte*)&memberCount)+2); 565 numberBytes[6] = *((cast(byte*)&memberCount)+1); 566 numberBytes[7] = *((cast(byte*)&memberCount)+0); 567 568 /* Append the length */ 569 reply ~= numberBytes; 570 } 571 } 572 /* If `memberlist` command (requires: authed, client) */ 573 else if(command == Command.MEMBER_LIST && hasAuthed && connType == ConnectionType.CLIENT) 574 { 575 /* Status */ 576 bool status = true; 577 578 /* Get the channel name */ 579 string channelName = cast(string)message.data[1..message.data.length]; 580 581 /* Get the channel */ 582 DChannel channel = server.getChannelByName(channelName); 583 584 /* Encode the status */ 585 reply ~= [channel !is null]; 586 587 /* If the channel exists */ 588 if(channel) 589 { 590 /* Get the list of members in the channel */ 591 DConnection[] members = channel.getMembers(); 592 593 /* Construct a CSV string of the members */ 594 string memberString; 595 596 for(ulong i = 0; i < members.length; i++) 597 { 598 if(i == members.length-1) 599 { 600 memberString ~= members[i].getUsername(); 601 } 602 else 603 { 604 memberString ~= members[i].getUsername()~","; 605 } 606 } 607 608 /* Encode the string into the reply */ 609 reply ~= cast(byte[])memberString; 610 } 611 /* If the channel does not exist */ 612 else 613 { 614 status = false; 615 } 616 617 618 619 620 621 622 623 624 } 625 /* If `serverinfo` command (requires: authed, !unspec) */ 626 else if(command == Command.SERVER_INFO && hasAuthed && connType != ConnectionType.UNSPEC) 627 { 628 /* Status */ 629 bool status = true; 630 631 /* Get the server info */ 632 string serverInfo = server.getServerInfo(); 633 634 /* Encode the reply */ 635 reply ~= [status]; 636 reply ~= serverInfo; 637 } 638 /* If `motd` command (requires: _nothing_) */ 639 else if(command == Command.MOTD) 640 { 641 /* Status */ 642 bool status = true; 643 644 /* Get the message of the day */ 645 string motd = server.getConfig().getGeneral().getMotd(); 646 647 /* Encode the reply */ 648 reply ~= [status]; 649 reply ~= motd; 650 } 651 /* If `memberinfo` command (requires: authed, client) */ 652 else if(command == Command.MEMBER_INFO && hasAuthed && connType == ConnectionType.CLIENT) 653 { 654 /* Status */ 655 bool status = true; 656 657 /* TODO: Implement me */ 658 string user = cast(string)message.data[1..message.data.length]; 659 660 /* TODO: fetch longontime, serveron, status */ 661 string logontime; 662 string serveron; 663 664 /* Encode the reply */ 665 reply ~= [status]; 666 reply ~= [cast(byte)logontime.length]; 667 reply ~= logontime; 668 reply ~= [cast(byte)serveron.length]; 669 reply ~= serveron; 670 reply ~= server.getStatusMessage(user); 671 } 672 /* If `status` command (requires: authed, client) */ 673 else if(command == Command.STATUS && hasAuthed && connType == ConnectionType.CLIENT) 674 { 675 /* Get the new status line */ 676 string statusMessage = cast(string)message.data[1..message.data.length]; 677 678 /* Set the new status message */ 679 setStatusMessage(statusMessage); 680 681 /* Encode the reply */ 682 reply ~= [true]; 683 } 684 685 /* If `get_user_props` (requires: authed, client) */ 686 else if(command == Command.GET_USER_PROPS && hasAuthed && connType == ConnectionType.CLIENT) 687 { 688 /* Get the username */ 689 string username = cast(string)message.data[1..message.data.length]; 690 691 /* Get the user */ 692 DConnection connection = server.findUser(username); 693 694 /* Get all properties of the user */ 695 string[] propertyKeys = connection.getProperties(); 696 697 /* Encode the status */ 698 reply ~= [true]; 699 700 /* Encode the keys */ 701 for(ulong i = 0; i < propertyKeys.length; i++) 702 { 703 /* The data to add to the reply */ 704 string replyData; 705 706 if(i == propertyKeys.length-1) 707 { 708 replyData = propertyKeys[i]; 709 } 710 else 711 { 712 replyData = propertyKeys[i]~","; 713 } 714 715 /* Encode the `replyData` */ 716 reply ~= replyData; 717 } 718 719 /* Encode the number of keys (TODO: FOr now you cannot have more than 255 keys) */ 720 // reply ~= [cast(byte)propertyKeys.length]; 721 } 722 /* If `get_user_prop` (requires: authed, client) */ 723 else if(command == Command.GET_USER_PROP && hasAuthed && connType == ConnectionType.CLIENT) 724 { 725 /* Get the <user>,<propertyName> */ 726 string[] dataLine = split(cast(string)message.data[1..message.data.length],","); 727 728 /* Get the username */ 729 string username = dataLine[0]; 730 731 /* Get the proerty */ 732 string propertyName = dataLine[1]; 733 734 /* Get the user */ 735 DConnection connection = server.findUser(username); 736 737 /* Determine if it is a valid property */ 738 bool status = connection.isProperty(propertyName); 739 740 /* Encode the status */ 741 reply ~= [status]; 742 743 /* Encode the property value if one exists */ 744 if(status) 745 { 746 /* Get the property value */ 747 string propertyValue = server.getProperty(username, propertyName); 748 749 /* Encode the property value */ 750 reply ~= propertyValue; 751 } 752 } 753 /* If `set_user_prop` (requires: authed, client) */ 754 else if(command == Command.SET_USER_PROP && hasAuthed && connType == ConnectionType.CLIENT) 755 { 756 /* Set the <propertyName>,<propertyValue> */ 757 string[] dataLine = split(cast(string)message.data[1..message.data.length],","); 758 759 /* Get the property */ 760 string propertyName = dataLine[0]; 761 762 /* Get the property value */ 763 string propertyValue = dataLine[1]; 764 765 /* Encode the status */ 766 reply ~= [true]; 767 768 /* Set the property value */ 769 setProperty(propertyName, propertyValue); 770 } 771 /* If `delete_user_prop` (requires: authed, client) */ 772 else if(command == Command.DELETE_USER_PROP && hasAuthed && connType == ConnectionType.CLIENT) 773 { 774 /* Get the property */ 775 string property = cast(string)message.data[1..message.data.length]; 776 777 /* Check if the key exists */ 778 bool keyExists = isProperty(property); 779 780 /* If the property exists */ 781 if(keyExists) 782 { 783 /* Delete the property */ 784 deleteProperty(property); 785 } 786 787 reply ~= [keyExists]; 788 } 789 /* If `is_user_prop` (requires: authed, client) */ 790 else if(command == Command.IS_USER_PROP && hasAuthed && connType == ConnectionType.CLIENT) 791 { 792 /* Get the <user>,<propertyName> */ 793 string[] dataLine = split(cast(string)message.data[1..message.data.length],","); 794 795 /* Get the username */ 796 string username = dataLine[0]; 797 798 /* Get the proerty */ 799 string propertyName = dataLine[1]; 800 801 /* Get the user */ 802 DConnection connection = server.findUser(username); 803 804 /* Determine if it is a valid property */ 805 bool status = connection.isProperty(propertyName); 806 807 /* Encode the status */ 808 reply ~= [true]; 809 reply ~= [status]; 810 } 811 812 813 814 815 816 /* TODO: `CHAN_PROP`, `SET_PROP` */ 817 /* If no matching built-in command was found */ 818 else 819 { 820 /* TODO: Check plugins */ 821 bool isPlugin = false; 822 823 /* A matching plugin was found */ 824 if(isPlugin) 825 { 826 /* TODO: Implement me */ 827 } 828 /* The command was invalid */ 829 else 830 { 831 /* Write error message */ 832 reply = [2]; 833 } 834 } 835 836 /* Write the response */ 837 writeSocket(tag, reply); 838 } 839 840 /** 841 * Authenticate 842 * 843 * Login as a user with the given credentials 844 */ 845 private bool authenticate(string username, string password) 846 { 847 /* TODO: Check username and password */ 848 /* TODO: Multi-client/session support */ 849 850 /* TODO: Implement me */ 851 this.username = username; 852 853 /* TODO (Sessions): Generate a session ID for this connection */ 854 855 856 return true; 857 } 858 859 private uint generateSessionID() 860 { 861 /* TODO: Basically find a number that isn't taken by matching usernames */ 862 return 1; 863 } 864 865 private uint getMySessionID() 866 { 867 /* TODO: */ 868 return 1; 869 } 870 871 /** 872 * Get member count 873 * 874 * Gets the member count of a given channel 875 */ 876 private bool getMemberCount(string channelName, ref long count) 877 { 878 /* Status of operation */ 879 bool status; 880 881 /* The channel */ 882 DChannel channel = server.getChannelByName(channelName); 883 884 /* Check if the channel exists */ 885 if(channel) 886 { 887 /* Get the channel count */ 888 count = channel.getMemberCount(); 889 890 891 status = true; 892 } 893 /* If the channel does not exist */ 894 else 895 { 896 status = false; 897 } 898 899 return status; 900 } 901 902 /** 903 * Send user a message 904 * 905 * Sends the provided user the specified message 906 */ 907 private bool sendUserMessage(string username, string message) 908 { 909 /* Find the user to send to */ 910 DConnection user = server.findUser(username); /*TODO: Ins erver not just use it directly */ 911 912 gprintln("sendUserMessage(): "~to!(string)(user)); 913 914 /* If the user was found */ 915 if(user) 916 { 917 /* The protocol data to send */ 918 byte[] protocolData; 919 920 /* Set the sub-type (ntype=0 / channel/directmessage) */ 921 protocolData ~= [0]; 922 923 /* Set to user message (direct message, sub-sub-type) */ 924 protocolData ~= [1]; 925 926 /* Encode the recipients's length */ 927 protocolData ~= [cast(byte)username.length]; 928 929 /* Encode the username */ 930 protocolData ~= cast(byte[])username; 931 932 /* Encode the sender's length */ 933 protocolData ~= [cast(byte)this.username.length]; 934 935 /* Encode the sender */ 936 protocolData ~= cast(byte[])this.username; 937 938 /* Encode the message */ 939 protocolData ~= cast(byte[])message; 940 941 /* Send the messge */ 942 bool sendStatus = user.writeSocket(0, protocolData); 943 944 return sendStatus; 945 } 946 /* If the user was not found */ 947 else 948 { 949 return false; 950 } 951 } 952 953 public string getUsername() 954 { 955 return username; 956 } 957 958 /** 959 * Set the status message 960 */ 961 private void setStatusMessage(string statusMessage) 962 { 963 /* Lock the status message mutex */ 964 statusMessageLock.lock(); 965 966 /* Set the status message */ 967 currentStatus = statusMessage; 968 969 /* Unlock the statue message mutex */ 970 statusMessageLock.unlock(); 971 } 972 973 /** 974 * Returns the current status message 975 */ 976 public string getStatusMessage(string username) 977 { 978 /* The status message */ 979 string statusMessage; 980 981 gprintln("hfsjkhfjsdkhfdskj"); 982 983 /* Lock the status message mutex */ 984 statusMessageLock.lock(); 985 986 /* Get the status message */ 987 statusMessage = currentStatus; 988 989 /* Unlock the statue message mutex */ 990 statusMessageLock.unlock(); 991 992 return statusMessage; 993 } 994 995 /** 996 * Sets a property for this user 997 */ 998 public void setProperty(string propertyName, string propertyValue) 999 { 1000 /* Lock the properties store */ 1001 propertiesLock.lock(); 1002 1003 /* Set the property's value */ 1004 properties[propertyName] = propertyValue; 1005 1006 /* Unlock the properties store */ 1007 propertiesLock.unlock(); 1008 } 1009 1010 /** 1011 * Determines whether or not the given property 1012 * exists 1013 */ 1014 public bool isProperty(string propertyName) 1015 { 1016 /** 1017 * Whether or not the given property is a property 1018 * of this user 1019 */ 1020 bool status; 1021 1022 /* Lock the properties store */ 1023 propertiesLock.lock(); 1024 1025 /* Check for the property's existence */ 1026 status = cast(bool)(propertyName in properties); 1027 1028 /* Unlock the properties store */ 1029 propertiesLock.unlock(); 1030 1031 return status; 1032 } 1033 1034 /** 1035 * Returns a property 1036 */ 1037 public string getProperty(string propertyName) 1038 { 1039 /* The fetched property */ 1040 string propertyValue; 1041 1042 /* Lock the properties store */ 1043 propertiesLock.lock(); 1044 1045 /* Check for the property's existence */ 1046 propertyValue = properties[propertyName]; 1047 1048 /* Unlock the properties store */ 1049 propertiesLock.unlock(); 1050 1051 /* TODO: Error handling */ 1052 return propertyValue; 1053 } 1054 1055 /** 1056 * Returns a list of proerties 1057 */ 1058 public string[] getProperties() 1059 { 1060 /* The list of keys */ 1061 string[] propertiesString; 1062 1063 /* Lock the properties store */ 1064 propertiesLock.lock(); 1065 1066 /* Check for the property's existence */ 1067 propertiesString = properties.keys(); 1068 1069 /* Unlock the properties store */ 1070 propertiesLock.unlock(); 1071 1072 return propertiesString; 1073 } 1074 1075 /** 1076 * Deletes a given proerty 1077 */ 1078 public void deleteProperty(string propertyName) 1079 { 1080 /* Lock the properties store */ 1081 propertiesLock.lock(); 1082 1083 /* Remove the property */ 1084 properties.remove(propertyName); 1085 1086 /* Unlock the properties store */ 1087 propertiesLock.unlock(); 1088 } 1089 1090 public ConnectionType getConnectionType() 1091 { 1092 return connType; 1093 } 1094 1095 public override string toString() 1096 { 1097 string toStr = "["~to!(string)(connType)~" ("; 1098 toStr ~= socket.remoteAddress.toString(); 1099 1100 1101 toStr ~= ")]: "; 1102 1103 1104 if(connType == ConnectionType.CLIENT) 1105 { 1106 toStr = toStr ~ getUsername(); 1107 } 1108 else 1109 { 1110 /* TODO Implement me */ 1111 } 1112 1113 return toStr; 1114 } 1115 }