2 * This file is part of NixNote/NeighborNote
3 * Copyright 2009 Randy Baumgarte
4 * Copyright 2013 Yuki Takahashi
6 * This file may be licensed under the terms of of the
7 * GNU General Public License Version 2 (the ``GPL'').
9 * Software distributed under the License is distributed
10 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
11 * express or implied. See the GPL for the specific language
12 * governing rights and limitations.
14 * You should have received a copy of the GPL along with this
15 * program. If not, go to http://www.gnu.org/licenses/gpl.html
16 * or write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package cx.fbn.nevernote.threads;
22 import java.io.BufferedOutputStream;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.UnsupportedEncodingException;
28 import java.net.UnknownHostException;
29 import java.util.ArrayList;
30 import java.util.Calendar;
31 import java.util.Date;
32 import java.util.GregorianCalendar;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.TreeSet;
36 import java.util.concurrent.LinkedBlockingQueue;
38 import org.apache.http.HttpEntity;
39 import org.apache.http.HttpResponse;
40 import org.apache.http.NameValuePair;
41 import org.apache.http.client.ClientProtocolException;
42 import org.apache.http.client.HttpClient;
43 import org.apache.http.client.entity.UrlEncodedFormEntity;
44 import org.apache.http.client.methods.HttpPost;
45 import org.apache.http.impl.client.DefaultHttpClient;
46 import org.apache.http.message.BasicNameValuePair;
47 import org.apache.http.protocol.HTTP;
49 import com.evernote.edam.error.EDAMNotFoundException;
50 import com.evernote.edam.error.EDAMSystemException;
51 import com.evernote.edam.error.EDAMUserException;
52 import com.evernote.edam.notestore.NoteStore;
53 import com.evernote.edam.notestore.NoteStore.Client;
54 import com.evernote.edam.notestore.SyncChunk;
55 import com.evernote.edam.notestore.SyncState;
56 import com.evernote.edam.type.Data;
57 import com.evernote.edam.type.LinkedNotebook;
58 import com.evernote.edam.type.Note;
59 import com.evernote.edam.type.Notebook;
60 import com.evernote.edam.type.Resource;
61 import com.evernote.edam.type.SavedSearch;
62 import com.evernote.edam.type.SharedNotebook;
63 import com.evernote.edam.type.Tag;
64 import com.evernote.edam.type.User;
65 import com.evernote.edam.userstore.AuthenticationResult;
66 import com.evernote.edam.userstore.UserStore;
67 import com.evernote.thrift.TException;
68 import com.evernote.thrift.protocol.TBinaryProtocol;
69 import com.evernote.thrift.transport.THttpClient;
70 import com.evernote.thrift.transport.TTransportException;
71 import com.trolltech.qt.core.QByteArray;
72 import com.trolltech.qt.core.QFile;
73 import com.trolltech.qt.core.QIODevice.OpenModeFlag;
74 import com.trolltech.qt.core.QObject;
75 import com.trolltech.qt.core.QTextCodec;
76 import com.trolltech.qt.gui.QMessageBox;
78 import cx.fbn.nevernote.signals.NoteIndexSignal;
79 import cx.fbn.nevernote.signals.NoteResourceSignal;
80 import cx.fbn.nevernote.signals.NoteSignal;
81 import cx.fbn.nevernote.signals.NotebookSignal;
82 import cx.fbn.nevernote.signals.SavedSearchSignal;
83 import cx.fbn.nevernote.signals.StatusSignal;
84 import cx.fbn.nevernote.signals.SyncSignal;
85 import cx.fbn.nevernote.signals.TagSignal;
86 import cx.fbn.nevernote.sql.DatabaseConnection;
87 import cx.fbn.nevernote.sql.DeletedItemRecord;
88 import cx.fbn.nevernote.utilities.ApplicationLogger;
89 //import org.apache.thrift.transport.THttpClient;
90 //import org.apache.thrift.transport.TTransportException;
91 //import org.apache.thrift.protocol.TBinaryProtocol;
92 //import org.apache.thrift.TException;
94 public class SyncRunner extends QObject implements Runnable {
96 private final ApplicationLogger logger;
97 private DatabaseConnection conn;
100 public volatile List<String> errorSharedNotebooks;
101 public volatile HashMap<String,String> errorSharedNotebooksIgnored;
102 public volatile boolean isConnected;
103 public volatile boolean keepRunning;
104 public volatile String authToken;
105 private long evernoteUpdateCount;
106 private final String userAgent = "NeighborNote/" + System.getProperty("os.name")
107 +"/"+System.getProperty("java.vendor") + "/"
108 + System.getProperty("java.version") +";";
110 public volatile NoteStore.Client localNoteStore;
111 private UserStore.Client userStore;
113 public volatile StatusSignal status;
114 public volatile TagSignal tagSignal;
115 public volatile NotebookSignal notebookSignal;
116 public volatile NoteIndexSignal noteIndexSignal;
117 public volatile NoteSignal noteSignal;
118 public volatile SavedSearchSignal searchSignal;
119 public volatile NoteResourceSignal resourceSignal;
120 public volatile SyncSignal syncSignal;
121 public volatile boolean authRefreshNeeded;
122 public volatile boolean syncNeeded;
123 public volatile boolean disableUploads;
124 public volatile boolean syncDeletedContent;
125 private volatile List<String> dirtyNoteGuids;
127 public volatile String username = "";
128 public volatile String password = "";
129 public volatile String userStoreUrl;
130 public String noteStoreUrlBase;
131 private THttpClient userStoreTrans;
132 private TBinaryProtocol userStoreProt;
133 //private AuthenticationResult authResult;
134 private AuthenticationResult linkedAuthResult;
136 // private long authTimeRemaining;
137 public long authRefreshTime;
138 public long failedRefreshes = 0;
139 public THttpClient noteStoreTrans;
140 public TBinaryProtocol noteStoreProt;
141 public String noteStoreUrl;
142 public long sequenceDate;
143 public int updateSequenceNumber;
144 private boolean refreshNeeded;
145 private volatile LinkedBlockingQueue<String> workQueue;
146 private static int MAX_QUEUED_WAITING = 1000;
156 private final TreeSet<String> ignoreTags;
157 private final TreeSet<String> ignoreNotebooks;
158 private final TreeSet<String> ignoreLinkedNotebooks;
159 private HashMap<String,String> badTagSync;
162 // ICHANGED String bを追加
163 public SyncRunner(String logname, String u, String i, String r, String b, String uid, String pswd, String cpswd) {
164 logger = new ApplicationLogger(logname);
166 noteSignal = new NoteSignal();
167 status = new StatusSignal();
168 tagSignal = new TagSignal();
169 notebookSignal = new NotebookSignal();
170 noteIndexSignal = new NoteIndexSignal();
171 noteSignal = new NoteSignal();
172 searchSignal = new SavedSearchSignal();
173 syncSignal = new SyncSignal();
174 resourceSignal = new NoteResourceSignal();
184 // this.setAutoDelete(false);
188 authRefreshNeeded = false;
191 disableUploads = false;
192 ignoreTags = new TreeSet<String>();
193 ignoreNotebooks = new TreeSet<String>();
194 ignoreLinkedNotebooks = new TreeSet<String>();
196 // setAutoDelete(false);
197 workQueue=new LinkedBlockingQueue<String>(MAX_QUEUED_WAITING);
201 errorSharedNotebooks = new ArrayList<String>();
202 errorSharedNotebooksIgnored = new HashMap<String,String>();
204 logger.log(logger.EXTREME, "Starting thread");
205 // ICHANGED behaviorUrlを追加
206 conn = new DatabaseConnection(logger, dburl, indexUrl, resourceUrl, behaviorUrl, dbuid, dbpswd, dbcpswd, 200);
208 logger.log(logger.EXTREME, "Blocking until work is found");
209 String work = workQueue.take();
210 logger.log(logger.LOW, "Dirty Notes Before Sync: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
211 logger.log(logger.EXTREME, "Work found: " +work);
212 if (work.equalsIgnoreCase("stop")) {
216 conn.getNoteTable().dumpDirtyNotes(); // Debugging statement
220 logger.log(logger.EXTREME, "SyncNeeded is true");
222 sequenceDate = conn.getSyncTable().getLastSequenceDate();
223 updateSequenceNumber = conn.getSyncTable().getUpdateSequenceNumber();
225 logger.log(logger.EXTREME, "Beginning sync");
226 evernoteSync(localNoteStore);
227 logger.log(logger.EXTREME, "Sync finished");
228 } catch (UnknownHostException e) {
229 status.message.emit(e.getMessage());
233 logger.log(logger.EXTREME, "Signaling refresh finished. refreshNeeded=" +refreshNeeded);
234 syncSignal.finished.emit(refreshNeeded);
236 syncSignal.errorDisconnect.emit();
237 status.message.emit(tr("Error synchronizing - see log for details."));
239 logger.log(logger.LOW, "Dirty Notes After Sync: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
240 conn.getNoteTable().dumpDirtyNotes();
241 logger.log(logger.LOW, "---");
244 catch (InterruptedException e1) {
245 e1.printStackTrace();
251 public DatabaseConnection getConnection() {
255 public boolean isIdle() {
260 public void setConnected(boolean c) {
263 public void setKeepRunning(boolean r) {
264 logger.log(logger.EXTREME, "Setting keepRunning=" +r);
267 public void setNoteStore(NoteStore.Client c) {
268 logger.log(logger.EXTREME, "Setting NoteStore in sync thread");
271 public void setUserStore(UserStore.Client c) {
272 logger.log(logger.EXTREME, "Setting UserStore in sync thread");
276 public void setEvernoteUpdateCount(long s) {
277 logger.log(logger.EXTREME, "Setting Update Count in sync thread");
278 evernoteUpdateCount = s;
281 //***************************************************************
282 //***************************************************************
283 //** These functions deal with Evernote communications
284 //***************************************************************
285 //***************************************************************
286 // Synchronize changes with Evernote
287 @SuppressWarnings("unused")
288 private void evernoteSync(Client noteStore) throws java.net.UnknownHostException {
289 logger.log(logger.HIGH, "Entering SyncRunner.evernoteSync");
291 // Rebuild list of tags & notebooks to ignore
292 ignoreNotebooks.clear();
293 List<String> ignore = conn.getSyncTable().getIgnoreRecords("NOTEBOOK");
294 for (int i=0; i<ignore.size(); i++)
295 ignoreNotebooks.add(ignore.get(i));
298 ignore = conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK");
299 for (int i=0; i<ignore.size(); i++)
300 ignoreLinkedNotebooks.add(ignore.get(i));
303 ignore = conn.getSyncTable().getIgnoreRecords("TAG");
304 for (int i=0; i<ignore.size(); i++)
305 ignoreTags.add(ignore.get(i));
307 // Make sure we are connected & should keep running
308 if (isConnected && keepRunning) {
310 logger.log(logger.EXTREME, "Synchronizing with Evernote");
311 status.message.emit(tr("Synchronizing with Evernote"));
313 // Get user information
315 logger.log(logger.EXTREME, "getting user from userstore");
316 User user = userStore.getUser(authToken);
317 logger.log(logger.EXTREME, "Saving user information");
318 syncSignal.saveUserInformation.emit(user);
319 } catch (EDAMUserException e1) {
320 e1.printStackTrace();
321 status.message.emit(tr("User exception getting user account information. Aborting sync and disconnecting"));
322 syncSignal.errorDisconnect.emit();
326 } catch (EDAMSystemException e1) {
327 e1.printStackTrace();
328 status.message.emit(tr("System error user account information. Aborting sync and disconnecting!"));
329 syncSignal.errorDisconnect.emit();
333 } catch (TException e1) {
334 e1.printStackTrace();
335 syncSignal.errorDisconnect.emit();
337 status.message.emit(tr("Transaction error getting user account information. Aborting sync and disconnecting!"));
343 SyncState syncState = null;
345 logger.log(logger.EXTREME, "Getting sync state");
346 syncState = noteStore.getSyncState(authToken);
347 syncSignal.saveUploadAmount.emit(syncState.getUploaded());
348 syncSignal.saveEvernoteUpdateCount.emit(syncState.getUpdateCount());
349 evernoteUpdateCount = syncState.getUpdateCount();
350 } catch (EDAMUserException e) {
352 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
353 syncSignal.errorDisconnect.emit();
356 } catch (EDAMSystemException e) {
358 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
359 syncSignal.errorDisconnect.emit();
362 } catch (TException e) {
364 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
365 syncSignal.errorDisconnect.emit();
370 if (syncState == null) {
371 logger.log(logger.EXTREME, "Sync State is null");
372 status.message.emit(tr("Syncronization Error!"));
376 // Determine what to do.
377 // If we need to do a full sync.
378 logger.log(logger.LOW, "Full Sequence Before: " +syncState.getFullSyncBefore());
379 logger.log(logger.LOW, "Last Sequence Date: " +sequenceDate);
380 logger.log(logger.LOW, "Var Last Sequence Number: " +updateSequenceNumber);
381 logger.log(logger.LOW, "DB Last Sequence Number: " + conn.getSyncTable().getUpdateSequenceNumber());
382 if (syncState.getFullSyncBefore() > sequenceDate) {
383 logger.log(logger.EXTREME, "Full sequence date has expired");
385 conn.getSyncTable().setLastSequenceDate(0);
386 updateSequenceNumber = 0;
387 conn.getSyncTable().setUpdateSequenceNumber(0);
389 // Check for "special" sync instructions
390 String syncLinked = conn.getSyncTable().getRecord("FullLinkedNotebookSync");
391 String syncShared = conn.getSyncTable().getRecord("FullSharedNotebookSync");
392 String syncNotebooks = conn.getSyncTable().getRecord("FullNotebookSync");
393 String syncInkNoteImages = conn.getSyncTable().getRecord("FullInkNoteImageSync");
394 if (syncLinked != null) {
395 downloadAllLinkedNotebooks(localNoteStore);
397 if (syncShared != null) {
398 downloadAllSharedNotebooks(localNoteStore);
400 if (syncNotebooks != null) {
401 downloadAllNotebooks(localNoteStore);
404 if (syncInkNoteImages != null) {
405 List<String> guids = conn.getNoteTable().noteResourceTable.findInkNotes();
406 for (int i=0; i<guids.size(); i++) {
407 downloadInkNoteImage(guids.get(i), authToken);
409 conn.getSyncTable().deleteRecord("FullInkNoteImageSync");
412 // If there are remote changes
413 logger.log(logger.LOW, "Update Count: " +syncState.getUpdateCount());
414 logger.log(logger.LOW, "Last Update Count: " +updateSequenceNumber);
416 if (syncState.getUpdateCount() > updateSequenceNumber) {
417 logger.log(logger.EXTREME, "Refresh needed is true");
418 refreshNeeded = true;
419 logger.log(logger.EXTREME, "Downloading changes");
420 syncRemoteToLocal(localNoteStore);
423 //*****************************************
424 //* Sync linked/shared notebooks
425 //*****************************************
426 //syncLinkedNotebooks();
427 //conn.getNoteTable().getDirty();
428 //disableUploads = true; /// DELETE THIS LINE!!!!
429 if (!disableUploads) {
430 logger.log(logger.EXTREME, "Uploading changes");
431 // Synchronize remote changes
433 syncExpunged(localNoteStore);
435 syncLocalTags(localNoteStore);
437 syncLocalNotebooks(localNoteStore);
439 syncLocalLinkedNotebooks(localNoteStore);
441 syncDeletedNotes(localNoteStore);
445 syncLocalSavedSearches(localNoteStore);
448 status.message.emit(tr("Cleaning up"));
449 List<String> notes = conn.getNoteTable().expungeIgnoreSynchronizedNotes(conn.getSyncTable().getIgnoreRecords("NOTEBOOK"),
450 conn.getSyncTable().getIgnoreRecords("TAG"), conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK"));
451 if (notes.size() > 0)
452 syncSignal.refreshLists.emit();
454 //*****************************************
455 //* End of synchronization
456 //*****************************************
458 syncSignal.refreshLists.emit();
461 logger.log(logger.LOW, "Sync completed. Errors=" +error);
463 status.message.emit(tr("Synchronizing complete"));
465 status.message.emit(tr("Download syncronization complete. Uploads have been disabled."));
467 logger.log(logger.EXTREME, "Saving sync time");
468 if (syncState.getCurrentTime() > sequenceDate)
469 sequenceDate = syncState.getCurrentTime();
470 if (syncState.getUpdateCount() > updateSequenceNumber)
471 updateSequenceNumber = syncState.getUpdateCount();
472 conn.getSyncTable().setLastSequenceDate(sequenceDate);
473 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
476 logger.log(logger.HIGH, "Leaving SyncRunner.evernoteSync");
479 // Sync deleted items with Evernote
480 private void syncExpunged(Client noteStore) {
481 logger.log(logger.HIGH, "Entering SyncRunner.syncExpunged");
483 List<DeletedItemRecord> expunged = conn.getDeletedTable().getAllDeleted();
484 boolean error = false;
485 for (int i=0; i<expunged.size() && keepRunning; i++) {
487 // if (authRefreshNeeded)
488 // if (!refreshConnection())
492 if (expunged.get(i).type.equalsIgnoreCase("TAG")) {
493 logger.log(logger.EXTREME, "Tag expunged");
494 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "TAG");
\r
495 updateSequenceNumber = noteStore.expungeTag(authToken, expunged.get(i).guid);
496 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
497 conn.getSyncTable().setLastSequenceDate(sequenceDate);
498 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
500 if (expunged.get(i).type.equalsIgnoreCase("NOTEBOOK")) {
501 logger.log(logger.EXTREME, "Notebook expunged");
502 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "NOTEBOOK");
\r
503 updateSequenceNumber = noteStore.expungeNotebook(authToken, expunged.get(i).guid);
504 conn.getSyncTable().setLastSequenceDate(sequenceDate);
505 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
507 if (expunged.get(i).type.equalsIgnoreCase("NOTE")) {
508 logger.log(logger.EXTREME, "Note expunged");
509 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "NOTE");
\r
510 updateSequenceNumber = noteStore.deleteNote(authToken, expunged.get(i).guid);
511 refreshNeeded = true;
512 conn.getSyncTable().setLastSequenceDate(sequenceDate);
513 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
515 if (expunged.get(i).type.equalsIgnoreCase("SAVEDSEARCH")) {
516 logger.log(logger.EXTREME, "saved search expunged");
517 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "SAVEDSEARCH");
\r
518 updateSequenceNumber = noteStore.expungeSearch(authToken, expunged.get(i).guid);
519 conn.getSyncTable().setLastSequenceDate(sequenceDate);
520 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
522 } catch (EDAMUserException e) {
523 logger.log(logger.LOW, "EDAM User Excepton in syncExpunged: " +expunged.get(i).guid); // This can happen if we try to delete a deleted note
524 } catch (EDAMSystemException e) {
525 logger.log(logger.LOW, "EDAM System Excepton in syncExpunged: "+expunged.get(i).guid);
526 logger.log(logger.LOW, e.getStackTrace());
528 } catch (EDAMNotFoundException e) {
529 logger.log(logger.LOW, "EDAM Not Found Excepton in syncExpunged: "+expunged.get(i).guid);
530 } catch (TException e) {
531 logger.log(logger.LOW, "EDAM TExcepton in syncExpunged: "+expunged.get(i).guid);
532 logger.log(logger.LOW, e.getStackTrace());
537 conn.getDeletedTable().expungeAllDeletedRecords();
539 logger.log(logger.HIGH, "Leaving SyncRunner.syncExpunged");
542 private void syncDeletedNotes(Client noteStore) {
543 if (syncDeletedContent)
545 logger.log(logger.HIGH, "Entering SyncRunner.syncDeletedNotes");
546 status.message.emit(tr("Synchronizing deleted notes."));
548 List<Note> notes = conn.getNoteTable().getDirty();
549 // Sync the local notebooks with Evernote's
550 for (int i=0; i<notes.size() && keepRunning; i++) {
552 // if (authRefreshNeeded)
553 // if (!refreshConnection())
556 Note enNote = notes.get(i);
558 if (enNote.getUpdateSequenceNum() > 0 && (enNote.isActive() == false || enNote.getDeleted() > 0)) {
559 // Check that the note is valid.
\r
560 if (enNote.isActive() == true || enNote.getDeleted() == 0) {
\r
561 conn.getNoteTable().deleteNote(enNote.getGuid());
\r
562 enNote = conn.getNoteTable().getNote(enNote.getGuid(), false, false, false, false, false);
\r
564 if (syncDeletedContent) {
565 logger.log(logger.EXTREME, "Deleted note found & synch content selected");
566 Note delNote = conn.getNoteTable().getNote(enNote.getGuid(), true, true, true, true, true);
567 delNote = getNoteContent(delNote);
568 delNote = noteStore.updateNote(authToken, delNote);
569 enNote.setUpdateSequenceNum(delNote.getUpdateSequenceNum());
570 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
572 logger.log(logger.EXTREME, "Deleted note found & sync content not selected");
573 int usn = noteStore.deleteNote(authToken, enNote.getGuid());
574 enNote.setUpdateSequenceNum(usn);
575 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
577 logger.log(logger.EXTREME, "Resetting deleted dirty flag");
578 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());
579 updateSequenceNumber = enNote.getUpdateSequenceNum();
580 logger.log(logger.EXTREME, "Saving sequence number");
581 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
583 } catch (EDAMUserException e) {
584 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);
\r
585 //status.message.emit("Error sending local note: " +e.getParameter());
586 //logger.log(logger.LOW, e.toString());
588 } catch (EDAMSystemException e) {
589 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);
590 status.message.emit(tr("Error: ") +e);
591 logger.log(logger.LOW, e.toString());
593 } catch (EDAMNotFoundException e) {
594 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);
\r
595 //status.message.emit("Error deleting local note: " +e +" - Continuing");
596 //logger.log(logger.LOW, e.toString());
598 } catch (TException e) {
599 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);
600 status.message.emit(tr("Error sending local note: ") +e);
601 logger.log(logger.LOW, e.toString());
606 // Sync notes with Evernote
607 private void syncLocalNotes() {
608 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");
609 logger.log(logger.LOW, "Dirty local notes found: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
610 status.message.emit(tr("Sending local notes."));
612 List<Note> notes = conn.getNoteTable().getDirty();
613 // Sync the local notebooks with Evernote's
614 for (int i=0; i<notes.size() && keepRunning; i++) {
615 syncLocalNote(localNoteStore, notes.get(i), authToken);
617 logger.log(logger.HIGH, "Leaving SyncRunner.syncNotes");
620 // Sync notes with Evernote
621 private void syncLocalNote(Client noteStore, Note enNote, String token) {
622 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");
623 status.message.emit(tr("Sending local notes."));
625 if (enNote.isActive()) {
627 if (enNote.getUpdateSequenceNum() > 0) {
628 logger.log(logger.EXTREME, "Active dirty note found - non new - " +enNote.getGuid());
629 logger.log(logger.EXTREME, "Fetching note content");
630 enNote = getNoteContent(enNote);
631 logger.log(logger.MEDIUM, "Updating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
632 enNote = noteStore.updateNote(token, enNote);
634 logger.log(logger.EXTREME, "Active dirty found - new note " +enNote.getGuid());
635 String oldGuid = enNote.getGuid();
636 logger.log(logger.MEDIUM, "Fetching note content");
637 enNote = getNoteContent(enNote);
638 logger.log(logger.MEDIUM, "Creating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
639 enNote = noteStore.createNote(token, enNote);
640 logger.log(logger.MEDIUM, "New note Guid : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
641 noteSignal.guidChanged.emit(oldGuid, enNote.getGuid());
642 conn.getNoteTable().updateNoteGuid(oldGuid, enNote.getGuid());
644 updateSequenceNumber = enNote.getUpdateSequenceNum();
645 logger.log(logger.EXTREME, "Saving note");
646 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
647 List<Resource> rl = enNote.getResources();
648 logger.log(logger.EXTREME, "Getting note resources");
649 for (int j=0; j<enNote.getResourcesSize() && keepRunning; j++) {
650 Resource newRes = rl.get(j);
651 Data d = newRes.getData();
653 logger.log(logger.EXTREME, "Calculating resource hash");
654 String hash = byteArrayToHexString(d.getBodyHash());
655 logger.log(logger.EXTREME, "updating resources by hash");
656 String oldGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(enNote.getGuid(), hash);
657 conn.getNoteTable().updateNoteResourceGuidbyHash(enNote.getGuid(), newRes.getGuid(), hash);
658 resourceSignal.resourceGuidChanged.emit(enNote.getGuid(), oldGuid, newRes.getGuid());
661 logger.log(logger.EXTREME, "Resetting note dirty flag");
662 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());
663 updateSequenceNumber = enNote.getUpdateSequenceNum();
664 logger.log(logger.EXTREME, "Emitting note sequence number change");
665 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
667 } catch (EDAMUserException e) {
668 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);
669 status.message.emit(tr("Error sending local note: ") +e.getParameter());
670 logger.log(logger.LOW, e.toString());
672 } catch (EDAMSystemException e) {
673 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);
674 status.message.emit(tr("Error: ") +e);
675 logger.log(logger.LOW, e.toString());
677 } catch (EDAMNotFoundException e) {
678 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);
679 status.message.emit(tr("Error sending local note: ") +e);
680 logger.log(logger.LOW, e.toString());
682 } catch (TException e) {
683 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);
684 status.message.emit(tr("Error sending local note: ") +e);
685 logger.log(logger.LOW, e.toString());
689 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNote");
693 // Sync Notebooks with Evernote
694 private void syncLocalNotebooks(Client noteStore) {
695 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalNotebooks");
697 status.message.emit(tr("Sending local notebooks."));
698 List<Notebook> remoteList = new ArrayList<Notebook>();
700 logger.log(logger.EXTREME, "Getting remote notebooks to compare with local");
701 remoteList = noteStore.listNotebooks(authToken);
702 } catch (EDAMUserException e1) {
703 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks getting remote Notebook List");
704 status.message.emit(tr("Error: ") +e1);
705 logger.log(logger.LOW, e1.toString());
707 } catch (EDAMSystemException e1) {
708 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks getting remote Notebook List");
709 status.message.emit(tr("Error: ") +e1);
710 logger.log(logger.LOW, e1.toString());
712 } catch (TException e1) {
713 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalNotebooks getting remote Notebook List");
714 status.message.emit(tr("Error: ") +e1);
715 logger.log(logger.LOW, e1.toString());
718 logger.log(logger.EXTREME, "Getting local dirty notebooks");
719 List<Notebook> notebooks = conn.getNotebookTable().getDirty();
721 // Sync the local notebooks with Evernote's
722 for (int i=0; i<notebooks.size() && keepRunning; i++) {
724 // if (authRefreshNeeded)
725 // if (!refreshConnection())
728 Notebook enNotebook = notebooks.get(i);
730 if (enNotebook.getUpdateSequenceNum() > 0) {
731 logger.log(logger.EXTREME, "Existing notebook is dirty");
732 sequence = noteStore.updateNotebook(authToken, enNotebook);
734 logger.log(logger.EXTREME, "New dirty notebook found");
735 String oldGuid = enNotebook.getGuid();
736 boolean found = false;
738 // Look for a notebook with the same name. If one is found, we don't need
739 // to create another one
740 logger.log(logger.EXTREME, "Looking for matching notebook name");
741 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
742 if (remoteList.get(k).getName().equalsIgnoreCase(enNotebook.getName())) {
743 enNotebook = remoteList.get(k);
744 logger.log(logger.EXTREME, "Matching notebook found");
749 enNotebook = noteStore.createNotebook(authToken, enNotebook);
751 logger.log(logger.EXTREME, "Updating notebook in database");
752 conn.getNotebookTable().updateNotebookGuid(oldGuid, enNotebook.getGuid());
753 sequence = enNotebook.getUpdateSequenceNum();
755 logger.log(logger.EXTREME, "Updating notebook sequence in database");
756 conn.getNotebookTable().updateNotebookSequence(enNotebook.getGuid(), sequence);
757 logger.log(logger.EXTREME, "Resetting dirty flag in notebook");
758 conn.getNotebookTable().resetDirtyFlag(enNotebook.getGuid());
759 updateSequenceNumber = sequence;
760 logger.log(logger.EXTREME, "Emitting sequence number to main thread");
761 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
762 } catch (EDAMUserException e) {
763 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks");
764 logger.log(logger.LOW, e.toString() + ": Stack : " +enNotebook.getStack());
766 } catch (EDAMSystemException e) {
767 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks");
768 logger.log(logger.LOW, e.toString());
770 } catch (EDAMNotFoundException e) {
771 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotebooks");
772 logger.log(logger.LOW, e.toString());
774 } catch (TException e) {
775 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotebooks");
776 logger.log(logger.LOW, e.toString());
780 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNotebooks");
783 // Sync Tags with Evernote
784 private void syncLocalTags(Client noteStore) {
785 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalTags");
786 List<Tag> remoteList = new ArrayList<Tag>();
787 status.message.emit(tr("Sending local tags."));
790 logger.log(logger.EXTREME, "Getting remote tags to compare names with the local tags");
791 remoteList = noteStore.listTags(authToken);
792 } catch (EDAMUserException e1) {
793 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote Tag List");
794 status.message.emit(tr("Error: ") +e1);
795 logger.log(logger.LOW, e1.toString());
797 } catch (EDAMSystemException e1) {
798 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote Tag List");
799 status.message.emit(tr("Error: ") +e1);
800 logger.log(logger.LOW, e1.toString());
802 } catch (TException e1) {
803 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote Tag List");
804 status.message.emit(tr("Error: ") +e1);
805 logger.log(logger.LOW, e1.toString());
811 if (badTagSync == null)
812 badTagSync = new HashMap<String,String>();
816 Tag enTag = findNextTag();
818 // This is a hack. Sometimes this function goes flookey and goes into a
819 // perpetual loop. This causes NeverNote to flood Evernote's servers.
820 // This is a safety valve to prevent unlimited loops.
821 int maxCount = conn.getTagTable().getDirty().size()+10;
824 while(enTag!=null && loopCount < maxCount) {
826 // if (authRefreshNeeded)
827 // if (!refreshConnection())
831 if (enTag.getUpdateSequenceNum() > 0) {
832 logger.log(logger.EXTREME, "Updating tag");
833 sequence = noteStore.updateTag(authToken, enTag);
836 // Look for a tag with the same name. If one is found, we don't need
837 // to create another one
838 logger.log(logger.EXTREME, "New tag. Comparing with remote names");
839 boolean found = false;
840 String oldGuid = enTag.getGuid();
841 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
842 if (remoteList.get(k).getName().equalsIgnoreCase(enTag.getName())) {
843 conn.getTagTable().updateTagGuid(enTag.getGuid(), remoteList.get(k).getGuid());
844 enTag = remoteList.get(k);
845 logger.log(logger.EXTREME, "Matching tag name found");
850 enTag = noteStore.createTag(authToken, enTag);
852 enTag.setUpdateSequenceNum(noteStore.updateTag(authToken,enTag));
853 sequence = enTag.getUpdateSequenceNum();
854 if (!oldGuid.equals(enTag.getGuid())) {
855 logger.log(logger.EXTREME, "Updating tag guid");
856 conn.getTagTable().updateTagGuid(oldGuid, enTag.getGuid());
859 logger.log(logger.EXTREME, "Updating tag sequence number");
860 conn.getTagTable().updateTagSequence(enTag.getGuid(), sequence);
861 logger.log(logger.EXTREME, "Resetting tag dirty flag");
862 conn.getTagTable().resetDirtyFlag(enTag.getGuid());
863 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");
864 updateSequenceNumber = sequence;
865 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
866 } catch (EDAMUserException e) {
867 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags: " +enTag.getName());
868 logger.log(logger.LOW, e.toString());
869 badTagSync.put(enTag.getGuid(),null);
871 } catch (EDAMSystemException e) {
872 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags: " +enTag.getName());
873 logger.log(logger.LOW, e.toString());
874 badTagSync.put(enTag.getGuid(),null);
876 } catch (EDAMNotFoundException e) {
877 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags: " +enTag.getName());
878 logger.log(logger.LOW, e.toString());
879 badTagSync.put(enTag.getGuid(),null);
881 } catch (TException e) {
882 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags: " +enTag.getName());
883 logger.log(logger.LOW, e.toString());
884 badTagSync.put(enTag.getGuid(),null);
889 logger.log(logger.EXTREME, "Finding next tag");
890 enTag = findNextTag();
892 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalTags");
894 private void syncLocalLinkedNotebooks(Client noteStore) {
895 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalLinkedNotebooks");
897 List<String> list = conn.getLinkedNotebookTable().getDirtyGuids();
898 for (int i=0; i<list.size(); i++) {
899 LinkedNotebook book = conn.getLinkedNotebookTable().getNotebook(list.get(i));
901 noteStore.updateLinkedNotebook(authToken, book);
902 } catch (EDAMUserException e) {
903 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalLinkedNotebooks");
904 status.message.emit(tr("Error: ") +e);
905 logger.log(logger.LOW, e.toString());
908 } catch (EDAMNotFoundException e) {
909 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalLinkedNotebooks");
910 status.message.emit(tr("Error: ") +e);
911 logger.log(logger.LOW, e.toString());
914 } catch (EDAMSystemException e) {
915 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalLinkedNotebooks");
916 status.message.emit(tr("Error: ") +e);
917 logger.log(logger.LOW, e.toString());
920 } catch (TException e) {
921 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalLinkedNotebooks");
922 status.message.emit(tr("Error: ") +e);
923 logger.log(logger.LOW, e.toString());
928 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalLinkedNotebooks");
930 // Sync Saved Searches with Evernote
931 private void syncLocalSavedSearches(Client noteStore) {
932 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");
933 List<SavedSearch> remoteList = new ArrayList<SavedSearch>();
934 status.message.emit(tr("Sending saved searches."));
936 logger.log(logger.EXTREME, "Getting saved searches to compare with local");
938 remoteList = noteStore.listSearches(authToken);
939 } catch (EDAMUserException e1) {
940 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote saved search List");
941 status.message.emit(tr("Error: ") +e1);
942 logger.log(logger.LOW, e1.toString());
944 } catch (EDAMSystemException e1) {
945 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote saved search List");
946 status.message.emit(tr("Error: ") +e1);
947 logger.log(logger.LOW, e1.toString());
949 } catch (TException e1) {
950 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote saved search List");
951 status.message.emit(tr("Error: ") +e1);
952 logger.log(logger.LOW, e1.toString());
956 List<SavedSearch> searches = conn.getSavedSearchTable().getDirty();
958 // Sync the local notebooks with Evernote's
959 logger.log(logger.EXTREME, "Beginning to send saved searches");
960 for (int i=0; i<searches.size() && keepRunning; i++) {
962 // if (authRefreshNeeded)
963 // if (!refreshConnection())
966 SavedSearch enSearch = searches.get(i);
968 if (enSearch.getUpdateSequenceNum() > 0)
969 sequence = noteStore.updateSearch(authToken, enSearch);
971 logger.log(logger.EXTREME, "New saved search found.");
972 // Look for a tag with the same name. If one is found, we don't need
973 // to create another one
974 boolean found = false;
975 logger.log(logger.EXTREME, "Matching remote saved search names with local");
976 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
977 if (remoteList.get(k).getName().equalsIgnoreCase(enSearch.getName())) {
978 enSearch = remoteList.get(k);
980 logger.log(logger.EXTREME, "Matching saved search found");
981 sequence = enSearch.getUpdateSequenceNum();
985 String oldGuid = enSearch.getGuid();
987 enSearch = noteStore.createSearch(authToken, enSearch);
988 sequence = enSearch.getUpdateSequenceNum();
989 logger.log(logger.EXTREME, "Updating tag guid in local database");
990 conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, enSearch.getGuid());
992 logger.log(logger.EXTREME, "Updating tag sequence in local database");
993 conn.getSavedSearchTable().updateSavedSearchSequence(enSearch.getGuid(), sequence);
994 logger.log(logger.EXTREME, "Resetting tag dirty flag");
995 conn.getSavedSearchTable().resetDirtyFlag(enSearch.getGuid());
996 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");
997 updateSequenceNumber = sequence;
998 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
999 } catch (EDAMUserException e) {
1000 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");
1001 logger.log(logger.LOW, e.toString());
1003 } catch (EDAMSystemException e) {
1004 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");
1005 logger.log(logger.LOW, e.toString());
1007 } catch (EDAMNotFoundException e) {
1008 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");
1009 logger.log(logger.LOW, e.toString());
1011 } catch (TException e) {
1012 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");
1013 logger.log(logger.LOW, e.toString());
1018 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");
1021 // Sync evernote changes with local database
1022 private void syncRemoteToLocal(Client noteStore) {
1023 logger.log(logger.HIGH, "Entering SyncRunner.syncRemoteToLocal");
1025 List<Note> dirtyNotes = conn.getNoteTable().getDirty();
1026 dirtyNoteGuids = new ArrayList<String>();
\r
1027 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {
1028 dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());
1032 SyncChunk chunk = null;
1033 boolean fullSync = false;
1034 boolean more = true;
1036 if (updateSequenceNumber == 0)
1039 status.message.emit(tr("Downloading 0% complete."));
1041 while(more && keepRunning) {
1043 // if (authRefreshNeeded)
1044 // if (!refreshConnection())
1047 int sequence = updateSequenceNumber;
1049 // conn.beginTransaction();
1050 logger.log(logger.EXTREME, "Getting chunk from Evernote");
1051 chunk = noteStore.getSyncChunk(authToken, sequence, chunkSize, fullSync);
1052 logger.log(logger.LOW, "Chunk High Sequence: " +chunk.getChunkHighUSN());
1053 } catch (EDAMUserException e) {
1055 e.printStackTrace();
1056 status.message.emit(e.getMessage());
1057 } catch (EDAMSystemException e) {
1059 e.printStackTrace();
1060 status.message.emit(e.getMessage());
1061 } catch (TException e) {
1063 e.printStackTrace();
1064 status.message.emit(e.getMessage());
1066 if (error || chunk == null)
1071 syncRemoteTags(chunk.getTags());
1072 syncRemoteSavedSearches(chunk.getSearches());
1073 syncRemoteNotebooks(chunk.getNotebooks());
1074 syncRemoteNotes(noteStore, chunk.getNotes(), fullSync, authToken);
1075 syncRemoteResources(noteStore, chunk.getResources());
1076 syncRemoteLinkedNotebooks(chunk.getLinkedNotebooks());
1078 // Signal about any updated notes to invalidate the cache
1079 for (int i=0; i<chunk.getNotesSize(); i++)
1080 noteSignal.noteChanged.emit(chunk.getNotes().get(i).getGuid(), null);
1081 syncExpungedNotes(chunk);
1084 // Check for more notes
1085 if (chunk.getChunkHighUSN() <= updateSequenceNumber)
1089 logger.log(logger.EXTREME, "More notes? " +more);
1092 // Save the chunk sequence number
1093 if (!error && chunk.getChunkHighUSN() > 0 && keepRunning) {
1094 logger.log(logger.EXTREME, "emitting sequence number to main thread");
1095 updateSequenceNumber = chunk.getChunkHighUSN();
1096 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());
1097 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
1098 // conn.commitTransaction();
1103 long pct = chunk.getChunkHighUSN() * 100;
1104 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());
1105 pct = pct/evernoteUpdateCount;
1106 status.message.emit(tr("Downloading ") +new Long(pct).toString()+tr("% complete."));
1108 // conn.commitTransaction();
1110 logger.log(logger.HIGH, "Leaving SyncRunner.syncRemoteToLocal");
1112 // Sync expunged notes
1113 private void syncExpungedNotes(SyncChunk chunk) {
1114 // Do the local deletes
1115 logger.log(logger.EXTREME, "Doing local deletes");
1116 List<String> guid = chunk.getExpungedNotes();
1118 for (int i=0; i<guid.size() && keepRunning; i++) {
1119 String notebookGuid = "";
1120 Note localNote = conn.getNoteTable().getNote(guid.get(i), false, false, false, false, false);
1121 if (localNote != null) {
1122 conn.getNoteTable().updateNoteSequence(guid.get(i), 0);
1123 notebookGuid = localNote.getNotebookGuid();
1125 // If the note is in a local notebook (which means we moved it) or if the
\r
1126 // note returned is null (which means it is already deleted or flagged expunged)
\r
1128 if (!conn.getNotebookTable().isNotebookLocal(notebookGuid) || localNote == null) {
\r
1129 logger.log(logger.EXTREME, "Expunging local note from database");
1130 conn.getNoteTable().expungeNote(guid.get(i), true, false);
1134 guid = chunk.getExpungedNotebooks();
1136 for (int i=0; i<guid.size() && keepRunning; i++) {
1137 logger.log(logger.EXTREME, "Expunging local notebook from database");
1138 conn.getNotebookTable().expungeNotebook(guid.get(i), false);
1140 guid = chunk.getExpungedTags();
1142 for (int i=0; i<guid.size() && keepRunning; i++) {
1143 logger.log(logger.EXTREME, "Expunging tags from local database");
1144 conn.getTagTable().expungeTag(guid.get(i), false);
1146 guid = chunk.getExpungedSearches();
1148 for (int i=0; i<guid.size() && keepRunning; i++) {
1149 logger.log(logger.EXTREME, "Expunging saved search from local database");
1150 conn.getSavedSearchTable().expungeSavedSearch(guid.get(i), false);
1152 guid = chunk.getExpungedLinkedNotebooks();
1154 for (int i=0; i<guid.size() && keepRunning; i++) {
1155 logger.log(logger.EXTREME, "Expunging linked notebook from local database");
1156 conn.getLinkedNotebookTable().expungeNotebook(guid.get(i), false);
1161 private void syncRemoteTags(List<Tag> tags) {
1162 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");
1164 for (int i=0; i<tags.size() && keepRunning; i++) {
1166 oldGuid = conn.getTagTable().findTagByName(tags.get(i).getName());
1167 if (oldGuid != null && !tags.get(i).getGuid().equalsIgnoreCase(oldGuid))
1168 conn.getTagTable().updateTagGuid(oldGuid, tags.get(i).getGuid());
1169 conn.getTagTable().syncTag(tags.get(i), false);
1172 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");
1174 // Sync remote saved searches
1175 private void syncRemoteSavedSearches(List<SavedSearch> searches) {
1176 logger.log(logger.EXTREME, "Entering SyncRunner.syncSavedSearches");
1177 if (searches != null) {
1178 for (int i=0; i<searches.size() && keepRunning; i++) {
1180 oldGuid = conn.getSavedSearchTable().findSavedSearchByName(searches.get(i).getName());
1181 if (oldGuid != null && !searches.get(i).getGuid().equalsIgnoreCase(oldGuid))
1182 conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, searches.get(i).getGuid());
1183 conn.getSavedSearchTable().syncSavedSearch(searches.get(i), false);
1186 logger.log(logger.EXTREME, "Leaving SyncRunner.syncSavedSearches");
1188 // Sync remote linked notebooks
1189 private void syncRemoteLinkedNotebooks(List<LinkedNotebook> books) {
1190 logger.log(logger.EXTREME, "Entering SyncRunner.syncLinkedNotebooks");
1191 if (books != null) {
1192 for (int i=0; i<books.size() && keepRunning; i++) {
1193 conn.getLinkedNotebookTable().updateNotebook(books.get(i), false);
1196 logger.log(logger.EXTREME, "Leaving SyncRunner.syncLinkedNotebooks");
1198 // Sync remote Notebooks 2
1199 private void syncRemoteNotebooks(List<Notebook> notebooks) {
1200 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");
1201 if (notebooks != null) {
1202 for (int i=0; i<notebooks.size() && keepRunning; i++) {
1204 oldGuid = conn.getNotebookTable().findNotebookByName(notebooks.get(i).getName());
1205 if (oldGuid != null && !conn.getNotebookTable().isNotebookLocal(oldGuid) && !notebooks.get(i).getGuid().equalsIgnoreCase(oldGuid))
1206 conn.getNotebookTable().updateNotebookGuid(oldGuid, notebooks.get(i).getGuid());
1207 conn.getNotebookTable().syncNotebook(notebooks.get(i), false);
1209 // Synchronize shared notebook information
1210 // if (notebooks.get(i).getSharedNotebookIdsSize() > 0) {
1211 // conn.getSharedNotebookTable().expungeNotebookByGuid(notebooks.get(i).getGuid(), false);
1212 // for (int j=0; j<notebooks.get(i).getSharedNotebookIdsSize(); j++) {
1213 // syncRemoteSharedNotebook(notebooks.get(i).getGuid(), notebooks.get(i).getSharedNotebookIds().get(j), authToken);
1218 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");
1220 // Sync remote shared notebook
1221 // private void syncRemoteSharedNotebook(String guid, Long id, String token) {
1222 // List<SharedNotebook> books = noteStore.getSharedNotebookByAuth(authToken);
1224 // Sync remote Resources
1225 private void syncRemoteResources(Client noteStore, List<Resource> resource) {
1226 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteResources");
1227 if (resource != null) {
1228 for (int i=0; i<resource.size() && keepRunning; i++) {
1229 syncRemoteResource(noteStore, resource.get(i), authToken);
1232 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteResources");
1234 // Sync remote resource
1235 private void syncRemoteResource(Client noteStore, Resource resource, String authToken) {
1236 // This is how the logic for this works.
1237 // 1.) If the resource is not in the local database, we add it.
1238 // 2.) If a copy of the resource is in the local database and the note isn't dirty, we update the local copy
1239 // 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
1240 // is a conflict. The note conflict should get a copy of the resource at that time.
1242 Note n = conn.getNoteTable().getNote(resource.getNoteGuid(), false, false, false, false, false);
1244 logger.log(logger.HIGH, "Resource for note " +n.getGuid() +" : " +n.getTitle());
1246 boolean saveNeeded = false;
1247 /* #1 */ Resource r = getEvernoteResource(noteStore, resource.getGuid(), true,true,true, authToken);
1248 Resource l = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), false);
1250 logger.log(logger.HIGH, "Local resource not found");
1253 /* #2 */ boolean isNoteDirty = conn.getNoteTable().isNoteDirty(r.getNoteGuid());
1255 logger.log(logger.HIGH, "Local resource found, but is not dirty");
1258 /* #3 */ String remoteHash = "";
1259 if (r != null && r.getData() != null && r.getData().getBodyHash() != null)
1260 remoteHash = byteArrayToHexString(r.getData().getBodyHash());
1261 String localHash = "";
1262 if (l != null && l.getData() != null && l.getData().getBodyHash() != null)
1263 remoteHash = byteArrayToHexString(l.getData().getBodyHash());
1265 if (localHash.equalsIgnoreCase(remoteHash))
1270 logger.log(logger.HIGH, "Resource save needed: " +saveNeeded);
1272 conn.getNoteTable().noteResourceTable.updateNoteResource(r, false);
1273 if (r.getMime().equalsIgnoreCase("application/vnd.evernote.ink"))
1274 downloadInkNoteImage(r.getGuid(), authToken);
1278 // Sync remote notes
1279 private void syncRemoteNotes(Client noteStore, List<Note> note, boolean fullSync, String token) {
1283 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotes");
\r
1284 logger.log(logger.LOW, "Local Dirty Notes: ");
\r
1285 logger.log(logger.LOW, "Remote Dirty Notes:");
\r
1286 for (int i=0; i<note.size();i++) {
\r
1287 logger.log(logger.LOW, i +" : " +note.get(i).getGuid() + " : " +note.get(i).getTitle() );
\r
1289 logger.log(logger.LOW, "---");
\r
1291 for (int i=0; i<note.size() && keepRunning; i++) {
1292 Note n = getEvernoteNote(noteStore, note.get(i).getGuid(), true, fullSync, true,true, token);
1293 syncRemoteNote(n, fullSync, token);
1296 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotes");
1298 private void syncRemoteNote(Note n, boolean fullSync, String token) {
1301 // Basically, this is how the sync logic for a note works.
1302 // If the remote note has changed and the local has not, we
1303 // accept the change.
1304 // If both the local & remote have changed but the sequence
1305 // numbers are the same, we don't accept the change. This
1306 // seems to happen when attachments are indexed by the server.
1307 // If both the local & remote have changed and the sequence numbers
1308 // are different we move the local copy to a local notebook (making sure
1309 // to copy all resources) and we accept the new one.
1310 boolean conflictingNote = true;
1311 logger.log(logger.EXTREME, "Checking for duplicate note " +n.getGuid() +" : " +n.getTitle());
\r
1312 if (dirtyNoteGuids != null && dirtyNoteGuids.contains(n.getGuid())) {
1313 logger.log(logger.EXTREME, "Conflict check beginning");
1314 conflictingNote = checkForConflict(n);
1315 logger.log(logger.EXTREME, "Conflict check results " +conflictingNote);
1316 if (conflictingNote)
1317 moveConflictingNote(n.getGuid());
1319 boolean ignoreNote = false;
1320 if (ignoreNotebooks.contains(n.getNotebookGuid()))
1322 for (int i=0; i<n.getTagGuidsSize(); i++) {
1323 if (ignoreTags.contains(n.getTagGuids().get(i))) {
1325 i=n.getTagGuidsSize();
1329 if ((conflictingNote || fullSync) && !ignoreNote) {
1330 logger.log(logger.EXTREME, "Saving Note");
1331 conn.getNoteTable().syncNote(n);
1332 // The following was commented out because it caused a race condition on the database where resources
1333 // may be lost. We do the same thing elsewhere;.
1334 // noteSignal.noteChanged.emit(n.getGuid(), null); // Signal to ivalidate note cache
1335 noteSignal.noteDownloaded.emit(n, true); // Signal to add note to index
1336 logger.log(logger.EXTREME, "Note Saved");
1337 if (fullSync && n.getResources() != null) {
1338 for (int q=0; q<n.getResources().size() && keepRunning; q++) {
1339 logger.log(logger.EXTREME, "Getting note resources.");
1340 conn.getNoteTable().noteResourceTable.updateNoteResource(n.getResources().get(q), false);
1341 if (n.getResources().get(q).getMime().equalsIgnoreCase("application/vnd.evernote.ink"))
1342 downloadInkNoteImage(n.getResources().get(q).getGuid(), token);
1348 private Note getEvernoteNote(Client noteStore, String guid, boolean withContent, boolean withResourceData, boolean withResourceRecognition, boolean withResourceAlternateData, String token) {
1351 logger.log(logger.EXTREME, "Retrieving note " +guid);
1352 n = noteStore.getNote(token, guid, withContent, withResourceData, withResourceRecognition, withResourceAlternateData);
1353 logger.log(logger.EXTREME, "Note " +guid +" has been retrieved.");
1354 } catch (EDAMUserException e) {
1355 logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");
1356 logger.log(logger.LOW, e.toString());
1358 e.printStackTrace();
1359 } catch (EDAMSystemException e) {
1360 logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");
1361 logger.log(logger.LOW, e.toString());
1363 e.printStackTrace();
1364 } catch (EDAMNotFoundException e) {
1365 logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");
1366 logger.log(logger.LOW, e.toString());
1368 e.printStackTrace();
1369 } catch (TException e) {
1370 logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");
1371 logger.log(logger.LOW, e.toString());
1373 e.printStackTrace();
1377 private Resource getEvernoteResource(Client noteStore, String guid, boolean withData, boolean withRecognition, boolean withAttributes, String token) {
1380 logger.log(logger.EXTREME, "Retrieving resource " +guid);
1381 n = noteStore.getResource(token, guid, withData, withRecognition, withAttributes, withAttributes);
1382 logger.log(logger.EXTREME, "Resource " +guid +" has been retrieved.");
1383 } catch (EDAMUserException e) {
1384 logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");
1385 logger.log(logger.LOW, e.toString());
1387 e.printStackTrace();
1388 } catch (EDAMSystemException e) {
1389 logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");
1390 logger.log(logger.LOW, e.toString());
1392 e.printStackTrace();
1393 } catch (EDAMNotFoundException e) {
1394 logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");
1395 logger.log(logger.LOW, e.toString());
1397 e.printStackTrace();
1398 } catch (TException e) {
1399 logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");
1400 logger.log(logger.LOW, e.toString());
1402 e.printStackTrace();
1408 private boolean checkForConflict(Note n) {
1409 logger.log(logger.EXTREME, "Checking note sequence number " +n.getGuid());
1410 Note oldNote = conn.getNoteTable().getNote(n.getGuid(), false, false, false, false, false);
1411 logger.log(logger.EXTREME, "Local/Remote sequence numbers: " +oldNote.getUpdateSequenceNum()+"/"+n.getUpdateSequenceNum());
1412 logger.log(logger.LOW, "Remote Note Title:" +n.getTitle());
\r
1413 logger.log(logger.LOW, "Local Note Title:" +oldNote.getTitle());
\r
1414 if (oldNote.getUpdateSequenceNum() == n.getUpdateSequenceNum()) {
\r
1417 boolean oldIsDirty = conn.getNoteTable().isNoteDirty(n.getGuid());
\r
1423 private void moveConflictingNote(String guid) {
1424 logger.log(logger.EXTREME, "Conflicting change found for note " +guid);
1425 List<Notebook> books = conn.getNotebookTable().getAllLocal();
1426 String notebookGuid = null;
1427 for (int i=0; i<books.size() && keepRunning; i++) {
1428 if (books.get(i).getName().equalsIgnoreCase("Conflicting Changes (local)") ||
1429 books.get(i).getName().equalsIgnoreCase("Conflicting Changes")) {
1430 notebookGuid = books.get(i).getGuid();
1435 if (notebookGuid == null) {
1436 logger.log(logger.EXTREME, "Building conflicting change notebook " +guid);
1437 Calendar currentTime = new GregorianCalendar();
1438 Long l = new Long(currentTime.getTimeInMillis());
1440 while (prevTime==l) {
1441 currentTime = new GregorianCalendar();
1442 l=currentTime.getTimeInMillis();
1444 String randint = new String(Long.toString(l));
1446 Notebook newBook = new Notebook();
1447 newBook.setUpdateSequenceNum(0);
1448 newBook.setGuid(randint);
1449 newBook.setName("Conflicting Changes");
1450 newBook.setServiceCreated(new Date().getTime());
1451 newBook.setServiceUpdated(new Date().getTime());
1452 newBook.setDefaultNotebook(false);
1453 newBook.setPublished(false);
1455 conn.getNotebookTable().addNotebook(newBook, false, true);
1456 notebookSignal.listChanged.emit();
1457 notebookGuid = newBook.getGuid();
1458 refreshNeeded = true;
1461 // Now that we have a good notebook guid, we need to move the conflicting note
1462 // to the local notebook
1463 logger.log(logger.EXTREME, "Moving conflicting note " +guid);
1464 Calendar currentTime = new GregorianCalendar();
1465 Long l = new Long(currentTime.getTimeInMillis());
1467 while (prevTime==l) {
1468 currentTime = new GregorianCalendar();
1469 l = currentTime.getTimeInMillis();
1471 String newGuid = new String(Long.toString(l));
1473 Note oldNote = conn.getNoteTable().getNote(guid, true, true, false, false, false);
1474 for (int i=0; i<oldNote.getResources().size() && keepRunning; i++) {
1475 l = new Long(currentTime.getTimeInMillis());
1476 String newResG = new String(Long.toString(l));
1477 String oldResG = oldNote.getResources().get(i).getGuid();
1478 conn.getNoteTable().noteResourceTable.resetUpdateSequenceNumber(oldResG, true);
1479 conn.getNoteTable().noteResourceTable.updateNoteResourceGuid(oldResG, newResG, true);
1482 conn.getNoteTable().resetNoteSequence(guid);
1483 conn.getNoteTable().updateNoteGuid(guid, newGuid);
1484 conn.getNoteTable().updateNoteNotebook(newGuid, notebookGuid, true);
1486 noteSignal.notebookChanged.emit(newGuid, notebookGuid);
1487 refreshNeeded = true;
1488 noteSignal.guidChanged.emit(guid,newGuid);
1494 //******************************************************
1495 //******************************************************
1496 //** Utility Functions
1497 //******************************************************
1498 //******************************************************
1499 // Convert a byte array to a hex string
1500 private static String byteArrayToHexString(byte data[]) {
1501 StringBuffer buf = new StringBuffer();
1502 for (byte element : data) {
1503 int halfbyte = (element >>> 4) & 0x0F;
1506 if ((0 <= halfbyte) && (halfbyte <= 9))
1507 buf.append((char) ('0' + halfbyte));
1509 buf.append((char) ('a' + (halfbyte - 10)));
1510 halfbyte = element & 0x0F;
1511 } while(two_halfs++ < 1);
1513 return buf.toString();
1518 //*******************************************************
1519 //* Find dirty tags, which do not have newly created parents
1520 //*******************************************************
1521 private Tag findNextTag() {
1522 logger.log(logger.HIGH, "Entering SyncRunner.findNextTag");
1524 List<Tag> tags = conn.getTagTable().getDirty();
1526 // Find the parent. If the parent has a sequence > 0 then it is a good
1528 for (int i=0; i<tags.size() && keepRunning; i++) {
1529 if (!badTagSync.containsKey(tags.get(i).getGuid())) {
1530 if (tags.get(i).getParentGuid() == null) {
1531 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found without parent");
1534 Tag parentTag = conn.getTagTable().getTag(tags.get(i).getParentGuid());
1535 if (parentTag.getUpdateSequenceNum() > 0) {
1536 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found");
1542 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - no tags returned");
1547 // Connect to Evernote
1548 public boolean enConnect() {
1550 userStoreTrans = new THttpClient(userStoreUrl);
1551 userStoreTrans.setCustomHeader("User-Agent", userAgent);
1552 } catch (TTransportException e) {
1553 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1555 e.printStackTrace();
1557 userStoreProt = new TBinaryProtocol(userStoreTrans);
1558 userStore = new UserStore.Client(userStoreProt, userStoreProt);
1560 syncSignal.saveUserStore.emit(userStore);
1562 //authResult = userStore.authenticate(username, password, consumerKey, consumerSecret);
1563 user = userStore.getUser(authToken);
1564 } catch (EDAMUserException e) {
1565 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Error", "Invalid Authorization");
1567 isConnected = false;
1569 } catch (EDAMSystemException e) {
1570 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "EDAM System Excepton", e.getLocalizedMessage());
1572 e.printStackTrace();
1573 isConnected = false;
1574 } catch (TException e) {
1575 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1577 e.printStackTrace();
1578 isConnected = false;
1581 boolean versionOk = false;
1583 versionOk = userStore.checkVersion("NeighborNote",
1584 com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR,
1585 com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR);
1586 } catch (TException e) {
1587 e.printStackTrace();
1588 isConnected = false;
1591 System.err.println("Incomatible EDAM client protocol version");
1592 isConnected = false;
1594 //if (authResult != null) {
1595 //user = authResult.getUser();
1596 //authToken = authResult.getAuthenticationToken();
1597 if (user == null || noteStoreUrlBase == null) {
\r
1598 logger.log(logger.LOW, "Error retrieving user information. Aborting.");
\r
1599 System.err.println("Error retrieving user information.");
\r
1600 isConnected = false;
\r
1601 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, tr("Connection Error"), tr("Error retrieving user information. Synchronization not complete"));
\r
1606 noteStoreUrl = noteStoreUrlBase + user.getShardId();
1607 syncSignal.saveAuthToken.emit(authToken);
1608 syncSignal.saveNoteStore.emit(localNoteStore);
1612 noteStoreTrans = new THttpClient(noteStoreUrl);
1613 noteStoreTrans.setCustomHeader("User-Agent", userAgent);
1614 } catch (TTransportException e) {
1615 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1617 e.printStackTrace();
1618 isConnected = false;
1620 noteStoreProt = new TBinaryProtocol(noteStoreTrans);
1622 new NoteStore.Client(noteStoreProt, noteStoreProt);
1624 //authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();
1625 //authRefreshTime = authTimeRemaining / 2;
1628 // Get user information
1630 User user = userStore.getUser(authToken);
1631 syncSignal.saveUserInformation.emit(user);
1632 } catch (EDAMUserException e1) {
1633 e1.printStackTrace();
1634 } catch (EDAMSystemException e1) {
1635 e1.printStackTrace();
1636 } catch (TException e1) {
1637 e1.printStackTrace();
1642 // Disconnect from the database
1643 public void enDisconnect() {
1644 isConnected = false;
1648 // Refresh the connection
1649 private synchronized boolean refreshConnection() {
1651 logger.log(logger.EXTREME, "Entering SyncRunner.refreshConnection()");
1652 // Calendar cal = Calendar.getInstance();
1654 // If we are not connected let's get out of here
1658 // If we fail too many times, then let's give up.
1659 if (failedRefreshes >=5) {
1660 logger.log(logger.EXTREME, "Refresh attempts have failed. Disconnecting.");
1661 isConnected = false;
1662 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1666 // If this is the first time through, then we need to set this
1667 // if (authRefreshTime == 0 || cal.getTimeInMillis() > authRefreshTime)
1668 // authRefreshTime = cal.getTimeInMillis();
1670 // // Default to checking again in 5 min. This in case we fail.
1671 // authRefreshTime = authRefreshTime +(5*60*1000);
1673 // Try to get a new token
1674 AuthenticationResult newAuth = null;
1675 logger.log(logger.EXTREME, "Beginning to try authentication refresh");
1677 if (userStore != null && authToken != null)
1678 newAuth = userStore.refreshAuthentication(authToken);
1681 logger.log(logger.EXTREME, "UserStore.refreshAuthentication has succeeded.");
1682 } catch (EDAMUserException e) {
1683 e.printStackTrace();
1684 syncSignal.authRefreshComplete.emit(false);
1687 } catch (EDAMSystemException e) {
1688 e.printStackTrace();
1689 syncSignal.authRefreshComplete.emit(false);
1692 } catch (TException e) {
1693 e.printStackTrace();
1694 syncSignal.authRefreshComplete.emit(false);
1699 // If we didn't get a good auth, then we've failed
1700 if (newAuth == null) {
1702 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1703 logger.log(logger.EXTREME, "Authentication failure #" +failedRefreshes);
1704 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1705 syncSignal.authRefreshComplete.emit(false);
1709 // We got a good token. Now we need to setup the time to renew it.
1710 logger.log(logger.EXTREME, "Saving authentication tokens");
1711 authResult = newAuth;
1712 authToken = new String(newAuth.getAuthenticationToken());
1713 // authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();
1714 // authRefreshTime = cal.getTimeInMillis() + (authTimeRemaining/4);
1716 syncSignal.authRefreshComplete.emit(true);
1717 authRefreshNeeded = false;
1719 // This should never happen, but if it does we consider this a faild attempt.
1720 // if (authTimeRemaining <= 0) {
1721 // failedRefreshes++;
1722 // syncSignal.authRefreshComplete.emit(false);
1730 public synchronized boolean addWork(String request) {
1731 if (workQueue.offer(request))
1736 private Note getNoteContent(Note n) {
1737 QTextCodec codec = QTextCodec.codecForLocale();
1738 codec = QTextCodec.codecForName("UTF-8");
1739 n.setContent(codec.toUnicode(new QByteArray(n.getContent())));
1745 //*********************************************************
1746 //* Special download instructions. Used for DB upgrades
1747 //*********************************************************
1748 private void downloadAllSharedNotebooks(Client noteStore) {
1750 List<SharedNotebook> books = noteStore.listSharedNotebooks(authToken);
1751 logger.log(logger.LOW, "Shared notebooks found = " +books.size());
1752 for (int i=0; i<books.size(); i++) {
1753 conn.getSharedNotebookTable().updateNotebook(books.get(i), false);
1755 conn.getSyncTable().deleteRecord("FullSharedNotebookSync");
1756 } catch (EDAMUserException e1) {
1757 e1.printStackTrace();
1758 status.message.emit(tr("User exception Listing shared notebooks."));
1759 logger.log(logger.LOW, e1.getMessage());
1761 } catch (EDAMSystemException e1) {
1762 e1.printStackTrace();
1763 status.message.emit(tr("System exception Listing shared notebooks."));
1764 logger.log(logger.LOW, e1.getMessage());
1766 } catch (TException e1) {
1767 e1.printStackTrace();
1768 status.message.emit(tr("Transaction exception Listing shared notebooks."));
1769 logger.log(logger.LOW, e1.getMessage());
1771 } catch (EDAMNotFoundException e1) {
1772 e1.printStackTrace();
1773 status.message.emit(tr("EDAM Not Found exception Listing shared notebooks."));
1774 logger.log(logger.LOW, e1.getMessage());
1777 private void downloadAllNotebooks(Client noteStore) {
1779 List<Notebook> books = noteStore.listNotebooks(authToken);
1780 logger.log(logger.LOW, "Shared notebooks found = " +books.size());
1781 for (int i=0; i<books.size(); i++) {
1782 conn.getNotebookTable().updateNotebook(books.get(i), false);
1784 conn.getSyncTable().deleteRecord("FullNotebookSync");
1785 } catch (EDAMUserException e1) {
1786 e1.printStackTrace();
1787 status.message.emit(tr("User exception Listing notebooks."));
1788 logger.log(logger.LOW, e1.getMessage());
1790 } catch (EDAMSystemException e1) {
1791 e1.printStackTrace();
1792 status.message.emit(tr("System exception Listing notebooks."));
1793 logger.log(logger.LOW, e1.getMessage());
1795 } catch (TException e1) {
1796 e1.printStackTrace();
1797 status.message.emit(tr("Transaction exception Listing notebooks."));
1798 logger.log(logger.LOW, e1.getMessage());
1802 private void downloadAllLinkedNotebooks(Client noteStore) {
1804 List<LinkedNotebook> books = noteStore.listLinkedNotebooks(authToken);
1805 logger.log(logger.LOW, "Linked notebooks found = " +books.size());
1806 for (int i=0; i<books.size(); i++) {
1807 conn.getLinkedNotebookTable().updateNotebook(books.get(i), false);
1809 conn.getSyncTable().deleteRecord("FullLinkedNotebookSync");
1810 } catch (EDAMUserException e1) {
1811 e1.printStackTrace();
1812 status.message.emit(tr("User exception Listing linked notebooks."));
1813 logger.log(logger.LOW, e1.getMessage());
1815 } catch (EDAMSystemException e1) {
1816 e1.printStackTrace();
1817 status.message.emit(tr("System exception Listing linked notebooks."));
1818 logger.log(logger.LOW, e1.getMessage());
1820 } catch (TException e1) {
1821 e1.printStackTrace();
1822 status.message.emit(tr("Transaction exception Listing lineked notebooks."));
1823 logger.log(logger.LOW, e1.getMessage());
1825 } catch (EDAMNotFoundException e1) {
1826 e1.printStackTrace();
1827 status.message.emit(tr("EDAM Not Found exception Listing linked notebooks."));
1828 logger.log(logger.LOW, e1.getMessage());
1833 private void downloadInkNoteImage(String guid, String authToken) {
1834 String urlBase = noteStoreUrl.replace("/edam/note/", "/shard/") + "/res/"+guid+".ink?slice=";
1835 // urlBase = "https://www.evernote.com/shard/s1/res/52b567a9-54ae-4a08-afc5-d5bae275b2a8.ink?slice=";
1837 Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(guid, false);
1838 conn.getInkImagesTable().expungeImage(r.getGuid());
1839 int sliceCount = 1+((r.getHeight()-1)/480);
1840 HttpClient http = new DefaultHttpClient();
1841 for (int i=0; i<sliceCount; i++) {
1842 String url = urlBase + slice.toString();
1843 HttpPost post = new HttpPost(url);
1844 post.getParams().setParameter("auth", authToken);
1845 List <NameValuePair> nvps = new ArrayList <NameValuePair>();
1846 nvps.add(new BasicNameValuePair("auth", authToken));
1849 post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
1850 } catch (UnsupportedEncodingException e1) {
1851 e1.printStackTrace();
1854 HttpResponse response = http.execute(post);
1855 HttpEntity resEntity = response.getEntity();
1856 InputStream is = resEntity.getContent();
1857 QByteArray data = writeToFile(is);
1858 conn.getInkImagesTable().saveImage(guid, slice, data);
1859 } catch (ClientProtocolException e) {
1860 e.printStackTrace();
1861 } catch (IOException e) {
1862 e.printStackTrace();
1867 http.getConnectionManager().shutdown();
1868 noteSignal.noteChanged.emit(r.getNoteGuid(), null); // Signal to ivalidate note cache
1872 public QByteArray writeToFile(InputStream iStream) throws IOException {
1874 File temp = File.createTempFile("nn-inknote-temp", ".png");
1876 // Save InputStream to the file.
1877 BufferedOutputStream fOut = null;
1879 fOut = new BufferedOutputStream(new FileOutputStream(temp));
1880 byte[] buffer = new byte[32 * 1024];
1882 while ((bytesRead = iStream.read(buffer)) != -1) {
1883 fOut.write(buffer, 0, bytesRead);
1890 QFile tempFile = new QFile(temp.getAbsoluteFile().toString());
1891 tempFile.open(OpenModeFlag.ReadOnly);
1892 QByteArray data = tempFile.readAll();
1899 //******************************************
1900 //* Begin syncing shared notebooks
1901 //******************************************
1902 private void syncLinkedNotebooks() {
1903 logger.log(logger.MEDIUM, "Authenticating linked Notebooks");
1904 status.message.emit(tr("Synchronizing shared notebooks."));
1905 List<LinkedNotebook> books = conn.getLinkedNotebookTable().getAll();
1907 errorSharedNotebooks.clear();
1909 for (int i=0; i<books.size(); i++) {
1910 if (errorSharedNotebooksIgnored.containsKey(books.get(i).getGuid()))
1913 logger.log(logger.EXTREME, "Checking notebook: " +books.get(i).getShareName());
1914 long lastSyncDate = conn.getLinkedNotebookTable().getLastSequenceDate(books.get(i).getGuid());
1915 int lastSequenceNumber = conn.getLinkedNotebookTable().getLastSequenceNumber(books.get(i).getGuid());
1917 logger.log(logger.EXTREME, "Last Sequence Number on file: " +lastSequenceNumber);
1919 // Authenticate to the owner's shard
1920 String linkedNoteStoreUrl = noteStoreUrlBase + books.get(i).getShardId();
1921 logger.log(logger.EXTREME, "linkedNoteStoreURL: " +linkedNoteStoreUrl);
1922 THttpClient linkedNoteStoreTrans = new THttpClient(linkedNoteStoreUrl);
1923 TBinaryProtocol linkedNoteStoreProt = new TBinaryProtocol(linkedNoteStoreTrans);
1924 Client linkedNoteStore = new NoteStore.Client(linkedNoteStoreProt, linkedNoteStoreProt);
1926 linkedAuthResult = null;
1927 if (books.get(i).getShareKey() != null) {
1928 logger.log(logger.EXTREME, "Share Key Not Null: " +books.get(i).getShareKey());
1929 linkedAuthResult = linkedNoteStore.authenticateToSharedNotebook(books.get(i).getShareKey(), authToken);
1930 logger.log(logger.EXTREME, "Authentication Token" +linkedAuthResult.getAuthenticationToken());
1932 logger.log(logger.EXTREME, "Share key is null");
1933 linkedAuthResult = new AuthenticationResult();
1934 linkedAuthResult.setAuthenticationToken("");
1936 SyncState linkedSyncState =
1937 linkedNoteStore.getLinkedNotebookSyncState(linkedAuthResult.getAuthenticationToken(), books.get(i));
1938 if (linkedSyncState.getUpdateCount() > lastSequenceNumber) {
1939 logger.log(logger.EXTREME, "Remote changes found");
1940 if (lastSyncDate < linkedSyncState.getFullSyncBefore()) {
1941 lastSequenceNumber = 0;
1943 logger.log(logger.EXTREME, "Calling syncLinkedNotebook for " +books.get(i).getShareName());
1944 syncLinkedNotebook(linkedNoteStore, books.get(i),
1945 lastSequenceNumber, linkedSyncState.getUpdateCount(), authToken);
1948 // Synchronize local changes
1949 syncLocalLinkedNoteChanges(linkedNoteStore, books.get(i));
1951 } catch (EDAMUserException e) {
1952 e.printStackTrace();
1953 } catch (EDAMNotFoundException e) {
1954 status.message.emit(tr("Error synchronizing \" " +
1955 books.get(i).getShareName()+"\". Please verify you still have access to that shared notebook."));
1956 errorSharedNotebooks.add(books.get(i).getGuid());
1957 errorSharedNotebooksIgnored.put(books.get(i).getGuid(), books.get(i).getGuid());
1958 logger.log(logger.LOW, "Error synchronizing shared notebook. EDAMNotFound: "+e.getMessage());
1959 logger.log(logger.LOW, e.getStackTrace());
1961 e.printStackTrace();
1962 } catch (EDAMSystemException e) {
1964 logger.log(logger.LOW, "System error authenticating against shared notebook. "+
1965 "Key: "+books.get(i).getShareKey() +" Error:" +e.getMessage());
1966 e.printStackTrace();
1967 } catch (TException e) {
1969 e.printStackTrace();
1974 conn.getTagTable().removeUnusedLinkedTags();
1975 conn.getTagTable().cleanupTags();
1976 tagSignal.listChanged.emit();
1981 //**************************************************************
1982 //* Linked notebook contents (from someone else's account)
1983 //*************************************************************
1984 private void syncLinkedNotebook(Client linkedNoteStore, LinkedNotebook book, int usn, int highSequence, String token) {
1985 logger.log(logger.EXTREME, "Entering syncLinkedNotebook");
1986 if (ignoreLinkedNotebooks.contains(book.getGuid()))
1988 List<Note> dirtyNotes = conn.getNoteTable().getDirtyLinkedNotes();
1989 if (dirtyNoteGuids == null)
1990 dirtyNoteGuids = new ArrayList<String>();
\r
1992 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {
1993 dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());
1995 boolean fullSync = false;
1998 boolean syncError = false;
1999 while (usn < highSequence && !syncError) {
2000 refreshNeeded = true;
2003 linkedNoteStore.getLinkedNotebookSyncChunk(token, book, usn, 10, fullSync);
2006 syncExpungedNotes(chunk);
2008 logger.log(logger.EXTREME, "Syncing remote notes: " +chunk.getNotesSize());
2009 syncRemoteNotes(linkedNoteStore, chunk.getNotes(), fullSync, linkedAuthResult.getAuthenticationToken());
2010 logger.log(logger.EXTREME, "Finding new linked tags");
2011 findNewLinkedTags(linkedNoteStore, chunk.getNotes(), linkedAuthResult.getAuthenticationToken());
2013 logger.log(logger.EXTREME, "Synchronizing tags: " +chunk.getTagsSize());
2014 for (int i=0; i<chunk.getResourcesSize(); i++) {
2015 syncRemoteResource(linkedNoteStore, chunk.getResources().get(i), linkedAuthResult.getAuthenticationToken());
2017 logger.log(logger.EXTREME, "Synchronizing linked notebooks: " +chunk.getNotebooksSize());
2018 syncRemoteLinkedNotebooks(linkedNoteStore, chunk.getNotebooks(), false, book);
2019 syncLinkedTags(chunk.getTags(), book.getGuid());
2021 // Go through & signal any notes that have changed so we can refresh the user's view
2022 for (int i=0; i<chunk.getNotesSize(); i++)
2023 noteSignal.noteChanged.emit(chunk.getNotes().get(i).getGuid(), null);
2025 // Expunge Notebook records
2026 logger.log(logger.EXTREME, "Expunging linked notebooks: " +chunk.getExpungedLinkedNotebooksSize());
2027 for (int i=0; i<chunk.getExpungedLinkedNotebooksSize(); i++) {
2028 conn.getLinkedNotebookTable().expungeNotebook(chunk.getExpungedLinkedNotebooks().get(i), false);
2030 usn = chunk.getChunkHighUSN();
2031 conn.getLinkedNotebookTable().setLastSequenceDate(book.getGuid(),chunk.getCurrentTime());
2032 conn.getLinkedNotebookTable().setLastSequenceNumber(book.getGuid(),chunk.getChunkHighUSN());
2033 } catch (EDAMUserException e) {
2035 status.message.emit(tr("EDAM UserException synchronizing linked notbook. See the log for datails."));
2036 e.printStackTrace();
2037 logger.log(logger.LOW, tr("EDAM UserException synchronizing linked notbook ")+ e.getMessage());
2038 } catch (EDAMSystemException e) {
2040 status.message.emit(tr("EDAM SystemException synchronizing linked notbook. See the log for datails."));
2041 e.printStackTrace();
2042 logger.log(logger.LOW, tr("EDAM SystemException synchronizing linked notbook. See the log for datails") +e.getMessage());
2043 } catch (EDAMNotFoundException e) {
2045 status.message.emit(tr("Notebook URL not found. Removing notobook ") +book.getShareName());
2046 conn.getNotebookTable().deleteLinkedTags(book.getGuid());
2047 conn.getLinkedNotebookTable().expungeNotebook(book.getGuid(), false);
2048 logger.log(logger.LOW, tr("Notebook URL not found. Removing notobook ") +e.getMessage());
2049 } catch (TException e) {
2051 status.message.emit(tr("EDAM TException synchronizing linked notbook. See the log for datails."));
2052 e.printStackTrace();
2053 logger.log(logger.LOW, tr("EDAM TException synchronizing linked notbook. See the log for datails." )+e.getMessage());
2056 logger.log(logger.EXTREME, "leaving syncLinkedNotebook");
2059 private void syncLinkedTags(List<Tag> tags, String notebookGuid) {
2060 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");
2062 for (int i=0; i<tags.size() && keepRunning; i++) {
2063 conn.getTagTable().syncLinkedTag(tags.get(i), notebookGuid, false);
2066 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");
2069 // Sync notebooks from a linked notebook
2070 private void syncRemoteLinkedNotebooks(Client noteStore, List<Notebook> notebooks, boolean readOnly, LinkedNotebook linked) {
2071 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");
2072 if (notebooks != null) {
2073 for (int i=0; i<notebooks.size() && keepRunning; i++) {
2075 logger.log(logger.EXTREME, "auth token:" +linkedAuthResult.getAuthenticationToken());
2076 if (!linkedAuthResult.getAuthenticationToken().equals("")) {
2077 SharedNotebook s = noteStore.getSharedNotebookByAuth(linkedAuthResult.getAuthenticationToken());
2078 logger.log(logger.EXTREME, "share key:"+s.getShareKey() +" notebookGuid" +s.getNotebookGuid());
2079 conn.getLinkedNotebookTable().setNotebookGuid(s.getShareKey(), s.getNotebookGuid());
2080 readOnly = !s.isNotebookModifiable();
2084 notebooks.get(i).setName(linked.getShareName());
2085 notebooks.get(i).setDefaultNotebook(false);
2086 conn.getNotebookTable().syncLinkedNotebook(notebooks.get(i), false, readOnly);
2087 } catch (EDAMUserException e) {
2089 e.printStackTrace();
2090 } catch (EDAMNotFoundException e) {
2092 e.printStackTrace();
2093 } catch (EDAMSystemException e) {
2095 e.printStackTrace();
2096 } catch (TException e) {
2098 e.printStackTrace();
2103 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");
2106 private void findNewLinkedTags(Client noteStore, List<Note> newNotes, String token) {
2107 if (newNotes == null)
2109 for (int i=0; i<newNotes.size(); i++) {
2110 Note n = newNotes.get(i);
2111 for (int j=0; j<n.getTagGuidsSize(); j++) {
2112 String tag = n.getTagGuids().get(j);
2113 if (!conn.getTagTable().exists(tag)) {
2116 newTag = noteStore.getTag(token, tag);
2117 conn.getTagTable().addTag(newTag, false);
2118 } catch (EDAMUserException e) {
2119 e.printStackTrace();
2120 } catch (EDAMSystemException e) {
2121 e.printStackTrace();
2122 } catch (EDAMNotFoundException e) {
2123 e.printStackTrace();
2124 } catch (TException e) {
2125 e.printStackTrace();
2133 // Synchronize changes locally done to linked notes
2134 private void syncLocalLinkedNoteChanges(Client noteStore, LinkedNotebook book) {
2135 logger.log(logger.EXTREME, "Entering SyncRunner.synclocalLinkedNoteChanges");
2136 String notebookGuid = conn.getLinkedNotebookTable().getNotebookGuid(book.getGuid());
2137 logger.log(logger.EXTREME, "Finding changes for " +book.getShareName() +":" +book.getGuid() + ":" +notebookGuid);
2138 List<Note> notes = conn.getNoteTable().getDirtyLinked(notebookGuid);
2139 logger.log(logger.EXTREME, "Number of changes found: " +notes.size());
2140 for (int i=0; i<notes.size(); i++) {
2141 logger.log(logger.EXTREME, "Calling syncLocalNote with key " +linkedAuthResult.getAuthenticationToken());
2142 syncLocalNote(noteStore, notes.get(i), linkedAuthResult.getAuthenticationToken());
2144 logger.log(logger.EXTREME, "Leaving SyncRunner.synclocalLinkedNoteChanges");