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