1 /** 2 * Configuration-related types 3 */ 4 module birchwood.config.conninfo; 5 6 import std.socket : SocketException, Address, getAddress; 7 import birchwood.client.exceptions; 8 import std.conv : to, ConvException; 9 10 /** 11 * The mode describes how birchwood will act 12 * when encounterin invalid characters that 13 * were provided BY the user TO birchwood 14 */ 15 public enum ChecksMode 16 { 17 /** 18 * In this mode any invalid characters 19 * will be automatically stripped 20 */ 21 EASY, 22 23 /** 24 * In this mode any invalid characters 25 * will result in the throwing of a 26 * `BirchwoodException` 27 */ 28 HARDCORE 29 } 30 31 /** 32 * Represents the connection details for a server 33 * to connect to 34 */ 35 public shared struct ConnectionInfo 36 { 37 /** 38 * Server address 39 */ 40 private Address addrInfo; 41 42 /** 43 * Nickname to use 44 */ 45 public string nickname; 46 47 /** 48 * Username 49 */ 50 public string username; 51 52 /** 53 * Real name 54 */ 55 public string realname; 56 57 /** 58 * Size to use to dequeue bytes 59 * from socket in read-loop 60 */ 61 private ulong bulkReadSize; 62 63 //TODO: Make this a Duration 64 /** 65 * Time to wait (in seconds) between 66 * sending messages 67 */ 68 private ulong fakeLag; 69 70 /** 71 * Quit message 72 */ 73 public const string quitMessage; 74 75 /** 76 * Key-value pairs learnt from the 77 * server 78 */ 79 private string[string] db; 80 81 /* TODO: before publishing change this bulk size */ 82 83 private ChecksMode mode; 84 85 /** 86 * Constructs a new ConnectionInfo instance with the 87 * provided details 88 * 89 * Params: 90 * addrInfo = the server's endpoint 91 * nickname = the nickname to use 92 * bulkReadSize = the dequeue read size 93 * quitMessage = the message to use when quitting 94 */ 95 private this(Address addrInfo, string nickname, string username, string realname, ulong bulkReadSize = 20, string quitMessage = "birchwood client disconnecting...") 96 { 97 // NOTE: Not sure if much mutable in Address anyways 98 this.addrInfo = cast(shared Address)addrInfo; 99 this.nickname = nickname; 100 this.username = username; 101 this.realname = realname; 102 this.bulkReadSize = bulkReadSize; 103 this.quitMessage = quitMessage; 104 105 // Set the default fakelag to 0 seconds (no send lag) 106 this.fakeLag = 0; 107 108 // Set the validity mode to easy 109 this.mode = ChecksMode.EASY; 110 } 111 112 public ChecksMode getMode() 113 { 114 return this.mode; 115 } 116 117 public void setMode(ChecksMode mode) 118 { 119 this.mode = mode; 120 } 121 122 /** 123 * Retrieve the read-dequeue size 124 * 125 * Returns: the number of bytes 126 */ 127 public ulong getBulkReadSize() 128 { 129 return this.bulkReadSize; 130 } 131 132 /** 133 * Sets the read-dequeue size 134 * 135 * Params: 136 * bytes = the number of bytes to dequeue at a time 137 */ 138 public void setBulkReadSize(ulong bytes) 139 { 140 this.bulkReadSize = bytes; 141 } 142 143 /** 144 * Get the address of the endpoint server 145 * 146 * Returns: the server's address 147 */ 148 public Address getAddr() 149 { 150 return cast(Address)addrInfo; 151 } 152 153 /** 154 * Get the chosen fake lag 155 * 156 * Returns: the fake lag in seconds 157 */ 158 public ulong getFakeLag() 159 { 160 return fakeLag; 161 } 162 163 /** 164 * Sets the fake lag in seconds 165 * 166 * Params: 167 * fakeLag = the fake lag to use 168 */ 169 public void setFakeLag(ulong fakeLag) 170 { 171 this.fakeLag = fakeLag; 172 } 173 174 /** 175 * Update a value in the key-value pair database 176 * 177 * Params: 178 * key = the key to set 179 * value = the value to set to 180 */ 181 public void updateDB(string key, string value) 182 { 183 db[key] = value; 184 } 185 186 /** 187 * Retrieve a value from the key-value pair database 188 * 189 * Params: 190 * key = the key to lookup 191 * Returns: the value as type T, if not able to convert then T.init 192 * Throws: 193 * BirchwoodException if the key is not found 194 */ 195 public T getDB(T)(string key) 196 { 197 if(key in db) 198 { 199 /* Attempt conversion into T */ 200 try 201 { 202 /* Fetch and convert */ 203 T value = to!(T)(db[key]); 204 return value; 205 } 206 /* If conversion to type T fails */ 207 catch(ConvException e) 208 { 209 /* Return the initial value for such a paremeter */ 210 return T.init; 211 } 212 } 213 else 214 { 215 throw new BirchwoodException(ErrorType.DB_KEY_NOT_FOUND, "Could not find key '"~key~"'"); 216 } 217 } 218 219 220 /** 221 * Creates a ConnectionInfo struct representing a client configuration which 222 * can be provided to the Client class to create a new connection based on its 223 * parameters 224 * 225 * Params: 226 * hostname = hostname of the server 227 * port = server port 228 * nickname = nickname to use 229 * 230 * Returns: ConnectionInfo for this server 231 */ 232 public static ConnectionInfo newConnection(string hostname, ushort port, string nickname, string username, string realname) 233 { 234 try 235 { 236 /* Attempt to resolve the address (may throw SocketException) */ 237 Address[] addrInfo = getAddress(hostname, port); 238 239 /* Username check */ 240 if(!nickname.length) 241 { 242 throw new BirchwoodException(ErrorType.INVALID_CONN_INFO); 243 } 244 245 /* TODO: Add feature to choose which address to use, prefer v4 or v6 type of thing */ 246 Address chosenAddress = addrInfo[0]; 247 248 return ConnectionInfo(chosenAddress, nickname, username, realname); 249 } 250 catch(SocketException e) 251 { 252 throw new BirchwoodException(ErrorType.INVALID_CONN_INFO); 253 } 254 } 255 256 /** 257 * Tests invalid conneciton information 258 * 259 * 1. Invalid hostnames 260 * 2. Invalid usernames 261 */ 262 unittest 263 { 264 try 265 { 266 newConnection("1.", 21, "deavmi", "thedeavmi", "Tristan Brice Birchwood Kildaire"); 267 assert(false); 268 } 269 catch(BirchwoodException e) 270 { 271 assert(e.getType() == ErrorType.INVALID_CONN_INFO); 272 } 273 274 try 275 { 276 newConnection("1.1.1.1", 21, "", "thedeavmi", "Tristan Brice Birchwood Kildaire"); 277 assert(false); 278 } 279 catch(BirchwoodException e) 280 { 281 assert(e.getType() == ErrorType.INVALID_CONN_INFO); 282 } 283 284 } 285 } 286 287 /** 288 * Sets the default values as per rfc1459 in the 289 * key-value pair DB 290 * 291 * Params: 292 * connInfo = a reference to the ConnectionInfo struct to update 293 */ 294 public void setDefaults(ref ConnectionInfo connInfo) 295 { 296 /* Set the `MAXNICKLEN` to a default of 9 */ 297 connInfo.updateDB("MAXNICKLEN", "9"); 298 }