OSDN Git Service

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