1 module client.mail; 2 3 import std.json; 4 import std.file; 5 import std.stdio; 6 import std..string; 7 import gogga; 8 9 /** 10 * Mailbox 11 * 12 * This represents an in-memory representation of a user's 13 * mailbox. 14 */ 15 public final class Mailbox 16 { 17 18 /** 19 * The owner of this Mailbox 20 */ 21 public string username; 22 23 /** 24 * Returns `true` if the given mailbox, `username`, exists. 25 */ 26 public static bool isMailbox(string username) 27 { 28 return exists("mailboxes/"~username) && isDir("mailboxes/"~username); 29 } 30 31 public static Mailbox createMailbox(string username) 32 { 33 Mailbox newMailbox; 34 35 /* Create the mailbox directory */ 36 mkdir("mailboxes/"~username); /* TODO: Error handling */ 37 38 newMailbox = new Mailbox(username); 39 40 /* TODO: Create the base set of folders */ 41 newMailbox.addBaseFolder("Inbox"); 42 newMailbox.addBaseFolder("Drafts"); 43 newMailbox.addBaseFolder("Outbox"); 44 newMailbox.addBaseFolder("Sent"); 45 newMailbox.addBaseFolder("Trash"); 46 47 return newMailbox; 48 } 49 50 this(string username) 51 { 52 this.username = username; 53 } 54 55 public Folder[] getFolders() 56 { 57 Folder[] folders; 58 59 /* Get a list of all the directories within this directory */ 60 foreach(DirEntry dirEntry; dirEntries("mailboxes/"~username~"/", SpanMode.shallow)) 61 { 62 folders ~= new Folder(this, dirEntry.name()); 63 } 64 65 return folders; 66 } 67 68 public Folder addBaseFolder(string folderName) 69 { 70 Folder newFolder; 71 72 /* Create the directory */ 73 mkdir("mailboxes/"~username~"/"~folderName); 74 75 newFolder = new Folder(this, folderName); 76 77 return newFolder; 78 } 79 80 public void storeMessage(Folder folder, string mailID, JSONValue mailBlock) 81 { 82 /* Generate the filename to store the message under */ 83 string filename = "mailboxes/"~username~"/"~folder.folderPath~"/"~mailID; 84 85 /* Save the message to the file system */ 86 File mailFile; 87 mailFile.open(filename, "wb"); 88 mailFile.rawWrite(cast(byte[])toJSON(mailBlock)); 89 mailFile.close(); 90 } 91 92 93 94 public void deleteMailbox() 95 { 96 /* TODO: Run deletion on all folders */ 97 Folder[] folders = getFolders(); 98 99 foreach(Folder folder; folders) 100 { 101 /* Delete the folder */ 102 folder.deleteFolder(); 103 } 104 105 /* TODO: Delete the mailbox directory */ 106 } 107 } 108 109 /** 110 * Folder 111 * 112 * This represents an in-memory representation of a folder 113 * that resides in another folder or mailbox. 114 */ 115 public final class Folder 116 { 117 /** 118 * The parent folder of this folder 119 */ 120 private Folder parentFolder; 121 122 /** 123 * The path of this folder 124 */ 125 private string folderPath; 126 127 /** 128 * The associated Mailbox 129 */ 130 private Mailbox mailbox; 131 132 /** 133 * Constructs a new Folder object which represents an 134 * existing folder in a user's Mailbox. 135 * 136 * The Mailbox is specified by `mailbox` and the location 137 * of the folder within the mailbox by `folderPath`. 138 * 139 * TODO: DIsallow simply empty folder name (somewhere) 140 */ 141 this(Mailbox mailbox, string folderPath) 142 { 143 /* Ensure that the folder exists */ 144 if(!(exists("mailboxes/"~mailbox.username~"/"~folderPath) && isDir("mailboxes/"~mailbox.username~"/"~folderPath))) 145 { 146 /* TODO: Throw exception */ 147 } 148 149 this.folderPath = folderPath; 150 this.mailbox = mailbox; 151 } 152 153 /** 154 * Get the mail inside this folder 155 */ 156 public Mail[] getMessages() 157 { 158 Mail[] messages; 159 160 /* Get a list of all the files within this directory */ 161 foreach(DirEntry dirEntry; dirEntries("mailboxes/"~mailbox.username~"/"~folderPath, SpanMode.shallow)) 162 { 163 /* Only append files */ 164 if(dirEntry.isFile()) 165 { 166 string[] paths = split(dirEntry.name(), "/"); 167 string mailID = paths[paths.length-1]; 168 messages ~= new Mail(mailbox, this, mailID); 169 } 170 } 171 172 gprintln("fhjdf"); 173 return messages; 174 } 175 176 /** 177 * Get the folders within this folder 178 */ 179 public Folder[] getFolders() 180 { 181 Folder[] folders; 182 183 /* Get a list of all the directories within this directory */ 184 foreach(DirEntry dirEntry; dirEntries("mailboxes/"~mailbox.username~"/"~folderPath, SpanMode.shallow)) 185 { 186 /* Only append folders */ 187 if(dirEntry.isDir()) 188 { 189 folders ~= new Folder(mailbox, folderPath~"/"~dirEntry.name()); 190 } 191 } 192 193 return folders; 194 } 195 196 /** 197 * Delete this folder 198 * 199 * This deletes all the sub-folders and the mail 200 * within each. 201 */ 202 public void deleteFolder() 203 { 204 /* TODO: Implement me */ 205 206 /* Get a list of all files in this folder */ 207 Mail[] messages = getMessages(); 208 209 /* Delete all messages in this folder */ 210 foreach(Mail message; messages) 211 { 212 message.deleteMessage(); 213 } 214 215 /* Get a list of all folders in this folder */ 216 Folder[] folders = getFolders(); 217 218 /** 219 * Delete all child folders of the current 220 */ 221 foreach(Folder child; folders) 222 { 223 child.deleteFolder(); 224 } 225 226 /* Delete this folder */ 227 rmdir("mailboxes/"~mailbox.username~"/"~folderPath); 228 } 229 230 /** 231 * Create a new Folder 232 */ 233 public Folder createFolder(string folderName) 234 { 235 Folder newFolder; 236 237 /* Create the folder in the filesystem */ 238 mkdir("mailboxes/"~mailbox.username~"/"~folderPath~"/"~folderName); 239 240 /* Create an instance of the newly created folder */ 241 newFolder = new Folder(mailbox, folderPath~"/"~folderName); 242 243 244 return newFolder; 245 } 246 247 override public string toString() 248 { 249 string[] paths = split(folderPath, "/"); 250 string folderName = paths[paths.length-1]; 251 return "\""~folderName~"\""; 252 } 253 } 254 255 /** 256 * Mail 257 * 258 * This represents an in-memory representation of a mail message. 259 */ 260 public final class Mail 261 { 262 263 /** 264 * The associated Mailbox 265 */ 266 private Mailbox mailbox; 267 268 /** 269 * The associated Folder 270 */ 271 private Folder folder; 272 273 /** 274 * The mail message's name 275 */ 276 private string mailID; 277 278 public static Mail createMail(Mailbox mailbox, Folder folder, JSONValue mailBlock) 279 { 280 Mail newMail; 281 282 /* Generate a unique mailID of this message */ 283 string mailID = getNameForMail(mailBlock); 284 285 /* Save the mail into the mailbox */ 286 mailbox.storeMessage(folder, mailID, mailBlock); 287 288 /* fetch the mail object */ 289 newMail = new Mail(mailbox, folder, mailID); 290 291 /* TODO: Re think our system */ 292 return newMail; 293 } 294 295 /** 296 * Returns a name unique to this message 297 */ 298 private static string getNameForMail(JSONValue mailBlock) 299 { 300 import std.digest.md; 301 MD5Digest digester = new MD5Digest(); 302 303 ubyte[] hash = digester.digest(cast(ubyte[])toJSON(mailBlock)); 304 305 306 /* TODO: Hash message here and return hex of hash */ 307 return toHexString(hash); 308 } 309 310 this(Mailbox mailbox, Folder folder, string mailID) 311 { 312 this.mailbox = mailbox; 313 this.folder = folder; 314 this.mailID = mailID; 315 } 316 317 /** 318 * Removes the mail message from the folder 319 */ 320 public void deleteMessage() 321 { 322 /* Get the file system path to this message */ 323 string messageFilePath = mailbox.username~"/"~folder.folderPath~"/"~mailID; 324 325 /* Remove the message from the filesystem */ 326 remove(messageFilePath); 327 } 328 329 private void sanityCheck() 330 { 331 /* TODO: Throw error if the message is somehow malformed */ 332 333 /** 334 * Check for `from` field 335 * Check for `to` field 336 */ 337 } 338 339 340 341 342 /** 343 * Returns the mail message JSON 344 */ 345 public JSONValue getMessage() 346 { 347 JSONValue messageBlock; 348 349 /* Get the file system path to this message */ 350 string messageFilePath = "mailboxes/"~mailbox.username~"/"~folder.folderPath~"/"~mailID; 351 352 /* TODO: Implement me */ 353 File file; 354 file.open(messageFilePath); 355 356 byte[] fileBytes; 357 fileBytes.length = file.size(); 358 fileBytes = file.rawRead(fileBytes); 359 file.close(); 360 361 messageBlock = parseJSON(cast(string)fileBytes); 362 363 364 365 return messageBlock; 366 } 367 368 override public string toString() 369 { 370 return "\""~mailID~"\""; 371 } 372 373 public string getMailID() 374 { 375 return mailID; 376 } 377 }