1 module libdnet.dclient; 2 3 import tristanable.manager : Manager; 4 import tristanable.queue : Queue; 5 import tristanable.encoding : DataMessage; 6 import tristanable.queueitem : QueueItem; 7 import std.socket; 8 import std.stdio; 9 import std.conv : to; 10 import std.string : split; 11 import bmessage : bSendMessage = sendMessage; 12 13 public final class DClient 14 { 15 /** 16 * tristanabale tag manager 17 */ 18 private Manager manager; 19 private Socket socket; 20 21 /* Create a queue for normal traffic (request-reply on tag: 0) */ 22 private Queue reqRepQueue; 23 24 /* Create a queue for notifications (replies-only on tag: 1) */ 25 private Queue notificationQueue; 26 27 /** 28 * Constructs a new DClient and connects 29 * it to the given endpoint Address 30 * 31 * @param address the endpoint (server) to 32 * connect to 33 */ 34 this(Address address) 35 { 36 /* Initialize the socket */ 37 /* TODO: Error handling */ 38 socket = new Socket(address.addressFamily, SocketType.STREAM, ProtocolType.TCP); 39 socket.connect(address); 40 41 /* Initialize tristanable */ 42 initTristanable(socket); 43 } 44 45 /* TODO: Create a new queue actually for each command (this is for performance) - as we will have server spawn workers */ 46 47 private void initTristanable(Socket socket) 48 { 49 /* Initialize the manager */ 50 manager = new Manager(socket); 51 52 /* Create a queue for normal traffic (request-reply on tag: 1) */ 53 reqRepQueue = new Queue(1); 54 55 /* Create a queue for notifications (replies-only on tag: 0) */ 56 notificationQueue = new Queue(0); 57 58 /* Add these queues to the tracker */ 59 manager.addQueue(reqRepQueue); 60 manager.addQueue(notificationQueue); 61 } 62 63 /** 64 * Receives the head of the notification queue 65 */ 66 public byte[] awaitNotification() 67 { 68 /* The received notification */ 69 byte[] notification; 70 71 /* Await the notification */ 72 QueueItem queueItem = notificationQueue.dequeue(); 73 74 /* Grab the notification's data */ 75 notification = queueItem.getData(); 76 77 return notification; 78 } 79 80 /** 81 * Authenticates as a client with the server 82 * 83 * @param username the username to use 84 * @param password the password to use 85 * @returns bool true on successful authentication, 86 * false otherwise 87 */ 88 public bool auth(string username, string password) 89 { 90 /* The protocol data to send */ 91 byte[] data = [0]; 92 data ~= cast(byte)username.length; 93 data ~= username; 94 data ~= password; 95 96 /* Send the protocol data */ 97 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 98 bSendMessage(socket, protocolData.encode()); 99 100 /* Receive the server's response */ 101 byte[] resp = reqRepQueue.dequeue().getData(); 102 103 return cast(bool)resp[0]; 104 } 105 106 107 /** 108 * Joins the given channel 109 * 110 * @param channel the channel to join 111 * @returns bool true if the join was 112 * successful, false otherwise 113 */ 114 public bool join(string channel) 115 { 116 /* TODO: DO oneshot as protocol supports csv channels */ 117 118 /* The protocol data to send */ 119 byte[] data = [3]; 120 data ~= channel; 121 122 /* Send the protocol data */ 123 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 124 bSendMessage(socket, protocolData.encode()); 125 126 /* Receive the server's response */ 127 byte[] resp = reqRepQueue.dequeue().getData(); 128 129 return cast(bool)resp[0]; 130 } 131 132 /** 133 * Set your status 134 * 135 * 136 */ 137 public void setStatus(string status) 138 { 139 /* The protocol data to send */ 140 byte[] data = [13]; 141 data ~= status; 142 143 /* Send the protocol data */ 144 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 145 bSendMessage(socket, protocolData.encode()); 146 147 /* Receive the server's response */ 148 byte[] resp = reqRepQueue.dequeue().getData(); 149 150 // return cast(bool)resp[0]; 151 } 152 153 /** 154 * Get the memberinfo of a given user 155 * 156 * TODO: Return more thna just status 157 */ 158 public string getMemberInfo(string user) 159 { 160 /* The protocol data to send */ 161 byte[] data = [12]; 162 data ~= user; 163 164 /* Send the protocol data */ 165 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 166 bSendMessage(socket, protocolData.encode()); 167 168 /* Receive the server's response */ 169 byte[] resp = reqRepQueue.dequeue().getData(); 170 171 172 string status; 173 174 /* If it worked */ 175 if(cast(bool)resp[0]) 176 { 177 /* The member info */ 178 ubyte len1 = resp[1]; 179 ubyte len2 = resp[1+len1+1]; 180 status = cast(string)resp[1+len1+1+len2+1..resp.length]; 181 } 182 else 183 { 184 /* TODO: Handle error */ 185 } 186 187 return status; 188 } 189 190 191 /** 192 * Lists all properties of the given user 193 */ 194 public string[] getProperties(string user) 195 { 196 /* The protocol data to send */ 197 byte[] data = [15]; 198 data ~= user; 199 200 /* Send the protocol data */ 201 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 202 bSendMessage(socket, protocolData.encode()); 203 204 /* Receive the server's response */ 205 byte[] resp = reqRepQueue.dequeue().getData(); 206 207 /* Received list of properties */ 208 string[] properties; 209 210 /* If it worked */ 211 if(cast(bool)resp[0]) 212 { 213 /* Get the property line */ 214 string propertyLine = cast(string)resp[1..resp.length]; 215 216 properties = split(propertyLine, ","); 217 } 218 219 return properties; 220 } 221 222 /** 223 * Get a property's value 224 */ 225 public string getProperty(string user, string property) 226 { 227 /* The property's value */ 228 string propertyValue; 229 230 /* The protocol data to send */ 231 byte[] data = [16]; 232 data ~= user~","~property; 233 234 /* Send the protocol data */ 235 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 236 bSendMessage(socket, protocolData.encode()); 237 238 /* Receive the server's response */ 239 byte[] resp = reqRepQueue.dequeue().getData(); 240 241 /* If it worked */ 242 if(cast(bool)resp[0]) 243 { 244 /* Get the property line */ 245 propertyValue = cast(string)resp[1..resp.length]; 246 } 247 248 return propertyValue; 249 } 250 251 /** 252 * Check's whether the user has the given property 253 */ 254 public bool isProperty(string user, string property) 255 { 256 /* The property's value */ 257 bool status; 258 259 /* The protocol data to send */ 260 byte[] data = [19]; 261 data ~= user~","~property; 262 263 /* Send the protocol data */ 264 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 265 bSendMessage(socket, protocolData.encode()); 266 267 /* Receive the server's response */ 268 byte[] resp = reqRepQueue.dequeue().getData(); 269 270 /* If it worked */ 271 if(cast(bool)resp[0]) 272 { 273 /* Get the property line */ 274 status = cast(bool)resp[1]; 275 } 276 277 return status; 278 } 279 280 /** 281 * Set's the given property of yourself to the given value 282 */ 283 public void setProperty(string property, string propertyValue) 284 { 285 /* The property's value */ 286 bool status; 287 288 /* The protocol data to send */ 289 byte[] data = [17]; 290 data ~= property~","~propertyValue; 291 292 /* Send the protocol data */ 293 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 294 bSendMessage(socket, protocolData.encode()); 295 296 /* Receive the server's response */ 297 byte[] resp = reqRepQueue.dequeue().getData(); 298 299 /* If it worked */ 300 if(cast(bool)resp[0]) 301 { 302 303 } 304 } 305 306 /** 307 * Delete's the given property of yourself 308 */ 309 public void deleteProperty(string property) 310 { 311 /* The property's value */ 312 bool status; 313 314 /* The protocol data to send */ 315 byte[] data = [18]; 316 data ~= property; 317 318 /* Send the protocol data */ 319 DataMessage protocolData = new DataMessage(reqRepQueue.getTag(), data); 320 bSendMessage(socket, protocolData.encode()); 321 322 /* Receive the server's response */ 323 byte[] resp = reqRepQueue.dequeue().getData(); 324 325 /* If it worked */ 326 if(cast(bool)resp[0]) 327 { 328 329 } 330 } 331 332 333 334 335 /** 336 * Lists all the channels on the server 337 * 338 * @returns string[] the list of channels 339 */ 340 public string[] list() 341 { 342 /* List of channels */ 343 string[] channels; 344 345 /* The protocol data to send */ 346 byte[] data = [6]; 347 348 /* Send the protocol data */ 349 DataMessage protocolDataMsg = new DataMessage(reqRepQueue.getTag(), data); 350 bSendMessage(socket, protocolDataMsg.encode()); 351 352 /* Receive the server's response */ 353 byte[] resp = reqRepQueue.dequeue().getData(); 354 355 /* Only generate a list if command was successful */ 356 if(resp[0]) 357 { 358 /* Generate the channel list */ 359 string channelList = cast(string)resp[1..resp.length]; 360 channels = split(channelList, ","); 361 } 362 363 return channels; 364 } 365 366 public Manager getManager() 367 { 368 return manager; 369 } 370 371 /** 372 * Sends a message to either a channel of user 373 * 374 * @param isUser whether or not we are sending to 375 * a user, true if user, false if channel 376 * @param location the username/channel to send to 377 * @param message the message to send 378 * @returns bool whether the send worked or not 379 */ 380 public bool sendMessage(bool isUser, string location, string message) 381 { 382 /* The protocol data to send */ 383 byte[] protocolData = [5]; 384 385 /** 386 * If we are sending to a user then the 387 * type field is 0, however if to a channel 388 * then it is one 389 * 390 * Here we encode that 391 */ 392 protocolData ~= [!isUser]; 393 394 /* Encode the length of `location` */ 395 protocolData ~= [cast(byte)location.length]; 396 397 /* Encode the user/channel name */ 398 protocolData ~= cast(byte[])location; 399 400 /* Encode the message */ 401 protocolData ~= cast(byte[])message; 402 403 /* Send the protocol data */ 404 DataMessage protocolDataMsg = new DataMessage(reqRepQueue.getTag(), protocolData); 405 bSendMessage(socket, protocolDataMsg.encode()); 406 407 /* Receive the server's response */ 408 byte[] resp = reqRepQueue.dequeue().getData(); 409 410 return cast(bool)resp[0]; 411 } 412 413 /** 414 * Returns the list of members in the 415 * given channel 416 */ 417 public string[] getMembers(string channel) 418 { 419 /* The list of members */ 420 string[] members; 421 422 /* The protocol data to send */ 423 byte[] protocolData = [9]; 424 425 /** 426 * Encode the channel name 427 */ 428 protocolData ~= cast(byte[])channel; 429 430 /* Send the protocol data */ 431 DataMessage protocolDataMsg = new DataMessage(reqRepQueue.getTag(), protocolData); 432 bSendMessage(socket, protocolDataMsg.encode()); 433 434 /* Receive the server's response */ 435 byte[] resp = reqRepQueue.dequeue().getData(); 436 437 /* If the operation completed successfully */ 438 if(resp[0]) 439 { 440 string memberList = cast(string)resp[1..resp.length]; 441 members = split(memberList, ","); 442 } 443 /* If there was an error */ 444 else 445 { 446 /* TODO: Error handling */ 447 } 448 449 return members; 450 } 451 452 /** 453 * Returns the count of members in the 454 * given channel 455 */ 456 public ulong getMemberCount(string channelName) 457 { 458 /* The member count */ 459 long memberCount; 460 461 /* The protocol data to send */ 462 byte[] protocolData = [8]; 463 464 /* Encode the channel name */ 465 protocolData ~= cast(byte[])channelName; 466 467 /* Send the protocol data */ 468 DataMessage protocolDataMsg = new DataMessage(reqRepQueue.getTag(), protocolData); 469 bSendMessage(socket, protocolDataMsg.encode()); 470 471 /* Receive the server's response */ 472 byte[] resp = reqRepQueue.dequeue().getData(); 473 474 /* Check if the operation completed successfully */ 475 if(resp[0]) 476 { 477 /* Length as byte array */ 478 byte[] numberBytes; 479 numberBytes.length = 8; 480 481 /* As Skippy would say, this is jank, but I literay am so lazy now hehe */ 482 memberCount = *cast(long*)resp[1..resp.length].ptr; 483 484 /* Decode the length (Big Endian) to Little Endian */ 485 numberBytes[0] = *((cast(byte*)&memberCount)+7); 486 numberBytes[1] = *((cast(byte*)&memberCount)+6); 487 numberBytes[2] = *((cast(byte*)&memberCount)+5); 488 numberBytes[3] = *((cast(byte*)&memberCount)+4); 489 numberBytes[4] = *((cast(byte*)&memberCount)+3); 490 numberBytes[5] = *((cast(byte*)&memberCount)+2); 491 numberBytes[6] = *((cast(byte*)&memberCount)+1); 492 numberBytes[7] = *((cast(byte*)&memberCount)+0); 493 494 memberCount = *cast(long*)numberBytes.ptr; 495 496 } 497 else 498 { 499 /* TODO: Error handling */ 500 } 501 502 return memberCount; 503 } 504 505 public string getMotd() 506 { 507 /* The message of the day */ 508 string motd; 509 510 /* The protocol data to send */ 511 byte[] protocolData = [11]; 512 513 /* Send the protocol data */ 514 DataMessage protocolDataMsg = new DataMessage(reqRepQueue.getTag(), protocolData); 515 bSendMessage(socket, protocolDataMsg.encode()); 516 517 /* Receive the server's response */ 518 byte[] resp = reqRepQueue.dequeue().getData(); 519 520 /* Check if the operation completed successfully */ 521 if(resp[0]) 522 { 523 /* Set the message of the day */ 524 motd = cast(string)resp[1..resp.length]; 525 } 526 else 527 { 528 /* TODO: Error handling */ 529 } 530 531 return motd; 532 } 533 534 /** 535 * Disconnect from the server 536 * 537 * TODO: This is still a work in progress 538 * due to tristanable's disconnect still 539 * being a work in progress 540 */ 541 public void close() 542 { 543 /* FIXME: I must fix manager for this, the socket stays active and hangs the .join */ 544 /* FIXME (above): due to it being blocking, although I did think I closed the socket */ 545 /* TODO: Not the above, it's actually garbage collector */ 546 //manager.stopManager(); 547 writeln("manager stopped"); 548 } 549 }