OSDN Git Service

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