package kidozen.client; import android.os.AsyncTask; import android.util.Log; import org.apache.http.HttpStatus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.security.InvalidParameterException; import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutionException; import kidozen.client.authentication.KidoZenUser; import kidozen.client.internal.Constants; import kidozen.client.internal.SyncHelper; import kidozen.client.internal.Utilities; /** * Mail service interface * * @author kidozen * @version 1.00, April 2013 */ public class MailSender extends KZService { public KZApplication Application; private String KEY = "MailSender"; private MailSender mSelf; /** * Constructor * * @param endpoint The Configuration service endpoint */ public MailSender(String mailsender, String provider , String username, String pass, String clientId, KidoZenUser userIdentity, KidoZenUser applicationIdentity) { super(mailsender, "", provider, username, pass, clientId, userIdentity, applicationIdentity); mSelf = this; } /** * @param mail The Email message to send * @param callback The callback with the result of the service call */ public void Send(final Mail mail, final ServiceEventListener callback) throws ExecutionException, InterruptedException, JSONException { if ( mail == null ) throw new IllegalArgumentException(Utilities.GetInvalidParameterMessage("mail")); if ( mail.from()==null || mail.from().isEmpty() ) throw new IllegalArgumentException(Utilities.GetInvalidParameterMessage("mail.from")); if ( mail.to()==null || mail.to().isEmpty() ) throw new IllegalArgumentException(Utilities.GetInvalidParameterMessage("mail.to")); JSONObject message= new JSONObject(mail.GetHashMap()); HashMap<String, String> params = new HashMap<String, String>(); HashMap<String, String> headers = new HashMap<String, String>(); headers.put(Constants.CONTENT_TYPE, Constants.APPLICATION_JSON); headers.put(Constants.ACCEPT, Constants.APPLICATION_JSON); if ( mail.Attachments!=null) { Uploader upd = new Uploader(mSelf, mail, headers, params, callback, mEndpoint, mail.Attachments); upd.execute(); } else { new KZServiceAsyncTask(KZHttpMethod.POST,params,headers,message,callback, getStrictSSL()).execute(mEndpoint); } } public boolean Send(Mail mail) throws TimeoutException, SynchronousException { SyncHelper<String> helper = new SyncHelper<String>(this, "Send", Mail.class); helper.Invoke(new Object[]{mail}); return (helper.getStatusCode() == HttpStatus.SC_OK); } private class Uploader extends AsyncTask { public static final String HTTP_METHOD_POST = "POST"; public static final String CONNECTION_HEADER = "Connection"; public static final String CONTENT_TYPE_HEADER = "Content-Type"; public static final String CONNECTION_KEEP_ALIVE = "Keep-Alive"; public static final String MULTIPART_FORM_DATA_BOUNDARY = "multipart/form-data;boundary="; private static final String TAG = "UPLOAD_ATTACH"; private String lineEnd = "\r\n"; private String twoHyphens = "--"; private String boundary = "*****"; private int maxBufferSize = 1*1024*1024; private List<String> _attachments; private ArrayList<String> _ids = new ArrayList<String>(); private String _baseUrl; private String _authHeaderValue; private KZService _mailService; private Mail _message; private HashMap<String, String> _headers; private HashMap<String, String> _params; private ServiceEventListener _callback; private boolean _sent = false; public Uploader(KZService service, Mail message, HashMap<String, String> headers, HashMap<String, String> params, ServiceEventListener callback, String baseUrl, List<String> attachments) { _mailService = service; _message = message; _headers = headers; _params = params; _callback = callback; _baseUrl = baseUrl; _attachments = attachments; } private AbstractMap.SimpleEntry<String, String> getNameAndPath(String fullFilePath) { String[] paths = fullFilePath.split("/"); String file = paths[paths.length-1]; String path = fullFilePath.replace("/" + file, ""); return new AbstractMap.SimpleEntry<String, String>(file,path); } @Override protected void onPreExecute() { CreateAuthHeaderValue(new KZServiceEvent<String>() { @Override public void Fire(String token) { _authHeaderValue = token; } }); } @Override protected Object doInBackground(Object[] objects) { try { for(Iterator<String> it = _attachments.iterator(); it.hasNext();) { String f = doFileUpload(it.next()); if (_sent) _ids.add(f); else throw new Exception("couldn't attach file"); } return _ids; } catch (Exception e) { _callback.onFinish(new ServiceEvent(this,HttpStatus.SC_INTERNAL_SERVER_ERROR,e.getMessage(),null,e)); } return null; } @Override protected void onPostExecute(Object o){ super.onPostExecute(o); try { if (!_sent || _ids.size()<=0) throw new Exception("couldn't attach file"); JSONObject jo = getJsonObjectMessage(); _mailService.ExecuteTask(mEndpoint, KZHttpMethod.POST, _params, _headers, _callback, jo, getStrictSSL()); } catch (Exception e) { _callback.onFinish(new ServiceEvent(this, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), null, e)); } } private JSONObject getJsonObjectMessage() throws JSONException { JSONArray array = new JSONArray(); for(Iterator<String> it = _ids.iterator(); it.hasNext();) { String itm = new JSONArray(it.next()).getString(0); array.put(itm); } JSONObject msg = new JSONObject(_message.GetHashMap()); // Weird problems in a real android device: // - Cannot use JSONObject.put to add a new Array property, it throws an "NoSuchMethodError" exception with a reference to virtual method call // - Cannot use HashMap.put: It serializes the array as following: "[ "..." ]" instead of ["..."] // So I decided to do this string manipulation String messageAsString = msg.toString(); String arrayAsString = array.toString(); messageAsString = messageAsString.replace("}", ", attachments=" + arrayAsString + "}"); return new JSONObject(messageAsString); } private String doFileUpload(String fileName) throws IOException { HttpURLConnection conn = null; DataOutputStream dos = null; DataInputStream inStream = null; int bytesRead, bytesAvailable, bufferSize; byte[] buffer; FileInputStream fileInputStream = new FileInputStream(new File(fileName) ); AbstractMap.SimpleEntry<String, String> nameAndPath = getNameAndPath(fileName); URL url = new URL(_baseUrl + "/" + "attachments"); conn = (HttpURLConnection) url.openConnection(); conn.setDoInput(true); conn.setDoOutput(true); conn.setUseCaches(false); conn.setRequestMethod(HTTP_METHOD_POST); conn.setRequestProperty(CONNECTION_HEADER, CONNECTION_KEEP_ALIVE); conn.setRequestProperty(Constants.AUTHORIZATION_HEADER, _authHeaderValue); conn.setRequestProperty(CONTENT_TYPE_HEADER, MULTIPART_FORM_DATA_BOUNDARY + boundary); dos = new DataOutputStream( conn.getOutputStream() ); dos.writeBytes(twoHyphens + boundary + lineEnd); dos.writeBytes("Content-Disposition: form-data; name=\"file\";filename=\"" + nameAndPath.getKey() + "\"" + lineEnd); dos.writeBytes(lineEnd); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; bytesRead = fileInputStream.read(buffer, 0, bufferSize); while (bytesRead > 0) { dos.write(buffer, 0, bufferSize); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); bytesRead = fileInputStream.read(buffer, 0, bufferSize); } dos.writeBytes(lineEnd); dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); fileInputStream.close(); dos.flush(); dos.close(); String id= null; if (conn.getResponseCode()>= HttpStatus.SC_BAD_REQUEST) { _sent = false; id = Utilities.convertStreamToString(conn.getErrorStream()); } else { _sent = true; id = Utilities.convertStreamToString(conn.getInputStream()); } Log.i(TAG,"raw service response :" + id); return id; } } }