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 }