OSDN Git Service

Add Linked notebooks to the ignore sync process.
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / threads / SyncRunner.java
index 5ce4636..1fcc42a 100644 (file)
 */\r
 package cx.fbn.nevernote.threads;\r
 \r
+import java.io.BufferedOutputStream;\r
+import java.io.File;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.UnsupportedEncodingException;\r
 import java.net.UnknownHostException;\r
 import java.util.ArrayList;\r
 import java.util.Calendar;\r
 import java.util.Date;\r
 import java.util.GregorianCalendar;\r
 import java.util.List;\r
+import java.util.TreeSet;\r
 import java.util.Vector;\r
 import java.util.concurrent.LinkedBlockingQueue;\r
 \r
+import org.apache.http.HttpEntity;\r
+import org.apache.http.HttpResponse;\r
+import org.apache.http.NameValuePair;\r
+import org.apache.http.client.ClientProtocolException;\r
+import org.apache.http.client.HttpClient;\r
+import org.apache.http.client.entity.UrlEncodedFormEntity;\r
+import org.apache.http.client.methods.HttpPost;\r
+import org.apache.http.impl.client.DefaultHttpClient;\r
+import org.apache.http.message.BasicNameValuePair;\r
+import org.apache.http.protocol.HTTP;\r
 import org.apache.thrift.TException;\r
 import org.apache.thrift.protocol.TBinaryProtocol;\r
 import org.apache.thrift.transport.THttpClient;\r
@@ -39,18 +56,22 @@ import com.evernote.edam.notestore.NoteStore;
 import com.evernote.edam.notestore.SyncChunk;\r
 import com.evernote.edam.notestore.SyncState;\r
 import com.evernote.edam.type.Data;\r
+import com.evernote.edam.type.LinkedNotebook;\r
 import com.evernote.edam.type.Note;\r
 import com.evernote.edam.type.Notebook;\r
 import com.evernote.edam.type.Resource;\r
 import com.evernote.edam.type.SavedSearch;\r
+import com.evernote.edam.type.SharedNotebook;\r
 import com.evernote.edam.type.Tag;\r
 import com.evernote.edam.type.User;\r
 import com.evernote.edam.userstore.AuthenticationResult;\r
 import com.evernote.edam.userstore.UserStore;\r
+import com.trolltech.qt.core.QByteArray;\r
+import com.trolltech.qt.core.QFile;\r
+import com.trolltech.qt.core.QIODevice.OpenModeFlag;\r
 import com.trolltech.qt.core.QObject;\r
 import com.trolltech.qt.gui.QMessageBox;\r
 \r
-import cx.fbn.nevernote.Global;\r
 import cx.fbn.nevernote.signals.NoteIndexSignal;\r
 import cx.fbn.nevernote.signals.NoteResourceSignal;\r
 import cx.fbn.nevernote.signals.NoteSignal;\r
@@ -60,15 +81,15 @@ import cx.fbn.nevernote.signals.StatusSignal;
 import cx.fbn.nevernote.signals.SyncSignal;\r
 import cx.fbn.nevernote.signals.TagSignal;\r
 import cx.fbn.nevernote.sql.DatabaseConnection;\r
