1 module client.sender; 2 3 import core.thread; 4 import std.json : JSONValue, JSONException, parseJSON, toJSON; 5 import bmessage; 6 import std.socket; 7 import gogga; 8 import std.string; 9 import std.conv : to; 10 import client.client; 11 12 /** 13 * The MailSender class is used to instantiate an object 14 * which is used to start its own thread which will deliver 15 * mail (remote-only) to the respective recipients of the mail 16 * message specified. This is done such that the user need not 17 * wait and hang whilst mail is being delivered 18 */ 19 public final class MailSender : Thread 20 { 21 /** 22 * Delivery information 23 */ 24 private string[] remoteRecipients; 25 private JSONValue mailBlock; 26 27 /* Failed recipients (at the beginning it will be only local) */ 28 private string[] failedRecipients; 29 30 private ButterflyClient client; 31 32 /** 33 * Constructs a new MailSender with the given 34 * email to be delivered (remotely) 35 */ 36 this(string[] remoteRecipients, JSONValue mailBlock, string[] failedRecipients, ButterflyClient client) 37 { 38 /* Set the worker function */ 39 super(&run); 40 41 /* Save delivery information */ 42 this.remoteRecipients = remoteRecipients; 43 this.mailBlock = mailBlock; 44 45 /* Save the failed local recipients */ 46 this.failedRecipients = failedRecipients; 47 48 this.client = client; 49 50 /* Start the delivery */ 51 start(); 52 } 53 54 /** 55 * Does the remote mail delivery 56 */ 57 private void remoteDeliver() 58 { 59 /* Deliver mail to each recipient */ 60 foreach (string remoteRecipient; remoteRecipients) 61 { 62 /* TODO: Do remote mail delivery */ 63 gprintln("Remote delivery occurring..."); 64 65 /* Get the mail address */ 66 string[] mailAddress = split(remoteRecipient, "@"); 67 68 /* Get the username */ 69 string username = mailAddress[0]; 70 71 /* Get the domain */ 72 string domain = mailAddress[1]; 73 74 try 75 { 76 /** 77 * Construct the server message to send to the 78 * remote server. 79 */ 80 JSONValue messageBlock; 81 messageBlock["command"] = "deliverMail"; 82 83 JSONValue requestBlock; 84 requestBlock["mail"] = mailBlock; 85 messageBlock["request"] = requestBlock; 86 87 /* Deliver the mail to the remote server */ 88 Socket remoteServer = new Socket(AddressFamily.INET, 89 SocketType.STREAM, ProtocolType.TCP); 90 91 /* TODO: Add check over here to make sure these are met */ 92 string remoteHost = split(domain, ":")[0]; 93 ushort remotePort = to!(ushort)(split(domain, ":")[1]); 94 95 remoteServer.connect(parseAddress(remoteHost, remotePort)); 96 bool sendStatus = sendMessage(remoteServer, cast(byte[]) toJSON(messageBlock)); 97 98 if (!sendStatus) 99 { 100 goto deliveryFailed; 101 } 102 103 byte[] receivedBytes; 104 bool recvStatus = receiveMessage(remoteServer, receivedBytes); 105 106 if (!recvStatus) 107 { 108 goto deliveryFailed; 109 } 110 111 /* Close the connection with the remote host */ 112 remoteServer.close(); 113 114 JSONValue responseBlock = parseJSON(cast(string) receivedBytes); 115 116 /* TODO: Get ["status"]["code"] code here an act on it */ 117 if (responseBlock["status"]["code"].integer() == 0) 118 { 119 gprintln("Message delivered to user " ~ remoteRecipient); 120 } 121 else 122 { 123 goto deliveryFailed; 124 } 125 } 126 catch (SocketOSException) 127 { 128 goto deliveryFailed; 129 } 130 catch (JSONException) 131 { 132 /* When delivery fails */ 133 deliveryFailed: 134 gprintln("Error delivering to " ~ remoteRecipient); 135 136 /* Append failed recipient to array of failed recipients */ 137 failedRecipients ~= remoteRecipient; 138 139 continue; 140 } 141 } 142 143 gprintln("Sent mail message to " ~ remoteRecipients); 144 } 145 146 /** 147 * Sends a mail message to the sender's INbox specifying that there 148 * was a mail delivery failure to one or more of the provided addresses 149 */ 150 private void mailReport() 151 { 152 /* Create the error message */ 153 JSONValue deliveryReport; 154 JSONValue[] errorRecipients = [ 155 JSONValue(client.mailbox.username ~ "@" ~ client.listener.getDomain()) 156 ]; 157 deliveryReport["recipients"] = errorRecipients; 158 159 /* TODO: Make more indepth, and have copy of the mail that was tried to be sent */ 160 /* Get a list of the recipients of the mail message */ 161 string[] recipients; 162 foreach(JSONValue recipient; mailBlock["recipients"].array()) 163 { 164 recipients ~= recipient.str(); 165 } 166 string errorMessage = "There was an error delivery the mail to: " ~ to!( 167 string)(recipients) ~ "\n"; 168 errorMessage ~= "\nThe message was:\n\n" ~ mailBlock.toPrettyString(); 169 deliveryReport["message"] = errorMessage; 170 171 gprintln(deliveryReport); 172 173 /* Deliver the error message (and don't put the report in the sent box) */ 174 client.sendMail(deliveryReport, false); 175 176 gprintln("Mail delivery report sent: " ~ deliveryReport.toPrettyString()); 177 178 } 179 180 private void run() 181 { 182 /* Do the remote mail delivery */ 183 remoteDeliver(); 184 185 /* If there were failed recipients send a report to the sender */ 186 if (failedRecipients.length) 187 { 188 /* Send the mail report */ 189 mailReport(); 190 } 191 } 192 }