package com.Pau.ImapNotes2.Sync; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.GeneralSecurityException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.mail.Flags; import javax.mail.Folder; import javax.mail.MessagingException; import javax.mail.Store; import javax.mail.UIDFolder; import javax.mail.internet.MimeMessage; import org.apache.commons.io.FileUtils; import javax.mail.Message; import javax.mail.Session; import com.Pau.ImapNotes2.Data.NotesDb; import com.Pau.ImapNotes2.Miscs.ImapNotes2Result; import com.Pau.ImapNotes2.Miscs.OneNote; import com.Pau.ImapNotes2.Miscs.Sticky; import com.sun.mail.util.MailSSLSocketFactory; import com.sun.mail.imap.IMAPStore; import com.sun.mail.imap.AppendUID; import com.sun.mail.imap.IMAPFolder; import android.accounts.Account; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; public class SyncUtils { static Store store; static Session session; static final String TAG = "IN_SyncUtils"; static String proto; static String acceptcrt; static String sfolder = "Notes"; static private String folderoverride; static Folder notesFolder = null; static ImapNotes2Result res; static Long UIDValidity; private final static int NEW = 1; private final static int DELETED = 2; private final static int ROOT_AND_NEW = 3; private static Boolean useProxy = false; public static ImapNotes2Result ConnectToRemote(String username, String password, String server, String portnum, String security, String usesticky, String override) throws MessagingException{ if (IsConnected()) store.close(); res = new ImapNotes2Result(); if (override==null) { folderoverride = ""; } else { folderoverride = override; } proto = ""; acceptcrt = ""; int security_i = Integer.parseInt(security); switch (security_i) { case 0: // None proto = "imap"; acceptcrt = ""; break; case 1: // SSL/TLS proto = "imaps"; acceptcrt = "false"; break; case 2: // SSL/TLS/TRUST ALL proto = "imaps"; acceptcrt = "true"; break; case 3: // STARTTLS proto = "imap"; acceptcrt = "false"; break; case 4: // STARTTLS/TRUST ALL proto = "imap"; acceptcrt = "true"; break; ////////////////////// Change this default: proto = "Invalid proto"; break; } MailSSLSocketFactory sf = null; try { sf = new MailSSLSocketFactory(); } catch (GeneralSecurityException e) { e.printStackTrace(); res.errorMessage = "Can't connect to server"; res.returnCode = -1; return res; } Properties props = new Properties(); props.setProperty(String.format("mail.%s.host", proto), server); props.setProperty(String.format("mail.%s.port", proto), portnum); props.setProperty("mail.store.protocol", proto); if ((acceptcrt.equals("true"))) { sf.setTrustedHosts(new String[] {server}); if (proto.equals("imap")) { props.put("mail.imap.ssl.socketFactory", sf); props.put("mail.imap.starttls.enable", "true"); } } else if (acceptcrt.equals("false")) { props.put(String.format("mail.%s.ssl.checkserveridentity", proto), "true"); if (proto.equals("imap")) { props.put("mail.imap.starttls.enable", "true"); } } if (proto.equals("imaps")) { props.put("mail.imaps.socketFactory", sf); } props.setProperty("mail.imap.connectiontimeout","1000"); if (useProxy) { props.put("mail.imap.socks.host","10.0.2.2"); props.put("mail.imap.socks.port","1080"); } session = Session.getInstance(props, null); //this.session.setDebug(true); store = session.getStore(proto); try { store.connect(server, username, password); res.hasUIDPLUS = ((IMAPStore) store).hasCapability("UIDPLUS"); //Log.d(TAG, "has UIDPLUS="+res.hasUIDPLUS); Folder[] folders = store.getPersonalNamespaces(); Folder folder = folders[0]; //Log.d(TAG,"Personal Namespaces="+folder.getFullName()); if (folderoverride.length() > 0) { sfolder = folderoverride; } else if (folder.getFullName().length() == 0) { sfolder = "Notes"; } else { char separator = folder.getSeparator(); sfolder = folder.getFullName() + separator + "Notes"; } // Get UIDValidity notesFolder = store.getFolder(sfolder); res.UIDValidity = ((IMAPFolder) notesFolder).getUIDValidity(); res.errorMessage = ""; res.returnCode = 0; res.notesFolder = notesFolder; return res; } catch (Exception e) { Log.d(TAG, e.getMessage()); res.errorMessage = e.getMessage(); res.returnCode = -2; return res; } } public static void GetNotes(Account account, Folder notesFolder, Context ctx, NotesDb storedNotes) throws MessagingException, IOException{ Long UIDM; Message notesMessage; File directory = new File (ctx.getFilesDir() + "/" + account.name); if (notesFolder.isOpen()) { if ((notesFolder.getMode() & Folder.READ_ONLY) != 0) notesFolder.open(Folder.READ_ONLY); } else { notesFolder.open(Folder.READ_ONLY); } UIDValidity = GetUIDValidity(account, ctx); SetUIDValidity(account, UIDValidity, ctx); Message[] notesMessages = notesFolder.getMessages(); //Log.d(TAG,"number of messages in folder="+(notesMessages.length)); for(int index=notesMessages.length-1; index>=0; index--) { notesMessage = notesMessages[index]; // write every message in files/{accountname} directory // filename is the original message uid UIDM=((IMAPFolder)notesFolder).getUID(notesMessage); String suid = UIDM.toString(); File outfile = new File (directory, suid); GetOneNote(outfile, notesMessage, storedNotes, account.name, suid, true); } } public static Sticky ReadStickynote(String stringres) { String color=new String(""); String position=new String(""); String text=new String(""); Pattern p = null; Matcher m = null; p = Pattern.compile("^COLOR:(.*?)$",Pattern.MULTILINE); m = p.matcher(stringres); if (m.find()) { color = m.group(1); } p = Pattern.compile("^POSITION:(.*?)$",Pattern.MULTILINE); m = p.matcher(stringres); if (m.find()) { position = m.group(1); } p = Pattern.compile("TEXT:(.*?)(END:|POSITION:)",Pattern.DOTALL); m = p.matcher(stringres); if (m.find()) { text = m.group(1); // Kerio Connect puts CR+LF+space every 78 characters from line 2 // first line seem to be smaller. We remove these characters text = text.replaceAll("\r\n ", ""); // newline in Kerio is the string (not the character) "\n" text = text.replaceAll("\\\\n", "<br>"); } return new Sticky(text, position, color); } public static boolean IsConnected(){ return store!=null && store.isConnected(); } public static void DeleteNote(Folder notesFolder, int numMessage) throws MessagingException, IOException { notesFolder = store.getFolder(sfolder); if (notesFolder.isOpen()) { if ((notesFolder.getMode() & Folder.READ_WRITE) != 0) notesFolder.open(Folder.READ_WRITE); } else { notesFolder.open(Folder.READ_WRITE); } //Log.d(TAG,"UID to remove:"+numMessage); Message[] msgs = {((IMAPFolder)notesFolder).getMessageByUID(numMessage)}; notesFolder.setFlags(msgs, new Flags(Flags.Flag.DELETED), true); ((IMAPFolder)notesFolder).expunge(msgs); } // Put values in shared preferences public static void SetUIDValidity(Account account, Long UIDValidity, Context ctx) { SharedPreferences preferences = ctx.getSharedPreferences(account.name, Context.MODE_MULTI_PROCESS); SharedPreferences.Editor editor = preferences.edit(); editor.putString("Name","valid_data"); //Log.d(TAG, "UIDValidity set to in shared_prefs:"+UIDValidity); editor.putLong("UIDValidity", UIDValidity); editor.apply(); } // Retrieve values from shared preferences: public static Long GetUIDValidity(Account account, Context ctx) { UIDValidity = (long) -1; SharedPreferences preferences = ctx.getSharedPreferences(account.name, Context.MODE_MULTI_PROCESS); String name = preferences.getString("Name", ""); if(!name.equalsIgnoreCase("")) { UIDValidity = preferences.getLong("UIDValidity", -1); //Log.d(TAG, "UIDValidity got from shared_prefs:"+UIDValidity); } return UIDValidity; } public static void DisconnectFromRemote() { try { store.close(); } catch (MessagingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static Message ReadMailFromFile (String uid, int where, boolean removeMinus, String nameDir) { File mailFile; Message message = null; mailFile = new File (nameDir,uid); switch (where){ case NEW: nameDir = nameDir + "/new"; if (removeMinus) uid = uid.substring(1); break; case DELETED: nameDir = nameDir + "/deleted"; break; case ROOT_AND_NEW: if (!mailFile.exists()) { nameDir = nameDir + "/new"; if (removeMinus) uid = uid.substring(1); } break; default: break; } mailFile = new File (nameDir,uid); InputStream mailFileInputStream = null; try { mailFileInputStream = new FileInputStream(mailFile); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { Properties props = new Properties(); Session session = Session.getDefaultInstance(props, null); message = new MimeMessage(session, mailFileInputStream); } catch (MessagingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return message; } public static AppendUID[] sendMessageToRemote (Message[] message) throws MessagingException, IOException { notesFolder = store.getFolder(sfolder); if (notesFolder.isOpen()) { if ((notesFolder.getMode() & Folder.READ_WRITE) != 0) notesFolder.open(Folder.READ_WRITE); } else { notesFolder.open(Folder.READ_WRITE); } AppendUID[] uids = ((IMAPFolder) notesFolder).appendUIDMessages(message); return uids; } public static void ClearHomeDir(Account account, Context ctx) { File directory = new File (ctx.getFilesDir() + "/" + account.name); try { FileUtils.deleteDirectory(directory); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void CreateDirs (String accountName, Context ctx) { String stringDir = ctx.getFilesDir() + "/" + accountName; File directory = new File (stringDir); directory.mkdirs(); directory = new File (stringDir + "/new"); directory.mkdirs(); directory = new File (stringDir + "/deleted"); directory.mkdirs(); } public static void GetOneNote(File outfile, Message notesMessage, NotesDb storedNotes, String accountName, String suid, boolean updateDb) { OutputStream str=null; try { str = new FileOutputStream(outfile); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { notesMessage.writeTo(str); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MessagingException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (!(updateDb)) return; String title = null; String[] rawvalue = null; // Some servers (such as posteo.de) don't encode non us-ascii characters in subject // This is a workaround to handle them // "lä ö ë" subject should be stored as =?charset?encoding?encoded-text?= // either =?utf-8?B?bMOkIMO2IMOr?= -> Quoted printable // or =?utf-8?Q?l=C3=A4 =C3=B6 =C3=AB?= -> Base64 try { rawvalue = notesMessage.getHeader("Subject"); } catch (Exception e) {e.printStackTrace(); }; try { title = notesMessage.getSubject(); } catch (Exception e) {e.printStackTrace();} if (rawvalue[0].length() >= 2) { if (!(rawvalue[0].substring(0,2).equals("=?"))) { try { title = new String ( title.getBytes("ISO-8859-1")); } catch (Exception e) {e.printStackTrace();} } } else { try { title = new String ( title.getBytes("ISO-8859-1")); } catch (Exception e) {e.printStackTrace();} } // Get INTERNALDATE String internaldate = null; Date MessageInternaldate = null; try { MessageInternaldate = notesMessage.getReceivedDate(); } catch (MessagingException e) { // TODO Auto-generated catch block e.printStackTrace(); } String DATE_FORMAT = "yyyy-MM-dd HH:MM:ss"; SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); internaldate = sdf.format(MessageInternaldate); OneNote aNote = new OneNote( title, internaldate, suid); storedNotes.InsertANoteInDb(aNote, accountName); } public static boolean handleRemoteNotes (Context context, Folder notesFolder, NotesDb storedNotes, String accountName, String usesticky) throws MessagingException, IOException { Message notesMessage; boolean result = false; ArrayList<Long> uids = new ArrayList<Long>(); ArrayList<String> localListOfNotes = new ArrayList<String>(); String remoteInternaldate; String localInternaldate; Flags flags; Boolean deleted; if (notesFolder.isOpen()) { if ((notesFolder.getMode() & Folder.READ_ONLY) != 0) notesFolder.open(Folder.READ_WRITE); } else { notesFolder.open(Folder.READ_WRITE); } // Get local list of notes uids String rootString = context.getFilesDir() + "/" + accountName; File rootDir = new File (rootString); File[] files = rootDir.listFiles(); for (File file : files) { if (file.isFile()) { localListOfNotes.add(file.getName()); } } // Add to local device, new notes added to remote Message[] notesMessages = ((IMAPFolder)notesFolder).getMessagesByUID(1, UIDFolder.LASTUID); for(int index=notesMessages.length-1; index>=0; index--) { notesMessage = notesMessages[index]; Long uid = ((IMAPFolder)notesFolder).getUID(notesMessage); // Get FLAGS flags = notesMessage.getFlags(); deleted = notesMessage.isSet(Flags.Flag.DELETED); // Buils remote list while in the loop, but only if not deleted on remote if (!deleted) { uids.add(((IMAPFolder)notesFolder).getUID(notesMessage)); } String suid = uid.toString(); if (!(localListOfNotes.contains(suid))) { File outfile = new File (rootDir, suid); GetOneNote(outfile, notesMessage, storedNotes, accountName, suid, true); result = true; } else if (usesticky.equals("true")) { //Log.d (TAG,"MANAGE STICKY"); remoteInternaldate = notesMessage.getSentDate().toLocaleString(); localInternaldate = storedNotes.GetDate(suid, accountName); if (!(remoteInternaldate.equals(localInternaldate))) { File outfile = new File (rootDir, suid); GetOneNote(outfile, notesMessage, storedNotes, accountName, suid, false); result = true; } } } // Remove from local device, notes removed from remote for(String suid : localListOfNotes) { int uid = Integer.valueOf(suid); if (!(uids.contains(new Long(uid)))) { // remove file from deleted File toDelete = new File (rootDir, suid); toDelete.delete(); // Remove note from database storedNotes.DeleteANote(suid, accountName); result = true; } } return result; } public static void RemoveAccount(Context context, Account account) { // remove Shared Preference file String rootString = context.getFilesDir().getParent() + File.separator + "shared_prefs"; File rootDir = new File (rootString); File toDelete = new File (rootDir, account.name + ".xml"); toDelete.delete(); // Remove all files and sub directories File filesDir = context.getFilesDir(); File[] files = filesDir.listFiles(); for (int i = 0; i < files.length; i++) { files[i].delete(); } // Delete account name entries in database NotesDb storedNotes = new NotesDb(context); storedNotes.OpenDb(); storedNotes.ClearDb(account.name); storedNotes.CloseDb(); } }