I want to fetch emails in android by using javamail.
but I encountered some problems.
in POP3, if I want to fetch the content of a certain mail, the javamail will download all the content of the mail including the attachments. if the attachments are large enough, the android program will throw oom exception. But I tried in IMAP protocal, it only fetch the skeleton of the content first and even if there are some large attachments, it works well.
so, how could I fetch the content of a certain email using pop3 protocal when there are large attachment in the email?
following codes work well with imap, but not with pop3.
public void getContent(Part part) throws Exception {
String contentType = part.getContentType();
int nameindex = contentType.indexOf("name");
boolean conname = false;
if (nameindex != -1)
conname = true;
if (part.isMimeType("text/plain") && !conname) {
bodytext.append((String) part.getContent());
} else if (part.isMimeType("text/html") && !conname) {
String html = (String) part.getContent();
Spanned plainText = Html.fromHtml(html);
bodytext.append(plainText);
} else if (part.isMimeType("multipart/*")) {
Multipart multipart = (Multipart) part.getContent();
int counts = multipart.getCount();
for (int i = 0; i < counts; i++) {
BodyPart mpart = multipart.getBodyPart(i);
Log.d("type,"," "+ i+mpart.getContentType());
String disposition = mpart.getDisposition();
if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)) || (disposition
.equals(Part.INLINE))))
continue;
String contype = mpart.getContentType();
if(contype.toLowerCase().indexOf("application")!=-1||contype.toLowerCase().indexOf("name")!=-1)
continue;
getContent(multipart.getBodyPart(i));
}
} else if (part.isMimeType("message/rfc822")) {
getContent((Part) part.getContent());
}
}
I do not know the specifics of JavaMail, however:
IMAP provides methods in it's protocol for downloading MIME Parts of a message, allowing the message to be picked apart on the server and downloaded in parts.
POP3s basic protocol only has support for downloading the entire message. With the optional TOP extension, it will allow you to download the first n lines of a message.
If JavaMail has specific extensions to stream the download of a message to disk (rather than all in memory), that should allow you to get around your Out Of Memory error.
JavaMail 1.4.4 and later can cache POP3 messages in a tmp file on disk, thus using less memory. See the javadocs for the com.sun.mail.pop3 package for the properties to set. You'll still need to be careful how you process the contents of the mail, e.g., using getInputStream instead of getContent to process large attachments.
Related
Currently Iam sending string and image file as string. to sheet via Apps script (sheet extension). Now I need to send a document files like (.zip, .glb, .obj, .arp, etc) I need to send these type of files to google sheet. so now I need to make user selected files as .zip. so now I need to send that .zip file to google sheet. but I couldnt do that.
private fun startUploading() {
try {
val map: HashMap<String, String> = HashMap()
map["instagramLink"] = binding.creatorInstaID.text.toString()
map["tutorialLink"] = binding.youTubeLink.text.toString()
map["detailDescription"] = binding.detailDescription.text.toString()
map["coverImage"] = userImage!!
mainViewModel.saveProduct(map)
}
catch (e:Exception){
Toast.makeText(context, "Fill all the details", Toast.LENGTH_SHORT).show()
}
}
Here userImage is the user selected image which is converted as string.
other values are string values it can be sent to google sheet easily.
now how can i send a custom file types to google sheet. as like this.
The only workaround I found is converting the encode the file to base64. Then add the string of base64 to the Google Sheet. I made a sample file with a Zip file store in Google Drive. You might need to adjust the code as needed.
Also, remember that character limit of a Google Cell is 50,000, so I added an if statement in case the encode file has more than 50,000 characters and split the string in an array of 45000 characters.
Note: The same sample code can be use on any type of files.
function encode64() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1")
// get the file from Google Drive using the ID
let file = DriveApp.getFileById("Drive_Zip_File_ID");
//convert the zip file to blob
let file_in_blop = file.getBlob();
//convert the blop to bytes
let file_in_bytes = file_in_blop.getBytes();
//encode the bytes to base64
let encoded = Utilities.base64Encode(file_in_bytes);
//get the length of charactes from the base 64 encode
let length_string = encoded.length
//split the string to array in chunks less than 45000 characters
if (length_string > 50000){
const result = encoded.match(/.{1,45000}/g) ?? [];
for (let i = 0; i < result.length; i++) {
sheet.getRange(i+2,2).setValue(result[i]);
}
}else(sheet.getRange("B2").setValue(result));
}
I'm building an android VOIP application with push notifications support, based on PJSUA2.
I need to send the push notification (FCM) token to the server (Asterisk in my case) as contact uri parameter, so that I can retrieve it with a script from the server and send notification to wake up client before sending an incoming call request.
I put the parameters in the contact uri parameters with
acfg.getSipConfig().setContactUriParams(buildParams(contactParameters));
contactParams is a HashMap<String, String> with parameters name and value, while buildParams is the following method:
private String buildParams(Map<String, String> params) {
StringBuilder builder = new StringBuilder();
for (String k : params.keySet()) {
builder.append(';');
builder.append(k);
String v = params.get(k);
if (v != null && v.trim().length() > 0) {
builder.append("=\"");
builder.append(v);
builder.append('\"');
}
}
return builder.toString();
}
without FCM parameters everything works well, but
building the contact uri with the following parameters
;pn-provider="fcm";pn-tok="LONG FCM TOKEN"
makes the calls hangup after 32 seconds (see question PJSUA2 Android - Incoming calls drop after 32 seconds)
removing ;pn-provider="fcm" works
sending just a portion of the token works (in pn-tok, together with the pn-provider parameter)
I thought it may be a "invalid characters issue", but it actually seems to be a "max length issue".
Is there a Contact header max length or a URI max length for Contact header? If yes, is it a PJSIP limit or a SIP limit?
My Android app at the moment consumes a URL for a Google spreadsheet through pasting from the clipboard, reading a QR code, or reading from NFC. I'm having trouble writing to an NFC tag and I get this error:
[ERROR:nfa_rw_act.cc(1571)] Unable to write NDEF. Tag maxsize=137, request write size=171
I cannot write to this tag because the payload I'm trying to write it is larger than the writable space on it.
All I'm trying to do is write the URL I've already read (from clipboard or QR) to an NFC tag, and also add my app record so it launches the Play Store to my app in case the user doesn't have it installed already. Unfortunately, it seems this is too much data. I thought about maybe only including the spreadsheetID value, but I think this will add complications in the future when I inevitably want to add support for spreadsheets outside of Google Sheets.
Here's how I'm writing it currently:
public NdefMessage createNdefMessage() {
String text = "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit?usp=sharing";
NdefRecord appRecord = NdefRecord.createApplicationRecord(context.getPackageName());
NdefRecord relayRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, new String("application/" + context.getPackageName()).getBytes(Charset.forName("US-ASCII")), null, text.getBytes());
return new NdefMessage(new NdefRecord[] {relayRecord, appRecord});
}
Is writing just the ID ("1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms" in this case) my only option? I'm also assuming the NFC tag I'm using is an average size and not a super tiny one for 2018.
EDIT:
Thanks to Michael I was able to get it to fit, though barely (134/137 bytes). I write the URI to NFC via this:
NdefRecord relayRecord = NdefRecord.createUri(text);
I added this intent filter to catch it:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https" android:host="docs.google.com"/>
</intent-filter>
And I read the NFC tag with this:
NdefMessage[] messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < rawMessages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
}
for (NdefRecord r : messages[0].getRecords()) {
if (r.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
byte[] payload = r.getPayload();
try {
String payloadText = new String(payload, 1, payload.length - 1, "UTF-8");
int firstByte = payload[0];
return getUriPrefix(firstByte) + payloadText;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return "Read error";
}
}
}
And the first byte from the URI compression I get via this, even though I only ever assign "04" (4 when I read it as int) in my app:
private String getUriPrefix(int firstByte) {
if (firstByte == 0) {
return "";
} else if (firstByte == 1) {
return "http://www.";
} else if (firstByte == 2) {
return "https://www.";
} else if (firstByte == 3) {
return "http://";
} else if (firstByte == 4) {
return "https://";
} else {
return "";
}
}
The error message that you got in the log is pretty clear. Your NDEF message is too large to fit the data area of the tag. The obvious solution would be to use NFC tags with a sufficiently large storage capacity -- there's quite a few larger tags available. There's nothing else you could do about it if you want to store exactly that NDEF message.
However, there is ways to improve the NDEF message itself. You currently use a MIME type record to store a URL. That's definitely not the best choice of a record type to store a URL. (or actually any application-specific data). The MIME type that you chose costs ("application/" + context.getPackageName()).length() = 31 bytes (assuming your package name is 19 bytes).
If you used an NFC Forum external type record instead, you could create a much shorter type name of the form "mydomain.tld:appurl", so this would save quite a few bytes.
relayRecord = NdefRecord.createExternal("mydomain.tld", "appurl", text.getBytes());
Since you want to store a URL, there's even a more efficient (and readily available) record type: the NFC Forum URI well-known type. The type name of that record consists only of the single letter "U", so this would already save 30 bytes compared to your MIME type record. Moreover, the URI record type uses a compression scheme to further reduce the size of the record: there is a list of well-known URI prefixes (e.g. "https://") that can be represented as a single byte in the URI record (the method NdefRecord.createUri() will automatically take care of that compression). Consequently, you would save another 7 bytes.
relayRecord = NdefRecord.createUri(text);
I have an application that uses a webview in order to display content and the Javascript calls are the controller of my application.
In order to provide a level of security I obfuscated the code. This is not enough as I would like to encrypt the html and js files and then decrypt them at runtime. I packed the apk file with these resources encrypted with RC4 algorithm. When loading the files, I am decrypting the javascript files, load them and then decrypt the html file and load it. However this doesn't work as the webcontent displays a message in the form of: the web page at data:text/html might be temporarily down or it may have moved permanently, etc, etc.
I overloaded onLoadResource in order to see what content is loaded and I can see it loads the Javascript content, but the content loaded is html escaped also.
My questions are:
1. How to secure the html and javascript files (located in assets folder) in order to not be accessible?
2. In case my approach is correct, has anyone any idea on what I am doing wrong?
Thanks!
Below is the code that decrypts and loads the resources:
protected void loadWebContent() {
checkEncryptionEnabled();
loadJSFiles();
logger.info("Loaded js ... going for html");
loadAssetFile("www/index.html", "text/html");
}
private void loadJSFiles() {
String[] jsFilesArray = { "app.js", "iscroll.js", "iui.js", "json.js" };
for (String js : jsFilesArray) {
loadAssetFile("www/js/" + js, "application/javascript");
}
}
private void loadAssetFile(String filePath, String mimeType) {
AssetManager assetMgr = getAssets();
InputStream is = null;
try {
is = assetMgr.open(filePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] temp = new byte[512];
int bytesRead = -1;
while ((bytesRead = is.read(temp)) > 0) {
baos.write(temp, 0, bytesRead);
}
byte[] encrypted = baos.toByteArray();
String content = null;
/**
* true
* */
if (Config.ENCRYPTION_ENABLED) {
byte[] decrypted = new RC4Encrypter("rc4_key").rc4(encrypted);
content = new String(decrypted, "utf-8");
} else {
content = new String(encrypted, "utf-8");
}
/**
* The webview to use
* */
if("application/javascript".equals(mimeType)) {
webContent.loadUrl("javascript:" + content);
} else {
webContent.loadData(content, mimeType, "utf-8");
}
} catch (IOException ex) {
logger.error(null, ex);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
}
found the answer for the second question question instead of: webContent.loadData(content, mimeType, "utf-8"); I used: webContent.loadDataWithBaseURL("file:///android_asset/www/", content, mimeType, "utf-8", null); Content is shown with no problems ... However, the first question kind of stands and not; but considering there was no answer for more than a year, I'll consider encrypting data is OK.
Data encryption is OK as long as you can also keep the decryption key confidential, which is not the case in the above code. The hardcoded decryption key can be easily spotted after decompiling the DEX files embedded inside the APK.
If you want to hide the application logic inside the HTML and JavaScript files and if that application logic doesn't require offline capabilities then you could outsource the code of that application logic on a server.
From here you have two choices:
Load the application code dynamically from the server whenever
you need it (and run the application code on the client).
Implement the application logic on the server side, e.g., as a
web service (and run the application code on the server, the client
knows only how to call the web service)
The short answer to your first question is that there is no methodology or technology to perfectly protect your application. I recommend to you to take a look at How to avoid reverse engineering of an APK file? for an overview of possible protection methods.
I am writing an Android GMail client application.
When it creates a message, I add a header to it like this:
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(username));
msg.setSubject(subject);
msg.setText(message);
msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
long time = someTime;
msg.addHeader("My_Header", Long.toString(time));
//IMAPFolder f declaration & initialization
f.open(Folder.READ_WRITE);
f.addMessages(new Message[]{msg});
f.close(true);
It all works fine and I am able to see the header correctly added to the message when viewing my GMail account on my PC. However, when I later try to retrieve the header information, it gets very weird.
When I call
String[] str = msg.getHeader("My_Header");
getHeader() returns null if I am running the app. However, when I debug the app and set a breakpoint before the getHeader() call, it returns the header value corrently.
My source code:
MimeMessage msg = getNextMessage();
String subject = msg.getSubject();
InternetAddresses to[] = msg.getAllRecipients()
String when[] = msg.getHeader(GMailClient.TIME_TO_SEND);
if (when == null) {
Log.i(TAG, "Null Header");
} else {
long time = Long.parseLong(when[0]);
Log.i(TAG, "Value retrieved: " + when[0]);
}
Possibly Gmail isn't returning the header information correctly via IMAP? You might want to look at the protocol trace to see exactly what the server is returning for your request.
Also, try writing out the entire message using (e.g.)
msg.writeTo(new FileOutputStream("msg.txt"));
and see if the header is there. If it is, and the protocol trace shows that it's not being returned for the getHeader call, it's a bug in the server. In that case, see the JavaMail FAQ entry for how to work around such server bugs.