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 }