OSDN Git Service

Add debug messages for shared notebooks.
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / threads / SyncRunner.java
1 /*\r
2  * This file is part of NeverNote \r
3  * Copyright 2009 Randy Baumgarte\r
4  * \r
5  * This file may be licensed under the terms of of the\r
6  * GNU General Public License Version 2 (the ``GPL'').\r
7  *\r
8  * Software distributed under the License is distributed\r
9  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either\r
10  * express or implied. See the GPL for the specific language\r
11  * governing rights and limitations.\r
12  *\r
13  * You should have received a copy of the GPL along with this\r
14  * program. If not, go to http://www.gnu.org/licenses/gpl.html\r
15  * or write to the Free Software Foundation, Inc.,\r
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
17  *\r
18 */\r
19 package cx.fbn.nevernote.threads;\r
20 \r
21 import java.io.BufferedOutputStream;\r
22 import java.io.File;\r
23 import java.io.FileOutputStream;\r
24 import java.io.IOException;\r
25 import java.io.InputStream;\r
26 import java.io.UnsupportedEncodingException;\r
27 import java.net.UnknownHostException;\r
28 import java.util.ArrayList;\r
29 import java.util.Calendar;\r
30 import java.util.Date;\r
31 import java.util.GregorianCalendar;\r
32 import java.util.List;\r
33 import java.util.TreeSet;\r
34 import java.util.Vector;\r
35 import java.util.concurrent.LinkedBlockingQueue;\r
36 \r
37 import org.apache.http.HttpEntity;\r
38 import org.apache.http.HttpResponse;\r
39 import org.apache.http.NameValuePair;\r
40 import org.apache.http.client.ClientProtocolException;\r
41 import org.apache.http.client.HttpClient;\r
42 import org.apache.http.client.entity.UrlEncodedFormEntity;\r
43 import org.apache.http.client.methods.HttpPost;\r
44 import org.apache.http.impl.client.DefaultHttpClient;\r
45 import org.apache.http.message.BasicNameValuePair;\r
46 import org.apache.http.protocol.HTTP;\r
47 import org.apache.thrift.TException;\r
48 import org.apache.thrift.protocol.TBinaryProtocol;\r
49 import org.apache.thrift.transport.THttpClient;\r
50 import org.apache.thrift.transport.TTransportException;\r
51 \r
52 import com.evernote.edam.error.EDAMNotFoundException;\r
53 import com.evernote.edam.error.EDAMSystemException;\r
54 import com.evernote.edam.error.EDAMUserException;\r
55 import com.evernote.edam.notestore.NoteStore;\r
56 import com.evernote.edam.notestore.SyncChunk;\r
57 import com.evernote.edam.notestore.SyncState;\r
58 import com.evernote.edam.type.Data;\r
59 import com.evernote.edam.type.LinkedNotebook;\r
60 import com.evernote.edam.type.Note;\r
61 import com.evernote.edam.type.Notebook;\r
62 import com.evernote.edam.type.Resource;\r
63 import com.evernote.edam.type.SavedSearch;\r
64 import com.evernote.edam.type.SharedNotebook;\r
65 import com.evernote.edam.type.Tag;\r
66 import com.evernote.edam.type.User;\r
67 import com.evernote.edam.userstore.AuthenticationResult;\r
68 import com.evernote.edam.userstore.UserStore;\r
69 import com.trolltech.qt.core.QByteArray;\r
70 import com.trolltech.qt.core.QFile;\r
71 import com.trolltech.qt.core.QIODevice.OpenModeFlag;\r
72 import com.trolltech.qt.core.QObject;\r
73 import com.trolltech.qt.gui.QMessageBox;\r
74 \r
75 import cx.fbn.nevernote.signals.NoteIndexSignal;\r
76 import cx.fbn.nevernote.signals.NoteResourceSignal;\r
77 import cx.fbn.nevernote.signals.NoteSignal;\r
78 import cx.fbn.nevernote.signals.NotebookSignal;\r
79 import cx.fbn.nevernote.signals.SavedSearchSignal;\r
80 import cx.fbn.nevernote.signals.StatusSignal;\r
81 import cx.fbn.nevernote.signals.SyncSignal;\r
82 import cx.fbn.nevernote.signals.TagSignal;\r
83 import cx.fbn.nevernote.sql.DatabaseConnection;\r
84 import cx.fbn.nevernote.sql.DeletedItemRecord;\r
85 import cx.fbn.nevernote.utilities.ApplicationLogger;\r
86 \r
87 public class SyncRunner extends QObject implements Runnable {\r
88         \r
89         private final ApplicationLogger logger;\r
90                 private DatabaseConnection              conn;\r
91                 private boolean                                 idle;\r
92                 public boolean                                  error;\r
93                 public volatile boolean                 isConnected;\r
94                 public volatile boolean                 keepRunning;\r
95                 public volatile String                  authToken;\r
96                 private long                                    evernoteUpdateCount;\r
97                 private final String userAgent = "NeverNote/" + System.getProperty("os.name")\r
98                                                                 +"/"+System.getProperty("java.vendor") + "/"\r
99                                                                 + System.getProperty("java.version") +";";\r
100                 \r
101                 public volatile NoteStore.Client                noteStore;\r
102                 private UserStore.Client                                userStore;\r
103                 \r
104                 public volatile StatusSignal                    status;\r
105                 public volatile TagSignal                               tagSignal;\r
106                 public volatile NotebookSignal                  notebookSignal;\r
107                 public volatile NoteIndexSignal                 noteIndexSignal;\r
108                 public volatile NoteSignal                              noteSignal;\r
109                 public volatile SavedSearchSignal               searchSignal;\r
110                 public volatile NoteResourceSignal              resourceSignal;\r
111                 public volatile SyncSignal                              syncSignal;\r
112                 public volatile boolean                                 authRefreshNeeded;\r
113                 public volatile boolean                                 syncNeeded;\r
114                 public volatile boolean                                 disableUploads;\r
115                 public volatile boolean                                 syncDeletedContent;\r
116                 private volatile Vector<String>                 dirtyNoteGuids;\r
117                 \r
118             public volatile String username = ""; \r
119             public volatile String password = ""; \r
120                 public volatile String userStoreUrl;\r
121             private final static String consumerKey = "baumgarte"; \r
122             private final static String consumerSecret = "eb8b5740e17cb55f";\r
123             public String noteStoreUrlBase;\r
124             private THttpClient userStoreTrans;\r
125             private TBinaryProtocol userStoreProt;\r
126             private AuthenticationResult authResult;\r
127             private AuthenticationResult linkedAuthResult;\r
128             private User user; \r
129             private long authTimeRemaining;\r
130             public long authRefreshTime;\r
131             public long failedRefreshes = 0;\r
132             public  THttpClient noteStoreTrans;\r
133             public TBinaryProtocol noteStoreProt;\r
134             public String noteStoreUrl;\r
135             public long sequenceDate;\r
136             public int updateSequenceNumber;\r
137             private boolean refreshNeeded;\r
138             private volatile LinkedBlockingQueue<String> workQueue;\r
139                 private static int MAX_QUEUED_WAITING = 1000;\r
140                 String dbuid;\r
141                 String dburl;\r
142                 String indexUrl;\r
143                 String resourceUrl;\r
144                 String dbpswd;\r
145                 String dbcpswd;\r
146                 private final TreeSet<String> ignoreTags;\r
147                 private final TreeSet<String> ignoreNotebooks;\r
148                 private final TreeSet<String> ignoreLinkedNotebooks;\r
149         \r
150                 \r
151                 \r
152         public SyncRunner(String logname, String u, String i, String r, String uid, String pswd, String cpswd) {\r
153                 logger = new ApplicationLogger(logname);\r
154                 \r
155                 noteSignal = new NoteSignal();\r
156                 status = new StatusSignal();\r
157                 tagSignal = new TagSignal();\r
158                 notebookSignal = new NotebookSignal();\r
159                 noteIndexSignal = new NoteIndexSignal();\r
160                 noteSignal = new NoteSignal();\r
161                 searchSignal = new SavedSearchSignal();\r
162                 syncSignal = new SyncSignal();\r
163                 resourceSignal = new NoteResourceSignal();\r
164                 resourceUrl = r;\r
165                 indexUrl = i;\r
166                 dbuid = uid;\r
167                 dburl = u;\r
168                 dbpswd = pswd;\r
169                 dbcpswd = cpswd;\r
170 //              this.setAutoDelete(false);\r
171                 \r
172                 isConnected = false;\r
173                 syncNeeded = false;\r
174                 authRefreshNeeded = false;\r
175                 keepRunning = true;\r
176                 idle = true;\r
177                 disableUploads = false;\r
178                 ignoreTags = new TreeSet<String>();\r
179                 ignoreNotebooks = new TreeSet<String>();\r
180                 ignoreLinkedNotebooks = new TreeSet<String>();\r
181                 \r
182 //              setAutoDelete(false);\r
183                 workQueue=new LinkedBlockingQueue<String>(MAX_QUEUED_WAITING);\r
184         }\r
185         @Override\r
186         public void run() {\r
187                 try {\r
188                         logger.log(logger.EXTREME, "Starting thread");\r
189                         conn = new DatabaseConnection(logger, dburl, indexUrl, resourceUrl, dbuid, dbpswd, dbcpswd, 200);\r
190                         while(keepRunning) {\r
191                                 String work = workQueue.take();\r
192                                 logger.log(logger.EXTREME, "Work found: " +work);\r
193                                 if (work.equalsIgnoreCase("stop"))\r
194                                         return;\r
195                                 idle=false;\r
196                                 error=false;\r
197                                 if (authRefreshNeeded == true || !isConnected) {\r
198                                         logger.log(logger.EXTREME, "Refreshing connection");\r
199                                         refreshConnection();\r
200                                 }\r
201                                 if (syncNeeded) {\r
202                                         logger.log(logger.EXTREME, "SyncNeeded is true");\r
203                                         refreshNeeded=false;\r
204                                         sequenceDate = conn.getSyncTable().getLastSequenceDate();\r
205                                         updateSequenceNumber = conn.getSyncTable().getUpdateSequenceNumber();\r
206                                         try {\r
207                                                 logger.log(logger.EXTREME, "Beginning sync");\r
208                                                 evernoteSync();\r
209                                                 logger.log(logger.EXTREME, "Sync finished");\r
210                                         } catch (UnknownHostException e) {\r
211                                                 status.message.emit(e.getMessage());\r
212                                         }\r
213                                 }\r
214                                 idle=true;\r
215                                 logger.log(logger.EXTREME, "Signaling refresh finished.  refreshNeeded=" +refreshNeeded);\r
216                                 syncSignal.finished.emit(refreshNeeded);\r
217                                 if (error) {\r
218                                         syncSignal.errorDisconnect.emit();\r
219                                         status.message.emit(tr("Error synchronizing - see log for details."));\r
220                                 }\r
221                         }\r
222                 }       \r
223                 catch (InterruptedException e1) {\r
224                         e1.printStackTrace();\r
225                 }\r
226                 conn.dbShutdown();\r
227         }\r
228 \r
229         \r
230         public DatabaseConnection getConnection() {\r
231                 return conn;\r
232         }\r
233 \r
234         public boolean isIdle() {\r
235                 return idle;\r
236         }\r
237 \r
238 \r
239         public void setConnected(boolean c) {\r
240                 isConnected = c;\r
241         }\r
242         public void setKeepRunning(boolean r) {\r
243                 logger.log(logger.EXTREME, "Setting keepRunning=" +r);\r
244                 keepRunning = r;\r
245         }\r
246         public void setNoteStore(NoteStore.Client c) {\r
247                 logger.log(logger.EXTREME, "Setting NoteStore in sync thread");\r
248                 noteStore = c;\r
249         }\r
250         public void setUserStore(UserStore.Client c) {\r
251                 logger.log(logger.EXTREME, "Setting UserStore in sync thread");\r
252                 userStore = c;\r
253         }\r
254 \r
255         public void setEvernoteUpdateCount(long s) {\r
256                 logger.log(logger.EXTREME, "Setting Update Count in sync thread");\r
257                 evernoteUpdateCount = s;\r
258         }\r
259         \r
260         //***************************************************************\r
261     //***************************************************************\r
262     //** These functions deal with Evernote communications\r
263     //***************************************************************\r
264     //***************************************************************\r
265         // Synchronize changes with Evernote\r
266         @SuppressWarnings("unused")\r
267         private void evernoteSync() throws java.net.UnknownHostException {\r
268                 logger.log(logger.HIGH, "Entering SyncRunner.evernoteSync");\r
269                 \r
270                 // Rebuild list of tags & notebooks to ignore\r
271                 ignoreNotebooks.clear();\r
272                 List<String> ignore = conn.getSyncTable().getIgnoreRecords("NOTEBOOK");\r
273                 for (int i=0; i<ignore.size(); i++) \r
274                         ignoreNotebooks.add(ignore.get(i));\r
275                 \r
276                 ignore.clear();\r
277                 ignore = conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK");\r
278                 for (int i=0; i<ignore.size(); i++) \r
279                         ignoreLinkedNotebooks.add(ignore.get(i));\r
280                 \r
281                 ignoreTags.clear();\r
282                 ignore = conn.getSyncTable().getIgnoreRecords("TAG");\r
283                 for (int i=0; i<ignore.size(); i++) \r
284                         ignoreTags.add(ignore.get(i));\r
285 \r
286                 // Make sure we are connected & should keep running\r
287                 if (isConnected && keepRunning) {\r
288                         error = false;\r
289                         logger.log(logger.EXTREME, "Synchronizing with Evernote");\r
290                         status.message.emit(tr("Synchronizing with Evernote"));\r
291                         \r
292                         // Get user information\r
293                         try {\r
294                                 logger.log(logger.EXTREME, "getting user from userstore");\r
295                                 User user = userStore.getUser(authToken);\r
296                                 logger.log(logger.EXTREME, "Saving user information");\r
297                                 syncSignal.saveUserInformation.emit(user);\r
298                         } catch (EDAMUserException e1) {\r
299                                 e1.printStackTrace();\r
300                                 status.message.emit(tr("User exception getting user account information.  Aborting sync and disconnecting"));\r
301                                 syncSignal.errorDisconnect.emit();\r
302                                 error = true;\r
303                                 enDisconnect();\r
304                                 return;\r
305                         } catch (EDAMSystemException e1) {\r
306                                 e1.printStackTrace();\r
307                                 status.message.emit(tr("System error user account information.  Aborting sync and disconnecting!"));\r
308                                 syncSignal.errorDisconnect.emit();\r
309                                 error = true;\r
310                                 enDisconnect();\r
311                                 return;\r
312                         } catch (TException e1) {\r
313                                 e1.printStackTrace();\r
314                                 syncSignal.errorDisconnect.emit();\r
315                                 error = true;\r
316                                 status.message.emit(tr("Transaction error getting user account information.  Aborting sync and disconnecting!"));\r
317                                 enDisconnect();\r
318                                 return;\r
319                         }\r
320                         \r
321                         // Get sync state\r
322                         SyncState syncState = null;\r
323                         try {   \r
324                                 logger.log(logger.EXTREME, "Getting sync state");\r
325                                 syncState = noteStore.getSyncState(authToken);  \r
326                                 syncSignal.saveUploadAmount.emit(syncState.getUploaded());\r
327                                 syncSignal.saveEvernoteUpdateCount.emit(syncState.getUpdateCount());\r
328                                 evernoteUpdateCount = syncState.getUpdateCount();\r
329                         } catch (EDAMUserException e) {\r
330                                 e.printStackTrace();\r
331                                 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));\r
332                                 syncSignal.errorDisconnect.emit();\r
333                                 enDisconnect();\r
334                                 return;\r
335                         } catch (EDAMSystemException e) {\r
336                                 e.printStackTrace();\r
337                                 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));\r
338                                 syncSignal.errorDisconnect.emit();\r
339                                 enDisconnect();\r
340                                 return;\r
341                         } catch (TException e) {\r
342                                 e.printStackTrace();\r
343                                 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));\r
344                                 syncSignal.errorDisconnect.emit();\r
345                                 enDisconnect();\r
346                                 return;\r
347                         }\r
348                         \r
349                         if (syncState == null) {\r
350                                 logger.log(logger.EXTREME, "Sync State is null");\r
351                                 status.message.emit(tr("Syncronization Error!"));\r
352                                 return;\r
353                         }\r
354 \r
355                         // Determine what to do. \r
356                         // If we need to do a full sync.\r
357                         logger.log(logger.LOW, "Full Sequence Before: " +syncState.getFullSyncBefore());\r
358                         logger.log(logger.LOW, "Last Sequence Date: " +sequenceDate);\r
359                         if (syncState.getFullSyncBefore() > sequenceDate) {\r
360                                 logger.log(logger.EXTREME, "Full sequence date has expired");\r
361                                 sequenceDate = 0;\r
362                                 conn.getSyncTable().setLastSequenceDate(0);\r
363                                 updateSequenceNumber = 0;\r
364                                 conn.getSyncTable().setUpdateSequenceNumber(0);\r
365                         }\r
366                         // Check for "special" sync instructions\r
367                         String syncLinked = conn.getSyncTable().getRecord("FullLinkedNotebookSync");\r
368                         String syncShared = conn.getSyncTable().getRecord("FullLinkedNotebookSync");\r
369                         String syncNotebooks = conn.getSyncTable().getRecord("FullNotebookSync");\r
370                         String syncInkNoteImages = conn.getSyncTable().getRecord("FullInkNoteImageSync");\r
371                         if (syncLinked != null) {\r
372                                 downloadAllLinkedNotebooks();\r
373                         }\r
374                         if (syncShared != null) {\r
375                                 downloadAllSharedNotebooks();\r
376                         }\r
377                         if (syncNotebooks != null) {\r
378                                 downloadAllNotebooks();\r
379                         }\r
380                         \r
381                         if (syncInkNoteImages != null) {\r
382                                 List<String> guids = conn.getNoteTable().noteResourceTable.findInkNotes();\r
383                                 for (int i=0; i<guids.size(); i++) {\r
384                                         downloadInkNoteImage(guids.get(i), authToken);\r
385                                 }\r
386                                 conn.getSyncTable().deleteRecord("FullInkNoteImageSync");\r
387                         }\r
388                         \r
389                         // If there are remote changes\r
390                         logger.log(logger.LOW, "Update Count: " +syncState.getUpdateCount());\r
391                         logger.log(logger.LOW, "Last Update Count: " +updateSequenceNumber);\r
392                         \r
393                         if (syncState.getUpdateCount() > updateSequenceNumber) {\r
394                                 logger.log(logger.EXTREME, "Refresh needed is true");\r
395                                 refreshNeeded = true;\r
396                                 logger.log(logger.EXTREME, "Downloading changes");\r
397                                 syncRemoteToLocal();\r
398                         }\r
399                         \r
400                         //*****************************************\r
401                         //* Sync linked/shared notebooks \r
402                         //*****************************************\r
403                         syncLinkedNotebooks();\r
404                         \r
405                         if (!disableUploads) {\r
406                                 logger.log(logger.EXTREME, "Uploading changes");\r
407                                 // Synchronize remote changes\r
408                                 if (!error)\r
409                                         syncExpunged();\r
410                                 if (!error)\r
411                                         syncLocalTags();\r
412                                 if (!error)\r
413                                         syncLocalNotebooks();\r
414                                 if (!error)\r
415                                         syncLocalLinkedNotebooks();\r
416                                 if (!error) \r
417                                         syncDeletedNotes();\r
418                                 if (!error)\r
419                                         syncLocalNotes();\r
420                                 if (!error)\r
421                                         syncLocalSavedSearches();\r
422                         }\r
423                         \r
424                         status.message.emit(tr("Cleaning up"));\r
425                         List<String> notes = conn.getNoteTable().expungeIgnoreSynchronizedNotes(conn.getSyncTable().getIgnoreRecords("NOTEBOOK"), \r
426                                         conn.getSyncTable().getIgnoreRecords("TAG"), conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK"));\r
427                         if (notes.size() > 0)\r
428                                 syncSignal.refreshLists.emit();\r
429                         \r
430                         //*****************************************\r
431                         //* End of synchronization\r
432                         //*****************************************\r
433                         if (refreshNeeded)\r
434                                 syncSignal.refreshLists.emit();\r
435                         \r
436                         if (!error) {\r
437                                 logger.log(logger.EXTREME, "Sync completed.  Errors=" +error);\r
438                                 if (!disableUploads) \r
439                                         status.message.emit(tr("Synchronizing complete"));\r
440                                 else\r
441                                         status.message.emit(tr("Download syncronization complete.  Uploads have been disabled."));\r
442                                 \r
443                                 logger.log(logger.EXTREME, "Saving sync time");\r
444                                 if (syncState.getCurrentTime() > sequenceDate)\r
445                                         sequenceDate = syncState.getCurrentTime();\r
446                                 if (syncState.getUpdateCount() > updateSequenceNumber)\r
447                                         updateSequenceNumber = syncState.getUpdateCount();\r
448                                 conn.getSyncTable().setLastSequenceDate(sequenceDate);\r
449                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
450                         }\r
451                 }\r
452                 logger.log(logger.HIGH, "Leaving SyncRunner.evernoteSync");\r
453         }\r
454         \r
455         // Sync deleted items with Evernote\r
456         private void syncExpunged() {\r
457                 logger.log(logger.HIGH, "Entering SyncRunner.syncExpunged");\r
458                 \r
459                 List<DeletedItemRecord> expunged = conn.getDeletedTable().getAllDeleted();\r
460                 boolean error = false;\r
461                 for (int i=0; i<expunged.size() && keepRunning; i++) {\r
462                         \r
463                         if (authRefreshNeeded)\r
464                                 if (!refreshConnection())\r
465                                         return;\r
466 \r
467                         try {\r
468                                 if (expunged.get(i).type.equalsIgnoreCase("TAG")) {\r
469                                         logger.log(logger.EXTREME, "Tag expunged");\r
470                                         updateSequenceNumber = noteStore.expungeTag(authToken, expunged.get(i).guid);\r
471                                         conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
472                                         \r
473                                 }\r
474                                 if      (expunged.get(i).type.equalsIgnoreCase("NOTEBOOK")) {\r
475                                         logger.log(logger.EXTREME, "Notebook expunged");\r
476                                         updateSequenceNumber = noteStore.expungeNotebook(authToken, expunged.get(i).guid);\r
477                                         conn.getSyncTable().setLastSequenceDate(sequenceDate);\r
478                                 }\r
479                                 if (expunged.get(i).type.equalsIgnoreCase("NOTE")) {\r
480                                         logger.log(logger.EXTREME, "Note expunged");\r
481                                         updateSequenceNumber = noteStore.deleteNote(authToken, expunged.get(i).guid);\r
482                                         conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
483                                 }\r
484                                 if (expunged.get(i).type.equalsIgnoreCase("SAVEDSEARCH")) {\r
485                                         logger.log(logger.EXTREME, "saved search expunged");\r
486                                         updateSequenceNumber = noteStore.expungeSearch(authToken, expunged.get(i).guid);\r
487                                         conn.getSyncTable().setLastSequenceDate(sequenceDate);\r
488                                 }\r
489                         } catch (EDAMUserException e) {\r
490                                 logger.log(logger.LOW, "EDAM User Excepton in syncExpunged: " +expunged.get(i).guid);\r
491                                 logger.log(logger.LOW, e.getStackTrace());\r
492                                 error = true;\r
493                         } catch (EDAMSystemException e) {\r
494                                 logger.log(logger.LOW, "EDAM System Excepton in syncExpunged: "+expunged.get(i).guid);\r
495                                 logger.log(logger.LOW, e.getStackTrace());\r
496                                 error=true;\r
497                         } catch (EDAMNotFoundException e) {\r
498                                 logger.log(logger.LOW, "EDAM Not Found Excepton in syncExpunged: "+expunged.get(i).guid);\r
499 //                              logger.log(logger.LOW, e.getStackTrace());\r
500                                 //error=true;\r
501                         } catch (TException e) {\r
502                                 logger.log(logger.LOW, "EDAM TExcepton in syncExpunged: "+expunged.get(i).guid);\r
503                                 logger.log(logger.LOW, e.getStackTrace());\r
504                                 error=true;\r
505                         }\r
506                 }\r
507                 if (!error)\r
508                         conn.getDeletedTable().expungeAllDeletedRecords();\r
509                 \r
510                 logger.log(logger.HIGH, "Leaving SyncRunner.syncExpunged");\r
511 \r
512         }\r
513         private void syncDeletedNotes() {\r
514                 if (syncDeletedContent)\r
515                         return;\r
516                 logger.log(logger.HIGH, "Entering SyncRunner.syncDeletedNotes");\r
517                 status.message.emit(tr("Synchronizing deleted notes."));\r
518 \r
519                 List<Note> notes = conn.getNoteTable().getDirty();\r
520                 // Sync the local notebooks with Evernote's\r
521                 for (int i=0; i<notes.size() && keepRunning; i++) {\r
522                         \r
523                         if (authRefreshNeeded)\r
524                                 if (!refreshConnection())\r
525                                         return;\r
526                         \r
527                         Note enNote = notes.get(i);\r
528                         try {\r
529                                 if (enNote.getUpdateSequenceNum() > 0 && (enNote.isActive() == false || enNote.getDeleted() > 0)) {\r
530                                         if (syncDeletedContent) {\r
531                                                 logger.log(logger.EXTREME, "Deleted note found & synch content selected");\r
532                                                 Note delNote = conn.getNoteTable().getNote(enNote.getGuid(), true, true, true, true, true);\r
533                                                 delNote = getNoteContent(delNote);\r
534                                                 delNote = noteStore.updateNote(authToken, delNote);\r
535                                                 enNote.setUpdateSequenceNum(delNote.getUpdateSequenceNum());\r
536                                                 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());\r
537                                         } else {\r
538                                                 logger.log(logger.EXTREME, "Deleted note found & sync content not selected");\r
539                                                 int usn = noteStore.deleteNote(authToken, enNote.getGuid());\r
540                                                 enNote.setUpdateSequenceNum(usn);\r
541                                                 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());                                                \r
542                                         }\r
543                                         logger.log(logger.EXTREME, "Resetting deleted dirty flag");\r
544                                         conn.getNoteTable().resetDirtyFlag(enNote.getGuid());\r
545                                         updateSequenceNumber = enNote.getUpdateSequenceNum();\r
546                                         logger.log(logger.EXTREME, "Saving sequence number");\r
547                                         conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
548                                 }                               \r
549                         } catch (EDAMUserException e) {\r
550                                 //logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);\r
551                                 //status.message.emit("Error sending local note: " +e.getParameter());\r
552                                 //logger.log(logger.LOW, e.toString()); \r
553                                 //error = true;\r
554                         } catch (EDAMSystemException e) {\r
555                                 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);\r
556                                 status.message.emit(tr("Error: ") +e);\r
557                                 logger.log(logger.LOW, e.toString());           \r
558                                 error = true;\r
559                         } catch (EDAMNotFoundException e) {\r
560                                 //logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);\r
561                                 //status.message.emit("Error deleting local note: " +e +" - Continuing");\r
562                                 //logger.log(logger.LOW, e.toString());         \r
563                                 //error = true;\r
564                         } catch (TException e) {\r
565                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);\r
566                                 status.message.emit(tr("Error sending local note: ") +e);\r
567                                 logger.log(logger.LOW, e.toString());   \r
568                                 error = true;\r
569                         }               \r
570                 }\r
571         }\r
572         // Sync notes with Evernote\r
573         private void syncLocalNotes() {\r
574                 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
575                 status.message.emit(tr("Sending local notes."));\r
576 \r
577                 List<Note> notes = conn.getNoteTable().getDirty();\r
578                 // Sync the local notebooks with Evernote's\r
579                 for (int i=0; i<notes.size() && keepRunning; i++) {\r
580                         syncLocalNote(notes.get(i), authToken);\r
581                 }\r
582                 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
583 \r
584         }\r
585         // Sync notes with Evernote\r
586         private void syncLocalNote(Note enNote, String token) {\r
587                 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
588                 status.message.emit(tr("Sending local notes."));\r
589 \r
590                 if (authRefreshNeeded)\r
591                         if (!refreshConnection())\r
592                                 return;\r
593                         \r
594                 if (enNote.isActive()) {\r
595                         try {\r
596                                 logger.log(logger.EXTREME, "Active dirty note found - non new");\r
597                                 if (enNote.getUpdateSequenceNum() > 0) {\r
598                                         enNote = getNoteContent(enNote);\r
599                                         logger.log(logger.MEDIUM, "Updating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");\r
600                                         enNote = noteStore.updateNote(token, enNote);\r
601                                 } else { \r
602                                         logger.log(logger.EXTREME, "Active dirty found - new note");\r
603                                         logger.log(logger.MEDIUM, "Creating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");\r
604                                         String oldGuid = enNote.getGuid();\r
605                                         enNote = getNoteContent(enNote);\r
606                                         enNote = noteStore.createNote(token, enNote);\r
607                                         noteSignal.guidChanged.emit(oldGuid, enNote.getGuid());\r
608                                         conn.getNoteTable().updateNoteGuid(oldGuid, enNote.getGuid());\r
609                                 }\r
610                                 updateSequenceNumber = enNote.getUpdateSequenceNum();\r
611                                 logger.log(logger.EXTREME, "Saving note");\r
612                                 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());\r
613                                 List<Resource> rl = enNote.getResources();\r
614                                 logger.log(logger.EXTREME, "Getting note resources");\r
615                                 for (int j=0; j<enNote.getResourcesSize() && keepRunning; j++) {\r
616                                         Resource newRes = rl.get(j);\r
617                                         Data d = newRes.getData();\r
618                                         if (d!=null) {  \r
619                                                 logger.log(logger.EXTREME, "Calculating resource hash");\r
620                                                 String hash = byteArrayToHexString(d.getBodyHash());\r
621                                                 logger.log(logger.EXTREME, "updating resources by hash");\r
622                                                 String oldGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(enNote.getGuid(), hash);\r
623                                                 conn.getNoteTable().updateNoteResourceGuidbyHash(enNote.getGuid(), newRes.getGuid(), hash);\r
624                                                 resourceSignal.resourceGuidChanged.emit(enNote.getGuid(), oldGuid, newRes.getGuid());\r
625                                         }\r
626                                 }\r
627                                 logger.log(logger.EXTREME, "Resetting note dirty flag");\r
628                                 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());\r
629                                 updateSequenceNumber = enNote.getUpdateSequenceNum();\r
630                                 logger.log(logger.EXTREME, "Emitting note sequence number change");\r
631                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
632 \r
633                         } catch (EDAMUserException e) {\r
634                                 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);\r
635                                 status.message.emit(tr("Error sending local note: ")     +e.getParameter());\r
636                                 logger.log(logger.LOW, e.toString());   \r
637                                 error = true;\r
638                         } catch (EDAMSystemException e) {\r
639                                 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);\r
640                                 status.message.emit(tr("Error: ") +e);\r
641                                 logger.log(logger.LOW, e.toString());           \r
642                                 error = true;\r
643                         } catch (EDAMNotFoundException e) {\r
644                                 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);\r
645                                 status.message.emit(tr("Error sending local note: ") +e);\r
646                                 logger.log(logger.LOW, e.toString());   \r
647                                 error = true;\r
648                         } catch (TException e) {\r
649                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);\r
650                                 status.message.emit(tr("Error sending local note: ") +e);\r
651                                 logger.log(logger.LOW, e.toString());   \r
652                                 error = true;\r
653                         }\r
654                 }\r
655                 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNote");\r
656 \r
657         }\r
658 \r
659         // Sync Notebooks with Evernote\r
660         private void syncLocalNotebooks() {\r
661                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalNotebooks");\r
662                 \r
663                 status.message.emit(tr("Sending local notebooks."));\r
664                 List<Notebook> remoteList = new ArrayList<Notebook>();\r
665                 try {\r
666                         logger.log(logger.EXTREME, "Getting remote notebooks to compare with local");\r
667                         remoteList = noteStore.listNotebooks(authToken);\r
668                 } catch (EDAMUserException e1) {\r
669                         logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks getting remote Notebook List");\r
670                         status.message.emit(tr("Error: ") +e1);\r
671                         logger.log(logger.LOW, e1.toString());          \r
672                         error = true;\r
673                 } catch (EDAMSystemException e1) {\r
674                         logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks getting remote Notebook List");\r
675                         status.message.emit(tr("Error: ") +e1);\r
676                         logger.log(logger.LOW, e1.toString());  \r
677                         error = true;\r
678                 } catch (TException e1) {\r
679                         logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalNotebooks getting remote Notebook List");\r
680                         status.message.emit(tr("Error: ") +e1);\r
681                         logger.log(logger.LOW, e1.toString());  \r
682                         error = true;\r
683                 }\r
684                 logger.log(logger.EXTREME, "Getting local dirty notebooks");\r
685                 List<Notebook> notebooks = conn.getNotebookTable().getDirty();\r
686                 int sequence;\r
687                 // Sync the local notebooks with Evernote's\r
688                 for (int i=0; i<notebooks.size() && keepRunning; i++) {\r
689                         \r
690                         if (authRefreshNeeded)\r
691                                 if (!refreshConnection())\r
692                                         return;\r
693                         \r
694                         Notebook enNotebook = notebooks.get(i);\r
695                         try {\r
696                                 if (enNotebook.getUpdateSequenceNum() > 0) {\r
697                                         logger.log(logger.EXTREME, "Existing notebook is dirty");\r
698                                         sequence = noteStore.updateNotebook(authToken, enNotebook);\r
699                                 } else {\r
700                                         logger.log(logger.EXTREME, "New dirty notebook found");\r
701                                         String oldGuid = enNotebook.getGuid();\r
702                                         boolean found = false;\r
703                                         \r
704                                         // Look for a notebook with the same name.  If one is found, we don't need \r
705                                         // to create another one\r
706                                         logger.log(logger.EXTREME, "Looking for matching notebook name");\r
707                                         for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {\r
708                                                 if (remoteList.get(k).getName().equalsIgnoreCase(enNotebook.getName())) {\r
709                                                         enNotebook = remoteList.get(k);\r
710                                                         logger.log(logger.EXTREME, "Matching notebook found");\r
711                                                         found = true;\r
712                                                 }\r
713                                         }\r
714                                         if (!found)\r
715                                                 enNotebook = noteStore.createNotebook(authToken, enNotebook);\r
716                                         \r
717                                         logger.log(logger.EXTREME, "Updating notebook in database");\r
718                                         conn.getNotebookTable().updateNotebookGuid(oldGuid, enNotebook.getGuid());\r
719                                         sequence = enNotebook.getUpdateSequenceNum();\r
720                                 }\r
721                                 logger.log(logger.EXTREME, "Updating notebook sequence in database");\r
722                                 conn.getNotebookTable().updateNotebookSequence(enNotebook.getGuid(), sequence);\r
723                                 logger.log(logger.EXTREME, "Resetting dirty flag in notebook");\r
724                                 conn.getNotebookTable().resetDirtyFlag(enNotebook.getGuid());\r
725                                 updateSequenceNumber = sequence;\r
726                                 logger.log(logger.EXTREME, "Emitting sequence number to main thread");\r
727                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
728                         } catch (EDAMUserException e) {\r
729                                 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks");\r
730                                 logger.log(logger.LOW, e.toString());   \r
731                                 error = true;\r
732                         } catch (EDAMSystemException e) {\r
733                                 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks");\r
734                                 logger.log(logger.LOW, e.toString());           \r
735                                 error = true;\r
736                         } catch (EDAMNotFoundException e) {\r
737                                 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotebooks");\r
738                                 logger.log(logger.LOW, e.toString());           \r
739                                 error = true;\r
740                         } catch (TException e) {\r
741                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotebooks");\r
742                                 logger.log(logger.LOW, e.toString());   \r
743                                 error = true;\r
744                         }               \r
745                 }\r
746                 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNotebooks");\r
747 \r
748         }\r
749         // Sync Tags with Evernote\r
750         private void syncLocalTags() {\r
751                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalTags");\r
752                 List<Tag> remoteList = new ArrayList<Tag>();\r
753                 status.message.emit(tr("Sending local tags."));\r
754                 \r
755                 try {\r
756                         logger.log(logger.EXTREME, "Getting remote tags to compare names with the local tags");\r
757                         remoteList = noteStore.listTags(authToken);\r
758                 } catch (EDAMUserException e1) {\r
759                         logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote Tag List");\r
760                         status.message.emit(tr("Error: ") +e1);\r
761                         logger.log(logger.LOW, e1.toString());  \r
762                         error = true;\r
763                 } catch (EDAMSystemException e1) {\r
764                         logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote Tag List");\r
765                         status.message.emit(tr("Error: ") +e1);\r
766                         logger.log(logger.LOW, e1.toString());          \r
767                         error = true;\r
768                 } catch (TException e1) {\r
769                         logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote Tag List");\r
770                         status.message.emit(tr("Error: ") +e1);\r
771                         logger.log(logger.LOW, e1.toString());  \r
772                         error = true;\r
773                 }               \r
774                 \r
775                 int sequence;\r
776                 \r
777                 Tag enTag = findNextTag();\r
778                 while(enTag!=null) {\r
779                         if (authRefreshNeeded)\r
780                                 if (!refreshConnection())\r
781                                         return;\r
782 \r
783                         try {\r
784                                 if (enTag.getUpdateSequenceNum() > 0) {\r
785                                         logger.log(logger.EXTREME, "Updating tag");\r
786                                         sequence = noteStore.updateTag(authToken, enTag);\r
787                                 } else {\r
788                                         \r
789                                         // Look for a tag with the same name.  If one is found, we don't need \r
790                                         // to create another one\r
791                                         logger.log(logger.EXTREME, "New tag.  Comparing with remote names");\r
792                                         boolean found = false;\r
793                                         String oldGuid = enTag.getGuid();\r
794                                         for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {\r
795                                                 if (remoteList.get(k).getName().equalsIgnoreCase(enTag.getName())) {\r
796                                                         conn.getTagTable().updateTagGuid(enTag.getGuid(), remoteList.get(k).getGuid());\r
797                                                         enTag = remoteList.get(k);\r
798                                                         logger.log(logger.EXTREME, "Matching tag name found");\r
799                                                         found = true;\r
800                                                 }\r
801                                         }\r
802                                         if (!found)\r
803                                                 enTag = noteStore.createTag(authToken, enTag);\r
804                                         else\r
805                                                 enTag.setUpdateSequenceNum(noteStore.updateTag(authToken,enTag));\r
806                                         sequence = enTag.getUpdateSequenceNum();\r
807                                         if (!oldGuid.equals(enTag.getGuid())) {\r
808                                                 logger.log(logger.EXTREME, "Updating tag guid");\r
809                                                 conn.getTagTable().updateTagGuid(oldGuid, enTag.getGuid());\r
810                                         }\r
811                                 }\r
812                                 logger.log(logger.EXTREME, "Updating tag sequence number");\r
813                                 conn.getTagTable().updateTagSequence(enTag.getGuid(), sequence);\r
814                                 logger.log(logger.EXTREME, "Resetting tag dirty flag");\r
815                                 conn.getTagTable().resetDirtyFlag(enTag.getGuid());\r
816                                 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");\r
817                                 updateSequenceNumber = sequence;\r
818                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
819                         } catch (EDAMUserException e) {\r
820                                 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");\r
821                                 logger.log(logger.LOW, e.toString());           \r
822                                 error = true;\r
823                         } catch (EDAMSystemException e) {\r
824                                 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");\r
825                                 logger.log(logger.LOW, e.toString());   \r
826                                 error = true;\r
827                         } catch (EDAMNotFoundException e) {\r
828                                 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");\r
829                                 logger.log(logger.LOW, e.toString());   \r
830                                 error = true;\r
831                         } catch (TException e) {\r
832                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");\r
833                                 logger.log(logger.LOW, e.toString());           \r
834                                 error = true;\r
835                         }       \r
836                         \r
837                         // Find the next tag\r
838                         logger.log(logger.EXTREME, "Finding next tag");\r
839                         enTag = findNextTag();\r
840                 }\r
841                 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalTags");\r
842         }\r
843         private void syncLocalLinkedNotebooks() {\r
844                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalLinkedNotebooks");\r
845                 \r
846                 List<String> list = conn.getLinkedNotebookTable().getDirtyGuids();\r
847                 for (int i=0; i<list.size(); i++) {\r
848                         LinkedNotebook book = conn.getLinkedNotebookTable().getNotebook(list.get(i));\r
849                         try {\r
850                                 noteStore.updateLinkedNotebook(authToken, book);\r
851                         } catch (EDAMUserException e) {\r
852                                 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalLinkedNotebooks");\r
853                                 status.message.emit(tr("Error: ") +e);\r
854                                 logger.log(logger.LOW, e.toString());           \r
855                                 error = true;\r
856                                 e.printStackTrace();\r
857                         } catch (EDAMNotFoundException e) {\r
858                                 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalLinkedNotebooks");\r
859                                 status.message.emit(tr("Error: ") +e);\r
860                                 logger.log(logger.LOW, e.toString());           \r
861                                 error = true;\r
862                                 e.printStackTrace();\r
863                         } catch (EDAMSystemException e) {\r
864                                 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalLinkedNotebooks");\r
865                                 status.message.emit(tr("Error: ") +e);\r
866                                 logger.log(logger.LOW, e.toString());           \r
867                                 error = true;\r
868                                 e.printStackTrace();\r
869                         } catch (TException e) {\r
870                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalLinkedNotebooks");\r
871                                 status.message.emit(tr("Error: ") +e);\r
872                                 logger.log(logger.LOW, e.toString());           \r
873                                 error = true;\r
874                                 e.printStackTrace();\r
875                         }\r
876                 }\r
877                 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalLinkedNotebooks");\r
878         }\r
879         // Sync Saved Searches with Evernote\r
880         private void syncLocalSavedSearches() {\r
881                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");\r
882                 List<SavedSearch> remoteList = new ArrayList<SavedSearch>();\r
883                 status.message.emit(tr("Sending saved searches."));\r
884         \r
885                 logger.log(logger.EXTREME, "Getting saved searches to compare with local");\r
886                 try {\r
887                         remoteList = noteStore.listSearches(authToken);\r
888                 } catch (EDAMUserException e1) {\r
889                         logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote saved search List");\r
890                         status.message.emit(tr("Error: ") +e1);\r
891                         logger.log(logger.LOW, e1.toString());  \r
892                         error = true;\r
893                 } catch (EDAMSystemException e1) {\r
894                         logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote saved search List");\r
895                         status.message.emit(tr("Error: ") +e1);\r
896                         logger.log(logger.LOW, e1.toString());          \r
897                         error = true;\r
898                 } catch (TException e1) {\r
899                         logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote saved search List");\r
900                         status.message.emit(tr("Error: ") +e1);\r
901                         logger.log(logger.LOW, e1.toString());  \r
902                         error = true;\r
903                 }               \r
904                 \r
905                 List<SavedSearch> searches = conn.getSavedSearchTable().getDirty();\r
906                 int sequence;\r
907                 // Sync the local notebooks with Evernote's\r
908                 logger.log(logger.EXTREME, "Beginning to send saved searches");\r
909                 for (int i=0; i<searches.size() &&  keepRunning; i++) {\r
910                         \r
911                         if (authRefreshNeeded)\r
912                                 if (!refreshConnection())\r
913                                         return;\r
914                         \r
915                         SavedSearch enSearch = searches.get(i);\r
916                         try {\r
917                                 if (enSearch.getUpdateSequenceNum() > 0) \r
918                                         sequence = noteStore.updateSearch(authToken, enSearch);\r
919                                 else {\r
920                                         logger.log(logger.EXTREME, "New saved search found.");\r
921                                         // Look for a tag with the same name.  If one is found, we don't need \r
922                                         // to create another one\r
923                                         boolean found = false;\r
924                                         logger.log(logger.EXTREME, "Matching remote saved search names with local");\r
925                                         for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {\r
926                                                 if (remoteList.get(k).getName().equalsIgnoreCase(enSearch.getName())) {\r
927                                                         enSearch = remoteList.get(k);\r
928                                                         found = true;\r
929                                                         logger.log(logger.EXTREME, "Matching saved search found");\r
930                                                         sequence = enSearch.getUpdateSequenceNum();\r
931                                                 }\r
932                                         }\r
933 \r
934                                         String oldGuid = enSearch.getGuid();\r
935                                         if (!found)\r
936                                                 enSearch = noteStore.createSearch(authToken, enSearch);\r
937                                         sequence = enSearch.getUpdateSequenceNum();\r
938                                         logger.log(logger.EXTREME, "Updating tag guid in local database");\r
939                                         conn.getTagTable().updateTagGuid(oldGuid, enSearch.getGuid());\r
940                                 }\r
941                                 logger.log(logger.EXTREME, "Updating tag sequence in local database");\r
942                                 conn.getSavedSearchTable().updateSavedSearchSequence(enSearch.getGuid(), sequence);\r
943                                 logger.log(logger.EXTREME, "Resetting tag dirty flag");\r
944                                 conn.getSavedSearchTable().resetDirtyFlag(enSearch.getGuid());\r
945                                 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");\r
946                                 updateSequenceNumber = sequence;\r
947                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
948                         } catch (EDAMUserException e) {\r
949                                 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");\r
950                                 logger.log(logger.LOW, e.toString());   \r
951                                 error = true;\r
952                         } catch (EDAMSystemException e) {\r
953                                 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");\r
954                                 logger.log(logger.LOW, e.toString());   \r
955                                 error = true;\r
956                         } catch (EDAMNotFoundException e) {\r
957                                 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");\r
958                                 logger.log(logger.LOW, e.toString());   \r
959                                 error = true;\r
960                         } catch (TException e) {\r
961                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");\r
962                                 logger.log(logger.LOW, e.toString());   \r
963                                 error = true;\r
964                         }               \r
965                 }\r
966 \r
967                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");\r
968         }       \r
969 \r
970         // Sync evernote changes with local database\r
971         private void syncRemoteToLocal() {\r
972                 logger.log(logger.HIGH, "Entering SyncRunner.syncRemoteToLocal");\r
973 \r
974                 List<Note> dirtyNotes = conn.getNoteTable().getDirty();\r
975                 dirtyNoteGuids = new Vector<String>();\r
976                 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {\r
977                         dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());\r
978                 }\r
979                 \r
980                 int chunkSize = 10;\r
981                 SyncChunk chunk = null;\r
982                 boolean fullSync = false;\r
983                 boolean more = true;\r
984                 \r
985                 if (updateSequenceNumber == 0)\r
986                         fullSync = true;\r
987                 \r
988                 status.message.emit(tr("Downloading 0% complete."));\r
989                 \r
990                 while(more &&  keepRunning) {\r
991                         \r
992                         if (authRefreshNeeded)\r
993                                 if (!refreshConnection())\r
994                                         return;\r
995                         \r
996                         int sequence = updateSequenceNumber;\r
997                         try {\r
998 //                              conn.beginTransaction();\r
999                                 logger.log(logger.EXTREME, "Getting chunk from Evernote");\r
1000                                 chunk = noteStore.getSyncChunk(authToken, sequence, chunkSize, fullSync);\r
1001                         } catch (EDAMUserException e) {\r
1002                                 error = true;\r
1003                                 e.printStackTrace();\r
1004                                 status.message.emit(e.getMessage());\r
1005                         } catch (EDAMSystemException e) {\r
1006                                 error = true;\r
1007                                 e.printStackTrace();\r
1008                                 status.message.emit(e.getMessage());\r
1009                         } catch (TException e) {\r
1010                                 error = true;\r
1011                                 e.printStackTrace();\r
1012                                 status.message.emit(e.getMessage());\r
1013                         } \r
1014                         if (error || chunk == null) \r
1015                                 return;\r
1016                                 \r
1017                 \r
1018                         \r
1019                         syncRemoteTags(chunk.getTags());\r
1020                         syncRemoteSavedSearches(chunk.getSearches());\r
1021                         syncRemoteNotebooks(chunk.getNotebooks());\r
1022                         syncRemoteNotes(chunk.getNotes(), fullSync, authToken);\r
1023                         syncRemoteResources(chunk.getResources());\r
1024                         syncRemoteLinkedNotebooks(chunk.getLinkedNotebooks());\r
1025                         syncExpungedNotes(chunk);\r
1026                         \r
1027                         \r
1028                         // Check for more notes\r
1029                         if (chunk.getChunkHighUSN() <= updateSequenceNumber) \r
1030                                 more = false;\r
1031                         if (error)\r
1032                                 more = false;\r
1033                         logger.log(logger.EXTREME, "More notes? " +more);\r
1034 \r
1035                         \r
1036                         // Save the chunk sequence number\r
1037                         if (!error && chunk.getChunkHighUSN() > 0 && keepRunning) {\r
1038                                 logger.log(logger.EXTREME, "emitting sequence number to main thread");\r
1039                                 updateSequenceNumber = chunk.getChunkHighUSN();\r
1040                                 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());\r
1041                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
1042 //                              conn.commitTransaction();\r
1043                         }\r
1044                         \r
1045                         \r
1046                         if (more) {\r
1047                                 long pct = chunk.getChunkHighUSN() * 100;\r
1048                                 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());\r
1049                                 pct = pct/evernoteUpdateCount;\r
1050                                 status.message.emit(tr("Downloading ") +new Long(pct).toString()+tr("% complete."));\r
1051                         }\r
1052 //                      conn.commitTransaction();\r
1053                 }\r
1054                 logger.log(logger.HIGH, "Leaving SyncRunner.syncRemoteToLocal");\r
1055         }\r
1056         // Sync expunged notes\r
1057         private void syncExpungedNotes(SyncChunk chunk) {\r
1058                 // Do the local deletes\r
1059                 logger.log(logger.EXTREME, "Doing local deletes");\r
1060                 List<String> guid = chunk.getExpungedNotes();\r
1061                 if (guid != null) {\r
1062                         for (int i=0; i<guid.size() && keepRunning; i++) {\r
1063                                 String notebookGuid = "";\r
1064                                 Note localNote = conn.getNoteTable().getNote(guid.get(i), false, false, false, false, false);\r
1065                                 if (localNote != null) {\r
1066                                         conn.getNoteTable().updateNoteSequence(guid.get(i), 0);\r
1067                                         notebookGuid = localNote.getNotebookGuid();\r
1068                                 }\r
1069                                 if (!conn.getNotebookTable().isNotebookLocal(notebookGuid)) {\r
1070                                         logger.log(logger.EXTREME, "Expunging local note from database");\r
1071                                         conn.getNoteTable().expungeNote(guid.get(i), true, false);\r
1072                                 }\r
1073                         }\r
1074                 }\r
1075                 guid = chunk.getExpungedNotebooks();\r
1076                 if (guid != null)\r
1077                         for (int i=0; i<guid.size() && keepRunning; i++) {\r
1078                                 logger.log(logger.EXTREME, "Expunging local notebook from database");\r
1079                                 conn.getNotebookTable().expungeNotebook(guid.get(i), false);\r
1080                         }\r
1081                 guid = chunk.getExpungedTags();\r
1082                 if (guid != null)\r
1083                         for (int i=0; i<guid.size() && keepRunning; i++) {\r
1084                                 logger.log(logger.EXTREME, "Expunging tags from local database");\r
1085                                 conn.getTagTable().expungeTag(guid.get(i), false);\r
1086                         }\r
1087                 guid = chunk.getExpungedSearches();\r
1088                 if (guid != null) \r
1089                         for (int i=0; i<guid.size() && keepRunning; i++) {\r
1090                                 logger.log(logger.EXTREME, "Expunging saved search from local database");\r
1091                                 conn.getSavedSearchTable().expungeSavedSearch(guid.get(i), false);\r
1092                         }\r
1093                 guid = chunk.getExpungedLinkedNotebooks();\r
1094                 if (guid != null) \r
1095                         for (int i=0; i<guid.size() && keepRunning; i++) {\r
1096                                 logger.log(logger.EXTREME, "Expunging linked notebook from local database");\r
1097                                 conn.getLinkedNotebookTable().expungeNotebook(guid.get(i), false);\r
1098                         }\r
1099 \r
1100         }\r
1101         // Sync remote tags\r
1102         private void syncRemoteTags(List<Tag> tags) {\r
1103                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");\r
1104                 if (tags != null) {\r
1105                         for (int i=0; i<tags.size() && keepRunning; i++) {\r
1106                                 String oldGuid;\r
1107                                 oldGuid = conn.getTagTable().findTagByName(tags.get(i).getName());\r
1108                                 if (oldGuid != null && !tags.get(i).getGuid().equalsIgnoreCase(oldGuid))\r
1109                                         conn.getTagTable().updateTagGuid(oldGuid, tags.get(i).getGuid());\r
1110                                 conn.getTagTable().syncTag(tags.get(i), false);\r
1111                         }\r
1112                 }\r
1113                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");\r
1114         }\r
1115         // Sync remote saved searches\r
1116         private void syncRemoteSavedSearches(List<SavedSearch> searches) {\r
1117                 logger.log(logger.EXTREME, "Entering SyncRunner.syncSavedSearches");\r
1118                 if (searches != null) {\r
1119                         for (int i=0; i<searches.size() && keepRunning; i++) {\r
1120                                 String oldGuid;\r
1121                                 oldGuid = conn.getSavedSearchTable().findSavedSearchByName(searches.get(i).getName());\r
1122                                 if (oldGuid != null && !searches.get(i).getGuid().equalsIgnoreCase(oldGuid))\r
1123                                         conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, searches.get(i).getGuid());\r
1124                                 conn.getSavedSearchTable().syncSavedSearch(searches.get(i), false);\r
1125                         }\r
1126                 }\r
1127                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncSavedSearches");\r
1128         }\r
1129         // Sync remote linked notebooks\r
1130         private void syncRemoteLinkedNotebooks(List<LinkedNotebook> books) {\r
1131                 logger.log(logger.EXTREME, "Entering SyncRunner.syncLinkedNotebooks");\r
1132                 if (books != null) {\r
1133                         for (int i=0; i<books.size() && keepRunning; i++) {\r
1134                                 conn.getLinkedNotebookTable().updateNotebook(books.get(i), false); \r
1135                         }\r
1136                 }\r
1137                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncLinkedNotebooks");\r
1138         }\r
1139         // Sync remote Notebooks 2\r
1140         private void syncRemoteNotebooks(List<Notebook> notebooks) {\r
1141                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");\r
1142                 if (notebooks != null) {\r
1143                         for (int i=0; i<notebooks.size() && keepRunning; i++) {\r
1144                                 String oldGuid;\r
1145                                 oldGuid = conn.getNotebookTable().findNotebookByName(notebooks.get(i).getName());\r
1146                                 if (oldGuid != null && !conn.getNotebookTable().isNotebookLocal(oldGuid) && !notebooks.get(i).getGuid().equalsIgnoreCase(oldGuid))\r
1147                                         conn.getNotebookTable().updateNotebookGuid(oldGuid, notebooks.get(i).getGuid());\r
1148                                 conn.getNotebookTable().syncNotebook(notebooks.get(i), false); \r
1149                                 \r
1150                                 // Synchronize shared notebook information\r
1151 //                              if (notebooks.get(i).getSharedNotebookIdsSize() > 0) {\r
1152 //                                      conn.getSharedNotebookTable().expungeNotebookByGuid(notebooks.get(i).getGuid(), false);\r
1153 //                                      for (int j=0; j<notebooks.get(i).getSharedNotebookIdsSize(); j++) {\r
1154 //                                              syncRemoteSharedNotebook(notebooks.get(i).getGuid(), notebooks.get(i).getSharedNotebookIds().get(j), authToken);\r
1155 //                                      }\r
1156 //                              }\r
1157                         }\r
1158                 }                       \r
1159                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");\r
1160         }\r
1161         // Sync remote shared notebook\r
1162 //      private void syncRemoteSharedNotebook(String guid, Long id, String token) {\r
1163 //              List<SharedNotebook> books = noteStore.getSharedNotebookByAuth(authToken);\r
1164 //      }\r
1165         // Sync remote Resources\r
1166         private void syncRemoteResources(List<Resource> resource) {\r
1167                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteResources");\r
1168                 if (resource != null) {\r
1169                         for (int i=0; i<resource.size() && keepRunning; i++) {\r
1170                                 syncRemoteResource(resource.get(i), authToken);\r
1171                         }\r
1172                 }\r
1173                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteResources");\r
1174         }\r
1175         // Sync remote resource\r
1176         private void syncRemoteResource(Resource resource, String authToken) {\r
1177                 // This is how the logic for this works.\r
1178                 // 1.) If the resource is not in the local database, we add it.\r
1179                 // 2.) If a copy of the resource is in the local database and the note isn't dirty, we update the local copy\r
1180                 // 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
1181                 //     is a conflict.  The note conflict should get a copy of the resource at that time.\r
1182                 \r
1183                 boolean saveNeeded = false;\r
1184                 /* #1 */                Resource r = getEvernoteResource(resource.getGuid(), true,true,true, authToken);\r
1185                                                 Resource l = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), false);\r
1186                                                 if (l == null) {\r
1187                                                         saveNeeded = true;\r
1188                                                 } else {\r
1189                 /* #2 */                        boolean isNoteDirty = conn.getNoteTable().isNoteDirty(r.getNoteGuid());\r
1190                                                         if (!isNoteDirty)\r
1191                                                                 saveNeeded = true;\r
1192                                                         else {\r
1193                 /* #3 */                                String remoteHash = "";\r
1194                                                                 if (r != null && r.getData() != null && r.getData().getBodyHash() != null)\r
1195                                                                         remoteHash = byteArrayToHexString(r.getData().getBodyHash());\r
1196                                                                 String localHash = "";\r
1197                                                                 if (l != null && l.getData() != null && l.getData().getBodyHash() != null)\r
1198                                                                         remoteHash = byteArrayToHexString(l.getData().getBodyHash());\r
1199                                                 \r
1200                                                                 if (localHash.equalsIgnoreCase(remoteHash))\r
1201                                                                         saveNeeded = true;\r
1202                                                         }\r
1203                                                 }\r
1204                                                 \r
1205                                                 if (saveNeeded) \r
1206                                                         conn.getNoteTable().noteResourceTable.updateNoteResource(r, false);\r
1207                                                 if (r.getMime().equalsIgnoreCase("application/vnd.evernote.ink"))\r
1208                                                         downloadInkNoteImage(r.getGuid(), authToken);\r
1209 \r
1210         }\r
1211         // Sync remote notes\r
1212         private void syncRemoteNotes(List<Note> note, boolean fullSync, String token) {\r
1213                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotes");\r
1214                 if (note != null) {\r
1215                         for (int i=0; i<note.size() && keepRunning; i++) {\r
1216                                 Note n = getEvernoteNote(note.get(i).getGuid(), true, fullSync, true,true, token);\r
1217                                 syncRemoteNote(n, fullSync, token);\r
1218                         }\r
1219                 }\r
1220                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotes");\r
1221         }\r
1222         private void syncRemoteNote(Note n, boolean fullSync, String token) {\r
1223                 if (n!=null) {\r
1224                         \r
1225                         // Basically, this is how the sync logic for a note works.\r
1226                         // If the remote note has changed and the local has not, we\r
1227                         // accept the change.\r
1228                         // If both the local & remote have changed but the sequence\r
1229                         // numbers are the same, we don't accept the change.  This\r
1230                         // seems to happen when attachments are indexed by the server.\r
1231                         // If both the local & remote have changed and the sequence numbers\r
1232                         // are different we move the local copy to a local notebook (making sure\r
1233                         // to copy all resources) and we accept the new one.                    \r
1234                         boolean conflictingNote = true;\r
1235                         logger.log(logger.EXTREME, "Checking for duplicate note " +n.getGuid());\r
1236                         if (dirtyNoteGuids != null && dirtyNoteGuids.contains(n.getGuid())) { \r
1237                                 logger.log(logger.EXTREME, "Conflict check beginning");\r
1238                                 conflictingNote = checkForConflict(n);\r
1239                                 logger.log(logger.EXTREME, "Conflict check results " +conflictingNote);\r
1240                                 if (conflictingNote)\r
1241                                         moveConflictingNote(n.getGuid());\r
1242                         }\r
1243                         boolean ignoreNote = false;\r
1244                         if (ignoreNotebooks.contains(n.getNotebookGuid()))\r
1245                                 ignoreNote = true;\r
1246                         for (int i=0; i<n.getTagGuidsSize(); i++) {\r
1247                                 if (ignoreTags.contains(n.getTagGuids().get(i))) {\r
1248                                         ignoreNote = true;\r
1249                                         i=n.getTagGuidsSize();\r
1250                                 }\r
1251                         }\r
1252                                 \r
1253                         if ((conflictingNote || fullSync) && !ignoreNote) {\r
1254                                 logger.log(logger.EXTREME, "Saving Note");\r
1255                                 conn.getNoteTable().syncNote(n, false);\r
1256                                 noteSignal.noteChanged.emit(n.getGuid(), null);   // Signal to ivalidate note cache\r
1257                                 noteSignal.noteDownloaded.emit(n, true);                // Signal to add note to index\r
1258                                         logger.log(logger.EXTREME, "Note Saved");\r
1259                                 if (fullSync && n.getResources() != null) {\r
1260                                         for (int q=0; q<n.getResources().size() && keepRunning; q++) {\r
1261                                                 logger.log(logger.EXTREME, "Getting note resources.");\r
1262                                                 conn.getNoteTable().noteResourceTable.updateNoteResource(n.getResources().get(q), false);\r
1263                                                 if (n.getResources().get(q).getMime().equalsIgnoreCase("application/vnd.evernote.ink"))\r
1264                                                         downloadInkNoteImage(n.getResources().get(q).getGuid(), token);\r
1265                                         }\r
1266                                 }\r
1267                         }\r
1268                 }\r
1269         }\r
1270         private Note getEvernoteNote(String guid, boolean withContent, boolean withResourceData, boolean withResourceRecognition, boolean withResourceAlternateData, String token) { \r
1271                 Note n = null;\r
1272                 try {\r
1273                         logger.log(logger.EXTREME, "Retrieving note " +guid);\r
1274                         n = noteStore.getNote(token, guid, withContent, withResourceData, withResourceRecognition, withResourceAlternateData);\r
1275                         logger.log(logger.EXTREME, "Note " +guid +" has been retrieved.");\r
1276                 } catch (EDAMUserException e) {\r
1277                         logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");\r
1278                         logger.log(logger.LOW, e.toString());   \r
1279                         error = true;\r
1280                         e.printStackTrace();\r
1281                 } catch (EDAMSystemException e) {\r
1282                         logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");\r
1283                         logger.log(logger.LOW, e.toString());   \r
1284                         error = true;\r
1285                         e.printStackTrace();\r
1286                 } catch (EDAMNotFoundException e) {\r
1287                         logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");\r
1288                         logger.log(logger.LOW, e.toString());   \r
1289                         error = true;\r
1290                         e.printStackTrace();\r
1291                 } catch (TException e) {\r
1292                         logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");\r
1293                         logger.log(logger.LOW, e.toString());   \r
1294                         error = true;\r
1295                         e.printStackTrace();\r
1296                 }\r
1297                 return n;\r
1298         }\r
1299         private Resource getEvernoteResource(String guid, boolean withData, boolean withRecognition, boolean withAttributes, String token) { \r
1300                 Resource n = null;\r
1301                 try {\r
1302                         logger.log(logger.EXTREME, "Retrieving resource " +guid);\r
1303                         n = noteStore.getResource(token, guid, withData, withRecognition, withAttributes, withAttributes);\r
1304                         logger.log(logger.EXTREME, "Resource " +guid +" has been retrieved.");\r
1305                 } catch (EDAMUserException e) {\r
1306                         logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");\r
1307                         logger.log(logger.LOW, e.toString());   \r
1308                         error = true;\r
1309                         e.printStackTrace();\r
1310                 } catch (EDAMSystemException e) {\r
1311                         logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");\r
1312                         logger.log(logger.LOW, e.toString());   \r
1313                         error = true;\r
1314                         e.printStackTrace();\r
1315                 } catch (EDAMNotFoundException e) {\r
1316                         logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");\r
1317                         logger.log(logger.LOW, e.toString());   \r
1318                         error = true;\r
1319                         e.printStackTrace();\r
1320                 } catch (TException e) {\r
1321                         logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");\r
1322                         logger.log(logger.LOW, e.toString());   \r
1323                         error = true;\r
1324                         e.printStackTrace();\r
1325                 }\r
1326                 return n;\r
1327         }\r
1328 \r
1329         \r
1330         private boolean checkForConflict(Note n) {\r
1331                 logger.log(logger.EXTREME, "Checking note sequence number  " +n.getGuid());\r
1332                 Note oldNote = conn.getNoteTable().getNote(n.getGuid(), false, false, false, false, false);\r
1333                 logger.log(logger.EXTREME, "Local/Remote sequence numbers: " +oldNote.getUpdateSequenceNum()+"/"+n.getUpdateSequenceNum());\r
1334                 if (oldNote.getUpdateSequenceNum() == n.getUpdateSequenceNum())\r
1335                         return false;\r
1336                 return true;\r
1337         }\r
1338         \r
1339         private void moveConflictingNote(String guid) {\r
1340                 logger.log(logger.EXTREME, "Conflicting change found for note " +guid);\r
1341                 List<Notebook> books = conn.getNotebookTable().getAllLocal();\r
1342                 String notebookGuid = null;\r
1343                 for (int i=0; i<books.size() && keepRunning; i++) {\r
1344                         if (books.get(i).getName().equalsIgnoreCase("Conflicting Changes (local)") ||\r
1345                                         books.get(i).getName().equalsIgnoreCase("Conflicting Changes")) {\r
1346                                 notebookGuid = books.get(i).getGuid();\r
1347                                 i=books.size();\r
1348                         }\r
1349                 }\r
1350                 \r
1351                 if (notebookGuid == null) {\r
1352                         logger.log(logger.EXTREME, "Building conflicting change notebook " +guid);\r
1353                         Calendar currentTime = new GregorianCalendar();\r
1354                         Long l = new Long(currentTime.getTimeInMillis());\r
1355                         long prevTime = l;\r
1356                         while (prevTime==l) {\r
1357                                 currentTime = new GregorianCalendar();\r
1358                                 l=currentTime.getTimeInMillis();\r
1359                         }\r
1360                         String randint = new String(Long.toString(l));\r
1361                 \r
1362                         Notebook newBook = new Notebook();\r
1363                         newBook.setUpdateSequenceNum(0);\r
1364                         newBook.setGuid(randint);\r
1365                         newBook.setName("Conflicting Changes");\r
1366                         newBook.setServiceCreated(new Date().getTime());\r
1367                         newBook.setServiceUpdated(new Date().getTime());\r
1368                         newBook.setDefaultNotebook(false);\r
1369                         newBook.setPublished(false);\r
1370                         \r
1371                         conn.getNotebookTable().addNotebook(newBook, false, true);\r
1372                         notebookSignal.listChanged.emit();\r
1373                         notebookGuid = newBook.getGuid();\r
1374                         refreshNeeded = true;\r
1375                 }\r
1376                 \r
1377                 // Now that we have a good notebook guid, we need to move the conflicting note\r
1378                 // to the local notebook\r
1379                 logger.log(logger.EXTREME, "Moving conflicting note " +guid);\r
1380                 Calendar currentTime = new GregorianCalendar();\r
1381                 Long l = new Long(currentTime.getTimeInMillis());\r
1382                 long prevTime = l;\r
1383                 while (prevTime==l) {\r
1384                         currentTime = new GregorianCalendar();\r
1385                         l = currentTime.getTimeInMillis();\r
1386                 }\r
1387                 String newGuid = new String(Long.toString(l));\r
1388                                         \r
1389                 Note oldNote = conn.getNoteTable().getNote(guid, true, true, false, false, false);\r
1390                 for (int i=0; i<oldNote.getResources().size() && keepRunning; i++) {\r
1391                         l = new Long(currentTime.getTimeInMillis());\r
1392                         String newResG = new String(Long.toString(l));\r
1393                         String oldResG = oldNote.getResources().get(i).getGuid();\r
1394                         conn.getNoteTable().noteResourceTable.resetUpdateSequenceNumber(oldResG, true);\r
1395                         conn.getNoteTable().noteResourceTable.updateNoteResourceGuid(oldResG, newResG, true);\r
1396                 }\r
1397                 \r
1398                 conn.getNoteTable().resetNoteSequence(guid);\r
1399                 conn.getNoteTable().updateNoteGuid(guid, newGuid);\r
1400                 conn.getNoteTable().updateNoteNotebook(newGuid, notebookGuid, true);\r
1401                 \r
1402                 noteSignal.notebookChanged.emit(newGuid, notebookGuid);\r
1403                 refreshNeeded = true;\r
1404                 noteSignal.guidChanged.emit(guid,newGuid);\r
1405         }\r
1406         \r
1407 \r
1408 \r
1409         \r
1410         //******************************************************\r
1411         //******************************************************\r
1412         //** Utility Functions\r
1413         //******************************************************\r
1414         //******************************************************\r
1415         // Convert a byte array to a hex string\r
1416         private static String byteArrayToHexString(byte data[]) {\r
1417                 StringBuffer buf = new StringBuffer();\r
1418             for (byte element : data) {\r
1419                 int halfbyte = (element >>> 4) & 0x0F;\r
1420                 int two_halfs = 0;\r
1421                 do {\r
1422                         if ((0 <= halfbyte) && (halfbyte <= 9))\r
1423                                buf.append((char) ('0' + halfbyte));\r
1424                            else\r
1425                                 buf.append((char) ('a' + (halfbyte - 10)));\r
1426                         halfbyte = element & 0x0F;\r
1427                 } while(two_halfs++ < 1);\r
1428             }\r
1429             return buf.toString();              \r
1430         }\r
1431 \r
1432         \r
1433         \r
1434         //*******************************************************\r
1435         //* Find dirty tags, which do not have newly created parents\r
1436         //*******************************************************\r
1437         private Tag findNextTag() {\r
1438                 logger.log(logger.HIGH, "Entering SyncRunner.findNextTag");\r
1439                 Tag nextTag = null;\r
1440                 List<Tag> tags = conn.getTagTable().getDirty();\r
1441                 \r
1442                 // Find the parent.  If the parent has a sequence > 0 then it is a good\r
1443                 // parent.\r
1444                 for (int i=0; i<tags.size() && keepRunning; i++) {\r
1445                         if (tags.get(i).getParentGuid() == null) {\r
1446                                 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found without parent");\r
1447                                 return tags.get(i);\r
1448                         }\r
1449                         Tag parentTag = conn.getTagTable().getTag(tags.get(i).getParentGuid());\r
1450                         if (parentTag.getUpdateSequenceNum() > 0) {\r
1451                                 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found");\r
1452                                 return tags.get(i);\r
1453                         }\r
1454                 }\r
1455                 \r
1456                 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - no tags returned");\r
1457                 return nextTag;\r
1458         }\r
1459         \r
1460         \r
1461            // Connect to Evernote\r
1462     public boolean enConnect()  {\r
1463         try {\r
1464                         userStoreTrans = new THttpClient(userStoreUrl);\r
1465                         userStoreTrans.setCustomHeader("User-Agent", userAgent);\r
1466                 } catch (TTransportException e) {\r
1467                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());\r
1468                         mb.exec();\r
1469                         e.printStackTrace();\r
1470                 }\r
1471                 userStoreProt = new TBinaryProtocol(userStoreTrans);\r
1472             userStore = new UserStore.Client(userStoreProt, userStoreProt);\r
1473             syncSignal.saveUserStore.emit(userStore);\r
1474             try {\r
1475                         authResult = userStore.authenticate(username, password, consumerKey, consumerSecret);\r
1476                 } catch (EDAMUserException e) {\r
1477                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Error", "Incorrect username/password");\r
1478                         mb.exec();\r
1479                         isConnected = false;\r
1480                         return false;\r
1481                 } catch (EDAMSystemException e) {\r
1482                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "EDAM System Excepton", e.getLocalizedMessage());\r
1483                         mb.exec();\r
1484                         e.printStackTrace();\r
1485                         isConnected = false;\r
1486                 } catch (TException e) {\r
1487                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());\r
1488                         mb.exec();\r
1489                         e.printStackTrace();\r
1490                         isConnected = false;\r
1491                 }\r
1492                 \r
1493             boolean versionOk = false;\r
1494                 try {\r
1495                         versionOk = userStore.checkVersion("NeverNote", \r
1496                     com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR, \r
1497                       com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR);\r
1498                 } catch (TException e) {\r
1499                         e.printStackTrace();\r
1500                         isConnected = false;\r
1501                 } \r
1502             if (!versionOk) { \r
1503                 System.err.println("Incomatible EDAM client protocol version"); \r
1504                 isConnected = false;\r
1505             }\r
1506             if (authResult != null) {\r
1507                 user = authResult.getUser(); \r
1508                 authToken = authResult.getAuthenticationToken(); \r
1509                 noteStoreUrl = noteStoreUrlBase + user.getShardId();\r
1510                 syncSignal.saveAuthToken.emit(authToken);\r
1511                 syncSignal.saveNoteStore.emit(noteStore);\r
1512                 \r
1513          \r
1514                 try {\r
1515                         noteStoreTrans = new THttpClient(noteStoreUrl);\r
1516                         noteStoreTrans.setCustomHeader("User-Agent", userAgent);\r
1517                 } catch (TTransportException e) {\r
1518                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());\r
1519                         mb.exec();\r
1520                         e.printStackTrace();\r
1521                         isConnected = false;\r
1522                 } \r
1523                 noteStoreProt = new TBinaryProtocol(noteStoreTrans);\r
1524                 noteStore = \r
1525                         new NoteStore.Client(noteStoreProt, noteStoreProt); \r
1526                 isConnected = true;\r
1527                 authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();\r
1528                 authRefreshTime = authTimeRemaining / 2;\r
1529             }\r
1530             \r
1531                 // Get user information\r
1532                 try {\r
1533                         User user = userStore.getUser(authToken);\r
1534                         syncSignal.saveUserInformation.emit(user);\r
1535                 } catch (EDAMUserException e1) {\r
1536                         e1.printStackTrace();\r
1537                 } catch (EDAMSystemException e1) {\r
1538                         e1.printStackTrace();\r
1539                 } catch (TException e1) {\r
1540                         e1.printStackTrace();\r
1541                 }\r
1542             \r
1543             return isConnected;\r
1544     }\r
1545         // Disconnect from the database                         \r
1546     public void enDisconnect() {\r
1547         isConnected = false;\r
1548     }\r
1549     // Refresh the connection\r
1550     private synchronized boolean refreshConnection() {\r
1551                 logger.log(logger.EXTREME, "Entering SyncRunner.refreshConnection()");\r
1552 //        Calendar cal = Calendar.getInstance();\r
1553                 \r
1554         // If we are not connected let's get out of here\r
1555         if (!isConnected)\r
1556                 return false;\r
1557         \r
1558                 // If we fail too many times, then let's give up.\r
1559                 if (failedRefreshes >=5) {\r
1560                         logger.log(logger.EXTREME, "Refresh attempts have failed.  Disconnecting.");\r
1561                         isConnected = false;\r
1562                         status.message.emit(tr("Unable to synchronize - Authentication failed"));\r
1563                         return false;\r
1564                 }\r
1565         \r
1566                 // If this is the first time through, then we need to set this\r
1567 //              if (authRefreshTime == 0 || cal.getTimeInMillis() > authRefreshTime) \r
1568 //                      authRefreshTime = cal.getTimeInMillis();\r
1569                 \r
1570  //             // Default to checking again in 5 min.  This in case we fail.\r
1571  //             authRefreshTime = authRefreshTime +(5*60*1000);     \r
1572 \r
1573                 // Try to get a new token\r
1574                 AuthenticationResult newAuth = null; \r
1575                 logger.log(logger.EXTREME, "Beginning to try authentication refresh");\r
1576         try {\r
1577                 if (userStore != null && authToken != null) \r
1578                         newAuth = userStore.refreshAuthentication(authToken); \r
1579                 else\r
1580                         return false;\r
1581                 logger.log(logger.EXTREME, "UserStore.refreshAuthentication has succeeded.");\r
1582                 } catch (EDAMUserException e) {\r
1583                         e.printStackTrace();\r
1584                         syncSignal.authRefreshComplete.emit(false);\r
1585                         failedRefreshes++;\r
1586                         return false;\r
1587                 } catch (EDAMSystemException e) {\r
1588                         e.printStackTrace();\r
1589                         syncSignal.authRefreshComplete.emit(false);\r
1590                         failedRefreshes++;\r
1591                         return false;           \r
1592                 } catch (TException e) { \r
1593                         e.printStackTrace();\r
1594                         syncSignal.authRefreshComplete.emit(false);\r
1595                         failedRefreshes++;\r
1596                         return false;\r
1597                 }\r
1598                 \r
1599                 // If we didn't get a good auth, then we've failed\r
1600                 if (newAuth == null) {\r
1601                         failedRefreshes++;\r
1602                         status.message.emit(tr("Unable to synchronize - Authentication failed"));\r
1603                         logger.log(logger.EXTREME, "Authentication failure #" +failedRefreshes);\r
1604                         status.message.emit(tr("Unable to synchronize - Authentication failed"));\r
1605                         syncSignal.authRefreshComplete.emit(false);\r
1606                         return false;\r
1607                 }\r
1608                 \r
1609                 // We got a good token.  Now we need to setup the time to renew it.\r
1610                 logger.log(logger.EXTREME, "Saving authentication tokens");\r
1611                 authResult = newAuth;\r
1612                 authToken = new String(newAuth.getAuthenticationToken());\r
1613 //              authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();\r
1614 //              authRefreshTime = cal.getTimeInMillis() + (authTimeRemaining/4);        \r
1615                 failedRefreshes=0;\r
1616                 syncSignal.authRefreshComplete.emit(true);\r
1617                 authRefreshNeeded = false;\r
1618                         \r
1619                 // This should never happen, but if it does we consider this a faild attempt.\r
1620 //              if (authTimeRemaining <= 0) {\r
1621 //                      failedRefreshes++;\r
1622 //                      syncSignal.authRefreshComplete.emit(false);\r
1623 //              }\r
1624                 \r
1625                 return true;\r
1626     }\r
1627     \r
1628         public synchronized boolean addWork(String request) {\r
1629                 if (workQueue.offer(request))\r
1630                         return true;\r
1631                 return false;\r
1632         }\r
1633     \r
1634     private Note getNoteContent(Note n) {\r
1635         n.setContent(conn.getNoteTable().getNoteContentBinary(n.getGuid()));\r
1636         return n;\r
1637     }\r
1638 \r
1639 \r
1640 \r
1641     //*********************************************************\r
1642     //* Special download instructions.  Used for DB upgrades\r
1643     //*********************************************************\r
1644     private void downloadAllSharedNotebooks() {\r
1645         try {\r
1646                         List<SharedNotebook> books = noteStore.listSharedNotebooks(authToken);\r
1647                         logger.log(logger.LOW, "Shared notebooks found = " +books.size());\r
1648                         for (int i=0; i<books.size(); i++) {\r
1649                                 conn.getSharedNotebookTable().updateNotebook(books.get(i), false);\r
1650                         }\r
1651                         conn.getSyncTable().deleteRecord("FullSharedNotebookSync");\r
1652                 } catch (EDAMUserException e1) {\r
1653                         e1.printStackTrace();\r
1654                         status.message.emit(tr("User exception Listing shared notebooks."));\r
1655                         logger.log(logger.LOW, e1.getMessage());\r
1656                         return;\r
1657                 } catch (EDAMSystemException e1) {\r
1658                         e1.printStackTrace();\r
1659                         status.message.emit(tr("System exception Listing shared notebooks."));\r
1660                         logger.log(logger.LOW, e1.getMessage());\r
1661                         return;\r
1662                 } catch (TException e1) {\r
1663                         e1.printStackTrace();\r
1664                         status.message.emit(tr("Transaction exception Listing shared notebooks."));\r
1665                         logger.log(logger.LOW, e1.getMessage());\r
1666                         return;\r
1667                 } catch (EDAMNotFoundException e1) {\r
1668                         e1.printStackTrace();\r
1669                         status.message.emit(tr("EDAM Not Found exception Listing shared notebooks."));\r
1670                         logger.log(logger.LOW, e1.getMessage());\r
1671                 }\r
1672     }\r
1673     private void downloadAllNotebooks() {\r
1674         try {\r
1675                         List<Notebook> books = noteStore.listNotebooks(authToken);\r
1676                         logger.log(logger.LOW, "Shared notebooks found = " +books.size());\r
1677                         for (int i=0; i<books.size(); i++) {\r
1678                                 conn.getNotebookTable().updateNotebook(books.get(i), false);\r
1679                         }\r
1680                         conn.getSyncTable().deleteRecord("FullNotebookSync");\r
1681                 } catch (EDAMUserException e1) {\r
1682                         e1.printStackTrace();\r
1683                         status.message.emit(tr("User exception Listing notebooks."));\r
1684                         logger.log(logger.LOW, e1.getMessage());\r
1685                         return;\r
1686                 } catch (EDAMSystemException e1) {\r
1687                         e1.printStackTrace();\r
1688                         status.message.emit(tr("System exception Listing notebooks."));\r
1689                         logger.log(logger.LOW, e1.getMessage());\r
1690                         return;\r
1691                 } catch (TException e1) {\r
1692                         e1.printStackTrace();\r
1693                         status.message.emit(tr("Transaction exception Listing notebooks."));\r
1694                         logger.log(logger.LOW, e1.getMessage());\r
1695                         return;\r
1696                 }\r
1697     }\r
1698     private void downloadAllLinkedNotebooks() {\r
1699         try {\r
1700                         List<LinkedNotebook> books = noteStore.listLinkedNotebooks(authToken);\r
1701                         logger.log(logger.LOW, "Linked notebooks found = " +books.size());\r
1702                         for (int i=0; i<books.size(); i++) {\r
1703                                 conn.getLinkedNotebookTable().updateNotebook(books.get(i), false);\r
1704                         }\r
1705                         conn.getSyncTable().deleteRecord("FullLinkedNotebookSync");\r
1706                 } catch (EDAMUserException e1) {\r
1707                         e1.printStackTrace();\r
1708                         status.message.emit(tr("User exception Listing linked notebooks."));\r
1709                         logger.log(logger.LOW, e1.getMessage());\r
1710                         return;\r
1711                 } catch (EDAMSystemException e1) {\r
1712                         e1.printStackTrace();\r
1713                         status.message.emit(tr("System exception Listing linked notebooks."));\r
1714                         logger.log(logger.LOW, e1.getMessage());\r
1715                         return;\r
1716                 } catch (TException e1) {\r
1717                         e1.printStackTrace();\r
1718                         status.message.emit(tr("Transaction exception Listing lineked notebooks."));\r
1719                         logger.log(logger.LOW, e1.getMessage());\r
1720                         return;\r
1721                 } catch (EDAMNotFoundException e1) {\r
1722                         e1.printStackTrace();\r
1723                         status.message.emit(tr("EDAM Not Found exception Listing linked notebooks."));\r
1724                         logger.log(logger.LOW, e1.getMessage());\r
1725                 }\r
1726     }\r
1727 \r
1728     \r
1729     private void downloadInkNoteImage(String guid, String authToken) {\r
1730                 String urlBase = noteStoreUrl.replace("/edam/note/", "/shard/") + "/res/"+guid+".ink?slice=";\r
1731 //              urlBase = "https://www.evernote.com/shard/s1/res/52b567a9-54ae-4a08-afc5-d5bae275b2a8.ink?slice=";\r
1732                 Integer slice = 1;\r
1733                 Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(guid, false);\r
1734                 conn.getInkImagesTable().expungeImage(r.getGuid());\r
1735                 int sliceCount = 1+((r.getHeight()-1)/480);\r
1736                 HttpClient http = new DefaultHttpClient();\r
1737         for (int i=0; i<sliceCount; i++) {\r
1738                 String url = urlBase + slice.toString();\r
1739                 HttpPost post = new HttpPost(url);\r
1740                 post.getParams().setParameter("auth", authToken);\r
1741                 List <NameValuePair> nvps = new ArrayList <NameValuePair>();\r
1742             nvps.add(new BasicNameValuePair("auth", authToken));\r
1743 \r
1744             try {\r
1745                                 post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));\r
1746                         } catch (UnsupportedEncodingException e1) {\r
1747                                 // TODO Auto-generated catch block\r
1748                                 e1.printStackTrace();\r
1749                         }\r
1750                 try {\r
1751                         HttpResponse response = http.execute(post);\r
1752                         HttpEntity resEntity = response.getEntity();\r
1753                         InputStream is = resEntity.getContent();\r
1754                         QByteArray data = writeToFile(is);\r
1755                         conn.getInkImagesTable().saveImage(guid, slice, data);\r
1756                         } catch (ClientProtocolException e) {\r
1757                                 e.printStackTrace();\r
1758                         } catch (IOException e) {\r
1759                                 e.printStackTrace();\r
1760                         }\r
1761 \r
1762                         slice++;\r
1763         }\r
1764         http.getConnectionManager().shutdown(); \r
1765                 noteSignal.noteChanged.emit(r.getNoteGuid(), null);   // Signal to ivalidate note cache\r
1766     }\r
1767     \r
1768     \r
1769     public QByteArray writeToFile(InputStream iStream) throws IOException {\r
1770 \r
1771             File temp = File.createTempFile("nn-inknote-temp", ".png");\r
1772 \r
1773             // Save InputStream to the file.\r
1774             BufferedOutputStream fOut = null;\r
1775             try {\r
1776               fOut = new BufferedOutputStream(new FileOutputStream(temp));\r
1777               byte[] buffer = new byte[32 * 1024];\r
1778               int bytesRead = 0;\r
1779               while ((bytesRead = iStream.read(buffer)) != -1) {\r
1780                 fOut.write(buffer, 0, bytesRead);\r
1781               }\r
1782             }\r
1783             finally {\r
1784                 iStream.close();\r
1785                 fOut.close();\r
1786             }\r
1787             QFile tempFile = new QFile(temp.getAbsoluteFile().toString());\r
1788             tempFile.open(OpenModeFlag.ReadOnly);\r
1789             QByteArray data = tempFile.readAll();\r
1790             tempFile.close();\r
1791             tempFile.remove();\r
1792             return data;\r
1793     }\r
1794     \r
1795     \r
1796         //******************************************\r
1797         //* Begin syncing shared notebooks \r
1798         //******************************************\r
1799     private void syncLinkedNotebooks() {\r
1800         logger.log(logger.MEDIUM, "Authenticating Shared Notebooks");\r
1801         status.message.emit(tr("Synchronizing shared notebooks."));\r
1802         List<LinkedNotebook> books = conn.getLinkedNotebookTable().getAll();\r
1803 \r
1804         for (int i=0; i<books.size(); i++) {\r
1805                 try {\r
1806                                 long lastSyncDate = conn.getLinkedNotebookTable().getLastSequenceDate(books.get(i).getGuid());\r
1807                                 int lastSequenceNumber = conn.getLinkedNotebookTable().getLastSequenceNumber(books.get(i).getGuid());\r
1808                                 linkedAuthResult = noteStore.authenticateToSharedNotebook(books.get(i).getShareKey(), authToken);\r
1809                                 SyncState linkedSyncState = \r
1810                                         noteStore.getLinkedNotebookSyncState(linkedAuthResult.getAuthenticationToken(), books.get(i));\r
1811                                 if (linkedSyncState.getUpdateCount() > lastSequenceNumber) {\r
1812                                         if (lastSyncDate < linkedSyncState.getFullSyncBefore()) {\r
1813                                                 lastSequenceNumber = 0;\r
1814                                         } \r
1815                                         syncLinkedNotebook(books.get(i), lastSequenceNumber, linkedSyncState.getUpdateCount());\r
1816                                 }\r
1817                         \r
1818                         // Synchronize local changes\r
1819                         syncLocalLinkedNoteChanges(books.get(i));\r
1820                                 \r
1821                 } catch (EDAMUserException e) {\r
1822                         // TODO Auto-generated catch block\r
1823                         e.printStackTrace();\r
1824                 } catch (EDAMNotFoundException e) {\r
1825                         status.message.emit(tr("Error synchronizing \" " +\r
1826                                         books.get(i).getShareName()+"\". Please verify you still have access to that shared notebook."));\r
1827                         error = true;\r
1828                         e.printStackTrace();\r
1829                 } catch (EDAMSystemException e) {\r
1830                         logger.log(logger.LOW, "System error authenticating against shared notebook. "+\r
1831                                         "Key: "+books.get(i).getShareKey() +" Error:" +e.getMessage());\r
1832                         e.printStackTrace();\r
1833                 } catch (TException e) {\r
1834                         // TODO Auto-generated catch block\r
1835                         e.printStackTrace();\r
1836                 }\r
1837         }\r
1838         \r
1839         // Cleanup tags\r
1840         conn.getTagTable().removeUnusedLinkedTags();\r
1841         conn.getTagTable().cleanupTags();\r
1842         tagSignal.listChanged.emit();\r
1843         }\r
1844 \r
1845     \r
1846     //**************************************************************\r
1847     //* Linked notebook contents (from someone else's account)\r
1848     //*************************************************************\r
1849         private void syncLinkedNotebook(LinkedNotebook book, int usn, int highSequence) {\r
1850                 \r
1851                 List<Note> dirtyNotes = conn.getNoteTable().getDirtyLinkedNotes();\r
1852                 if (dirtyNoteGuids == null) \r
1853                         dirtyNoteGuids = new Vector<String>();\r
1854 \r
1855                 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {\r
1856                         dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());\r
1857                 }\r
1858                 boolean fullSync = false;\r
1859                 if (usn == 0)\r
1860                         fullSync = true;\r
1861                 while (usn < highSequence) {\r
1862                         try {\r
1863                                 SyncChunk chunk = \r
1864                                         noteStore.getLinkedNotebookSyncChunk(authToken, book, usn, 10, fullSync);\r
1865 \r
1866                                 if (!ignoreLinkedNotebooks.contains(book.getGuid()))\r
1867                                         syncRemoteNotes(chunk.getNotes(), fullSync, linkedAuthResult.getAuthenticationToken());\r
1868                                 findNewLinkedTags(chunk.getNotes(), linkedAuthResult.getAuthenticationToken());\r
1869                                 for (int i=0; i<chunk.getResourcesSize(); i++) {\r
1870                                         syncRemoteResource(chunk.getResources().get(i), linkedAuthResult.getAuthenticationToken());\r
1871                                 }\r
1872                                 syncRemoteLinkedNotebooks(chunk.getNotebooks(), false, book);\r
1873                                 SharedNotebook s = noteStore.getSharedNotebookByAuth(linkedAuthResult.getAuthenticationToken());\r
1874                                 syncLinkedTags(chunk.getTags(), s.getNotebookGuid());\r
1875                                 \r
1876                                 \r
1877                                 // Expunge records\r
1878                                 for (int i=0; i<chunk.getExpungedLinkedNotebooksSize(); i++) {\r
1879                                         conn.getLinkedNotebookTable().expungeNotebook(chunk.getExpungedLinkedNotebooks().get(i), false);\r
1880                                 }\r
1881                                 usn = chunk.getChunkHighUSN();\r
1882                                 conn.getLinkedNotebookTable().setLastSequenceDate(book.getGuid(),chunk.getCurrentTime());\r
1883                                 conn.getLinkedNotebookTable().setLastSequenceNumber(book.getGuid(),chunk.getChunkHighUSN());\r
1884                         } catch (EDAMUserException e) {\r
1885                                 // TODO Auto-generated catch block\r
1886                                 e.printStackTrace();\r
1887                         } catch (EDAMSystemException e) {\r
1888                                 // TODO Auto-generated catch block\r
1889                                 e.printStackTrace();\r
1890                         } catch (EDAMNotFoundException e) {\r
1891                                 // TODO Auto-generated catch block\r
1892                                 e.printStackTrace();\r
1893                         } catch (TException e) {\r
1894                                 // TODO Auto-generated catch block\r
1895                                 e.printStackTrace();\r
1896                         }\r
1897                 }\r
1898         }\r
1899         // Sync remote tags\r
1900         private void syncLinkedTags(List<Tag> tags, String notebookGuid) {\r
1901                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");\r
1902                 if (tags != null) {\r
1903                         for (int i=0; i<tags.size() && keepRunning; i++) {\r
1904                                 conn.getTagTable().syncLinkedTag(tags.get(i), notebookGuid, false);\r
1905                         }\r
1906                 }\r
1907                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");\r
1908         }\r
1909         \r
1910         // Sync notebooks from a linked notebook\r
1911         private void syncRemoteLinkedNotebooks(List<Notebook> notebooks, boolean readOnly, LinkedNotebook linked) {\r
1912                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");\r
1913                 if (notebooks != null) {\r
1914                         for (int i=0; i<notebooks.size() && keepRunning; i++) {\r
1915                                 try {\r
1916                                         SharedNotebook s = noteStore.getSharedNotebookByAuth(linkedAuthResult.getAuthenticationToken());\r
1917                                         conn.getLinkedNotebookTable().setNotebookGuid(s.getShareKey(), s.getNotebookGuid());\r
1918                                         readOnly = !s.isNotebookModifiable();\r
1919                                         notebooks.get(i).setName(linked.getShareName());\r
1920                                         notebooks.get(i).setDefaultNotebook(false);\r
1921                                         conn.getNotebookTable().syncLinkedNotebook(notebooks.get(i), false, readOnly); \r
1922                                 } catch (EDAMUserException e) {\r
1923                                         readOnly = true;\r
1924                                         e.printStackTrace();\r
1925                                 } catch (EDAMNotFoundException e) {\r
1926                                         readOnly = true;\r
1927                                         e.printStackTrace();\r
1928                                 } catch (EDAMSystemException e) {\r
1929                                         readOnly = true;\r
1930                                         e.printStackTrace();\r
1931                                 } catch (TException e) {\r
1932                                         readOnly = true;\r
1933                                         e.printStackTrace();\r
1934                                 }\r
1935 \r
1936                         }\r
1937                 }                       \r
1938                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");\r
1939         }\r
1940 \r
1941         private void findNewLinkedTags(List<Note> newNotes, String token) {\r
1942                 if (newNotes == null)\r
1943                         return;\r
1944                 for (int i=0; i<newNotes.size(); i++) {\r
1945                         Note n = newNotes.get(i);\r
1946                         for (int j=0; j<n.getTagGuidsSize(); j++) {\r
1947                                 String tag = n.getTagGuids().get(j);\r
1948                                 if (!conn.getTagTable().exists(tag)) {\r
1949                                         Tag newTag;\r
1950                                         try {\r
1951                                                 newTag = noteStore.getTag(token, tag);\r
1952                                                 conn.getTagTable().addTag(newTag, false);\r
1953                                         } catch (EDAMUserException e) {\r
1954                                                 // TODO Auto-generated catch block\r
1955                                                 e.printStackTrace();\r
1956                                         } catch (EDAMSystemException e) {\r
1957                                                 // TODO Auto-generated catch block\r
1958                                                 e.printStackTrace();\r
1959                                         } catch (EDAMNotFoundException e) {\r
1960                                                 // TODO Auto-generated catch block\r
1961                                                 e.printStackTrace();\r
1962                                         } catch (TException e) {\r
1963                                                 // TODO Auto-generated catch block\r
1964                                                 e.printStackTrace();\r
1965                                         }\r
1966                                         \r
1967                                 }\r
1968                         }\r
1969                 }\r
1970         }\r
1971 \r
1972         // Synchronize changes locally done to linked notes\r
1973         private void syncLocalLinkedNoteChanges(LinkedNotebook book) {\r
1974                 String notebookGuid = conn.getLinkedNotebookTable().getNotebookGuid(book.getGuid());\r
1975                 List<Note> notes = conn.getNoteTable().getDirtyLinked(notebookGuid);\r
1976                 for (int i=0; i<notes.size(); i++) {\r
1977                         syncLocalNote(notes.get(i), linkedAuthResult.getAuthenticationToken());\r
1978                 }\r
1979         }\r
1980 \r
1981 }\r