-import cx.fbn.nevernote.sql.runners.DeletedItemRecord;\r
+import cx.fbn.nevernote.sql.DeletedItemRecord;\r
 import cx.fbn.nevernote.utilities.ApplicationLogger;\r
 \r
 public class SyncRunner extends QObject implements Runnable {\r
        \r
        private final ApplicationLogger logger;\r
-               private final DatabaseConnection                conn;\r
+               private DatabaseConnection              conn;\r
                private boolean                                 idle;\r
-               private boolean                                 error;\r
+               public boolean                                  error;\r
                public volatile boolean                 isConnected;\r
                public volatile boolean                 keepRunning;\r
                public volatile String                  authToken;\r
@@ -100,6 +121,7 @@ public class SyncRunner extends QObject implements Runnable {
            private THttpClient userStoreTrans;\r
            private TBinaryProtocol userStoreProt;\r
            private AuthenticationResult authResult;\r
+           private AuthenticationResult linkedAuthResult;\r
            private User user; \r
            private long authTimeRemaining;\r
            public long authRefreshTime;\r
@@ -111,12 +133,18 @@ public class SyncRunner extends QObject implements Runnable {
            public int updateSequenceNumber;\r
            private boolean refreshNeeded;\r
            private volatile LinkedBlockingQueue<String> workQueue;\r
-//             private static int MAX_EMPTY_QUEUE_COUNT = 1;\r
                private static int MAX_QUEUED_WAITING = 1000;\r
+               String dbuid;\r
+               String dburl;\r
+               String dbpswd;\r
+               String dbcpswd;\r
+               private final TreeSet<String> ignoreTags;\r
+               private final TreeSet<String> ignoreNotebooks;\r
+               private final TreeSet<String> ignoreLinkedNotebooks;\r
        \r
                \r
                \r
-       public SyncRunner(String logname) {\r
+       public SyncRunner(String logname, String u, String uid, String pswd, String cpswd) {\r
                logger = new ApplicationLogger(logname);\r
                \r
                noteSignal = new NoteSignal();\r
@@ -128,9 +156,12 @@ public class SyncRunner extends QObject implements Runnable {
                searchSignal = new SavedSearchSignal();\r
                syncSignal = new SyncSignal();\r
                resourceSignal = new NoteResourceSignal();\r
-               \r
+               dbuid = uid;\r
+               dburl = u;\r
+               dbpswd = pswd;\r
+               dbcpswd = cpswd;\r
 //             this.setAutoDelete(false);\r
-               conn = new DatabaseConnection(logger, Global.syncThreadId);\r
+               \r
                isConnected = false;\r
                syncNeeded = false;\r
                authRefreshNeeded = false;\r
@@ -140,6 +171,10 @@ public class SyncRunner extends QObject implements Runnable {
                userStore = null;\r
                authToken = null;\r
                disableUploads = false;\r
+               ignoreTags = new TreeSet<String>();\r
+               ignoreNotebooks = new TreeSet<String>();\r
+               ignoreLinkedNotebooks = new TreeSet<String>();\r
+               \r
 //             setAutoDelete(false);\r
                workQueue=new LinkedBlockingQueue<String>(MAX_QUEUED_WAITING);\r
        }\r
@@ -147,6 +182,7 @@ public class SyncRunner extends QObject implements Runnable {
        public void run() {\r
                try {\r
                        logger.log(logger.EXTREME, "Starting thread");\r
+                       conn = new DatabaseConnection(logger, dburl, dbuid, dbpswd, dbcpswd);\r
                        while(keepRunning) {\r
                                String work = workQueue.take();\r
                                logger.log(logger.EXTREME, "Work found: " +work);\r
@@ -154,7 +190,7 @@ public class SyncRunner extends QObject implements Runnable {
                                        return;\r
                                idle=false;\r
                                error=false;\r
-                               if (authRefreshNeeded == true) {\r
+                               if (authRefreshNeeded == true || !isConnected) {\r
                                        logger.log(logger.EXTREME, "Refreshing connection");\r
                                        refreshConnection();\r
                                }\r
@@ -175,11 +211,16 @@ public class SyncRunner extends QObject implements Runnable {
                                idle=true;\r
                                logger.log(logger.EXTREME, "Signaling refresh finished.  refreshNeeded=" +refreshNeeded);\r
                                syncSignal.finished.emit(refreshNeeded);\r
+                               if (error) {\r
+                                       syncSignal.errorDisconnect.emit();\r
+                                       status.message.emit(tr("Error synchronizing - see log for details."));\r
+                               }\r
                        }\r
                }       \r
                catch (InterruptedException e1) {\r
                        e1.printStackTrace();\r
                }\r
+               conn.dbShutdown();\r
        }\r
 \r
        \r
@@ -222,11 +263,28 @@ public class SyncRunner extends QObject implements Runnable {
        @SuppressWarnings("unused")\r
        private void evernoteSync() throws java.net.UnknownHostException {\r
                logger.log(logger.HIGH, "Entering SyncRunner.evernoteSync");\r
+               \r
+               // Rebuild list of tags & notebooks to ignore\r
+               ignoreNotebooks.clear();\r
+               List<String> ignore = conn.getSyncTable().getIgnoreRecords("NOTEBOOK");\r
+               for (int i=0; i<ignore.size(); i++) \r
+                       ignoreNotebooks.add(ignore.get(i));\r
+               \r
+               ignore.clear();\r
+               ignore = conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK");\r
+               for (int i=0; i<ignore.size(); i++) \r
+                       ignoreLinkedNotebooks.add(ignore.get(i));\r
+               \r
+               ignoreTags.clear();\r
+               ignore = conn.getSyncTable().getIgnoreRecords("TAG");\r
+               for (int i=0; i<ignore.size(); i++) \r
+                       ignoreTags.add(ignore.get(i));\r
 \r
+               // Make sure we are connected & should keep running\r
                if (isConnected && keepRunning) {\r
                        error = false;\r
                        logger.log(logger.EXTREME, "Synchronizing with Evernote");\r
-                       status.message.emit("Synchronizing with Evernote");\r
+                       status.message.emit(tr("Synchronizing with Evernote"));\r
                        \r
                        // Get user information\r
                        try {\r
@@ -236,20 +294,23 @@ public class SyncRunner extends QObject implements Runnable {
                                syncSignal.saveUserInformation.emit(user);\r
                        } catch (EDAMUserException e1) {\r
                                e1.printStackTrace();\r
-                               status.message.emit("User exception getting user account information.  Aborting sync and disconnecting");\r
+                               status.message.emit(tr("User exception getting user account information.  Aborting sync and disconnecting"));\r
                                syncSignal.errorDisconnect.emit();\r
+                               error = true;\r
                                enDisconnect();\r
                                return;\r
                        } catch (EDAMSystemException e1) {\r
                                e1.printStackTrace();\r
-                               status.message.emit("System error user account information.  Aborting sync and disconnecting!");\r
+                               status.message.emit(tr("System error user account information.  Aborting sync and disconnecting!"));\r
                                syncSignal.errorDisconnect.emit();\r
+                               error = true;\r
                                enDisconnect();\r
                                return;\r
                        } catch (TException e1) {\r
                                e1.printStackTrace();\r
                                syncSignal.errorDisconnect.emit();\r
-                               status.message.emit("Transaction error getting user account information.  Aborting sync and disconnecting!");\r
+                               error = true;\r
+                               status.message.emit(tr("Transaction error getting user account information.  Aborting sync and disconnecting!"));\r
                                enDisconnect();\r
                                return;\r
                        }\r
@@ -264,19 +325,19 @@ public class SyncRunner extends QObject implements Runnable {
                                evernoteUpdateCount = syncState.getUpdateCount();\r
                        } catch (EDAMUserException e) {\r
                                e.printStackTrace();\r
-                               status.message.emit("Error getting sync state! Aborting sync and disconnecting!");\r
+                               status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));\r
                                syncSignal.errorDisconnect.emit();\r
                                enDisconnect();\r
                                return;\r
                        } catch (EDAMSystemException e) {\r
                                e.printStackTrace();\r
-                               status.message.emit("Error getting sync state! Aborting sync and disconnecting!");\r
+                               status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));\r
                                syncSignal.errorDisconnect.emit();\r
                                enDisconnect();\r
                                return;\r
                        } catch (TException e) {\r
                                e.printStackTrace();\r
-                               status.message.emit("Error getting sync state! Aborting sync and disconnecting!");\r
+                               status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));\r
                                syncSignal.errorDisconnect.emit();\r
                                enDisconnect();\r
                                return;\r
@@ -284,7 +345,7 @@ public class SyncRunner extends QObject implements Runnable {
                        \r
                        if (syncState == null) {\r
                                logger.log(logger.EXTREME, "Sync State is null");\r
-                               status.message.emit("Syncronization Error!");\r
+                               status.message.emit(tr("Syncronization Error!"));\r
                                return;\r
                        }\r
 \r
@@ -299,7 +360,29 @@ public class SyncRunner extends QObject implements Runnable {
                                updateSequenceNumber = 0;\r
                                conn.getSyncTable().setUpdateSequenceNumber(0);\r
                        }\r
-\r
+                       // Check for "special" sync instructions\r
+                       String syncLinked = conn.getSyncTable().getRecord("FullLinkedNotebookSync");\r
+                       String syncShared = conn.getSyncTable().getRecord("FullLinkedNotebookSync");\r
+                       String syncNotebooks = conn.getSyncTable().getRecord("FullNotebookSync");\r
+                       String syncInkNoteImages = conn.getSyncTable().getRecord("FullInkNoteImageSync");\r
+                       if (syncLinked != null) {\r
+                               downloadAllLinkedNotebooks();\r
+                       }\r
+                       if (syncShared != null) {\r
+                               downloadAllSharedNotebooks();\r
+                       }\r
+                       if (syncNotebooks != null) {\r
+                               downloadAllNotebooks();\r
+                       }\r
+                       \r
+                       if (syncInkNoteImages != null) {\r
+                               List<String> guids = conn.getNoteTable().noteResourceTable.findInkNotes();\r
+                               for (int i=0; i<guids.size(); i++) {\r
+                                       downloadInkNoteImage(guids.get(i), authToken);\r
+                               }\r
+                               conn.getSyncTable().deleteRecord("FullInkNoteImageSync");\r
+                       }\r
+                       \r
                        // If there are remote changes\r
                        logger.log(logger.LOW, "Update Count: " +syncState.getUpdateCount());\r
                        logger.log(logger.LOW, "Last Update Count: " +updateSequenceNumber);\r
@@ -311,6 +394,11 @@ public class SyncRunner extends QObject implements Runnable {
                                syncRemoteToLocal();\r
                        }\r
                        \r
+                       //*****************************************\r
+                       //* Sync linked/shared notebooks \r
+                       //*****************************************\r
+                       syncLinkedNotebooks();\r
+                       \r
                        if (!disableUploads) {\r
                                logger.log(logger.EXTREME, "Uploading changes");\r
                                // Synchronize remote changes\r
@@ -320,6 +408,8 @@ public class SyncRunner extends QObject implements Runnable {
                                        syncLocalTags();\r
                                if (!error)\r
                                        syncLocalNotebooks();\r
+                               if (!error)\r
+                                       syncLocalLinkedNotebooks();\r
                                if (!error) \r
                                        syncDeletedNotes();\r
                                if (!error)\r
@@ -327,14 +417,25 @@ public class SyncRunner extends QObject implements Runnable {
                                if (!error)\r
                                        syncLocalSavedSearches();\r
                        }\r
+                       \r
+                       status.message.emit(tr("Cleaning up"));\r
+                       List<String> notes = conn.getNoteTable().expungeIgnoreSynchronizedNotes(conn.getSyncTable().getIgnoreRecords("NOTEBOOK"), \r
+                                       conn.getSyncTable().getIgnoreRecords("TAG"), conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK"));\r
+                       if (notes.size() > 0)\r
+                               syncSignal.refreshLists.emit();\r
+                       \r
+                       //*****************************************\r
+                       //* End of synchronization\r
+                       //*****************************************\r
                        if (refreshNeeded)\r
                                syncSignal.refreshLists.emit();\r
+                       \r
                        if (!error) {\r
                                logger.log(logger.EXTREME, "Sync completed.  Errors=" +error);\r
-                               if (!disableUploads)\r
-                                       status.message.emit("Synchronizing complete");\r
+                               if (!disableUploads) \r
+                                       status.message.emit(tr("Synchronizing complete"));\r
                                else\r
-                                       status.message.emit("Download syncronization complete.  Uploads have been disabled.");\r
+                                       status.message.emit(tr("Download syncronization complete.  Uploads have been disabled."));\r
                                \r
                                logger.log(logger.EXTREME, "Saving sync time");\r
                                if (syncState.getCurrentTime() > sequenceDate)\r
@@ -347,16 +448,18 @@ public class SyncRunner extends QObject implements Runnable {
                }\r
                logger.log(logger.HIGH, "Leaving SyncRunner.evernoteSync");\r
        }\r
+       \r
        // Sync deleted items with Evernote\r
        private void syncExpunged() {\r
                logger.log(logger.HIGH, "Entering SyncRunner.syncExpunged");\r
                \r
                List<DeletedItemRecord> expunged = conn.getDeletedTable().getAllDeleted();\r
                boolean error = false;\r
-               for (int i=0; i<expunged.size(); i++) {\r
+               for (int i=0; i<expunged.size() && keepRunning; i++) {\r
                        \r
                        if (authRefreshNeeded)\r
-                               refreshConnection();\r
+                               if (!refreshConnection())\r
+                                       return;\r
 \r
                        try {\r
                                if (expunged.get(i).type.equalsIgnoreCase("TAG")) {\r
@@ -408,14 +511,15 @@ public class SyncRunner extends QObject implements Runnable {
                if (syncDeletedContent)\r
                        return;\r
                logger.log(logger.HIGH, "Entering SyncRunner.syncDeletedNotes");\r
-               status.message.emit("Synchronizing deleted notes.");\r
+               status.message.emit(tr("Synchronizing deleted notes."));\r
 \r
                List<Note> notes = conn.getNoteTable().getDirty();\r
                // Sync the local notebooks with Evernote's\r
                for (int i=0; i<notes.size() && keepRunning; i++) {\r
                        \r
                        if (authRefreshNeeded)\r
-                               refreshConnection();\r
+                               if (!refreshConnection())\r
+                                       return;\r
                        \r
                        Note enNote = notes.get(i);\r
                        try {\r
@@ -446,7 +550,7 @@ public class SyncRunner extends QObject implements Runnable {
                                //error = true;\r
                        } catch (EDAMSystemException e) {\r
                                logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);\r
-                               status.message.emit("Error: " +e);\r
+                               status.message.emit(tr("Error: ") +e);\r
                                logger.log(logger.LOW, e.toString());           \r
                                error = true;\r
                        } catch (EDAMNotFoundException e) {\r
@@ -456,7 +560,7 @@ public class SyncRunner extends QObject implements Runnable {
                                //error = true;\r
                        } catch (TException e) {\r
                                logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);\r
-                               status.message.emit("Error sending local note: " +e);\r
+                               status.message.emit(tr("Error sending local note: ") +e);\r
                                logger.log(logger.LOW, e.toString());   \r
                                error = true;\r
                        }               \r
@@ -465,103 +569,112 @@ public class SyncRunner extends QObject implements Runnable {
        // Sync notes with Evernote\r
        private void syncLocalNotes() {\r
                logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
-               status.message.emit("Sending local notes.");\r
+               status.message.emit(tr("Sending local notes."));\r
 \r
                List<Note> notes = conn.getNoteTable().getDirty();\r
                // Sync the local notebooks with Evernote's\r
                for (int i=0; i<notes.size() && keepRunning; i++) {\r
+                       syncLocalNote(notes.get(i), authToken);\r
+               }\r
+               logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
+\r
+       }\r
+       // Sync notes with Evernote\r
+       private void syncLocalNote(Note enNote, String token) {\r
+               logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
+               status.message.emit(tr("Sending local notes."));\r
+\r
+               if (authRefreshNeeded)\r
+                       if (!refreshConnection())\r
+                               return;\r
                        \r
-                       if (authRefreshNeeded)\r
-                               refreshConnection();\r
-                       \r
-                       Note enNote = notes.get(i);\r
-                       if (enNote.isActive()) {\r
-                               try {\r
-                                       logger.log(logger.EXTREME, "Active dirty note found - non new");\r
-                                       if (enNote.getUpdateSequenceNum() > 0) {\r
-                                               enNote = getNoteContent(enNote);\r
-                                               logger.log(logger.MEDIUM, "Updating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");\r
-                                               enNote = noteStore.updateNote(authToken, enNote);\r
-                                       } else { \r
-                                               logger.log(logger.EXTREME, "Active dirty found - new note");\r
-                                               logger.log(logger.MEDIUM, "Creating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");\r
-                                               String oldGuid = enNote.getGuid();\r
-                                               enNote = getNoteContent(enNote);\r
-                                               enNote = noteStore.createNote(authToken, enNote);\r
-                                               noteSignal.guidChanged.emit(oldGuid, enNote.getGuid());\r
-                                               conn.getNoteTable().updateNoteGuid(oldGuid, enNote.getGuid());\r
-                                       }\r
-                                       updateSequenceNumber = enNote.getUpdateSequenceNum();\r
-                                       logger.log(logger.EXTREME, "Saving note");\r
-                                       conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());\r
-                                       List<Resource> rl = enNote.getResources();\r
-                                       logger.log(logger.EXTREME, "Getting note resources");\r
-                                       for (int j=0; j<enNote.getResourcesSize() && keepRunning; j++) {\r
-                                               Resource newRes = rl.get(j);\r
-                                               Data d = newRes.getData();\r
-                                               if (d!=null) {  \r
-                                                       logger.log(logger.EXTREME, "Calculating resource hash");\r
-                                                       String hash = byteArrayToHexString(d.getBodyHash());\r
-                                                       logger.log(logger.EXTREME, "updating resources by hash");\r
-                                                       String oldGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(enNote.getGuid(), hash);\r
-                                                       conn.getNoteTable().updateNoteResourceGuidbyHash(enNote.getGuid(), newRes.getGuid(), hash);\r
-                                                       resourceSignal.resourceGuidChanged.emit(enNote.getGuid(), oldGuid, newRes.getGuid());\r
-                                               }\r
+               if (enNote.isActive()) {\r
+                       try {\r
+                               logger.log(logger.EXTREME, "Active dirty note found - non new");\r
+                               if (enNote.getUpdateSequenceNum() > 0) {\r
+                                       enNote = getNoteContent(enNote);\r
+                                       logger.log(logger.MEDIUM, "Updating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");\r
+                                       enNote = noteStore.updateNote(token, enNote);\r
+                               } else { \r
+                                       logger.log(logger.EXTREME, "Active dirty found - new note");\r
+                                       logger.log(logger.MEDIUM, "Creating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");\r
+                                       String oldGuid = enNote.getGuid();\r
+                                       enNote = getNoteContent(enNote);\r
+                                       enNote = noteStore.createNote(token, enNote);\r
+                                       noteSignal.guidChanged.emit(oldGuid, enNote.getGuid());\r
+                                       conn.getNoteTable().updateNoteGuid(oldGuid, enNote.getGuid());\r
+                               }\r
+                               updateSequenceNumber = enNote.getUpdateSequenceNum();\r
+                               logger.log(logger.EXTREME, "Saving note");\r
+                               conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());\r
+                               List<Resource> rl = enNote.getResources();\r
+                               logger.log(logger.EXTREME, "Getting note resources");\r
+                               for (int j=0; j<enNote.getResourcesSize() && keepRunning; j++) {\r
+                                       Resource newRes = rl.get(j);\r
+                                       Data d = newRes.getData();\r
+                                       if (d!=null) {  \r
+                                               logger.log(logger.EXTREME, "Calculating resource hash");\r
+                                               String hash = byteArrayToHexString(d.getBodyHash());\r
+                                               logger.log(logger.EXTREME, "updating resources by hash");\r
+                                               String oldGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(enNote.getGuid(), hash);\r
+                                               conn.getNoteTable().updateNoteResourceGuidbyHash(enNote.getGuid(), newRes.getGuid(), hash);\r
+                                               resourceSignal.resourceGuidChanged.emit(enNote.getGuid(), oldGuid, newRes.getGuid());\r
                                        }\r
-                                       logger.log(logger.EXTREME, "Resetting note dirty flag");\r
-                                       conn.getNoteTable().resetDirtyFlag(enNote.getGuid());\r
-                                       updateSequenceNumber = enNote.getUpdateSequenceNum();\r
-                                       logger.log(logger.EXTREME, "Emitting note sequence number change");\r
-                                       conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
-\r
-                               } catch (EDAMUserException e) {\r
-                                       logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);\r
-                                       status.message.emit("Error sending local note: " +e.getParameter());\r
-                                       logger.log(logger.LOW, e.toString());   \r
-                                       error = true;\r
-                               } catch (EDAMSystemException e) {\r
-                                       logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);\r
-                                       status.message.emit("Error: " +e);\r
-                                       logger.log(logger.LOW, e.toString());           \r
-                                       error = true;\r
-                               } catch (EDAMNotFoundException e) {\r
-                                       logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);\r
-                                       status.message.emit("Error sending local note: " +e);\r
-                                       logger.log(logger.LOW, e.toString());   \r
-                                       error = true;\r
-                               } catch (TException e) {\r
-                                       logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);\r
-                                       status.message.emit("Error sending local note: " +e);\r
-                                       logger.log(logger.LOW, e.toString());   \r
-                                       error = true;\r
                                }\r
+                               logger.log(logger.EXTREME, "Resetting note dirty flag");\r
+                               conn.getNoteTable().resetDirtyFlag(enNote.getGuid());\r
+                               updateSequenceNumber = enNote.getUpdateSequenceNum();\r
+                               logger.log(logger.EXTREME, "Emitting note sequence number change");\r
+                               conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
+\r
+                       } catch (EDAMUserException e) {\r
+                               logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);\r
+                               status.message.emit(tr("Error sending local note: ")     +e.getParameter());\r
+                               logger.log(logger.LOW, e.toString());   \r
+                               error = true;\r
+                       } catch (EDAMSystemException e) {\r
+                               logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);\r
+                               status.message.emit(tr("Error: ") +e);\r
+                               logger.log(logger.LOW, e.toString());           \r
+                               error = true;\r
+                       } catch (EDAMNotFoundException e) {\r
+                               logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);\r
+                               status.message.emit(tr("Error sending local note: ") +e);\r
+                               logger.log(logger.LOW, e.toString());   \r
+                               error = true;\r
+                       } catch (TException e) {\r
+                               logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);\r
+                               status.message.emit(tr("Error sending local note: ") +e);\r
+                               logger.log(logger.LOW, e.toString());   \r
+                               error = true;\r
                        }\r
                }\r
-               logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
+               logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNote");\r
 \r
        }\r
+\r
        // Sync Notebooks with Evernote\r
        private void syncLocalNotebooks() {\r
                logger.log(logger.HIGH, "Entering SyncRunner.syncLocalNotebooks");\r
                \r
-               status.message.emit("Sending local notebooks.");\r
+               status.message.emit(tr("Sending local notebooks."));\r
                List<Notebook> remoteList = new ArrayList<Notebook>();\r
                try {\r
                        logger.log(logger.EXTREME, "Getting remote notebooks to compare with local");\r
                        remoteList = noteStore.listNotebooks(authToken);\r
                } catch (EDAMUserException e1) {\r
                        logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks getting remote Notebook List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());          \r
                        error = true;\r
                } catch (EDAMSystemException e1) {\r
                        logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks getting remote Notebook List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());  \r
                        error = true;\r
                } catch (TException e1) {\r
                        logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalNotebooks getting remote Notebook List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());  \r
                        error = true;\r
                }\r
@@ -572,7 +685,8 @@ public class SyncRunner extends QObject implements Runnable {
                for (int i=0; i<notebooks.size() && keepRunning; i++) {\r
                        \r
                        if (authRefreshNeeded)\r
-                               refreshConnection();\r
+                               if (!refreshConnection())\r
+                                       return;\r
                        \r
                        Notebook enNotebook = notebooks.get(i);\r
                        try {\r
@@ -633,24 +747,24 @@ public class SyncRunner extends QObject implements Runnable {
        private void syncLocalTags() {\r
                logger.log(logger.HIGH, "Entering SyncRunner.syncLocalTags");\r
                List<Tag> remoteList = new ArrayList<Tag>();\r
-               status.message.emit("Sending local tags.");\r
+               status.message.emit(tr("Sending local tags."));\r
                \r
                try {\r
                        logger.log(logger.EXTREME, "Getting remote tags to compare names with the local tags");\r
                        remoteList = noteStore.listTags(authToken);\r
                } catch (EDAMUserException e1) {\r
                        logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote Tag List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());  \r
                        error = true;\r
                } catch (EDAMSystemException e1) {\r
                        logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote Tag List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());          \r
                        error = true;\r
                } catch (TException e1) {\r
                        logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote Tag List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());  \r
                        error = true;\r
                }               \r
@@ -660,7 +774,8 @@ public class SyncRunner extends QObject implements Runnable {
                Tag enTag = findNextTag();\r
                while(enTag!=null) {\r
                        if (authRefreshNeeded)\r
-                               refreshConnection();\r
+                               if (!refreshConnection())\r
+                                       return;\r
 \r
                        try {\r
                                if (enTag.getUpdateSequenceNum() > 0) {\r
@@ -722,28 +837,64 @@ public class SyncRunner extends QObject implements Runnable {
                }\r
                logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalTags");\r
        }\r
-       // Sync Tags with Evernote\r
+       private void syncLocalLinkedNotebooks() {\r
+               logger.log(logger.HIGH, "Entering SyncRunner.syncLocalLinkedNotebooks");\r
+               \r
+               List<String> list = conn.getLinkedNotebookTable().getDirtyGuids();\r
+               for (int i=0; i<list.size(); i++) {\r
+                       LinkedNotebook book = conn.getLinkedNotebookTable().getNotebook(list.get(i));\r
+                       try {\r
+                               noteStore.updateLinkedNotebook(authToken, book);\r
+                       } catch (EDAMUserException e) {\r
+                               logger.log(logger.LOW, "*** EDAM User Excepton syncLocalLinkedNotebooks");\r
+                               status.message.emit(tr("Error: ") +e);\r
+                               logger.log(logger.LOW, e.toString());           \r
+                               error = true;\r
+                               e.printStackTrace();\r
+                       } catch (EDAMNotFoundException e) {\r
+                               logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalLinkedNotebooks");\r
+                               status.message.emit(tr("Error: ") +e);\r
+                               logger.log(logger.LOW, e.toString());           \r
+                               error = true;\r
+                               e.printStackTrace();\r
+                       } catch (EDAMSystemException e) {\r
+                               logger.log(logger.LOW, "*** EDAM System Excepton syncLocalLinkedNotebooks");\r
+                               status.message.emit(tr("Error: ") +e);\r
+                               logger.log(logger.LOW, e.toString());           \r
+                               error = true;\r
+                               e.printStackTrace();\r
+                       } catch (TException e) {\r
+                               logger.log(logger.LOW, "*** EDAM TExcepton syncLocalLinkedNotebooks");\r
+                               status.message.emit(tr("Error: ") +e);\r
+                               logger.log(logger.LOW, e.toString());           \r
+                               error = true;\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+               logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalLinkedNotebooks");\r
+       }\r
+       // Sync Saved Searches with Evernote\r
        private void syncLocalSavedSearches() {\r
                logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");\r
                List<SavedSearch> remoteList = new ArrayList<SavedSearch>();\r
-               status.message.emit("Sending saved searches.");\r
+               status.message.emit(tr("Sending saved searches."));\r
        \r
                logger.log(logger.EXTREME, "Getting saved searches to compare with local");\r
                try {\r
                        remoteList = noteStore.listSearches(authToken);\r
                } catch (EDAMUserException e1) {\r
                        logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote saved search List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());  \r
                        error = true;\r
                } catch (EDAMSystemException e1) {\r
                        logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote saved search List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());          \r
                        error = true;\r
                } catch (TException e1) {\r
                        logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote saved search List");\r
-                       status.message.emit("Error: " +e1);\r
+                       status.message.emit(tr("Error: ") +e1);\r
                        logger.log(logger.LOW, e1.toString());  \r
                        error = true;\r
                }               \r
@@ -755,7 +906,8 @@ public class SyncRunner extends QObject implements Runnable {
                for (int i=0; i<searches.size() &&  keepRunning; i++) {\r
                        \r
                        if (authRefreshNeeded)\r
-                               refreshConnection();\r
+                               if (!refreshConnection())\r
+                                       return;\r
                        \r
                        SavedSearch enSearch = searches.get(i);\r
                        try {\r
@@ -829,12 +981,13 @@ public class SyncRunner extends QObject implements Runnable {
                if (updateSequenceNumber == 0)\r
                        fullSync = true;\r
                \r
-               status.message.emit("Downloading 0% complete.");\r
+               status.message.emit(tr("Downloading 0% complete."));\r
                \r
                while(more &&  keepRunning) {\r
                        \r
                        if (authRefreshNeeded)\r
-                               refreshConnection();\r
+                               if (!refreshConnection())\r
+                                       return;\r
                        \r
                        chunk = null;\r
                        int sequence = updateSequenceNumber;\r
@@ -862,36 +1015,11 @@ public class SyncRunner extends QObject implements Runnable {
                        syncRemoteTags(chunk.getTags());\r
                        syncRemoteSavedSearches(chunk.getSearches());\r
                        syncRemoteNotebooks(chunk.getNotebooks());\r
-                       syncRemoteNotes(chunk.getNotes(), fullSync);\r
+                       syncRemoteNotes(chunk.getNotes(), fullSync, authToken);\r
                        syncRemoteResources(chunk.getResources());\r
+                       syncRemoteLinkedNotebooks(chunk.getLinkedNotebooks());\r
+                       syncExpungedNotes(chunk);\r
                        \r
-                       // Do the local deletes\r
-                       logger.log(logger.EXTREME, "Doing local deletes");\r
-                       List<String> guid = chunk.getExpungedNotes();\r
-                       if (guid != null) \r
-                               for (int i=0; i<guid.size() && keepRunning; i++) {\r
-                                       logger.log(logger.EXTREME, "Expunging local note from database");\r
-                                       conn.getNoteTable().expungeNote(guid.get(i), true, false);\r
-                               }\r
-                       guid = chunk.getExpungedNotebooks();\r
-                       if (guid != null)\r
-                               for (int i=0; i<guid.size() && keepRunning; i++) {\r
-                                       logger.log(logger.EXTREME, "Expunging local notebook from database");\r
-                                       conn.getNotebookTable().expungeNotebook(guid.get(i), false);\r
-                               }\r
-                       guid = chunk.getExpungedTags();\r
-                       if (guid != null)\r
-                               for (int i=0; i<guid.size() && keepRunning; i++) {\r
-                                       logger.log(logger.EXTREME, "Expunging tags from local database");\r
-                                       conn.getTagTable().expungeTag(guid.get(i), false);\r
-                               }\r
-                       guid = chunk.getExpungedSearches();\r
-                       if (guid != null) \r
-                               for (int i=0; i<guid.size() && keepRunning; i++) {\r
-                                       logger.log(logger.EXTREME, "Expunging saved search from local database");\r
-                                       conn.getSavedSearchTable().expungeSavedSearch(guid.get(i), false);\r
-                               }\r
-\r
                        \r
                        // Check for more notes\r
                        if (chunk.getChunkHighUSN() <= updateSequenceNumber) \r
@@ -914,12 +1042,57 @@ public class SyncRunner extends QObject implements Runnable {
                                long pct = chunk.getChunkHighUSN() * 100;\r
                                conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());\r
                                pct = pct/evernoteUpdateCount;\r
-                               status.message.emit("Downloading " +new Long(pct).toString()+"% complete.");\r
+                               status.message.emit(tr("Downloading ") +new Long(pct).toString()+tr("% complete."));\r
                        }\r
                }\r
 \r
                logger.log(logger.HIGH, "Leaving SyncRunner.syncRemoteToLocal");\r
        }\r
+       // Sync expunged notes\r
+       private void syncExpungedNotes(SyncChunk chunk) {\r
+               // Do the local deletes\r
+               logger.log(logger.EXTREME, "Doing local deletes");\r
+               List<String> guid = chunk.getExpungedNotes();\r
+               if (guid != null) {\r
+                       for (int i=0; i<guid.size() && keepRunning; i++) {\r
+                               String notebookGuid = "";\r
+                               Note localNote = conn.getNoteTable().getNote(guid.get(i), false, false, false, false, false);\r
+                               if (localNote != null) {\r
+                                       conn.getNoteTable().updateNoteSequence(guid.get(i), 0);\r
+                                       notebookGuid = localNote.getNotebookGuid();\r
+                               }\r
+                               if (!conn.getNotebookTable().isNotebookLocal(notebookGuid)) {\r
+                                       logger.log(logger.EXTREME, "Expunging local note from database");\r
+                                       conn.getNoteTable().expungeNote(guid.get(i), true, false);\r
+                               }\r
+                       }\r
+               }\r
+               guid = chunk.getExpungedNotebooks();\r
+               if (guid != null)\r
+                       for (int i=0; i<guid.size() && keepRunning; i++) {\r
+                               logger.log(logger.EXTREME, "Expunging local notebook from database");\r
+                               conn.getNotebookTable().expungeNotebook(guid.get(i), false);\r
+                       }\r
+               guid = chunk.getExpungedTags();\r
+               if (guid != null)\r
+                       for (int i=0; i<guid.size() && keepRunning; i++) {\r
+                               logger.log(logger.EXTREME, "Expunging tags from local database");\r
+                               conn.getTagTable().expungeTag(guid.get(i), false);\r
+                       }\r
+               guid = chunk.getExpungedSearches();\r
+               if (guid != null) \r
+                       for (int i=0; i<guid.size() && keepRunning; i++) {\r
+                               logger.log(logger.EXTREME, "Expunging saved search from local database");\r
+                               conn.getSavedSearchTable().expungeSavedSearch(guid.get(i), false);\r
+                       }\r
+               guid = chunk.getExpungedLinkedNotebooks();\r
+               if (guid != null) \r
+                       for (int i=0; i<guid.size() && keepRunning; i++) {\r
+                               logger.log(logger.EXTREME, "Expunging linked notebook from local database");\r
+                               conn.getLinkedNotebookTable().expungeNotebook(guid.get(i), false);\r
+                       }\r
+\r
+       }\r
        // Sync remote tags\r
        private void syncRemoteTags(List<Tag> tags) {\r
                logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");\r
@@ -934,7 +1107,7 @@ public class SyncRunner extends QObject implements Runnable {
                }\r
                logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");\r
        }\r
-       // Sync remote tags\r
+       // Sync remote saved searches\r
        private void syncRemoteSavedSearches(List<SavedSearch> searches) {\r
                logger.log(logger.EXTREME, "Entering SyncRunner.syncSavedSearches");\r
                if (searches != null) {\r
@@ -948,6 +1121,16 @@ public class SyncRunner extends QObject implements Runnable {
                }\r
                logger.log(logger.EXTREME, "Leaving SyncRunner.syncSavedSearches");\r
        }\r
+       // Sync remote linked notebooks\r
+       private void syncRemoteLinkedNotebooks(List<LinkedNotebook> books) {\r
+               logger.log(logger.EXTREME, "Entering SyncRunner.syncLinkedNotebooks");\r
+               if (books != null) {\r
+                       for (int i=0; i<books.size() && keepRunning; i++) {\r
+                               conn.getLinkedNotebookTable().updateNotebook(books.get(i), false); \r
+                       }\r
+               }\r
+               logger.log(logger.EXTREME, "Leaving SyncRunner.syncLinkedNotebooks");\r
+       }\r
        // Sync remote Notebooks 2\r
        private void syncRemoteNotebooks(List<Notebook> notebooks) {\r
                logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");\r
@@ -958,98 +1141,132 @@ public class SyncRunner extends QObject implements Runnable {
                                if (oldGuid != null && !conn.getNotebookTable().isNotebookLocal(oldGuid) && !notebooks.get(i).getGuid().equalsIgnoreCase(oldGuid))\r
                                        conn.getNotebookTable().updateNotebookGuid(oldGuid, notebooks.get(i).getGuid());\r
                                conn.getNotebookTable().syncNotebook(notebooks.get(i), false); \r
+                               \r
+                               // Synchronize shared notebook information\r
+//                             if (notebooks.get(i).getSharedNotebookIdsSize() > 0) {\r
+//                                     conn.getSharedNotebookTable().expungeNotebookByGuid(notebooks.get(i).getGuid(), false);\r
+//                                     for (int j=0; j<notebooks.get(i).getSharedNotebookIdsSize(); j++) {\r
+//                                             syncRemoteSharedNotebook(notebooks.get(i).getGuid(), notebooks.get(i).getSharedNotebookIds().get(j), authToken);\r
+//                                     }\r
+//                             }\r
                        }\r
                }                       \r
                logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");\r
        }\r
+       // Sync remote shared notebook\r
+//     private void syncRemoteSharedNotebook(String guid, Long id, String token) {\r
+//             List<SharedNotebook> books = noteStore.getSharedNotebookByAuth(authToken);\r
+//     }\r
        // Sync remote Resources\r
        private void syncRemoteResources(List<Resource> resource) {\r
+               logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteResources");\r
+               if (resource != null) {\r
+                       for (int i=0; i<resource.size() && keepRunning; i++) {\r
+                               syncRemoteResource(resource.get(i), authToken);\r
+                       }\r
+               }\r
+               logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteResources");\r
+       }\r
+       // Sync remote resource\r
+       private void syncRemoteResource(Resource resource, String authToken) {\r
                // This is how the logic for this works.\r
                // 1.) If the resource is not in the local database, we add it.\r
                // 2.) If a copy of the resource is in the local database and the note isn't dirty, we update the local copy\r
                // 3.) If a copy of the resource is in the local databbase and it is dirty and the hash doesn't match, we ignore it because there\r
                //     is a conflict.  The note conflict should get a copy of the resource at that time.\r
                \r
-               logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteResources");\r
-               if (resource != null) {\r
-                       for (int i=0; i<resource.size() && keepRunning; i++) {\r
-                               boolean saveNeeded = false;\r
-/* #1 */               Resource r = getEvernoteResource(resource.get(i).getGuid(), true,true,true);\r
-                               Resource l = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), false);\r
-                               if (l == null) {\r
-                                       saveNeeded = true;\r
-                               } else {\r
-/* #2 */                       boolean isNoteDirty = conn.getNoteTable().isNoteDirty(r.getNoteGuid());\r
-                                       if (!isNoteDirty)\r
-                                               saveNeeded = true;\r
-                                       else {\r
-/* #3 */                               String remoteHash = "";\r
-                                               if (r != null && r.getData() != null && r.getData().getBodyHash() != null)\r
-                                                       remoteHash = byteArrayToHexString(r.getData().getBodyHash());\r
-                                               String localHash = "";\r
-                                               if (l != null && l.getData() != null && l.getData().getBodyHash() != null)\r
-                                                       remoteHash = byteArrayToHexString(l.getData().getBodyHash());\r
-                               \r
-                                               if (localHash.equalsIgnoreCase(remoteHash))\r
+               boolean saveNeeded = false;\r
+               /* #1 */                Resource r = getEvernoteResource(resource.getGuid(), true,true,true, authToken);\r
+                                               Resource l = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), false);\r
+                                               if (l == null) {\r
                                                        saveNeeded = true;\r
-                                       }\r
-                               }\r
-                               \r
-                               if (saveNeeded) \r
-                                       conn.getNoteTable().noteResourceTable.updateNoteResource(r, false);\r
+                                               } else {\r
+               /* #2 */                        boolean isNoteDirty = conn.getNoteTable().isNoteDirty(r.getNoteGuid());\r
+                                                       if (!isNoteDirty)\r
+                                                               saveNeeded = true;\r
+                                                       else {\r
+               /* #3 */                                String remoteHash = "";\r
+                                                               if (r != null && r.getData() != null && r.getData().getBodyHash() != null)\r
+                                                                       remoteHash = byteArrayToHexString(r.getData().getBodyHash());\r
+                                                               String localHash = "";\r
+                                                               if (l != null && l.getData() != null && l.getData().getBodyHash() != null)\r
+                                                                       remoteHash = byteArrayToHexString(l.getData().getBodyHash());\r
+                                               \r
+                                                               if (localHash.equalsIgnoreCase(remoteHash))\r
+                                                                       saveNeeded = true;\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               if (saveNeeded) \r
+                                                       conn.getNoteTable().noteResourceTable.updateNoteResource(r, false);\r
+                                               if (r.getMime().equalsIgnoreCase("application/vnd.evernote.ink"))\r
+                                                       downloadInkNoteImage(r.getGuid(), authToken);\r
 \r
-                       }\r
-               }\r
-               logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteResources");\r
        }\r
-       // Sync remote notebooks\r
-       private void syncRemoteNotes(List<Note> note, boolean fullSync) {\r
+       // Sync remote notes\r
+       private void syncRemoteNotes(List<Note> note, boolean fullSync, String token) {\r
                logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotes");\r
                if (note != null) {\r
                        for (int i=0; i<note.size() && keepRunning; i++) {\r
-                               Note n = getEvernoteNote(note.get(i).getGuid(), true, fullSync, true,true);\r
-                               if (n!=null) {\r
-                                       \r
-                                       // Basically, this is how the sync logic for a note works.\r
-                                       // If the remote note has changed and the local has not, we\r
-                                       // accept the change.\r
-                                       // If both the local & remote have changed but the sequence\r
-                                       // numbers are the same, we don't accept the change.  This\r
-                                       // seems to happen when attachments are indexed by the server.\r
-                                       // If both the local & remote have changed and the sequence numbers\r
-                                       // are different we move the local copy to a local notebook (making sure\r
-                                       // to copy all resources) and we accept the new one.                    \r
-                                       boolean conflictingNote = true;\r
-                                       logger.log(logger.EXTREME, "Checking for duplicate note " +n.getGuid());\r
-                                       if (dirtyNoteGuids.contains(n.getGuid())) { \r
-                                               logger.log(logger.EXTREME, "Conflict check beginning");\r
-                                               conflictingNote = checkForConflict(n);\r
-                                               logger.log(logger.EXTREME, "Conflict check results " +conflictingNote);\r
-                                               if (conflictingNote)\r
-                                                       moveConflictingNote(n.getGuid());\r
-                                       }\r
-                                       if (conflictingNote || fullSync) {\r
-                                               logger.log(logger.EXTREME, "Saving Note");\r
-                                               conn.getNoteTable().syncNote(n, false);\r
-                                               noteSignal.noteChanged.emit(n.getGuid(), null);   // Signal to ivalidate note cache\r
-                                               logger.log(logger.EXTREME, "Note Saved");\r
-                                               if (fullSync && n.getResources() != null) {\r
-                                                       for (int q=0; q<n.getResources().size() && keepRunning; q++) {\r
-                                                               logger.log(logger.EXTREME, "Getting note resources.");\r
-                                                               conn.getNoteTable().noteResourceTable.updateNoteResource(n.getResources().get(q), false);\r
-                                                       }\r
-                                               }\r
+                               Note n = getEvernoteNote(note.get(i).getGuid(), true, fullSync, true,true, token);\r
+                               syncRemoteNote(n, fullSync, token);\r
+                       }\r
+               }\r
+               logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotes");\r
+       }\r
+       private void syncRemoteNote(Note n, boolean fullSync, String token) {\r
+               if (n!=null) {\r
+                       \r
+                       // Basically, this is how the sync logic for a note works.\r
+                       // If the remote note has changed and the local has not, we\r
+                       // accept the change.\r
+                       // If both the local & remote have changed but the sequence\r
+                       // numbers are the same, we don't accept the change.  This\r
+                       // seems to happen when attachments are indexed by the server.\r
+                       // If both the local & remote have changed and the sequence numbers\r
+                       // are different we move the local copy to a local notebook (making sure\r
+                       // to copy all resources) and we accept the new one.                    \r
+                       boolean conflictingNote = true;\r
+                       logger.log(logger.EXTREME, "Checking for duplicate note " +n.getGuid());\r
+                       if (dirtyNoteGuids != null && dirtyNoteGuids.contains(n.getGuid())) { \r
+                               logger.log(logger.EXTREME, "Conflict check beginning");\r
+                               conflictingNote = checkForConflict(n);\r
+                               logger.log(logger.EXTREME, "Conflict check results " +conflictingNote);\r
+                               if (conflictingNote)\r
+                                       moveConflictingNote(n.getGuid());\r
+                       }\r
+                       boolean ignoreNote = false;\r
+                       if (ignoreNotebooks.contains(n.getNotebookGuid()))\r
+                               ignoreNote = true;\r
+                       for (int i=0; i<n.getTagGuidsSize(); i++) {\r
+                               if (ignoreTags.contains(n.getTagGuids().get(i))) {\r
+                                       ignoreNote = true;\r
+                                       i=n.getTagGuidsSize();\r
+                               }\r
+                       }\r
+                               \r
+                       if ((conflictingNote || fullSync) && !ignoreNote) {\r
+                               logger.log(logger.EXTREME, "Saving Note");\r
+                               conn.getNoteTable().syncNote(n, false);\r
+                               noteSignal.noteChanged.emit(n.getGuid(), null);   // Signal to ivalidate note cache\r
+                               noteSignal.noteDownloaded.emit(n, true);                // Signal to add note to index\r
+                                       logger.log(logger.EXTREME, "Note Saved");\r
+                               if (fullSync && n.getResources() != null) {\r
+                                       for (int q=0; q<n.getResources().size() && keepRunning; q++) {\r
+                                               logger.log(logger.EXTREME, "Getting note resources.");\r
+                                               conn.getNoteTable().noteResourceTable.updateNoteResource(n.getResources().get(q), false);\r
+                                               if (n.getResources().get(q).getMime().equalsIgnoreCase("application/vnd.evernote.ink"))\r
+                                                       downloadInkNoteImage(n.getResources().get(q).getGuid(), token);\r
                                        }\r
                                }\r
                        }\r
                }\r
-               logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotes");\r
        }\r
-       private Note getEvernoteNote(String guid, boolean withContent, boolean withResourceData, boolean withResourceRecognition, boolean withResourceAlternateData) { \r
+       private Note getEvernoteNote(String guid, boolean withContent, boolean withResourceData, boolean withResourceRecognition, boolean withResourceAlternateData, String token) { \r
                Note n = null;\r
                try {\r
                        logger.log(logger.EXTREME, "Retrieving note " +guid);\r
-                       n = noteStore.getNote(authToken, guid, withContent, withResourceData, withResourceRecognition, withResourceAlternateData);\r
+                       n = noteStore.getNote(token, guid, withContent, withResourceData, withResourceRecognition, withResourceAlternateData);\r
                        logger.log(logger.EXTREME, "Note " +guid +" has been retrieved.");\r
                } catch (EDAMUserException e) {\r
                        logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");\r
@@ -1074,11 +1291,11 @@ public class SyncRunner extends QObject implements Runnable {
                }\r
                return n;\r
        }\r
-       private Resource getEvernoteResource(String guid, boolean withData, boolean withRecognition, boolean withAttributes) { \r
+       private Resource getEvernoteResource(String guid, boolean withData, boolean withRecognition, boolean withAttributes, String token) { \r
                Resource n = null;\r
                try {\r
                        logger.log(logger.EXTREME, "Retrieving resource " +guid);\r
-                       n = noteStore.getResource(authToken, guid, withData, withRecognition, withAttributes, withAttributes);\r
+                       n = noteStore.getResource(token, guid, withData, withRecognition, withAttributes, withAttributes);\r
                        logger.log(logger.EXTREME, "Resource " +guid +" has been retrieved.");\r
                } catch (EDAMUserException e) {\r
                        logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");\r
@@ -1171,7 +1388,7 @@ public class SyncRunner extends QObject implements Runnable {
                        conn.getNoteTable().noteResourceTable.updateNoteResourceGuid(oldResG, newResG, true);\r
                }\r
                \r
-               conn.getNoteTable().resetSequenceNumber(guid);\r
+               conn.getNoteTable().resetNoteSequence(guid);\r
                conn.getNoteTable().updateNoteGuid(guid, newGuid);\r
                conn.getNoteTable().updateNoteNotebook(newGuid, notebookGuid, true);\r
                \r
@@ -1179,7 +1396,7 @@ public class SyncRunner extends QObject implements Runnable {
                noteSignal.guidChanged.emit(guid,newGuid);\r
        }\r
        \r
-       \r
+\r
 \r
        \r
        //******************************************************\r
@@ -1266,7 +1483,6 @@ public class SyncRunner extends QObject implements Runnable {
                \r
            boolean versionOk = false;\r
                try {\r
-//                     versionOk = userStore.checkVersion("Dave's EDAMDemo (Java)", \r
                        versionOk = userStore.checkVersion("NeverNote", \r
                    com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR, \r
                      com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR);\r
@@ -1321,19 +1537,20 @@ public class SyncRunner extends QObject implements Runnable {
        isConnected = false;\r
     }\r
     // Refresh the connection\r
-    private synchronized void refreshConnection() {\r
+    private synchronized boolean refreshConnection() {\r
                logger.log(logger.EXTREME, "Entering SyncRunner.refreshConnection()");\r
 //        Calendar cal = Calendar.getInstance();\r
                \r
         // If we are not connected let's get out of here\r
         if (!isConnected)\r
-               return;\r
+               return false;\r
         \r
                // If we fail too many times, then let's give up.\r
                if (failedRefreshes >=5) {\r
                        logger.log(logger.EXTREME, "Refresh attempts have failed.  Disconnecting.");\r
                        isConnected = false;\r
-                       return;\r
+                       status.message.emit(tr("Unable to synchronize - Authentication failed"));\r
+                       return false;\r
                }\r
         \r
                // If this is the first time through, then we need to set this\r
@@ -1350,31 +1567,33 @@ public class SyncRunner extends QObject implements Runnable {
                if (userStore != null && authToken != null) \r
                        newAuth = userStore.refreshAuthentication(authToken); \r
                else\r
-                       return;\r
+                       return false;\r
                logger.log(logger.EXTREME, "UserStore.refreshAuthentication has succeeded.");\r
                } catch (EDAMUserException e) {\r
                        e.printStackTrace();\r
                        syncSignal.authRefreshComplete.emit(false);\r
                        failedRefreshes++;\r
-                       return;\r
+                       return false;\r
                } catch (EDAMSystemException e) {\r
                        e.printStackTrace();\r
                        syncSignal.authRefreshComplete.emit(false);\r
                        failedRefreshes++;\r
-                       return;         \r
+                       return false;           \r
                } catch (TException e) { \r
                        e.printStackTrace();\r
                        syncSignal.authRefreshComplete.emit(false);\r
                        failedRefreshes++;\r
-                       return;\r
+                       return false;\r
                }\r
                \r
                // If we didn't get a good auth, then we've failed\r
                if (newAuth == null) {\r
                        failedRefreshes++;\r
+                       status.message.emit(tr("Unable to synchronize - Authentication failed"));\r
                        logger.log(logger.EXTREME, "Authentication failure #" +failedRefreshes);\r
+                       status.message.emit(tr("Unable to synchronize - Authentication failed"));\r
                        syncSignal.authRefreshComplete.emit(false);\r
-                       return;\r
+                       return false;\r
                }\r
                \r
                // We got a good token.  Now we need to setup the time to renew it.\r
@@ -1392,8 +1611,10 @@ public class SyncRunner extends QObject implements Runnable {
 //                     failedRefreshes++;\r
 //                     syncSignal.authRefreshComplete.emit(false);\r
 //             }\r
+               \r
+               return true;\r
     }\r
-       \r
+    \r
        public synchronized boolean addWork(String request) {\r
                if (workQueue.offer(request))\r
                        return true;\r
@@ -1404,4 +1625,337 @@ public class SyncRunner extends QObject implements Runnable {
        n.setContent(conn.getNoteTable().getNoteContentBinary(n.getGuid()));\r
        return n;\r
     }\r
+\r
+\r
+\r
+    //*********************************************************\r
+    //* Special download instructions.  Used for DB upgrades\r
+    //*********************************************************\r
+    private void downloadAllSharedNotebooks() {\r
+       try {\r
+                       List<SharedNotebook> books = noteStore.listSharedNotebooks(authToken);\r
+                       logger.log(logger.LOW, "Shared notebooks found = " +books.size());\r
+                       for (int i=0; i<books.size(); i++) {\r
+                               conn.getSharedNotebookTable().updateNotebook(books.get(i), false);\r
+                       }\r
+                       conn.getSyncTable().deleteRecord("FullSharedNotebookSync");\r
+               } catch (EDAMUserException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("User exception Listing shared notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               } catch (EDAMSystemException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("System exception Listing shared notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               } catch (TException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("Transaction exception Listing shared notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               } catch (EDAMNotFoundException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("EDAM Not Found exception Listing shared notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+               }\r
+    }\r
+    private void downloadAllNotebooks() {\r
+       try {\r
+                       List<Notebook> books = noteStore.listNotebooks(authToken);\r
+                       logger.log(logger.LOW, "Shared notebooks found = " +books.size());\r
+                       for (int i=0; i<books.size(); i++) {\r
+                               conn.getNotebookTable().updateNotebook(books.get(i), false);\r
+                       }\r
+                       conn.getSyncTable().deleteRecord("FullNotebookSync");\r
+               } catch (EDAMUserException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("User exception Listing notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               } catch (EDAMSystemException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("System exception Listing notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               } catch (TException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("Transaction exception Listing notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               }\r
+    }\r
+    private void downloadAllLinkedNotebooks() {\r
+       try {\r
+                       List<LinkedNotebook> books = noteStore.listLinkedNotebooks(authToken);\r
+                       logger.log(logger.LOW, "Linked notebooks found = " +books.size());\r
+                       for (int i=0; i<books.size(); i++) {\r
+                               conn.getLinkedNotebookTable().updateNotebook(books.get(i), false);\r
+                       }\r
+                       conn.getSyncTable().deleteRecord("FullLinkedNotebookSync");\r
+               } catch (EDAMUserException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("User exception Listing linked notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               } catch (EDAMSystemException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("System exception Listing linked notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               } catch (TException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("Transaction exception Listing lineked notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+                       return;\r
+               } catch (EDAMNotFoundException e1) {\r
+                       e1.printStackTrace();\r
+                       status.message.emit(tr("EDAM Not Found exception Listing linked notebooks."));\r
+                       logger.log(logger.LOW, e1.getMessage());\r
+               }\r
+    }\r
+\r
+    \r
+    private void downloadInkNoteImage(String guid, String authToken) {\r
+               String urlBase = noteStoreUrl.replace("/edam/note/", "/shard/") + "/res/"+guid+".ink?slice=";\r
+//             urlBase = "https://www.evernote.com/shard/s1/res/52b567a9-54ae-4a08-afc5-d5bae275b2a8.ink?slice=";\r
+               Integer slice = 1;\r
+               Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(guid, false);\r
+               conn.getInkImagesTable().expungeImage(r.getGuid());\r
+               int sliceCount = 1+((r.getHeight()-1)/480);\r
+               HttpClient http = new DefaultHttpClient();\r
+       for (int i=0; i<sliceCount; i++) {\r
+               String url = urlBase + slice.toString();\r
+               HttpPost post = new HttpPost(url);\r
+               post.getParams().setParameter("auth", authToken);\r
+               List <NameValuePair> nvps = new ArrayList <NameValuePair>();\r
+            nvps.add(new BasicNameValuePair("auth", authToken));\r
+\r
+            try {\r
+                               post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));\r
+                       } catch (UnsupportedEncodingException e1) {\r
+                               // TODO Auto-generated catch block\r
+                               e1.printStackTrace();\r
+                       }\r
+               try {\r
+                       HttpResponse response = http.execute(post);\r
+                       HttpEntity resEntity = response.getEntity();\r
+                       InputStream is = resEntity.getContent();\r
+                       QByteArray data = writeToFile(is);\r
+                       conn.getInkImagesTable().saveImage(guid, slice, data);\r
+                       } catch (ClientProtocolException e) {\r
+                               e.printStackTrace();\r
+                       } catch (IOException e) {\r
+                               e.printStackTrace();\r
+                       }\r
+\r
+                       slice++;\r
+       }\r
+       http.getConnectionManager().shutdown(); \r
+               noteSignal.noteChanged.emit(r.getNoteGuid(), null);   // Signal to ivalidate note cache\r
+    }\r
+    \r
+    \r
+    public QByteArray writeToFile(InputStream iStream) throws IOException {\r
+\r
+           File temp = File.createTempFile("nn-inknote-temp", ".png");\r
+\r
+           // Save InputStream to the file.\r
+           BufferedOutputStream fOut = null;\r
+           try {\r
+             fOut = new BufferedOutputStream(new FileOutputStream(temp));\r
+             byte[] buffer = new byte[32 * 1024];\r
+             int bytesRead = 0;\r
+             while ((bytesRead = iStream.read(buffer)) != -1) {\r
+               fOut.write(buffer, 0, bytesRead);\r
+             }\r
+           }\r
+           finally {\r
+               iStream.close();\r
+               fOut.close();\r
+           }\r
+           QFile tempFile = new QFile(temp.getAbsoluteFile().toString());\r
+           tempFile.open(OpenModeFlag.ReadOnly);\r
+           QByteArray data = tempFile.readAll();\r
+           tempFile.close();\r
+           tempFile.remove();\r
+           return data;\r
+    }\r
+    \r
+    \r
+       //******************************************\r
+       //* Begin syncing shared notebooks \r
+       //******************************************\r
+    private void syncLinkedNotebooks() {\r
+       logger.log(logger.MEDIUM, "Authenticating Shared Notebooks");\r
+       status.message.emit(tr("Synchronizing shared notebooks."));\r
+       List<LinkedNotebook> books = conn.getLinkedNotebookTable().getAll();\r
+       for (int i=0; i<books.size(); i++) {\r
+               try {\r
+                               long lastSyncDate = conn.getLinkedNotebookTable().getLastSequenceDate(books.get(i).getGuid());\r
+                               int lastSequenceNumber = conn.getLinkedNotebookTable().getLastSequenceNumber(books.get(i).getGuid());\r
+                               linkedAuthResult = noteStore.authenticateToSharedNotebook(books.get(i).getShareKey(), authToken);\r
+                               SyncState linkedSyncState = \r
+                                       noteStore.getLinkedNotebookSyncState(linkedAuthResult.getAuthenticationToken(), books.get(i));\r
+                               if (linkedSyncState.getUpdateCount() > lastSequenceNumber) {\r
+                                       if (lastSyncDate < linkedSyncState.getFullSyncBefore()) {\r
+                                               lastSequenceNumber = 0;\r
+                                       } \r
+                                       syncLinkedNotebook(books.get(i), lastSequenceNumber, linkedSyncState.getUpdateCount());\r
+                               }\r
+                       \r
+                       // Synchronize local changes\r
+                       syncLocalLinkedNoteChanges(books.get(i));\r
+                               \r
+               } catch (EDAMUserException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               } catch (EDAMNotFoundException e) {\r
+                       status.message.emit(tr("Error synchronizing \" " +\r
+                                       books.get(i).getShareName()+"\". Please verify you still have access to that shared notebook."));\r
+                       error = true;\r
+                       e.printStackTrace();\r
+               } catch (EDAMSystemException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               } catch (TException e) {\r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+       \r
+       // Cleanup tags\r
+       conn.getTagTable().removeUnusedLinkedTags();\r
+       conn.getTagTable().cleanupTags();\r
+       tagSignal.listChanged.emit();\r
+       }\r
+\r
+    \r
+    //**************************************************************\r
+    //* Linked notebook contents (from someone else's account)\r
+    //*************************************************************\r
+       private void syncLinkedNotebook(LinkedNotebook book, int usn, int highSequence) {\r
+               boolean fullSync = false;\r
+               if (usn == 0)\r
+                       fullSync = true;\r
+               while (usn < highSequence) {\r
+                       try {\r
+                               SyncChunk chunk = \r
+                                       noteStore.getLinkedNotebookSyncChunk(authToken, book, usn, 10, fullSync);\r
+\r
+                               if (!ignoreLinkedNotebooks.contains(book.getGuid()))\r
+                                       syncRemoteNotes(chunk.getNotes(), fullSync, linkedAuthResult.getAuthenticationToken());\r
+                               findNewLinkedTags(chunk.getNotes(), linkedAuthResult.getAuthenticationToken());\r
+                               for (int i=0; i<chunk.getResourcesSize(); i++) {\r
+                                       syncRemoteResource(chunk.getResources().get(i), linkedAuthResult.getAuthenticationToken());\r
+                               }\r
+                               syncRemoteLinkedNotebooks(chunk.getNotebooks(), false, book);\r
+                               SharedNotebook s = noteStore.getSharedNotebookByAuth(linkedAuthResult.getAuthenticationToken());\r
+                               syncLinkedTags(chunk.getTags(), s.getNotebookGuid());\r
+                               \r
+                               \r
+                               // Expunge records\r
+                               for (int i=0; i<chunk.getExpungedLinkedNotebooksSize(); i++) {\r
+                                       conn.getLinkedNotebookTable().expungeNotebook(chunk.getExpungedLinkedNotebooks().get(i), false);\r
+                               }\r
+                               usn = chunk.getChunkHighUSN();\r
+                               conn.getLinkedNotebookTable().setLastSequenceDate(book.getGuid(),chunk.getCurrentTime());\r
+                               conn.getLinkedNotebookTable().setLastSequenceNumber(book.getGuid(),chunk.getChunkHighUSN());\r
+                       } catch (EDAMUserException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       } catch (EDAMSystemException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       } catch (EDAMNotFoundException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       } catch (TException e) {\r
+                               // TODO Auto-generated catch block\r
+                               e.printStackTrace();\r
+                       }\r
+               }\r
+       }\r
+       // Sync remote tags\r
+       private void syncLinkedTags(List<Tag> tags, String notebookGuid) {\r
+               logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");\r
+               if (tags != null) {\r
+                       for (int i=0; i<tags.size() && keepRunning; i++) {\r
+                               conn.getTagTable().syncLinkedTag(tags.get(i), notebookGuid, false);\r
+                       }\r
+               }\r
+               logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");\r
+       }\r
+       \r
+       // Sync notebooks from a linked notebook\r
+       private void syncRemoteLinkedNotebooks(List<Notebook> notebooks, boolean readOnly, LinkedNotebook linked) {\r
+               logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");\r
+               if (notebooks != null) {\r
+                       for (int i=0; i<notebooks.size() && keepRunning; i++) {\r
+                               try {\r
+                                       SharedNotebook s = noteStore.getSharedNotebookByAuth(linkedAuthResult.getAuthenticationToken());\r
+                                       conn.getLinkedNotebookTable().setNotebookGuid(s.getShareKey(), s.getNotebookGuid());\r
+                                       readOnly = !s.isNotebookModifiable();\r
+                                       notebooks.get(i).setName(linked.getShareName());\r
+                                       notebooks.get(i).setDefaultNotebook(false);\r
+                                       conn.getNotebookTable().syncLinkedNotebook(notebooks.get(i), false, readOnly); \r
+                               } catch (EDAMUserException e) {\r
+                                       readOnly = true;\r
+                                       e.printStackTrace();\r
+                               } catch (EDAMNotFoundException e) {\r
+                                       readOnly = true;\r
+                                       e.printStackTrace();\r
+                               } catch (EDAMSystemException e) {\r
+                                       readOnly = true;\r
+                                       e.printStackTrace();\r
+                               } catch (TException e) {\r
+                                       readOnly = true;\r
+                                       e.printStackTrace();\r
+                               }\r
+\r
+                       }\r
+               }                       \r
+               logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");\r
+       }\r
+\r
+       private void findNewLinkedTags(List<Note> newNotes, String token) {\r
+               if (newNotes == null)\r
+                       return;\r
+               for (int i=0; i<newNotes.size(); i++) {\r
+                       Note n = newNotes.get(i);\r
+                       for (int j=0; j<n.getTagGuidsSize(); j++) {\r
+                               String tag = n.getTagGuids().get(j);\r
+                               if (!conn.getTagTable().exists(tag)) {\r
+                                       Tag newTag;\r
+                                       try {\r
+                                               newTag = noteStore.getTag(token, tag);\r
+                                               conn.getTagTable().addTag(newTag, false);\r
+                                       } catch (EDAMUserException e) {\r
+                                               // TODO Auto-generated catch block\r
+                                               e.printStackTrace();\r
+                                       } catch (EDAMSystemException e) {\r
+                                               // TODO Auto-generated catch block\r
+                                               e.printStackTrace();\r
+                                       } catch (EDAMNotFoundException e) {\r
+                                               // TODO Auto-generated catch block\r
+                                               e.printStackTrace();\r
+                                       } catch (TException e) {\r
+                                               // TODO Auto-generated catch block\r
+                                               e.printStackTrace();\r
+                                       }\r
+                                       \r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // Synchronize changes locally done to linked notes\r
+       private void syncLocalLinkedNoteChanges(LinkedNotebook book) {\r
+               String notebookGuid = conn.getLinkedNotebookTable().getNotebookGuid(book.getGuid());\r
+               List<Note> notes = conn.getNoteTable().getDirtyLinked(notebookGuid);\r
+               for (int i=0; i<notes.size(); i++) {\r
+                       syncLocalNote(notes.get(i), linkedAuthResult.getAuthenticationToken());\r
+               }\r
+       }\r
+\r
 }\r