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.EDAMErrorCode;
50 import com.evernote.edam.error.EDAMNotFoundException;
51 import com.evernote.edam.error.EDAMSystemException;
52 import com.evernote.edam.error.EDAMUserException;
53 import com.evernote.edam.notestore.NoteStore;
54 import com.evernote.edam.notestore.NoteStore.Client;
55 import com.evernote.edam.notestore.SyncChunk;
56 import com.evernote.edam.notestore.SyncState;
57 import com.evernote.edam.type.Data;
58 import com.evernote.edam.type.LinkedNotebook;
59 import com.evernote.edam.type.Note;
60 import com.evernote.edam.type.Notebook;
61 import com.evernote.edam.type.Resource;
62 import com.evernote.edam.type.SavedSearch;
63 import com.evernote.edam.type.SharedNotebook;
64 import com.evernote.edam.type.Tag;
65 import com.evernote.edam.type.User;
66 import com.evernote.edam.userstore.AuthenticationResult;
67 import com.evernote.edam.userstore.UserStore;
68 import com.evernote.thrift.TException;
69 import com.evernote.thrift.protocol.TBinaryProtocol;
70 import com.evernote.thrift.transport.THttpClient;
71 import com.evernote.thrift.transport.TTransportException;
72 import com.trolltech.qt.core.QByteArray;
73 import com.trolltech.qt.core.QFile;
74 import com.trolltech.qt.core.QIODevice.OpenModeFlag;
75 import com.trolltech.qt.core.QObject;
76 import com.trolltech.qt.core.QTextCodec;
77 import com.trolltech.qt.gui.QMessageBox;
79 import cx.fbn.nevernote.signals.LimitSignal;
80 import cx.fbn.nevernote.signals.NoteIndexSignal;
81 import cx.fbn.nevernote.signals.NoteResourceSignal;
82 import cx.fbn.nevernote.signals.NoteSignal;
83 import cx.fbn.nevernote.signals.NotebookSignal;
84 import cx.fbn.nevernote.signals.SavedSearchSignal;
85 import cx.fbn.nevernote.signals.StatusSignal;
86 import cx.fbn.nevernote.signals.SyncSignal;
87 import cx.fbn.nevernote.signals.TagSignal;
88 import cx.fbn.nevernote.sql.DatabaseConnection;
89 import cx.fbn.nevernote.sql.DeletedItemRecord;
90 import cx.fbn.nevernote.utilities.ApplicationLogger;
91 //import org.apache.thrift.transport.THttpClient;
92 //import org.apache.thrift.transport.TTransportException;
93 //import org.apache.thrift.protocol.TBinaryProtocol;
94 //import org.apache.thrift.TException;
96 public class SyncRunner extends QObject implements Runnable {
98 private final ApplicationLogger logger;
99 private DatabaseConnection conn;
100 private boolean idle;
101 public boolean error;
102 public volatile List<String> errorSharedNotebooks;
103 public volatile HashMap<String,String> errorSharedNotebooksIgnored;
104 public volatile boolean isConnected;
105 public volatile boolean keepRunning;
106 public volatile String authToken;
107 private long evernoteUpdateCount;
108 private final String userAgent = "NeighborNote/" + System.getProperty("os.name")
109 +"/"+System.getProperty("java.vendor") + "/"
110 + System.getProperty("java.version") +";";
112 public volatile NoteStore.Client localNoteStore;
113 private UserStore.Client userStore;
115 public volatile StatusSignal status;
116 public volatile TagSignal tagSignal;
117 public volatile NotebookSignal notebookSignal;
118 public volatile NoteIndexSignal noteIndexSignal;
119 public volatile NoteSignal noteSignal;
120 public volatile SavedSearchSignal searchSignal;
121 public volatile NoteResourceSignal resourceSignal;
122 public volatile SyncSignal syncSignal;
123 public volatile LimitSignal limitSignal;
124 public volatile boolean authRefreshNeeded;
125 public volatile boolean syncNeeded;
126 public volatile boolean disableUploads;
127 public volatile boolean syncDeletedContent;
128 private volatile List<String> dirtyNoteGuids;
130 public volatile String username = "";
131 public volatile String password = "";
132 public volatile String userStoreUrl;
133 public String noteStoreUrlBase;
134 private THttpClient userStoreTrans;
135 private TBinaryProtocol userStoreProt;
136 //private AuthenticationResult authResult;
137 private AuthenticationResult linkedAuthResult;
139 // private long authTimeRemaining;
140 public long authRefreshTime;
141 public long failedRefreshes = 0;
142 public THttpClient noteStoreTrans;
143 public TBinaryProtocol noteStoreProt;
144 public String noteStoreUrl;
145 public long sequenceDate;
146 public int updateSequenceNumber;
147 private boolean refreshNeeded;
148 private volatile LinkedBlockingQueue<String> workQueue;
149 private static int MAX_QUEUED_WAITING = 1000;
159 private final TreeSet<String> ignoreTags;
160 private final TreeSet<String> ignoreNotebooks;
161 private final TreeSet<String> ignoreLinkedNotebooks;
162 private HashMap<String,String> badTagSync;
165 // ICHANGED String bを追加
166 public SyncRunner(String logname, String u, String i, String r, String b, String uid, String pswd, String cpswd) {
167 logger = new ApplicationLogger(logname);
169 noteSignal = new NoteSignal();
170 status = new StatusSignal();
171 tagSignal = new TagSignal();
172 notebookSignal = new NotebookSignal();
173 noteIndexSignal = new NoteIndexSignal();
174 noteSignal = new NoteSignal();
175 searchSignal = new SavedSearchSignal();
176 syncSignal = new SyncSignal();
177 resourceSignal = new NoteResourceSignal();
178 limitSignal = new LimitSignal();
188 // this.setAutoDelete(false);
192 authRefreshNeeded = false;
195 disableUploads = false;
196 ignoreTags = new TreeSet<String>();
197 ignoreNotebooks = new TreeSet<String>();
198 ignoreLinkedNotebooks = new TreeSet<String>();
200 // setAutoDelete(false);
201 workQueue=new LinkedBlockingQueue<String>(MAX_QUEUED_WAITING);
205 errorSharedNotebooks = new ArrayList<String>();
206 errorSharedNotebooksIgnored = new HashMap<String,String>();
208 logger.log(logger.EXTREME, "Starting thread");
209 // ICHANGED behaviorUrlを追加
210 conn = new DatabaseConnection(logger, dburl, indexUrl, resourceUrl, behaviorUrl, dbuid, dbpswd, dbcpswd, 200);
212 logger.log(logger.EXTREME, "Blocking until work is found");
213 String work = workQueue.take();
214 logger.log(logger.LOW, "Dirty Notes Before Sync: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
215 logger.log(logger.EXTREME, "Work found: " +work);
216 if (work.equalsIgnoreCase("stop")) {
220 conn.getNoteTable().dumpDirtyNotes(); // Debugging statement
224 logger.log(logger.EXTREME, "SyncNeeded is true");
226 sequenceDate = conn.getSyncTable().getLastSequenceDate();
227 updateSequenceNumber = conn.getSyncTable().getUpdateSequenceNumber();
229 logger.log(logger.EXTREME, "Beginning sync");
230 evernoteSync(localNoteStore);
231 logger.log(logger.EXTREME, "Sync finished");
232 } catch (UnknownHostException e) {
233 status.message.emit(e.getMessage());
237 logger.log(logger.EXTREME, "Signaling refresh finished. refreshNeeded=" +refreshNeeded);
238 syncSignal.finished.emit(refreshNeeded);
240 syncSignal.errorDisconnect.emit();
241 status.message.emit(tr("Error synchronizing - see log for details."));
243 logger.log(logger.LOW, "Dirty Notes After Sync: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
244 conn.getNoteTable().dumpDirtyNotes();
245 logger.log(logger.LOW, "---");
248 catch (InterruptedException e1) {
249 e1.printStackTrace();
255 public DatabaseConnection getConnection() {
259 public boolean isIdle() {
264 public void setConnected(boolean c) {
267 public void setKeepRunning(boolean r) {
268 logger.log(logger.EXTREME, "Setting keepRunning=" +r);
271 public void setNoteStore(NoteStore.Client c) {
272 logger.log(logger.EXTREME, "Setting NoteStore in sync thread");
275 public void setUserStore(UserStore.Client c) {
276 logger.log(logger.EXTREME, "Setting UserStore in sync thread");
280 public void setEvernoteUpdateCount(long s) {
281 logger.log(logger.EXTREME, "Setting Update Count in sync thread");
282 evernoteUpdateCount = s;
285 //***************************************************************
286 //***************************************************************
287 //** These functions deal with Evernote communications
288 //***************************************************************
289 //***************************************************************
290 // Synchronize changes with Evernote
291 @SuppressWarnings("unused")
292 private void evernoteSync(Client noteStore) throws java.net.UnknownHostException {
293 logger.log(logger.HIGH, "Entering SyncRunner.evernoteSync");
295 // Rebuild list of tags & notebooks to ignore
296 ignoreNotebooks.clear();
297 List<String> ignore = conn.getSyncTable().getIgnoreRecords("NOTEBOOK");
298 for (int i=0; i<ignore.size(); i++)
299 ignoreNotebooks.add(ignore.get(i));
302 ignore = conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK");
303 for (int i=0; i<ignore.size(); i++)
304 ignoreLinkedNotebooks.add(ignore.get(i));
307 ignore = conn.getSyncTable().getIgnoreRecords("TAG");
308 for (int i=0; i<ignore.size(); i++)
309 ignoreTags.add(ignore.get(i));
311 // Make sure we are connected & should keep running
312 if (isConnected && keepRunning) {
314 logger.log(logger.EXTREME, "Synchronizing with Evernote");
315 status.message.emit(tr("Synchronizing with Evernote"));
317 // Get user information
319 logger.log(logger.EXTREME, "getting user from userstore");
320 User user = userStore.getUser(authToken);
321 logger.log(logger.EXTREME, "Saving user information");
322 syncSignal.saveUserInformation.emit(user);
323 } catch (EDAMUserException e1) {
324 e1.printStackTrace();
325 status.message.emit(tr("User exception getting user account information. Aborting sync and disconnecting"));
326 syncSignal.errorDisconnect.emit();
330 } catch (EDAMSystemException e1) {
331 if (e1.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
332 limitSignal.rateLimitReached.emit(e1.getRateLimitDuration());
335 e1.printStackTrace();
336 status.message.emit(tr("System error user account information. Aborting sync and disconnecting!"));
337 syncSignal.errorDisconnect.emit();
341 } catch (TException e1) {
342 e1.printStackTrace();
343 syncSignal.errorDisconnect.emit();
345 status.message.emit(tr("Transaction error getting user account information. Aborting sync and disconnecting!"));
351 SyncState syncState = null;
353 logger.log(logger.EXTREME, "Getting sync state");
354 syncState = noteStore.getSyncState(authToken);
355 syncSignal.saveUploadAmount.emit(syncState.getUploaded());
356 syncSignal.saveEvernoteUpdateCount.emit(syncState.getUpdateCount());
357 evernoteUpdateCount = syncState.getUpdateCount();
358 } catch (EDAMUserException e) {
360 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
361 syncSignal.errorDisconnect.emit();
364 } catch (EDAMSystemException e) {
365 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
366 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
369 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
370 syncSignal.errorDisconnect.emit();
373 } catch (TException e) {
375 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
376 syncSignal.errorDisconnect.emit();
381 if (syncState == null) {
382 logger.log(logger.EXTREME, "Sync State is null");
383 status.message.emit(tr("Syncronization Error!"));
387 // Determine what to do.
388 // If we need to do a full sync.
389 logger.log(logger.LOW, "Full Sequence Before: " +syncState.getFullSyncBefore());
390 logger.log(logger.LOW, "Last Sequence Date: " +sequenceDate);
391 logger.log(logger.LOW, "Var Last Sequence Number: " +updateSequenceNumber);
392 logger.log(logger.LOW, "DB Last Sequence Number: " + conn.getSyncTable().getUpdateSequenceNumber());
393 if (syncState.getFullSyncBefore() > sequenceDate) {
394 logger.log(logger.EXTREME, "Full sequence date has expired");
396 conn.getSyncTable().setLastSequenceDate(0);
397 updateSequenceNumber = 0;
398 conn.getSyncTable().setUpdateSequenceNumber(0);
400 // Check for "special" sync instructions
401 String syncLinked = conn.getSyncTable().getRecord("FullLinkedNotebookSync");
402 String syncShared = conn.getSyncTable().getRecord("FullSharedNotebookSync");
403 String syncNotebooks = conn.getSyncTable().getRecord("FullNotebookSync");
404 String syncInkNoteImages = conn.getSyncTable().getRecord("FullInkNoteImageSync");
405 if (syncLinked != null) {
406 downloadAllLinkedNotebooks(localNoteStore);
408 if (syncShared != null) {
409 downloadAllSharedNotebooks(localNoteStore);
411 if (syncNotebooks != null) {
412 downloadAllNotebooks(localNoteStore);
415 if (syncInkNoteImages != null) {
416 List<String> guids = conn.getNoteTable().noteResourceTable.findInkNotes();
417 for (int i=0; i<guids.size(); i++) {
418 downloadInkNoteImage(guids.get(i), authToken);
420 conn.getSyncTable().deleteRecord("FullInkNoteImageSync");
423 // If there are remote changes
424 logger.log(logger.LOW, "Update Count: " +syncState.getUpdateCount());
425 logger.log(logger.LOW, "Last Update Count: " +updateSequenceNumber);
427 if (syncState.getUpdateCount() > updateSequenceNumber) {
428 logger.log(logger.EXTREME, "Refresh needed is true");
429 refreshNeeded = true;
430 logger.log(logger.EXTREME, "Downloading changes");
431 syncRemoteToLocal(localNoteStore);
434 //*****************************************
435 //* Sync linked/shared notebooks
436 //*****************************************
437 //syncLinkedNotebooks();
438 //conn.getNoteTable().getDirty();
439 //disableUploads = true; /// DELETE THIS LINE!!!!
440 if (!disableUploads) {
441 logger.log(logger.EXTREME, "Uploading changes");
442 // Synchronize remote changes
444 syncExpunged(localNoteStore);
446 syncLocalTags(localNoteStore);
448 syncLocalNotebooks(localNoteStore);
450 syncLocalLinkedNotebooks(localNoteStore);
452 syncDeletedNotes(localNoteStore);
456 syncLocalSavedSearches(localNoteStore);
459 status.message.emit(tr("Cleaning up"));
460 List<String> notes = conn.getNoteTable().expungeIgnoreSynchronizedNotes(conn.getSyncTable().getIgnoreRecords("NOTEBOOK"),
461 conn.getSyncTable().getIgnoreRecords("TAG"), conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK"));
462 if (notes.size() > 0)
463 syncSignal.refreshLists.emit();
465 //*****************************************
466 //* End of synchronization
467 //*****************************************
469 syncSignal.refreshLists.emit();
472 logger.log(logger.LOW, "Sync completed. Errors=" +error);
474 status.message.emit(tr("Synchronizing complete"));
476 status.message.emit(tr("Download syncronization complete. Uploads have been disabled."));
478 logger.log(logger.EXTREME, "Saving sync time");
479 if (syncState.getCurrentTime() > sequenceDate)
480 sequenceDate = syncState.getCurrentTime();
481 if (syncState.getUpdateCount() > updateSequenceNumber)
482 updateSequenceNumber = syncState.getUpdateCount();
483 conn.getSyncTable().setLastSequenceDate(sequenceDate);
484 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
487 logger.log(logger.HIGH, "Leaving SyncRunner.evernoteSync");
490 // Sync deleted items with Evernote
491 private void syncExpunged(Client noteStore) {
492 logger.log(logger.HIGH, "Entering SyncRunner.syncExpunged");
494 List<DeletedItemRecord> expunged = conn.getDeletedTable().getAllDeleted();
495 boolean error = false;
496 for (int i=0; i<expunged.size() && keepRunning; i++) {
498 // if (authRefreshNeeded)
499 // if (!refreshConnection())
503 if (expunged.get(i).type.equalsIgnoreCase("TAG")) {
504 logger.log(logger.EXTREME, "Tag expunged");
505 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "TAG");
\r
506 updateSequenceNumber = noteStore.expungeTag(authToken, expunged.get(i).guid);
507 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
508 conn.getSyncTable().setLastSequenceDate(sequenceDate);
509 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
511 if (expunged.get(i).type.equalsIgnoreCase("NOTEBOOK")) {
512 logger.log(logger.EXTREME, "Notebook expunged");
513 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "NOTEBOOK");
\r
514 updateSequenceNumber = noteStore.expungeNotebook(authToken, expunged.get(i).guid);
515 conn.getSyncTable().setLastSequenceDate(sequenceDate);
516 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
518 if (expunged.get(i).type.equalsIgnoreCase("NOTE")) {
519 logger.log(logger.EXTREME, "Note expunged");
520 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "NOTE");
\r
521 updateSequenceNumber = noteStore.deleteNote(authToken, expunged.get(i).guid);
522 refreshNeeded = true;
523 conn.getSyncTable().setLastSequenceDate(sequenceDate);
524 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
526 if (expunged.get(i).type.equalsIgnoreCase("SAVEDSEARCH")) {
527 logger.log(logger.EXTREME, "saved search expunged");
528 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "SAVEDSEARCH");
\r
529 updateSequenceNumber = noteStore.expungeSearch(authToken, expunged.get(i).guid);
530 conn.getSyncTable().setLastSequenceDate(sequenceDate);
531 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
533 } catch (EDAMUserException e) {
534 logger.log(logger.LOW, "EDAM User Excepton in syncExpunged: " +expunged.get(i).guid); // This can happen if we try to delete a deleted note
535 } catch (EDAMSystemException e) {
536 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
537 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
539 logger.log(logger.LOW, "EDAM System Excepton in syncExpunged: "+expunged.get(i).guid);
540 logger.log(logger.LOW, e.getStackTrace());
542 } catch (EDAMNotFoundException e) {
543 logger.log(logger.LOW, "EDAM Not Found Excepton in syncExpunged: "+expunged.get(i).guid);
544 } catch (TException e) {
545 logger.log(logger.LOW, "EDAM TExcepton in syncExpunged: "+expunged.get(i).guid);
546 logger.log(logger.LOW, e.getStackTrace());
551 conn.getDeletedTable().expungeAllDeletedRecords();
553 logger.log(logger.HIGH, "Leaving SyncRunner.syncExpunged");
556 private void syncDeletedNotes(Client noteStore) {
557 if (syncDeletedContent)
559 logger.log(logger.HIGH, "Entering SyncRunner.syncDeletedNotes");
560 status.message.emit(tr("Synchronizing deleted notes."));
562 List<Note> notes = conn.getNoteTable().getDirty();
563 // Sync the local notebooks with Evernote's
564 for (int i=0; i<notes.size() && keepRunning; i++) {
566 // if (authRefreshNeeded)
567 // if (!refreshConnection())
570 Note enNote = notes.get(i);
572 if (enNote.getUpdateSequenceNum() > 0 && (enNote.isActive() == false || enNote.getDeleted() > 0)) {
573 // Check that the note is valid.
\r
574 if (enNote.isActive() == true || enNote.getDeleted() == 0) {
\r
575 conn.getNoteTable().deleteNote(enNote.getGuid());
\r
576 enNote = conn.getNoteTable().getNote(enNote.getGuid(), false, false, false, false, false);
\r
578 if (syncDeletedContent) {
579 logger.log(logger.EXTREME, "Deleted note found & synch content selected");
580 Note delNote = conn.getNoteTable().getNote(enNote.getGuid(), true, true, true, true, true);
581 delNote = getNoteContent(delNote);
582 delNote = noteStore.updateNote(authToken, delNote);
583 enNote.setUpdateSequenceNum(delNote.getUpdateSequenceNum());
584 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
586 logger.log(logger.EXTREME, "Deleted note found & sync content not selected");
587 int usn = noteStore.deleteNote(authToken, enNote.getGuid());
588 enNote.setUpdateSequenceNum(usn);
589 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
591 logger.log(logger.EXTREME, "Resetting deleted dirty flag");
592 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());
593 updateSequenceNumber = enNote.getUpdateSequenceNum();
594 logger.log(logger.EXTREME, "Saving sequence number");
595 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
597 } catch (EDAMUserException e) {
598 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);
\r
599 //status.message.emit("Error sending local note: " +e.getParameter());
600 //logger.log(logger.LOW, e.toString());
602 } catch (EDAMSystemException e) {
603 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
604 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
606 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);
607 status.message.emit(tr("Error: ") +e);
608 logger.log(logger.LOW, e.toString());
610 } catch (EDAMNotFoundException e) {
611 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);
\r
612 //status.message.emit("Error deleting local note: " +e +" - Continuing");
613 //logger.log(logger.LOW, e.toString());
615 } catch (TException e) {
616 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);
617 status.message.emit(tr("Error sending local note: ") +e);
618 logger.log(logger.LOW, e.toString());
623 // Sync notes with Evernote
624 private void syncLocalNotes() {
625 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");
626 logger.log(logger.LOW, "Dirty local notes found: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
627 status.message.emit(tr("Sending local notes."));
629 List<Note> notes = conn.getNoteTable().getDirty();
630 // Sync the local notebooks with Evernote's
631 for (int i=0; i<notes.size() && keepRunning; i++) {
632 syncLocalNote(localNoteStore, notes.get(i), authToken);
634 logger.log(logger.HIGH, "Leaving SyncRunner.syncNotes");
637 // Sync notes with Evernote
638 private void syncLocalNote(Client noteStore, Note enNote, String token) {
639 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");
640 status.message.emit(tr("Sending local notes."));
642 if (enNote.isActive()) {
644 if (enNote.getUpdateSequenceNum() > 0) {
645 logger.log(logger.EXTREME, "Active dirty note found - non new - " +enNote.getGuid());
646 logger.log(logger.EXTREME, "Fetching note content");
647 enNote = getNoteContent(enNote);
648 logger.log(logger.MEDIUM, "Updating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
649 enNote = noteStore.updateNote(token, enNote);
651 logger.log(logger.EXTREME, "Active dirty found - new note " +enNote.getGuid());
652 String oldGuid = enNote.getGuid();
653 logger.log(logger.MEDIUM, "Fetching note content");
654 enNote = getNoteContent(enNote);
655 logger.log(logger.MEDIUM, "Creating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
656 enNote = noteStore.createNote(token, enNote);
657 logger.log(logger.MEDIUM, "New note Guid : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
658 noteSignal.guidChanged.emit(oldGuid, enNote.getGuid());
659 conn.getNoteTable().updateNoteGuid(oldGuid, enNote.getGuid());
661 updateSequenceNumber = enNote.getUpdateSequenceNum();
662 logger.log(logger.EXTREME, "Saving note");
663 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
664 List<Resource> rl = enNote.getResources();
665 logger.log(logger.EXTREME, "Getting note resources");
666 for (int j=0; j<enNote.getResourcesSize() && keepRunning; j++) {
667 Resource newRes = rl.get(j);
668 Data d = newRes.getData();
670 logger.log(logger.EXTREME, "Calculating resource hash");
671 String hash = byteArrayToHexString(d.getBodyHash());
672 logger.log(logger.EXTREME, "updating resources by hash");
673 String oldGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(enNote.getGuid(), hash);
674 conn.getNoteTable().updateNoteResourceGuidbyHash(enNote.getGuid(), newRes.getGuid(), hash);
675 resourceSignal.resourceGuidChanged.emit(enNote.getGuid(), oldGuid, newRes.getGuid());
678 logger.log(logger.EXTREME, "Resetting note dirty flag");
679 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());
680 updateSequenceNumber = enNote.getUpdateSequenceNum();
681 logger.log(logger.EXTREME, "Emitting note sequence number change");
682 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
684 } catch (EDAMUserException e) {
685 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);
686 status.message.emit(tr("Error sending local note: ") +e.getParameter());
687 logger.log(logger.LOW, e.toString());
689 } catch (EDAMSystemException e) {
690 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
691 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
693 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);
694 status.message.emit(tr("Error: ") +e);
695 logger.log(logger.LOW, e.toString());
697 } catch (EDAMNotFoundException e) {
698 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);
699 status.message.emit(tr("Error sending local note: ") +e);
700 logger.log(logger.LOW, e.toString());
702 } catch (TException e) {
703 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);
704 status.message.emit(tr("Error sending local note: ") +e);
705 logger.log(logger.LOW, e.toString());
709 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNote");
713 // Sync Notebooks with Evernote
714 private void syncLocalNotebooks(Client noteStore) {
715 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalNotebooks");
717 status.message.emit(tr("Sending local notebooks."));
718 List<Notebook> remoteList = new ArrayList<Notebook>();
720 logger.log(logger.EXTREME, "Getting remote notebooks to compare with local");
721 remoteList = noteStore.listNotebooks(authToken);
722 } catch (EDAMUserException e1) {
723 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks getting remote Notebook List");
724 status.message.emit(tr("Error: ") +e1);
725 logger.log(logger.LOW, e1.toString());
727 } catch (EDAMSystemException e1) {
728 if (e1.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
729 limitSignal.rateLimitReached.emit(e1.getRateLimitDuration());
731 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks getting remote Notebook List");
732 status.message.emit(tr("Error: ") +e1);
733 logger.log(logger.LOW, e1.toString());
735 } catch (TException e1) {
736 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalNotebooks getting remote Notebook List");
737 status.message.emit(tr("Error: ") +e1);
738 logger.log(logger.LOW, e1.toString());
741 logger.log(logger.EXTREME, "Getting local dirty notebooks");
742 List<Notebook> notebooks = conn.getNotebookTable().getDirty();
744 // Sync the local notebooks with Evernote's
745 for (int i=0; i<notebooks.size() && keepRunning; i++) {
747 // if (authRefreshNeeded)
748 // if (!refreshConnection())
751 Notebook enNotebook = notebooks.get(i);
753 if (enNotebook.getUpdateSequenceNum() > 0) {
754 logger.log(logger.EXTREME, "Existing notebook is dirty");
755 sequence = noteStore.updateNotebook(authToken, enNotebook);
757 logger.log(logger.EXTREME, "New dirty notebook found");
758 String oldGuid = enNotebook.getGuid();
759 boolean found = false;
761 // Look for a notebook with the same name. If one is found, we don't need
762 // to create another one
763 logger.log(logger.EXTREME, "Looking for matching notebook name");
764 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
765 if (remoteList.get(k).getName().equalsIgnoreCase(enNotebook.getName())) {
766 enNotebook = remoteList.get(k);
767 logger.log(logger.EXTREME, "Matching notebook found");
772 enNotebook = noteStore.createNotebook(authToken, enNotebook);
774 logger.log(logger.EXTREME, "Updating notebook in database");
775 conn.getNotebookTable().updateNotebookGuid(oldGuid, enNotebook.getGuid());
776 sequence = enNotebook.getUpdateSequenceNum();
778 logger.log(logger.EXTREME, "Updating notebook sequence in database");
779 conn.getNotebookTable().updateNotebookSequence(enNotebook.getGuid(), sequence);
780 logger.log(logger.EXTREME, "Resetting dirty flag in notebook");
781 conn.getNotebookTable().resetDirtyFlag(enNotebook.getGuid());
782 updateSequenceNumber = sequence;
783 logger.log(logger.EXTREME, "Emitting sequence number to main thread");
784 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
785 } catch (EDAMUserException e) {
786 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks");
787 logger.log(logger.LOW, e.toString() + ": Stack : " +enNotebook.getStack());
789 } catch (EDAMSystemException e) {
790 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
791 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
793 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks");
794 logger.log(logger.LOW, e.toString());
796 } catch (EDAMNotFoundException e) {
797 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotebooks");
798 logger.log(logger.LOW, e.toString());
800 } catch (TException e) {
801 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotebooks");
802 logger.log(logger.LOW, e.toString());
806 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNotebooks");
809 // Sync Tags with Evernote
810 private void syncLocalTags(Client noteStore) {
811 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalTags");
812 List<Tag> remoteList = new ArrayList<Tag>();
813 status.message.emit(tr("Sending local tags."));
816 logger.log(logger.EXTREME, "Getting remote tags to compare names with the local tags");
817 remoteList = noteStore.listTags(authToken);
818 } catch (EDAMUserException e1) {
819 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote Tag List");
820 status.message.emit(tr("Error: ") +e1);
821 logger.log(logger.LOW, e1.toString());
823 } catch (EDAMSystemException e1) {
824 if (e1.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
825 limitSignal.rateLimitReached.emit(e1.getRateLimitDuration());
827 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote Tag List");
828 status.message.emit(tr("Error: ") +e1);
829 logger.log(logger.LOW, e1.toString());
831 } catch (TException e1) {
832 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote Tag List");
833 status.message.emit(tr("Error: ") +e1);
834 logger.log(logger.LOW, e1.toString());
840 if (badTagSync == null)
841 badTagSync = new HashMap<String,String>();
845 Tag enTag = findNextTag();
847 // This is a hack. Sometimes this function goes flookey and goes into a
848 // perpetual loop. This causes NeverNote to flood Evernote's servers.
849 // This is a safety valve to prevent unlimited loops.
850 int maxCount = conn.getTagTable().getDirty().size()+10;
853 while(enTag!=null && loopCount < maxCount) {
855 // if (authRefreshNeeded)
856 // if (!refreshConnection())
860 if (enTag.getUpdateSequenceNum() > 0) {
861 logger.log(logger.EXTREME, "Updating tag");
862 sequence = noteStore.updateTag(authToken, enTag);
865 // Look for a tag with the same name. If one is found, we don't need
866 // to create another one
867 logger.log(logger.EXTREME, "New tag. Comparing with remote names");
868 boolean found = false;
869 String oldGuid = enTag.getGuid();
870 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
871 if (remoteList.get(k).getName().equalsIgnoreCase(enTag.getName())) {
872 conn.getTagTable().updateTagGuid(enTag.getGuid(), remoteList.get(k).getGuid());
873 enTag = remoteList.get(k);
874 logger.log(logger.EXTREME, "Matching tag name found");
879 enTag = noteStore.createTag(authToken, enTag);
881 enTag.setUpdateSequenceNum(noteStore.updateTag(authToken,enTag));
882 sequence = enTag.getUpdateSequenceNum();
883 if (!oldGuid.equals(enTag.getGuid())) {
884 logger.log(logger.EXTREME, "Updating tag guid");
885 conn.getTagTable().updateTagGuid(oldGuid, enTag.getGuid());
888 logger.log(logger.EXTREME, "Updating tag sequence number");
889 conn.getTagTable().updateTagSequence(enTag.getGuid(), sequence);
890 logger.log(logger.EXTREME, "Resetting tag dirty flag");
891 conn.getTagTable().resetDirtyFlag(enTag.getGuid());
892 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");
893 updateSequenceNumber = sequence;
894 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
895 } catch (EDAMUserException e) {
896 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags: " +enTag.getName());
897 logger.log(logger.LOW, e.toString());
898 badTagSync.put(enTag.getGuid(),null);
900 } catch (EDAMSystemException e) {
901 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
902 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
904 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags: " +enTag.getName());
905 logger.log(logger.LOW, e.toString());
906 badTagSync.put(enTag.getGuid(),null);
908 } catch (EDAMNotFoundException e) {
909 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags: " +enTag.getName());
910 logger.log(logger.LOW, e.toString());
911 badTagSync.put(enTag.getGuid(),null);
913 } catch (TException e) {
914 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags: " +enTag.getName());
915 logger.log(logger.LOW, e.toString());
916 badTagSync.put(enTag.getGuid(),null);
921 logger.log(logger.EXTREME, "Finding next tag");
922 enTag = findNextTag();
924 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalTags");
926 private void syncLocalLinkedNotebooks(Client noteStore) {
927 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalLinkedNotebooks");
929 List<String> list = conn.getLinkedNotebookTable().getDirtyGuids();
930 for (int i=0; i<list.size(); i++) {
931 LinkedNotebook book = conn.getLinkedNotebookTable().getNotebook(list.get(i));
933 noteStore.updateLinkedNotebook(authToken, book);
934 } catch (EDAMUserException e) {
935 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalLinkedNotebooks");
936 status.message.emit(tr("Error: ") +e);
937 logger.log(logger.LOW, e.toString());
940 } catch (EDAMNotFoundException e) {
941 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalLinkedNotebooks");
942 status.message.emit(tr("Error: ") +e);
943 logger.log(logger.LOW, e.toString());
946 } catch (EDAMSystemException e) {
947 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
948 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
950 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalLinkedNotebooks");
951 status.message.emit(tr("Error: ") +e);
952 logger.log(logger.LOW, e.toString());
955 } catch (TException e) {
956 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalLinkedNotebooks");
957 status.message.emit(tr("Error: ") +e);
958 logger.log(logger.LOW, e.toString());
963 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalLinkedNotebooks");
965 // Sync Saved Searches with Evernote
966 private void syncLocalSavedSearches(Client noteStore) {
967 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");
968 List<SavedSearch> remoteList = new ArrayList<SavedSearch>();
969 status.message.emit(tr("Sending saved searches."));
971 logger.log(logger.EXTREME, "Getting saved searches to compare with local");
973 remoteList = noteStore.listSearches(authToken);
974 } catch (EDAMUserException e1) {
975 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote saved search List");
976 status.message.emit(tr("Error: ") +e1);
977 logger.log(logger.LOW, e1.toString());
979 } catch (EDAMSystemException e1) {
980 if (e1.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
981 limitSignal.rateLimitReached.emit(e1.getRateLimitDuration());
983 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote saved search List");
984 status.message.emit(tr("Error: ") +e1);
985 logger.log(logger.LOW, e1.toString());
987 } catch (TException e1) {
988 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote saved search List");
989 status.message.emit(tr("Error: ") +e1);
990 logger.log(logger.LOW, e1.toString());
994 List<SavedSearch> searches = conn.getSavedSearchTable().getDirty();
996 // Sync the local notebooks with Evernote's
997 logger.log(logger.EXTREME, "Beginning to send saved searches");
998 for (int i=0; i<searches.size() && keepRunning; i++) {
1000 // if (authRefreshNeeded)
1001 // if (!refreshConnection())
1004 SavedSearch enSearch = searches.get(i);
1006 if (enSearch.getUpdateSequenceNum() > 0)
1007 sequence = noteStore.updateSearch(authToken, enSearch);
1009 logger.log(logger.EXTREME, "New saved search found.");
1010 // Look for a tag with the same name. If one is found, we don't need
1011 // to create another one
1012 boolean found = false;
1013 logger.log(logger.EXTREME, "Matching remote saved search names with local");
1014 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
1015 if (remoteList.get(k).getName().equalsIgnoreCase(enSearch.getName())) {
1016 enSearch = remoteList.get(k);
1018 logger.log(logger.EXTREME, "Matching saved search found");
1019 sequence = enSearch.getUpdateSequenceNum();
1023 String oldGuid = enSearch.getGuid();
1025 enSearch = noteStore.createSearch(authToken, enSearch);
1026 sequence = enSearch.getUpdateSequenceNum();
1027 logger.log(logger.EXTREME, "Updating tag guid in local database");
1028 conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, enSearch.getGuid());
1030 logger.log(logger.EXTREME, "Updating tag sequence in local database");
1031 conn.getSavedSearchTable().updateSavedSearchSequence(enSearch.getGuid(), sequence);
1032 logger.log(logger.EXTREME, "Resetting tag dirty flag");
1033 conn.getSavedSearchTable().resetDirtyFlag(enSearch.getGuid());
1034 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");
1035 updateSequenceNumber = sequence;
1036 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
1037 } catch (EDAMUserException e) {
1038 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");
1039 logger.log(logger.LOW, e.toString());
1041 } catch (EDAMSystemException e) {
1042 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1043 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
1045 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");
1046 logger.log(logger.LOW, e.toString());
1048 } catch (EDAMNotFoundException e) {
1049 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");
1050 logger.log(logger.LOW, e.toString());
1052 } catch (TException e) {
1053 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");
1054 logger.log(logger.LOW, e.toString());
1059 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");
1062 // Sync evernote changes with local database
1063 private void syncRemoteToLocal(Client noteStore) {
1064 logger.log(logger.HIGH, "Entering SyncRunner.syncRemoteToLocal");
1066 List<Note> dirtyNotes = conn.getNoteTable().getDirty();
1067 dirtyNoteGuids = new ArrayList<String>();
\r
1068 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {
1069 dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());
1073 SyncChunk chunk = null;
1074 boolean fullSync = false;
1075 boolean more = true;
1077 if (updateSequenceNumber == 0)
1080 status.message.emit(tr("Downloading 0% complete."));
1082 while(more && keepRunning) {
1084 // if (authRefreshNeeded)
1085 // if (!refreshConnection())
1088 int sequence = updateSequenceNumber;
1090 // conn.beginTransaction();
1091 logger.log(logger.EXTREME, "Getting chunk from Evernote");
1092 chunk = noteStore.getSyncChunk(authToken, sequence, chunkSize, fullSync);
1093 logger.log(logger.LOW, "Chunk High Sequence: " +chunk.getChunkHighUSN());
1094 } catch (EDAMUserException e) {
1096 e.printStackTrace();
1097 status.message.emit(e.getMessage());
1098 } catch (EDAMSystemException e) {
1099 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1100 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
1103 e.printStackTrace();
1104 status.message.emit(e.getMessage());
1105 } catch (TException e) {
1107 e.printStackTrace();
1108 status.message.emit(e.getMessage());
1110 if (error || chunk == null)
1115 syncRemoteTags(chunk.getTags());
1116 syncRemoteSavedSearches(chunk.getSearches());
1117 syncRemoteNotebooks(chunk.getNotebooks());
1118 syncRemoteNotes(noteStore, chunk.getNotes(), fullSync, authToken);
1119 syncRemoteResources(noteStore, chunk.getResources());
1120 syncRemoteLinkedNotebooks(chunk.getLinkedNotebooks());
1122 // Signal about any updated notes to invalidate the cache
1123 for (int i=0; i<chunk.getNotesSize(); i++)
1124 noteSignal.noteChanged.emit(chunk.getNotes().get(i).getGuid(), null);
1125 syncExpungedNotes(chunk);
1128 // Check for more notes
1129 if (chunk.getChunkHighUSN() <= updateSequenceNumber)
1133 logger.log(logger.EXTREME, "More notes? " +more);
1136 // Save the chunk sequence number
1137 if (!error && chunk.getChunkHighUSN() > 0 && keepRunning) {
1138 logger.log(logger.EXTREME, "emitting sequence number to main thread");
1139 updateSequenceNumber = chunk.getChunkHighUSN();
1140 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());
1141 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
1142 // conn.commitTransaction();
1147 long pct = chunk.getChunkHighUSN() * 100;
1148 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());
1149 pct = pct/evernoteUpdateCount;
1150 status.message.emit(tr("Downloading ") +new Long(pct).toString()+tr("% complete."));
1152 // conn.commitTransaction();
1154 logger.log(logger.HIGH, "Leaving SyncRunner.syncRemoteToLocal");
1156 // Sync expunged notes
1157 private void syncExpungedNotes(SyncChunk chunk) {
1158 // Do the local deletes
1159 logger.log(logger.EXTREME, "Doing local deletes");
1160 List<String> guid = chunk.getExpungedNotes();
1162 for (int i=0; i<guid.size() && keepRunning; i++) {
1163 String notebookGuid = "";
1164 Note localNote = conn.getNoteTable().getNote(guid.get(i), false, false, false, false, false);
1165 if (localNote != null) {
1166 conn.getNoteTable().updateNoteSequence(guid.get(i), 0);
1167 notebookGuid = localNote.getNotebookGuid();
1169 // If the note is in a local notebook (which means we moved it) or if the
\r
1170 // note returned is null (which means it is already deleted or flagged expunged)
\r
1172 if (!conn.getNotebookTable().isNotebookLocal(notebookGuid) || localNote == null) {
\r
1173 logger.log(logger.EXTREME, "Expunging local note from database");
1174 conn.getNoteTable().expungeNote(guid.get(i), true, false);
1178 guid = chunk.getExpungedNotebooks();
1180 for (int i=0; i<guid.size() && keepRunning; i++) {
1181 logger.log(logger.EXTREME, "Expunging local notebook from database");
1182 conn.getNotebookTable().expungeNotebook(guid.get(i), false);
1184 guid = chunk.getExpungedTags();
1186 for (int i=0; i<guid.size() && keepRunning; i++) {
1187 logger.log(logger.EXTREME, "Expunging tags from local database");
1188 conn.getTagTable().expungeTag(guid.get(i), false);
1190 guid = chunk.getExpungedSearches();
1192 for (int i=0; i<guid.size() && keepRunning; i++) {
1193 logger.log(logger.EXTREME, "Expunging saved search from local database");
1194 conn.getSavedSearchTable().expungeSavedSearch(guid.get(i), false);
1196 guid = chunk.getExpungedLinkedNotebooks();
1198 for (int i=0; i<guid.size() && keepRunning; i++) {
1199 logger.log(logger.EXTREME, "Expunging linked notebook from local database");
1200 conn.getLinkedNotebookTable().expungeNotebook(guid.get(i), false);
1205 private void syncRemoteTags(List<Tag> tags) {
1206 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");
1208 for (int i=0; i<tags.size() && keepRunning; i++) {
1210 oldGuid = conn.getTagTable().findTagByName(tags.get(i).getName());
1211 if (oldGuid != null && !tags.get(i).getGuid().equalsIgnoreCase(oldGuid))
1212 conn.getTagTable().updateTagGuid(oldGuid, tags.get(i).getGuid());
1213 conn.getTagTable().syncTag(tags.get(i), false);
1216 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");
1218 // Sync remote saved searches
1219 private void syncRemoteSavedSearches(List<SavedSearch> searches) {
1220 logger.log(logger.EXTREME, "Entering SyncRunner.syncSavedSearches");
1221 if (searches != null) {
1222 for (int i=0; i<searches.size() && keepRunning; i++) {
1224 oldGuid = conn.getSavedSearchTable().findSavedSearchByName(searches.get(i).getName());
1225 if (oldGuid != null && !searches.get(i).getGuid().equalsIgnoreCase(oldGuid))
1226 conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, searches.get(i).getGuid());
1227 conn.getSavedSearchTable().syncSavedSearch(searches.get(i), false);
1230 logger.log(logger.EXTREME, "Leaving SyncRunner.syncSavedSearches");
1232 // Sync remote linked notebooks
1233 private void syncRemoteLinkedNotebooks(List<LinkedNotebook> books) {
1234 logger.log(logger.EXTREME, "Entering SyncRunner.syncLinkedNotebooks");
1235 if (books != null) {
1236 for (int i=0; i<books.size() && keepRunning; i++) {
1237 conn.getLinkedNotebookTable().updateNotebook(books.get(i), false);
1240 logger.log(logger.EXTREME, "Leaving SyncRunner.syncLinkedNotebooks");
1242 // Sync remote Notebooks 2
1243 private void syncRemoteNotebooks(List<Notebook> notebooks) {
1244 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");
1245 if (notebooks != null) {
1246 for (int i=0; i<notebooks.size() && keepRunning; i++) {
1248 oldGuid = conn.getNotebookTable().findNotebookByName(notebooks.get(i).getName());
1249 if (oldGuid != null && !conn.getNotebookTable().isNotebookLocal(oldGuid) && !notebooks.get(i).getGuid().equalsIgnoreCase(oldGuid))
1250 conn.getNotebookTable().updateNotebookGuid(oldGuid, notebooks.get(i).getGuid());
1251 conn.getNotebookTable().syncNotebook(notebooks.get(i), false);
1253 // Synchronize shared notebook information
1254 // if (notebooks.get(i).getSharedNotebookIdsSize() > 0) {
1255 // conn.getSharedNotebookTable().expungeNotebookByGuid(notebooks.get(i).getGuid(), false);
1256 // for (int j=0; j<notebooks.get(i).getSharedNotebookIdsSize(); j++) {
1257 // syncRemoteSharedNotebook(notebooks.get(i).getGuid(), notebooks.get(i).getSharedNotebookIds().get(j), authToken);
1262 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");
1264 // Sync remote shared notebook
1265 // private void syncRemoteSharedNotebook(String guid, Long id, String token) {
1266 // List<SharedNotebook> books = noteStore.getSharedNotebookByAuth(authToken);
1268 // Sync remote Resources
1269 private void syncRemoteResources(Client noteStore, List<Resource> resource) {
1270 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteResources");
1271 if (resource != null) {
1272 for (int i=0; i<resource.size() && keepRunning; i++) {
1273 syncRemoteResource(noteStore, resource.get(i), authToken);
1276 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteResources");
1278 // Sync remote resource
1279 private void syncRemoteResource(Client noteStore, Resource resource, String authToken) {
1280 // This is how the logic for this works.
1281 // 1.) If the resource is not in the local database, we add it.
1282 // 2.) If a copy of the resource is in the local database and the note isn't dirty, we update the local copy
1283 // 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
1284 // is a conflict. The note conflict should get a copy of the resource at that time.
1286 Note n = conn.getNoteTable().getNote(resource.getNoteGuid(), false, false, false, false, false);
1288 logger.log(logger.HIGH, "Resource for note " +n.getGuid() +" : " +n.getTitle());
1290 boolean saveNeeded = false;
1291 /* #1 */ Resource r = getEvernoteResource(noteStore, resource.getGuid(), true,true,true, authToken);
1292 Resource l = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), false);
1294 logger.log(logger.HIGH, "Local resource not found");
1297 /* #2 */ boolean isNoteDirty = conn.getNoteTable().isNoteDirty(r.getNoteGuid());
1299 logger.log(logger.HIGH, "Local resource found, but is not dirty");
1302 /* #3 */ String remoteHash = "";
1303 if (r != null && r.getData() != null && r.getData().getBodyHash() != null)
1304 remoteHash = byteArrayToHexString(r.getData().getBodyHash());
1305 String localHash = "";
1306 if (l != null && l.getData() != null && l.getData().getBodyHash() != null)
1307 remoteHash = byteArrayToHexString(l.getData().getBodyHash());
1309 if (localHash.equalsIgnoreCase(remoteHash))
1314 logger.log(logger.HIGH, "Resource save needed: " +saveNeeded);
1316 conn.getNoteTable().noteResourceTable.updateNoteResource(r, false);
1317 if (r.getMime().equalsIgnoreCase("application/vnd.evernote.ink"))
1318 downloadInkNoteImage(r.getGuid(), authToken);
1322 // Sync remote notes
1323 private void syncRemoteNotes(Client noteStore, List<Note> note, boolean fullSync, String token) {
1327 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotes");
\r
1328 logger.log(logger.LOW, "Local Dirty Notes: ");
\r
1329 logger.log(logger.LOW, "Remote Dirty Notes:");
\r
1330 for (int i=0; i<note.size();i++) {
\r
1331 logger.log(logger.LOW, i +" : " +note.get(i).getGuid() + " : " +note.get(i).getTitle() );
\r
1333 logger.log(logger.LOW, "---");
\r
1335 for (int i=0; i<note.size() && keepRunning; i++) {
1336 Note n = getEvernoteNote(noteStore, note.get(i).getGuid(), true, fullSync, true,true, token);
1337 syncRemoteNote(n, fullSync, token);
1340 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotes");
1342 private void syncRemoteNote(Note n, boolean fullSync, String token) {
1345 // Basically, this is how the sync logic for a note works.
1346 // If the remote note has changed and the local has not, we
1347 // accept the change.
1348 // If both the local & remote have changed but the sequence
1349 // numbers are the same, we don't accept the change. This
1350 // seems to happen when attachments are indexed by the server.
1351 // If both the local & remote have changed and the sequence numbers
1352 // are different we move the local copy to a local notebook (making sure
1353 // to copy all resources) and we accept the new one.
1354 boolean conflictingNote = true;
1355 logger.log(logger.EXTREME, "Checking for duplicate note " +n.getGuid() +" : " +n.getTitle());
\r
1356 if (dirtyNoteGuids != null && dirtyNoteGuids.contains(n.getGuid())) {
1357 logger.log(logger.EXTREME, "Conflict check beginning");
1358 conflictingNote = checkForConflict(n);
1359 logger.log(logger.EXTREME, "Conflict check results " +conflictingNote);
1360 if (conflictingNote)
1361 moveConflictingNote(n.getGuid());
1363 boolean ignoreNote = false;
1364 if (ignoreNotebooks.contains(n.getNotebookGuid()))
1366 for (int i=0; i<n.getTagGuidsSize(); i++) {
1367 if (ignoreTags.contains(n.getTagGuids().get(i))) {
1369 i=n.getTagGuidsSize();
1373 if ((conflictingNote || fullSync) && !ignoreNote) {
1374 logger.log(logger.EXTREME, "Saving Note");
1375 conn.getNoteTable().syncNote(n);
1376 // The following was commented out because it caused a race condition on the database where resources
1377 // may be lost. We do the same thing elsewhere;.
1378 // noteSignal.noteChanged.emit(n.getGuid(), null); // Signal to ivalidate note cache
1379 noteSignal.noteDownloaded.emit(n, true); // Signal to add note to index
1380 logger.log(logger.EXTREME, "Note Saved");
1381 if (fullSync && n.getResources() != null) {
1382 for (int q=0; q<n.getResources().size() && keepRunning; q++) {
1383 logger.log(logger.EXTREME, "Getting note resources.");
1384 conn.getNoteTable().noteResourceTable.updateNoteResource(n.getResources().get(q), false);
1385 if (n.getResources().get(q).getMime().equalsIgnoreCase("application/vnd.evernote.ink"))
1386 downloadInkNoteImage(n.getResources().get(q).getGuid(), token);
1392 private Note getEvernoteNote(Client noteStore, String guid, boolean withContent, boolean withResourceData, boolean withResourceRecognition, boolean withResourceAlternateData, String token) {
1395 logger.log(logger.EXTREME, "Retrieving note " +guid);
1396 n = noteStore.getNote(token, guid, withContent, withResourceData, withResourceRecognition, withResourceAlternateData);
1397 logger.log(logger.EXTREME, "Note " +guid +" has been retrieved.");
1398 } catch (EDAMUserException e) {
1399 logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");
1400 logger.log(logger.LOW, e.toString());
1402 e.printStackTrace();
1403 } catch (EDAMSystemException e) {
1404 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1405 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
1407 logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");
1408 logger.log(logger.LOW, e.toString());
1410 e.printStackTrace();
1411 } catch (EDAMNotFoundException e) {
1412 logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");
1413 logger.log(logger.LOW, e.toString());
1415 e.printStackTrace();
1416 } catch (TException e) {
1417 logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");
1418 logger.log(logger.LOW, e.toString());
1420 e.printStackTrace();
1424 private Resource getEvernoteResource(Client noteStore, String guid, boolean withData, boolean withRecognition, boolean withAttributes, String token) {
1427 logger.log(logger.EXTREME, "Retrieving resource " +guid);
1428 n = noteStore.getResource(token, guid, withData, withRecognition, withAttributes, withAttributes);
1429 logger.log(logger.EXTREME, "Resource " +guid +" has been retrieved.");
1430 } catch (EDAMUserException e) {
1431 logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");
1432 logger.log(logger.LOW, e.toString());
1434 e.printStackTrace();
1435 } catch (EDAMSystemException e) {
1436 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1437 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
1439 logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");
1440 logger.log(logger.LOW, e.toString());
1442 e.printStackTrace();
1443 } catch (EDAMNotFoundException e) {
1444 logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");
1445 logger.log(logger.LOW, e.toString());
1447 e.printStackTrace();
1448 } catch (TException e) {
1449 logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");
1450 logger.log(logger.LOW, e.toString());
1452 e.printStackTrace();
1458 private boolean checkForConflict(Note n) {
1459 logger.log(logger.EXTREME, "Checking note sequence number " +n.getGuid());
1460 Note oldNote = conn.getNoteTable().getNote(n.getGuid(), false, false, false, false, false);
1461 logger.log(logger.EXTREME, "Local/Remote sequence numbers: " +oldNote.getUpdateSequenceNum()+"/"+n.getUpdateSequenceNum());
1462 logger.log(logger.LOW, "Remote Note Title:" +n.getTitle());
\r
1463 logger.log(logger.LOW, "Local Note Title:" +oldNote.getTitle());
\r
1464 if (oldNote.getUpdateSequenceNum() == n.getUpdateSequenceNum()) {
\r
1467 boolean oldIsDirty = conn.getNoteTable().isNoteDirty(n.getGuid());
\r
1473 private void moveConflictingNote(String guid) {
1474 logger.log(logger.EXTREME, "Conflicting change found for note " +guid);
1475 List<Notebook> books = conn.getNotebookTable().getAllLocal();
1476 String notebookGuid = null;
1477 for (int i=0; i<books.size() && keepRunning; i++) {
1478 if (books.get(i).getName().equalsIgnoreCase("Conflicting Changes (local)") ||
1479 books.get(i).getName().equalsIgnoreCase("Conflicting Changes")) {
1480 notebookGuid = books.get(i).getGuid();
1485 if (notebookGuid == null) {
1486 logger.log(logger.EXTREME, "Building conflicting change notebook " +guid);
1487 Calendar currentTime = new GregorianCalendar();
1488 Long l = new Long(currentTime.getTimeInMillis());
1490 while (prevTime==l) {
1491 currentTime = new GregorianCalendar();
1492 l=currentTime.getTimeInMillis();
1494 String randint = new String(Long.toString(l));
1496 Notebook newBook = new Notebook();
1497 newBook.setUpdateSequenceNum(0);
1498 newBook.setGuid(randint);
1499 newBook.setName("Conflicting Changes");
1500 newBook.setServiceCreated(new Date().getTime());
1501 newBook.setServiceUpdated(new Date().getTime());
1502 newBook.setDefaultNotebook(false);
1503 newBook.setPublished(false);
1505 conn.getNotebookTable().addNotebook(newBook, false, true);
1506 notebookSignal.listChanged.emit();
1507 notebookGuid = newBook.getGuid();
1508 refreshNeeded = true;
1511 // Now that we have a good notebook guid, we need to move the conflicting note
1512 // to the local notebook
1513 logger.log(logger.EXTREME, "Moving conflicting note " +guid);
1514 Calendar currentTime = new GregorianCalendar();
1515 Long l = new Long(currentTime.getTimeInMillis());
1517 while (prevTime==l) {
1518 currentTime = new GregorianCalendar();
1519 l = currentTime.getTimeInMillis();
1521 String newGuid = new String(Long.toString(l));
1523 Note oldNote = conn.getNoteTable().getNote(guid, true, true, false, false, false);
1524 for (int i=0; i<oldNote.getResources().size() && keepRunning; i++) {
1525 l = new Long(currentTime.getTimeInMillis());
1526 String newResG = new String(Long.toString(l));
1527 String oldResG = oldNote.getResources().get(i).getGuid();
1528 conn.getNoteTable().noteResourceTable.resetUpdateSequenceNumber(oldResG, true);
1529 conn.getNoteTable().noteResourceTable.updateNoteResourceGuid(oldResG, newResG, true);
1532 conn.getNoteTable().resetNoteSequence(guid);
1533 conn.getNoteTable().updateNoteGuid(guid, newGuid);
1534 conn.getNoteTable().updateNoteNotebook(newGuid, notebookGuid, true);
1536 noteSignal.notebookChanged.emit(newGuid, notebookGuid);
1537 refreshNeeded = true;
1538 noteSignal.guidChanged.emit(guid,newGuid);
1544 //******************************************************
1545 //******************************************************
1546 //** Utility Functions
1547 //******************************************************
1548 //******************************************************
1549 // Convert a byte array to a hex string
1550 private static String byteArrayToHexString(byte data[]) {
1551 StringBuffer buf = new StringBuffer();
1552 for (byte element : data) {
1553 int halfbyte = (element >>> 4) & 0x0F;
1556 if ((0 <= halfbyte) && (halfbyte <= 9))
1557 buf.append((char) ('0' + halfbyte));
1559 buf.append((char) ('a' + (halfbyte - 10)));
1560 halfbyte = element & 0x0F;
1561 } while(two_halfs++ < 1);
1563 return buf.toString();
1568 //*******************************************************
1569 //* Find dirty tags, which do not have newly created parents
1570 //*******************************************************
1571 private Tag findNextTag() {
1572 logger.log(logger.HIGH, "Entering SyncRunner.findNextTag");
1574 List<Tag> tags = conn.getTagTable().getDirty();
1576 // Find the parent. If the parent has a sequence > 0 then it is a good
1578 for (int i=0; i<tags.size() && keepRunning; i++) {
1579 if (!badTagSync.containsKey(tags.get(i).getGuid())) {
1580 if (tags.get(i).getParentGuid() == null) {
1581 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found without parent");
1584 Tag parentTag = conn.getTagTable().getTag(tags.get(i).getParentGuid());
1585 if (parentTag.getUpdateSequenceNum() > 0) {
1586 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found");
1592 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - no tags returned");
1597 // Connect to Evernote
1598 public boolean enConnect() {
1600 userStoreTrans = new THttpClient(userStoreUrl);
1601 userStoreTrans.setCustomHeader("User-Agent", userAgent);
1602 } catch (TTransportException e) {
1603 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1605 e.printStackTrace();
1607 userStoreProt = new TBinaryProtocol(userStoreTrans);
1608 userStore = new UserStore.Client(userStoreProt, userStoreProt);
1610 syncSignal.saveUserStore.emit(userStore);
1612 //authResult = userStore.authenticate(username, password, consumerKey, consumerSecret);
1613 user = userStore.getUser(authToken);
1614 } catch (EDAMUserException e) {
1615 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Error", "Invalid Authorization");
1617 isConnected = false;
1619 } catch (EDAMSystemException e) {
1620 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1621 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
1623 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "EDAM System Excepton", e.getLocalizedMessage());
1625 e.printStackTrace();
1626 isConnected = false;
1627 } catch (TException e) {
1628 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1630 e.printStackTrace();
1631 isConnected = false;
1634 boolean versionOk = false;
1636 versionOk = userStore.checkVersion("NeighborNote",
1637 com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR,
1638 com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR);
1639 } catch (TException e) {
1640 e.printStackTrace();
1641 isConnected = false;
1644 System.err.println("Incomatible EDAM client protocol version");
1645 isConnected = false;
1647 //if (authResult != null) {
1648 //user = authResult.getUser();
1649 //authToken = authResult.getAuthenticationToken();
1650 if (user == null || noteStoreUrlBase == null) {
\r
1651 logger.log(logger.LOW, "Error retrieving user information. Aborting.");
\r
1652 System.err.println("Error retrieving user information.");
\r
1653 isConnected = false;
\r
1654 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, tr("Connection Error"), tr("Error retrieving user information. Synchronization not complete"));
\r
1659 noteStoreUrl = noteStoreUrlBase + user.getShardId();
1660 syncSignal.saveAuthToken.emit(authToken);
1661 syncSignal.saveNoteStore.emit(localNoteStore);
1665 noteStoreTrans = new THttpClient(noteStoreUrl);
1666 noteStoreTrans.setCustomHeader("User-Agent", userAgent);
1667 } catch (TTransportException e) {
1668 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1670 e.printStackTrace();
1671 isConnected = false;
1673 noteStoreProt = new TBinaryProtocol(noteStoreTrans);
1675 new NoteStore.Client(noteStoreProt, noteStoreProt);
1677 //authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();
1678 //authRefreshTime = authTimeRemaining / 2;
1681 // Get user information
1683 User user = userStore.getUser(authToken);
1684 syncSignal.saveUserInformation.emit(user);
1685 } catch (EDAMUserException e1) {
1686 e1.printStackTrace();
1687 } catch (EDAMSystemException e1) {
1688 if (e1.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1689 limitSignal.rateLimitReached.emit(e1.getRateLimitDuration());
1691 e1.printStackTrace();
1692 } catch (TException e1) {
1693 e1.printStackTrace();
1698 // Disconnect from the database
1699 public void enDisconnect() {
1700 isConnected = false;
1704 // Refresh the connection
1705 private synchronized boolean refreshConnection() {
1707 logger.log(logger.EXTREME, "Entering SyncRunner.refreshConnection()");
1708 // Calendar cal = Calendar.getInstance();
1710 // If we are not connected let's get out of here
1714 // If we fail too many times, then let's give up.
1715 if (failedRefreshes >=5) {
1716 logger.log(logger.EXTREME, "Refresh attempts have failed. Disconnecting.");
1717 isConnected = false;
1718 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1722 // If this is the first time through, then we need to set this
1723 // if (authRefreshTime == 0 || cal.getTimeInMillis() > authRefreshTime)
1724 // authRefreshTime = cal.getTimeInMillis();
1726 // // Default to checking again in 5 min. This in case we fail.
1727 // authRefreshTime = authRefreshTime +(5*60*1000);
1729 // Try to get a new token
1730 AuthenticationResult newAuth = null;
1731 logger.log(logger.EXTREME, "Beginning to try authentication refresh");
1733 if (userStore != null && authToken != null)
1734 newAuth = userStore.refreshAuthentication(authToken);
1737 logger.log(logger.EXTREME, "UserStore.refreshAuthentication has succeeded.");
1738 } catch (EDAMUserException e) {
1739 e.printStackTrace();
1740 syncSignal.authRefreshComplete.emit(false);
1743 } catch (EDAMSystemException e) {
1744 e.printStackTrace();
1745 syncSignal.authRefreshComplete.emit(false);
1748 } catch (TException e) {
1749 e.printStackTrace();
1750 syncSignal.authRefreshComplete.emit(false);
1755 // If we didn't get a good auth, then we've failed
1756 if (newAuth == null) {
1758 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1759 logger.log(logger.EXTREME, "Authentication failure #" +failedRefreshes);
1760 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1761 syncSignal.authRefreshComplete.emit(false);
1765 // We got a good token. Now we need to setup the time to renew it.
1766 logger.log(logger.EXTREME, "Saving authentication tokens");
1767 authResult = newAuth;
1768 authToken = new String(newAuth.getAuthenticationToken());
1769 // authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();
1770 // authRefreshTime = cal.getTimeInMillis() + (authTimeRemaining/4);
1772 syncSignal.authRefreshComplete.emit(true);
1773 authRefreshNeeded = false;
1775 // This should never happen, but if it does we consider this a faild attempt.
1776 // if (authTimeRemaining <= 0) {
1777 // failedRefreshes++;
1778 // syncSignal.authRefreshComplete.emit(false);
1786 public synchronized boolean addWork(String request) {
1787 if (workQueue.offer(request))
1792 private Note getNoteContent(Note n) {
1793 QTextCodec codec = QTextCodec.codecForLocale();
1794 codec = QTextCodec.codecForName("UTF-8");
1795 n.setContent(codec.toUnicode(new QByteArray(n.getContent())));
1801 //*********************************************************
1802 //* Special download instructions. Used for DB upgrades
1803 //*********************************************************
1804 private void downloadAllSharedNotebooks(Client noteStore) {
1806 List<SharedNotebook> books = noteStore.listSharedNotebooks(authToken);
1807 logger.log(logger.LOW, "Shared notebooks found = " +books.size());
1808 for (int i=0; i<books.size(); i++) {
1809 conn.getSharedNotebookTable().updateNotebook(books.get(i), false);
1811 conn.getSyncTable().deleteRecord("FullSharedNotebookSync");
1812 } catch (EDAMUserException e1) {
1813 e1.printStackTrace();
1814 status.message.emit(tr("User exception Listing shared notebooks."));
1815 logger.log(logger.LOW, e1.getMessage());
1817 } catch (EDAMSystemException e1) {
1818 if (e1.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1819 limitSignal.rateLimitReached.emit(e1.getRateLimitDuration());
1821 e1.printStackTrace();
1822 status.message.emit(tr("System exception Listing shared notebooks."));
1823 logger.log(logger.LOW, e1.getMessage());
1825 } catch (TException e1) {
1826 e1.printStackTrace();
1827 status.message.emit(tr("Transaction exception Listing shared notebooks."));
1828 logger.log(logger.LOW, e1.getMessage());
1830 } catch (EDAMNotFoundException e1) {
1831 e1.printStackTrace();
1832 status.message.emit(tr("EDAM Not Found exception Listing shared notebooks."));
1833 logger.log(logger.LOW, e1.getMessage());
1836 private void downloadAllNotebooks(Client noteStore) {
1838 List<Notebook> books = noteStore.listNotebooks(authToken);
1839 logger.log(logger.LOW, "Shared notebooks found = " +books.size());
1840 for (int i=0; i<books.size(); i++) {
1841 conn.getNotebookTable().updateNotebook(books.get(i), false);
1843 conn.getSyncTable().deleteRecord("FullNotebookSync");
1844 } catch (EDAMUserException e1) {
1845 e1.printStackTrace();
1846 status.message.emit(tr("User exception Listing notebooks."));
1847 logger.log(logger.LOW, e1.getMessage());
1849 } catch (EDAMSystemException e1) {
1850 if (e1.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1851 limitSignal.rateLimitReached.emit(e1.getRateLimitDuration());
1853 e1.printStackTrace();
1854 status.message.emit(tr("System exception Listing notebooks."));
1855 logger.log(logger.LOW, e1.getMessage());
1857 } catch (TException e1) {
1858 e1.printStackTrace();
1859 status.message.emit(tr("Transaction exception Listing notebooks."));
1860 logger.log(logger.LOW, e1.getMessage());
1864 private void downloadAllLinkedNotebooks(Client noteStore) {
1866 List<LinkedNotebook> books = noteStore.listLinkedNotebooks(authToken);
1867 logger.log(logger.LOW, "Linked notebooks found = " +books.size());
1868 for (int i=0; i<books.size(); i++) {
1869 conn.getLinkedNotebookTable().updateNotebook(books.get(i), false);
1871 conn.getSyncTable().deleteRecord("FullLinkedNotebookSync");
1872 } catch (EDAMUserException e1) {
1873 e1.printStackTrace();
1874 status.message.emit(tr("User exception Listing linked notebooks."));
1875 logger.log(logger.LOW, e1.getMessage());
1877 } catch (EDAMSystemException e1) {
1878 if (e1.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
1879 limitSignal.rateLimitReached.emit(e1.getRateLimitDuration());
1881 e1.printStackTrace();
1882 status.message.emit(tr("System exception Listing linked notebooks."));
1883 logger.log(logger.LOW, e1.getMessage());
1885 } catch (TException e1) {
1886 e1.printStackTrace();
1887 status.message.emit(tr("Transaction exception Listing lineked notebooks."));
1888 logger.log(logger.LOW, e1.getMessage());
1890 } catch (EDAMNotFoundException e1) {
1891 e1.printStackTrace();
1892 status.message.emit(tr("EDAM Not Found exception Listing linked notebooks."));
1893 logger.log(logger.LOW, e1.getMessage());
1898 private void downloadInkNoteImage(String guid, String authToken) {
1899 String urlBase = noteStoreUrl.replace("/edam/note/", "/shard/") + "/res/"+guid+".ink?slice=";
1900 // urlBase = "https://www.evernote.com/shard/s1/res/52b567a9-54ae-4a08-afc5-d5bae275b2a8.ink?slice=";
1902 Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(guid, false);
1903 conn.getInkImagesTable().expungeImage(r.getGuid());
1904 int sliceCount = 1+((r.getHeight()-1)/480);
1905 HttpClient http = new DefaultHttpClient();
1906 for (int i=0; i<sliceCount; i++) {
1907 String url = urlBase + slice.toString();
1908 HttpPost post = new HttpPost(url);
1909 post.getParams().setParameter("auth", authToken);
1910 List <NameValuePair> nvps = new ArrayList <NameValuePair>();
1911 nvps.add(new BasicNameValuePair("auth", authToken));
1914 post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
1915 } catch (UnsupportedEncodingException e1) {
1916 e1.printStackTrace();
1919 HttpResponse response = http.execute(post);
1920 HttpEntity resEntity = response.getEntity();
1921 InputStream is = resEntity.getContent();
1922 QByteArray data = writeToFile(is);
1923 conn.getInkImagesTable().saveImage(guid, slice, data);
1924 } catch (ClientProtocolException e) {
1925 e.printStackTrace();
1926 } catch (IOException e) {
1927 e.printStackTrace();
1932 http.getConnectionManager().shutdown();
1933 noteSignal.noteChanged.emit(r.getNoteGuid(), null); // Signal to ivalidate note cache
1937 public QByteArray writeToFile(InputStream iStream) throws IOException {
1939 File temp = File.createTempFile("nn-inknote-temp", ".png");
1941 // Save InputStream to the file.
1942 BufferedOutputStream fOut = null;
1944 fOut = new BufferedOutputStream(new FileOutputStream(temp));
1945 byte[] buffer = new byte[32 * 1024];
1947 while ((bytesRead = iStream.read(buffer)) != -1) {
1948 fOut.write(buffer, 0, bytesRead);
1955 QFile tempFile = new QFile(temp.getAbsoluteFile().toString());
1956 tempFile.open(OpenModeFlag.ReadOnly);
1957 QByteArray data = tempFile.readAll();
1964 //******************************************
1965 //* Begin syncing shared notebooks
1966 //******************************************
1967 private void syncLinkedNotebooks() {
1968 logger.log(logger.MEDIUM, "Authenticating linked Notebooks");
1969 status.message.emit(tr("Synchronizing shared notebooks."));
1970 List<LinkedNotebook> books = conn.getLinkedNotebookTable().getAll();
1972 errorSharedNotebooks.clear();
1974 for (int i=0; i<books.size(); i++) {
1975 if (errorSharedNotebooksIgnored.containsKey(books.get(i).getGuid()))
1978 logger.log(logger.EXTREME, "Checking notebook: " +books.get(i).getShareName());
1979 long lastSyncDate = conn.getLinkedNotebookTable().getLastSequenceDate(books.get(i).getGuid());
1980 int lastSequenceNumber = conn.getLinkedNotebookTable().getLastSequenceNumber(books.get(i).getGuid());
1982 logger.log(logger.EXTREME, "Last Sequence Number on file: " +lastSequenceNumber);
1984 // Authenticate to the owner's shard
1985 String linkedNoteStoreUrl = noteStoreUrlBase + books.get(i).getShardId();
1986 logger.log(logger.EXTREME, "linkedNoteStoreURL: " +linkedNoteStoreUrl);
1987 THttpClient linkedNoteStoreTrans = new THttpClient(linkedNoteStoreUrl);
1988 TBinaryProtocol linkedNoteStoreProt = new TBinaryProtocol(linkedNoteStoreTrans);
1989 Client linkedNoteStore = new NoteStore.Client(linkedNoteStoreProt, linkedNoteStoreProt);
1991 linkedAuthResult = null;
1992 if (books.get(i).getShareKey() != null) {
1993 logger.log(logger.EXTREME, "Share Key Not Null: " +books.get(i).getShareKey());
1994 linkedAuthResult = linkedNoteStore.authenticateToSharedNotebook(books.get(i).getShareKey(), authToken);
1995 logger.log(logger.EXTREME, "Authentication Token" +linkedAuthResult.getAuthenticationToken());
1997 logger.log(logger.EXTREME, "Share key is null");
1998 linkedAuthResult = new AuthenticationResult();
1999 linkedAuthResult.setAuthenticationToken("");
2001 SyncState linkedSyncState =
2002 linkedNoteStore.getLinkedNotebookSyncState(linkedAuthResult.getAuthenticationToken(), books.get(i));
2003 if (linkedSyncState.getUpdateCount() > lastSequenceNumber) {
2004 logger.log(logger.EXTREME, "Remote changes found");
2005 if (lastSyncDate < linkedSyncState.getFullSyncBefore()) {
2006 lastSequenceNumber = 0;
2008 logger.log(logger.EXTREME, "Calling syncLinkedNotebook for " +books.get(i).getShareName());
2009 syncLinkedNotebook(linkedNoteStore, books.get(i),
2010 lastSequenceNumber, linkedSyncState.getUpdateCount(), authToken);
2013 // Synchronize local changes
2014 syncLocalLinkedNoteChanges(linkedNoteStore, books.get(i));
2016 } catch (EDAMUserException e) {
2017 e.printStackTrace();
2018 } catch (EDAMNotFoundException e) {
2019 status.message.emit(tr("Error synchronizing \" " +
2020 books.get(i).getShareName()+"\". Please verify you still have access to that shared notebook."));
2021 errorSharedNotebooks.add(books.get(i).getGuid());
2022 errorSharedNotebooksIgnored.put(books.get(i).getGuid(), books.get(i).getGuid());
2023 logger.log(logger.LOW, "Error synchronizing shared notebook. EDAMNotFound: "+e.getMessage());
2024 logger.log(logger.LOW, e.getStackTrace());
2026 e.printStackTrace();
2027 } catch (EDAMSystemException e) {
2028 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
2029 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
2032 logger.log(logger.LOW, "System error authenticating against shared notebook. "+
2033 "Key: "+books.get(i).getShareKey() +" Error:" +e.getMessage());
2034 e.printStackTrace();
2035 } catch (TException e) {
2037 e.printStackTrace();
2042 conn.getTagTable().removeUnusedLinkedTags();
2043 conn.getTagTable().cleanupTags();
2044 tagSignal.listChanged.emit();
2049 //**************************************************************
2050 //* Linked notebook contents (from someone else's account)
2051 //*************************************************************
2052 private void syncLinkedNotebook(Client linkedNoteStore, LinkedNotebook book, int usn, int highSequence, String token) {
2053 logger.log(logger.EXTREME, "Entering syncLinkedNotebook");
2054 if (ignoreLinkedNotebooks.contains(book.getGuid()))
2056 List<Note> dirtyNotes = conn.getNoteTable().getDirtyLinkedNotes();
2057 if (dirtyNoteGuids == null)
2058 dirtyNoteGuids = new ArrayList<String>();
\r
2060 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {
2061 dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());
2063 boolean fullSync = false;
2066 boolean syncError = false;
2067 while (usn < highSequence && !syncError) {
2068 refreshNeeded = true;
2071 linkedNoteStore.getLinkedNotebookSyncChunk(token, book, usn, 10, fullSync);
2074 syncExpungedNotes(chunk);
2076 logger.log(logger.EXTREME, "Syncing remote notes: " +chunk.getNotesSize());
2077 syncRemoteNotes(linkedNoteStore, chunk.getNotes(), fullSync, linkedAuthResult.getAuthenticationToken());
2078 logger.log(logger.EXTREME, "Finding new linked tags");
2079 findNewLinkedTags(linkedNoteStore, chunk.getNotes(), linkedAuthResult.getAuthenticationToken());
2081 logger.log(logger.EXTREME, "Synchronizing tags: " +chunk.getTagsSize());
2082 for (int i=0; i<chunk.getResourcesSize(); i++) {
2083 syncRemoteResource(linkedNoteStore, chunk.getResources().get(i), linkedAuthResult.getAuthenticationToken());
2085 logger.log(logger.EXTREME, "Synchronizing linked notebooks: " +chunk.getNotebooksSize());
2086 syncRemoteLinkedNotebooks(linkedNoteStore, chunk.getNotebooks(), false, book);
2087 syncLinkedTags(chunk.getTags(), book.getGuid());
2089 // Go through & signal any notes that have changed so we can refresh the user's view
2090 for (int i=0; i<chunk.getNotesSize(); i++)
2091 noteSignal.noteChanged.emit(chunk.getNotes().get(i).getGuid(), null);
2093 // Expunge Notebook records
2094 logger.log(logger.EXTREME, "Expunging linked notebooks: " +chunk.getExpungedLinkedNotebooksSize());
2095 for (int i=0; i<chunk.getExpungedLinkedNotebooksSize(); i++) {
2096 conn.getLinkedNotebookTable().expungeNotebook(chunk.getExpungedLinkedNotebooks().get(i), false);
2098 usn = chunk.getChunkHighUSN();
2099 conn.getLinkedNotebookTable().setLastSequenceDate(book.getGuid(),chunk.getCurrentTime());
2100 conn.getLinkedNotebookTable().setLastSequenceNumber(book.getGuid(),chunk.getChunkHighUSN());
2101 } catch (EDAMUserException e) {
2103 status.message.emit(tr("EDAM UserException synchronizing linked notbook. See the log for datails."));
2104 e.printStackTrace();
2105 logger.log(logger.LOW, tr("EDAM UserException synchronizing linked notbook ")+ e.getMessage());
2106 } catch (EDAMSystemException e) {
2107 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
2108 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
2111 status.message.emit(tr("EDAM SystemException synchronizing linked notbook. See the log for datails."));
2112 e.printStackTrace();
2113 logger.log(logger.LOW, tr("EDAM SystemException synchronizing linked notbook. See the log for datails") +e.getMessage());
2114 } catch (EDAMNotFoundException e) {
2116 status.message.emit(tr("Notebook URL not found. Removing notobook ") +book.getShareName());
2117 conn.getNotebookTable().deleteLinkedTags(book.getGuid());
2118 conn.getLinkedNotebookTable().expungeNotebook(book.getGuid(), false);
2119 logger.log(logger.LOW, tr("Notebook URL not found. Removing notobook ") +e.getMessage());
2120 } catch (TException e) {
2122 status.message.emit(tr("EDAM TException synchronizing linked notbook. See the log for datails."));
2123 e.printStackTrace();
2124 logger.log(logger.LOW, tr("EDAM TException synchronizing linked notbook. See the log for datails." )+e.getMessage());
2127 logger.log(logger.EXTREME, "leaving syncLinkedNotebook");
2130 private void syncLinkedTags(List<Tag> tags, String notebookGuid) {
2131 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");
2133 for (int i=0; i<tags.size() && keepRunning; i++) {
2134 conn.getTagTable().syncLinkedTag(tags.get(i), notebookGuid, false);
2137 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");
2140 // Sync notebooks from a linked notebook
2141 private void syncRemoteLinkedNotebooks(Client noteStore, List<Notebook> notebooks, boolean readOnly, LinkedNotebook linked) {
2142 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");
2143 if (notebooks != null) {
2144 for (int i=0; i<notebooks.size() && keepRunning; i++) {
2146 logger.log(logger.EXTREME, "auth token:" +linkedAuthResult.getAuthenticationToken());
2147 if (!linkedAuthResult.getAuthenticationToken().equals("")) {
2148 SharedNotebook s = noteStore.getSharedNotebookByAuth(linkedAuthResult.getAuthenticationToken());
2149 logger.log(logger.EXTREME, "share key:"+s.getShareKey() +" notebookGuid" +s.getNotebookGuid());
2150 conn.getLinkedNotebookTable().setNotebookGuid(s.getShareKey(), s.getNotebookGuid());
2151 readOnly = !s.isNotebookModifiable();
2155 notebooks.get(i).setName(linked.getShareName());
2156 notebooks.get(i).setDefaultNotebook(false);
2157 conn.getNotebookTable().syncLinkedNotebook(notebooks.get(i), false, readOnly);
2158 } catch (EDAMUserException e) {
2160 e.printStackTrace();
2161 } catch (EDAMNotFoundException e) {
2163 e.printStackTrace();
2164 } catch (EDAMSystemException e) {
2165 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
2166 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
2169 e.printStackTrace();
2170 } catch (TException e) {
2172 e.printStackTrace();
2177 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");
2180 private void findNewLinkedTags(Client noteStore, List<Note> newNotes, String token) {
2181 if (newNotes == null)
2183 for (int i=0; i<newNotes.size(); i++) {
2184 Note n = newNotes.get(i);
2185 for (int j=0; j<n.getTagGuidsSize(); j++) {
2186 String tag = n.getTagGuids().get(j);
2187 if (!conn.getTagTable().exists(tag)) {
2190 newTag = noteStore.getTag(token, tag);
2191 conn.getTagTable().addTag(newTag, false);
2192 } catch (EDAMUserException e) {
2193 e.printStackTrace();
2194 } catch (EDAMSystemException e) {
2195 if (e.getErrorCode() == EDAMErrorCode.RATE_LIMIT_REACHED) {
2196 limitSignal.rateLimitReached.emit(e.getRateLimitDuration());
2198 e.printStackTrace();
2199 } catch (EDAMNotFoundException e) {
2200 e.printStackTrace();
2201 } catch (TException e) {
2202 e.printStackTrace();
2210 // Synchronize changes locally done to linked notes
2211 private void syncLocalLinkedNoteChanges(Client noteStore, LinkedNotebook book) {
2212 logger.log(logger.EXTREME, "Entering SyncRunner.synclocalLinkedNoteChanges");
2213 String notebookGuid = conn.getLinkedNotebookTable().getNotebookGuid(book.getGuid());
2214 logger.log(logger.EXTREME, "Finding changes for " +book.getShareName() +":" +book.getGuid() + ":" +notebookGuid);
2215 List<Note> notes = conn.getNoteTable().getDirtyLinked(notebookGuid);
2216 logger.log(logger.EXTREME, "Number of changes found: " +notes.size());
2217 for (int i=0; i<notes.size(); i++) {
2218 logger.log(logger.EXTREME, "Calling syncLocalNote with key " +linkedAuthResult.getAuthenticationToken());
2219 syncLocalNote(noteStore, notes.get(i), linkedAuthResult.getAuthenticationToken());
2221 logger.log(logger.EXTREME, "Leaving SyncRunner.synclocalLinkedNoteChanges");