1 module libutterfly.client;
2 
3 import std.socket;
4 import bmessage;
5 import std.json;
6 import libutterfly.exceptions : ButterflyException;
7 
8 public final class ButterflyClient
9 {
10 
11     /**
12     * Socket connection to the server
13     */
14     private Socket connection;
15 
16     /**
17     * Creates a new ButterflyClient connected
18     * to the butterfly server specified by the
19     * Address `endpoint`.
20     */
21     this(Address endpoint)
22     {
23         /**
24         * Connect to the remote server
25         */
26         connection = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
27         connection.connect(endpoint);
28     }
29 
30     /**
31     * Authenticate to the server with the given `username`
32     * and `password`.
33     *
34     * Throws a ButterflyException if the authentication
35     * details are incorrect.
36     */
37     public void authenticate(string username, string password)
38     {
39         /**
40         * Construct the command.
41         */
42         JSONValue commandBlock;
43         commandBlock["command"] = "authenticate";
44         
45         JSONValue requestBlock;
46         requestBlock["username"] = username;
47         requestBlock["password"] = password;
48 
49         commandBlock["request"] = requestBlock;
50 
51         /* Send the command */
52         sendMessage(connection, cast(byte[])toJSON(commandBlock));
53 
54         /* Get the status */
55         JSONValue response;
56 
57         byte[] receivedBytes;
58         receiveMessage(connection, receivedBytes);
59         response = parseJSON(cast(string)receivedBytes);
60 
61         if(response["status"]["code"].integer() == 0)
62         {
63             /* TODO: Good */
64         }
65         else
66         {
67             /* Throw an exception */
68             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
69         }
70     }
71 
72 
73     public string editMail(string mailPath, JSONValue messageBlock)
74     {
75         string mailID;
76 
77 
78         /**
79         * Construct the command.
80         */
81         JSONValue commandBlock;
82         commandBlock["command"] = "editMail";
83         
84         JSONValue requestBlock;
85         requestBlock["message"] = mailPath;
86         requestBlock["mail"] = messageBlock;
87 
88         commandBlock["request"] = requestBlock;
89 
90         /* Send the command */
91         sendMessage(connection, cast(byte[])toJSON(commandBlock));
92 
93         /* Get the status */
94         JSONValue response;
95 
96         byte[] receivedBytes;
97         receiveMessage(connection, receivedBytes);
98         response = parseJSON(cast(string)receivedBytes);
99 
100         if(response["status"]["code"].integer() == 0)
101         {
102             /* TODO: Good */
103         }
104         else
105         {
106             /* Throw an exception */
107             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
108         }
109 
110 
111         return mailID;
112     }
113 
114 
115     /**
116     * Send the provided JSONValue mail `messageBlock`
117     *
118     * Throws a ButterflyException if the mail sending
119     * fails.
120     */
121     public void sendMail(JSONValue messageBlock)
122     {
123         /**
124         * Construct the command.
125         */
126         JSONValue commandBlock;
127         commandBlock["command"] = "sendMail";
128         
129         JSONValue requestBlock;
130         requestBlock["mail"] = messageBlock;
131 
132         commandBlock["request"] = requestBlock;
133 
134         /* Send the command */
135         sendMessage(connection, cast(byte[])toJSON(commandBlock));
136 
137         /* Get the status */
138         JSONValue response;
139 
140         byte[] receivedBytes;
141         receiveMessage(connection, receivedBytes);
142         response = parseJSON(cast(string)receivedBytes);
143 
144         if(response["status"]["code"].integer() == 0)
145         {
146             /* TODO: Good */
147         }
148         else
149         {
150             /* Throw an exception */
151             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
152         }
153     }
154 
155     /**
156     * Returns a string[] of the folder names in the given
157     * folder, `folderPath`
158     *
159     * Throws a ButterflyException if the lookup fails.
160     */
161     public string[] listFolder(string folderPath)
162     {
163         /**
164         * Construct the command.
165         */
166         JSONValue commandBlock;
167         commandBlock["command"] = "listFolder";
168         
169         JSONValue requestBlock;
170         requestBlock["folderName"] = folderPath;
171 
172         commandBlock["request"] = requestBlock;
173 
174         /* Send the command */
175         sendMessage(connection, cast(byte[])toJSON(commandBlock));
176 
177         /* Get the status */
178         JSONValue response;
179 
180         byte[] receivedBytes;
181         receiveMessage(connection, receivedBytes);
182         response = parseJSON(cast(string)receivedBytes);
183 
184         if(response["status"]["code"].integer() == 0)
185         {
186             /* TODO: Good */
187             string[] folderNames;
188             foreach(JSONValue folderName; response["response"]["folders"].array())
189             {
190                 folderNames ~= folderName.str();
191             }
192             return folderNames;
193         }
194         else
195         {
196             /* Throw an exception */
197             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
198         }
199     }
200 
201     /**
202     * Deletes an existing folder under the path specified
203     * by `folderPath`.
204     *
205     * Throws a ButterflyException if the folder deletion
206     * fails.
207     */
208     public void deleteFolder(string folderPath)
209     {
210         /**
211         * Construct the command.
212         */
213         JSONValue commandBlock;
214         commandBlock["command"] = "deleteFolder";
215         
216         JSONValue requestBlock;
217         requestBlock["folderName"] = folderPath;
218 
219         commandBlock["request"] = requestBlock;
220 
221         /* Send the command */
222         sendMessage(connection, cast(byte[])toJSON(commandBlock));
223 
224         /* Get the status */
225         JSONValue response;
226 
227         byte[] receivedBytes;
228         receiveMessage(connection, receivedBytes);
229         response = parseJSON(cast(string)receivedBytes);
230 
231         if(response["status"]["code"].integer() == 0)
232         {
233             /* TODO: Good */
234         }
235         else
236         {
237             /* Throw an exception */
238             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
239         }
240     }
241 
242     /**
243     * Creates a new folder under the path specified
244     * by `folderPath`.
245     *
246     * Throws a ButterflyException if the folder creation
247     * fails.
248     */
249     public void createFolder(string folderPath)
250     {
251         /**
252         * Construct the command.
253         */
254         JSONValue commandBlock;
255         commandBlock["command"] = "createFolder";
256         
257         JSONValue requestBlock;
258         requestBlock["folderName"] = folderPath;
259 
260         commandBlock["request"] = requestBlock;
261 
262         /* Send the command */
263         sendMessage(connection, cast(byte[])toJSON(commandBlock));
264 
265         /* Get the status */
266         JSONValue response;
267 
268         byte[] receivedBytes;
269         receiveMessage(connection, receivedBytes);
270         response = parseJSON(cast(string)receivedBytes);
271 
272         if(response["status"]["code"].integer() == 0)
273         {
274             /* TODO: Good */
275         }
276         else
277         {
278             /* Throw an exception */
279             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
280         }
281     }
282 
283     /**
284     * Returns a string[] of the mail IDs within the
285     * given folder, `folderPath`.
286     *
287     * Throws a ButterflyException if the lookup fails.
288     */
289     public string[] listMail(string folderPath)
290     {
291         /**
292         * Construct the command.
293         */
294         JSONValue commandBlock;
295         commandBlock["command"] = "listMail";
296         
297         JSONValue requestBlock;
298         requestBlock["folderName"] = folderPath;
299 
300         commandBlock["request"] = requestBlock;
301 
302         /* Send the command */
303         sendMessage(connection, cast(byte[])toJSON(commandBlock));
304 
305         /* Get the status */
306         JSONValue response;
307 
308         byte[] receivedBytes;
309         receiveMessage(connection, receivedBytes);
310         response = parseJSON(cast(string)receivedBytes);
311 
312         if(response["status"]["code"].integer() == 0)
313         {
314             /* TODO: Good */
315             string[] mailIDs;
316             foreach(JSONValue mailID; response["response"]["mailIDs"].array())
317             {
318                 mailIDs ~= mailID.str();
319             }
320             return mailIDs;
321         }
322         else
323         {
324             /* Throw an exception */
325             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
326         }
327     }
328 
329     public string storeMail(string folderPath, JSONValue messageBlock)
330     {
331         string mailID;
332 
333 
334         /**
335         * Construct the command.
336         */
337         JSONValue commandBlock;
338         commandBlock["command"] = "storeMail";
339         
340         JSONValue requestBlock;
341         requestBlock["folder"] = folderPath;
342         requestBlock["mail"] = messageBlock;
343 
344         commandBlock["request"] = requestBlock;
345 
346         /* Send the command */
347         sendMessage(connection, cast(byte[])toJSON(commandBlock));
348 
349         /* Get the status */
350         JSONValue response;
351 
352         byte[] receivedBytes;
353         receiveMessage(connection, receivedBytes);
354         response = parseJSON(cast(string)receivedBytes);
355 
356         if(response["status"]["code"].integer() == 0)
357         {
358             /* TODO: Good */
359             mailID = response["response"]["mailID"].str();
360         }
361         else
362         {
363             /* Throw an exception */
364             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
365         }
366 
367 
368         return mailID;
369     }
370 
371     /**
372     * Fetches the message block (mail message JSON) of the
373     * mail message specified by `mailID` located in the
374     * folder specified by `folderPath`. Returns it as a
375     * JSONValue.
376     *
377     * Throws a ButterflyException if the mail fetch fails.
378     */
379     public JSONValue fetchMail(string folderPath, string mailID)
380     {
381         /**
382         * Construct the command.
383         */
384         JSONValue commandBlock;
385         commandBlock["command"] = "fetchMail";
386         
387         JSONValue requestBlock;
388         requestBlock["folder"] = folderPath;
389         requestBlock["id"] = mailID;
390 
391         commandBlock["request"] = requestBlock;
392 
393         /* Send the command */
394         sendMessage(connection, cast(byte[])toJSON(commandBlock));
395 
396         /* Get the status */
397         JSONValue response;
398 
399         byte[] receivedBytes;
400         receiveMessage(connection, receivedBytes);
401         response = parseJSON(cast(string)receivedBytes);
402 
403         if(response["status"]["code"].integer() == 0)
404         {
405             /* TODO: Good */
406             return response["response"]["mail"];
407         }
408         else
409         {
410             /* Throw an exception */
411             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
412         }
413     }
414 
415     /**
416     * Registers a new account with the given `username`
417     * and `password`.
418     *
419     * Throws a ButterflyException if the account creation
420     * fails.
421     */
422     public void register(string username, string password)
423     {
424         /**
425         * Construct the command.
426         */
427         JSONValue commandBlock;
428         commandBlock["command"] = "register";
429         
430         JSONValue requestBlock;
431         requestBlock["username"] = username;
432         requestBlock["password"] = password;
433 
434         commandBlock["request"] = requestBlock;
435 
436         /* Send the command */
437         sendMessage(connection, cast(byte[])toJSON(commandBlock));
438 
439         /* Get the status */
440         JSONValue response;
441 
442         byte[] receivedBytes;
443         receiveMessage(connection, receivedBytes);
444         response = parseJSON(cast(string)receivedBytes);
445 
446         if(response["status"]["code"].integer() == 0)
447         {
448             /* TODO: Good */
449         }
450         else
451         {
452             /* Throw an exception */
453             throw new ButterflyException(response["status"]["message"].str(), response["status"]["code"].integer());
454         }
455     }
456 }