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;
48 import org.apache.thrift.TException;
49 import org.apache.thrift.protocol.TBinaryProtocol;
50 import org.apache.thrift.transport.THttpClient;
51 import org.apache.thrift.transport.TTransportException;
53 import com.evernote.edam.error.EDAMNotFoundException;
54 import com.evernote.edam.error.EDAMSystemException;
55 import com.evernote.edam.error.EDAMUserException;
56 import com.evernote.edam.notestore.NoteStore;
57 import com.evernote.edam.notestore.NoteStore.Client;
58 import com.evernote.edam.notestore.SyncChunk;
59 import com.evernote.edam.notestore.SyncState;
60 import com.evernote.edam.type.Data;
61 import com.evernote.edam.type.LinkedNotebook;
62 import com.evernote.edam.type.Note;
63 import com.evernote.edam.type.Notebook;
64 import com.evernote.edam.type.Resource;
65 import com.evernote.edam.type.SavedSearch;
66 import com.evernote.edam.type.SharedNotebook;
67 import com.evernote.edam.type.Tag;
68 import com.evernote.edam.type.User;
69 import com.evernote.edam.userstore.AuthenticationResult;
70 import com.evernote.edam.userstore.UserStore;
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;
90 public class SyncRunner extends QObject implements Runnable {
92 private final ApplicationLogger logger;
93 private DatabaseConnection conn;
96 public volatile List<String> errorSharedNotebooks;
97 public volatile HashMap<String,String> errorSharedNotebooksIgnored;
98 public volatile boolean isConnected;
99 public volatile boolean keepRunning;
100 public volatile String authToken;
101 private long evernoteUpdateCount;
102 private final String userAgent = "NeighborNote/" + System.getProperty("os.name")
103 +"/"+System.getProperty("java.vendor") + "/"
104 + System.getProperty("java.version") +";";
106 public volatile NoteStore.Client localNoteStore;
107 private UserStore.Client userStore;
109 public volatile StatusSignal status;
110 public volatile TagSignal tagSignal;
111 public volatile NotebookSignal notebookSignal;
112 public volatile NoteIndexSignal noteIndexSignal;
113 public volatile NoteSignal noteSignal;
114 public volatile SavedSearchSignal searchSignal;
115 public volatile NoteResourceSignal resourceSignal;
116 public volatile SyncSignal syncSignal;
117 public volatile boolean authRefreshNeeded;
118 public volatile boolean syncNeeded;
119 public volatile boolean disableUploads;
120 public volatile boolean syncDeletedContent;
121 private volatile List<String> dirtyNoteGuids;
123 public volatile String username = "";
124 public volatile String password = "";
125 public volatile String userStoreUrl;
126 // private final static String consumerKey = "baumgarte";
127 // private final static String consumerSecret = "eb8b5740e17cb55f";
128 public String noteStoreUrlBase;
129 private THttpClient userStoreTrans;
130 private TBinaryProtocol userStoreProt;
131 //private AuthenticationResult authResult;
132 private AuthenticationResult linkedAuthResult;
134 // private long authTimeRemaining;
135 public long authRefreshTime;
136 public long failedRefreshes = 0;
137 public THttpClient noteStoreTrans;
138 public TBinaryProtocol noteStoreProt;
139 public String noteStoreUrl;
140 public long sequenceDate;
141 public int updateSequenceNumber;
142 private boolean refreshNeeded;
143 private volatile LinkedBlockingQueue<String> workQueue;
144 private static int MAX_QUEUED_WAITING = 1000;
154 private final TreeSet<String> ignoreTags;
155 private final TreeSet<String> ignoreNotebooks;
156 private final TreeSet<String> ignoreLinkedNotebooks;
157 private HashMap<String,String> badTagSync;
160 // ICHANGED String bを追加
161 public SyncRunner(String logname, String u, String i, String r, String b, String uid, String pswd, String cpswd) {
162 logger = new ApplicationLogger(logname);
164 noteSignal = new NoteSignal();
165 status = new StatusSignal();
166 tagSignal = new TagSignal();
167 notebookSignal = new NotebookSignal();
168 noteIndexSignal = new NoteIndexSignal();
169 noteSignal = new NoteSignal();
170 searchSignal = new SavedSearchSignal();
171 syncSignal = new SyncSignal();
172 resourceSignal = new NoteResourceSignal();
182 // this.setAutoDelete(false);
186 authRefreshNeeded = false;
189 disableUploads = false;
190 ignoreTags = new TreeSet<String>();
191 ignoreNotebooks = new TreeSet<String>();
192 ignoreLinkedNotebooks = new TreeSet<String>();
194 // setAutoDelete(false);
195 workQueue=new LinkedBlockingQueue<String>(MAX_QUEUED_WAITING);
199 errorSharedNotebooks = new ArrayList<String>();
200 errorSharedNotebooksIgnored = new HashMap<String,String>();
202 logger.log(logger.EXTREME, "Starting thread");
203 // ICHANGED behaviorUrlを追加
204 conn = new DatabaseConnection(logger, dburl, indexUrl, resourceUrl, behaviorUrl, dbuid, dbpswd, dbcpswd, 200);
206 logger.log(logger.EXTREME, "Blocking until work is found");
207 String work = workQueue.take();
208 logger.log(logger.LOW, "Dirty Notes Before Sync: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
209 logger.log(logger.EXTREME, "Work found: " +work);
210 if (work.equalsIgnoreCase("stop")) {
214 conn.getNoteTable().dumpDirtyNotes(); // Debugging statement
218 logger.log(logger.EXTREME, "SyncNeeded is true");
220 sequenceDate = conn.getSyncTable().getLastSequenceDate();
221 updateSequenceNumber = conn.getSyncTable().getUpdateSequenceNumber();
223 logger.log(logger.EXTREME, "Beginning sync");
224 evernoteSync(localNoteStore);
225 logger.log(logger.EXTREME, "Sync finished");
226 } catch (UnknownHostException e) {
227 status.message.emit(e.getMessage());
231 logger.log(logger.EXTREME, "Signaling refresh finished. refreshNeeded=" +refreshNeeded);
232 syncSignal.finished.emit(refreshNeeded);
234 syncSignal.errorDisconnect.emit();
235 status.message.emit(tr("Error synchronizing - see log for details."));
237 logger.log(logger.LOW, "Dirty Notes After Sync: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
238 conn.getNoteTable().dumpDirtyNotes();
239 logger.log(logger.LOW, "---");
242 catch (InterruptedException e1) {
243 e1.printStackTrace();
249 public DatabaseConnection getConnection() {
253 public boolean isIdle() {
258 public void setConnected(boolean c) {
261 public void setKeepRunning(boolean r) {
262 logger.log(logger.EXTREME, "Setting keepRunning=" +r);
265 public void setNoteStore(NoteStore.Client c) {
266 logger.log(logger.EXTREME, "Setting NoteStore in sync thread");
269 public void setUserStore(UserStore.Client c) {
270 logger.log(logger.EXTREME, "Setting UserStore in sync thread");
274 public void setEvernoteUpdateCount(long s) {
275 logger.log(logger.EXTREME, "Setting Update Count in sync thread");
276 evernoteUpdateCount = s;
279 //***************************************************************
280 //***************************************************************
281 //** These functions deal with Evernote communications
282 //***************************************************************
283 //***************************************************************
284 // Synchronize changes with Evernote
285 @SuppressWarnings("unused")
286 private void evernoteSync(Client noteStore) throws java.net.UnknownHostException {
287 logger.log(logger.HIGH, "Entering SyncRunner.evernoteSync");
289 // Rebuild list of tags & notebooks to ignore
290 ignoreNotebooks.clear();
291 List<String> ignore = conn.getSyncTable().getIgnoreRecords("NOTEBOOK");
292 for (int i=0; i<ignore.size(); i++)
293 ignoreNotebooks.add(ignore.get(i));
296 ignore = conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK");
297 for (int i=0; i<ignore.size(); i++)
298 ignoreLinkedNotebooks.add(ignore.get(i));
301 ignore = conn.getSyncTable().getIgnoreRecords("TAG");
302 for (int i=0; i<ignore.size(); i++)
303 ignoreTags.add(ignore.get(i));
305 // Make sure we are connected & should keep running
306 if (isConnected && keepRunning) {
308 logger.log(logger.EXTREME, "Synchronizing with Evernote");
309 status.message.emit(tr("Synchronizing with Evernote"));
311 // Get user information
313 logger.log(logger.EXTREME, "getting user from userstore");
314 User user = userStore.getUser(authToken);
315 logger.log(logger.EXTREME, "Saving user information");
316 syncSignal.saveUserInformation.emit(user);
317 } catch (EDAMUserException e1) {
318 e1.printStackTrace();
319 status.message.emit(tr("User exception getting user account information. Aborting sync and disconnecting"));
320 syncSignal.errorDisconnect.emit();
324 } catch (EDAMSystemException e1) {
325 e1.printStackTrace();
326 status.message.emit(tr("System error user account information. Aborting sync and disconnecting!"));
327 syncSignal.errorDisconnect.emit();
331 } catch (TException e1) {
332 e1.printStackTrace();
333 syncSignal.errorDisconnect.emit();
335 status.message.emit(tr("Transaction error getting user account information. Aborting sync and disconnecting!"));
341 SyncState syncState = null;
343 logger.log(logger.EXTREME, "Getting sync state");
344 syncState = noteStore.getSyncState(authToken);
345 syncSignal.saveUploadAmount.emit(syncState.getUploaded());
346 syncSignal.saveEvernoteUpdateCount.emit(syncState.getUpdateCount());
347 evernoteUpdateCount = syncState.getUpdateCount();
348 } catch (EDAMUserException e) {
350 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
351 syncSignal.errorDisconnect.emit();
354 } catch (EDAMSystemException e) {
356 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
357 syncSignal.errorDisconnect.emit();
360 } catch (TException e) {
362 status.message.emit(tr("Error getting sync state! Aborting sync and disconnecting!"));
363 syncSignal.errorDisconnect.emit();
368 if (syncState == null) {
369 logger.log(logger.EXTREME, "Sync State is null");
370 status.message.emit(tr("Syncronization Error!"));
374 // Determine what to do.
375 // If we need to do a full sync.
376 logger.log(logger.LOW, "Full Sequence Before: " +syncState.getFullSyncBefore());
377 logger.log(logger.LOW, "Last Sequence Date: " +sequenceDate);
378 logger.log(logger.LOW, "Var Last Sequence Number: " +updateSequenceNumber);
379 logger.log(logger.LOW, "DB Last Sequence Number: " + conn.getSyncTable().getUpdateSequenceNumber());
380 if (syncState.getFullSyncBefore() > sequenceDate) {
381 logger.log(logger.EXTREME, "Full sequence date has expired");
383 conn.getSyncTable().setLastSequenceDate(0);
384 updateSequenceNumber = 0;
385 conn.getSyncTable().setUpdateSequenceNumber(0);
387 // Check for "special" sync instructions
388 String syncLinked = conn.getSyncTable().getRecord("FullLinkedNotebookSync");
389 String syncShared = conn.getSyncTable().getRecord("FullSharedNotebookSync");
390 String syncNotebooks = conn.getSyncTable().getRecord("FullNotebookSync");
391 String syncInkNoteImages = conn.getSyncTable().getRecord("FullInkNoteImageSync");
392 if (syncLinked != null) {
393 downloadAllLinkedNotebooks(localNoteStore);
395 if (syncShared != null) {
396 downloadAllSharedNotebooks(localNoteStore);
398 if (syncNotebooks != null) {
399 downloadAllNotebooks(localNoteStore);
402 if (syncInkNoteImages != null) {
403 List<String> guids = conn.getNoteTable().noteResourceTable.findInkNotes();
404 for (int i=0; i<guids.size(); i++) {
405 downloadInkNoteImage(guids.get(i), authToken);
407 conn.getSyncTable().deleteRecord("FullInkNoteImageSync");
410 // If there are remote changes
411 logger.log(logger.LOW, "Update Count: " +syncState.getUpdateCount());
412 logger.log(logger.LOW, "Last Update Count: " +updateSequenceNumber);
414 if (syncState.getUpdateCount() > updateSequenceNumber) {
415 logger.log(logger.EXTREME, "Refresh needed is true");
416 refreshNeeded = true;
417 logger.log(logger.EXTREME, "Downloading changes");
418 syncRemoteToLocal(localNoteStore);
421 //*****************************************
422 //* Sync linked/shared notebooks
423 //*****************************************
424 //syncLinkedNotebooks();
425 //conn.getNoteTable().getDirty();
426 //disableUploads = true; /// DELETE THIS LINE!!!!
427 if (!disableUploads) {
428 logger.log(logger.EXTREME, "Uploading changes");
429 // Synchronize remote changes
431 syncExpunged(localNoteStore);
433 syncLocalTags(localNoteStore);
435 syncLocalNotebooks(localNoteStore);
437 syncLocalLinkedNotebooks(localNoteStore);
439 syncDeletedNotes(localNoteStore);
443 syncLocalSavedSearches(localNoteStore);
446 status.message.emit(tr("Cleaning up"));
447 List<String> notes = conn.getNoteTable().expungeIgnoreSynchronizedNotes(conn.getSyncTable().getIgnoreRecords("NOTEBOOK"),
448 conn.getSyncTable().getIgnoreRecords("TAG"), conn.getSyncTable().getIgnoreRecords("LINKEDNOTEBOOK"));
449 if (notes.size() > 0)
450 syncSignal.refreshLists.emit();
452 //*****************************************
453 //* End of synchronization
454 //*****************************************
456 syncSignal.refreshLists.emit();
459 logger.log(logger.LOW, "Sync completed. Errors=" +error);
461 status.message.emit(tr("Synchronizing complete"));
463 status.message.emit(tr("Download syncronization complete. Uploads have been disabled."));
465 logger.log(logger.EXTREME, "Saving sync time");
466 if (syncState.getCurrentTime() > sequenceDate)
467 sequenceDate = syncState.getCurrentTime();
468 if (syncState.getUpdateCount() > updateSequenceNumber)
469 updateSequenceNumber = syncState.getUpdateCount();
470 conn.getSyncTable().setLastSequenceDate(sequenceDate);
471 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
474 logger.log(logger.HIGH, "Leaving SyncRunner.evernoteSync");
477 // Sync deleted items with Evernote
478 private void syncExpunged(Client noteStore) {
479 logger.log(logger.HIGH, "Entering SyncRunner.syncExpunged");
481 List<DeletedItemRecord> expunged = conn.getDeletedTable().getAllDeleted();
482 boolean error = false;
483 for (int i=0; i<expunged.size() && keepRunning; i++) {
485 // if (authRefreshNeeded)
486 // if (!refreshConnection())
490 if (expunged.get(i).type.equalsIgnoreCase("TAG")) {
491 logger.log(logger.EXTREME, "Tag expunged");
492 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "TAG");
\r
493 updateSequenceNumber = noteStore.expungeTag(authToken, expunged.get(i).guid);
494 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
495 conn.getSyncTable().setLastSequenceDate(sequenceDate);
496 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
498 if (expunged.get(i).type.equalsIgnoreCase("NOTEBOOK")) {
499 logger.log(logger.EXTREME, "Notebook expunged");
500 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "NOTEBOOK");
\r
501 updateSequenceNumber = noteStore.expungeNotebook(authToken, expunged.get(i).guid);
502 conn.getSyncTable().setLastSequenceDate(sequenceDate);
503 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
505 if (expunged.get(i).type.equalsIgnoreCase("NOTE")) {
506 logger.log(logger.EXTREME, "Note expunged");
507 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "NOTE");
\r
508 updateSequenceNumber = noteStore.deleteNote(authToken, expunged.get(i).guid);
509 refreshNeeded = true;
510 conn.getSyncTable().setLastSequenceDate(sequenceDate);
511 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
513 if (expunged.get(i).type.equalsIgnoreCase("SAVEDSEARCH")) {
514 logger.log(logger.EXTREME, "saved search expunged");
515 conn.getDeletedTable().expungeDeletedItem(expunged.get(i).guid, "SAVEDSEARCH");
\r
516 updateSequenceNumber = noteStore.expungeSearch(authToken, expunged.get(i).guid);
517 conn.getSyncTable().setLastSequenceDate(sequenceDate);
518 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
520 } catch (EDAMUserException e) {
521 logger.log(logger.LOW, "EDAM User Excepton in syncExpunged: " +expunged.get(i).guid); // This can happen if we try to delete a deleted note
522 } catch (EDAMSystemException e) {
523 logger.log(logger.LOW, "EDAM System Excepton in syncExpunged: "+expunged.get(i).guid);
524 logger.log(logger.LOW, e.getStackTrace());
526 } catch (EDAMNotFoundException e) {
527 logger.log(logger.LOW, "EDAM Not Found Excepton in syncExpunged: "+expunged.get(i).guid);
528 } catch (TException e) {
529 logger.log(logger.LOW, "EDAM TExcepton in syncExpunged: "+expunged.get(i).guid);
530 logger.log(logger.LOW, e.getStackTrace());
535 conn.getDeletedTable().expungeAllDeletedRecords();
537 logger.log(logger.HIGH, "Leaving SyncRunner.syncExpunged");
540 private void syncDeletedNotes(Client noteStore) {
541 if (syncDeletedContent)
543 logger.log(logger.HIGH, "Entering SyncRunner.syncDeletedNotes");
544 status.message.emit(tr("Synchronizing deleted notes."));
546 List<Note> notes = conn.getNoteTable().getDirty();
547 // Sync the local notebooks with Evernote's
548 for (int i=0; i<notes.size() && keepRunning; i++) {
550 // if (authRefreshNeeded)
551 // if (!refreshConnection())
554 Note enNote = notes.get(i);
556 if (enNote.getUpdateSequenceNum() > 0 && (enNote.isActive() == false || enNote.getDeleted() > 0)) {
557 // Check that the note is valid.
\r
558 if (enNote.isActive() == true || enNote.getDeleted() == 0) {
\r
559 conn.getNoteTable().deleteNote(enNote.getGuid());
\r
560 enNote = conn.getNoteTable().getNote(enNote.getGuid(), false, false, false, false, false);
\r
562 if (syncDeletedContent) {
563 logger.log(logger.EXTREME, "Deleted note found & synch content selected");
564 Note delNote = conn.getNoteTable().getNote(enNote.getGuid(), true, true, true, true, true);
565 delNote = getNoteContent(delNote);
566 delNote = noteStore.updateNote(authToken, delNote);
567 enNote.setUpdateSequenceNum(delNote.getUpdateSequenceNum());
568 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
570 logger.log(logger.EXTREME, "Deleted note found & sync content not selected");
571 int usn = noteStore.deleteNote(authToken, enNote.getGuid());
572 enNote.setUpdateSequenceNum(usn);
573 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
575 logger.log(logger.EXTREME, "Resetting deleted dirty flag");
576 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());
577 updateSequenceNumber = enNote.getUpdateSequenceNum();
578 logger.log(logger.EXTREME, "Saving sequence number");
579 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
581 } catch (EDAMUserException e) {
582 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);
\r
583 //status.message.emit("Error sending local note: " +e.getParameter());
584 //logger.log(logger.LOW, e.toString());
586 } catch (EDAMSystemException e) {
587 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);
588 status.message.emit(tr("Error: ") +e);
589 logger.log(logger.LOW, e.toString());
591 } catch (EDAMNotFoundException e) {
592 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);
\r
593 //status.message.emit("Error deleting local note: " +e +" - Continuing");
594 //logger.log(logger.LOW, e.toString());
596 } catch (TException e) {
597 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);
598 status.message.emit(tr("Error sending local note: ") +e);
599 logger.log(logger.LOW, e.toString());
604 // Sync notes with Evernote
605 private void syncLocalNotes() {
606 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");
607 logger.log(logger.LOW, "Dirty local notes found: " +new Integer(conn.getNoteTable().getDirtyCount()).toString());
608 status.message.emit(tr("Sending local notes."));
610 List<Note> notes = conn.getNoteTable().getDirty();
611 // Sync the local notebooks with Evernote's
612 for (int i=0; i<notes.size() && keepRunning; i++) {
613 syncLocalNote(localNoteStore, notes.get(i), authToken);
615 logger.log(logger.HIGH, "Leaving SyncRunner.syncNotes");
618 // Sync notes with Evernote
619 private void syncLocalNote(Client noteStore, Note enNote, String token) {
620 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");
621 status.message.emit(tr("Sending local notes."));
623 if (enNote.isActive()) {
625 if (enNote.getUpdateSequenceNum() > 0) {
626 logger.log(logger.EXTREME, "Active dirty note found - non new - " +enNote.getGuid());
627 logger.log(logger.EXTREME, "Fetching note content");
628 enNote = getNoteContent(enNote);
629 logger.log(logger.MEDIUM, "Updating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
630 enNote = noteStore.updateNote(token, enNote);
632 logger.log(logger.EXTREME, "Active dirty found - new note " +enNote.getGuid());
633 String oldGuid = enNote.getGuid();
634 logger.log(logger.MEDIUM, "Fetching note content");
635 enNote = getNoteContent(enNote);
636 logger.log(logger.MEDIUM, "Creating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
637 enNote = noteStore.createNote(token, enNote);
638 logger.log(logger.MEDIUM, "New note Guid : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
639 noteSignal.guidChanged.emit(oldGuid, enNote.getGuid());
640 conn.getNoteTable().updateNoteGuid(oldGuid, enNote.getGuid());
642 updateSequenceNumber = enNote.getUpdateSequenceNum();
643 logger.log(logger.EXTREME, "Saving note");
644 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
645 List<Resource> rl = enNote.getResources();
646 logger.log(logger.EXTREME, "Getting note resources");
647 for (int j=0; j<enNote.getResourcesSize() && keepRunning; j++) {
648 Resource newRes = rl.get(j);
649 Data d = newRes.getData();
651 logger.log(logger.EXTREME, "Calculating resource hash");
652 String hash = byteArrayToHexString(d.getBodyHash());
653 logger.log(logger.EXTREME, "updating resources by hash");
654 String oldGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(enNote.getGuid(), hash);
655 conn.getNoteTable().updateNoteResourceGuidbyHash(enNote.getGuid(), newRes.getGuid(), hash);
656 resourceSignal.resourceGuidChanged.emit(enNote.getGuid(), oldGuid, newRes.getGuid());
659 logger.log(logger.EXTREME, "Resetting note dirty flag");
660 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());
661 updateSequenceNumber = enNote.getUpdateSequenceNum();
662 logger.log(logger.EXTREME, "Emitting note sequence number change");
663 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
665 } catch (EDAMUserException e) {
666 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);
667 status.message.emit(tr("Error sending local note: ") +e.getParameter());
668 logger.log(logger.LOW, e.toString());
670 } catch (EDAMSystemException e) {
671 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);
672 status.message.emit(tr("Error: ") +e);
673 logger.log(logger.LOW, e.toString());
675 } catch (EDAMNotFoundException e) {
676 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);
677 status.message.emit(tr("Error sending local note: ") +e);
678 logger.log(logger.LOW, e.toString());
680 } catch (TException e) {
681 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);
682 status.message.emit(tr("Error sending local note: ") +e);
683 logger.log(logger.LOW, e.toString());
687 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNote");
691 // Sync Notebooks with Evernote
692 private void syncLocalNotebooks(Client noteStore) {
693 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalNotebooks");
695 status.message.emit(tr("Sending local notebooks."));
696 List<Notebook> remoteList = new ArrayList<Notebook>();
698 logger.log(logger.EXTREME, "Getting remote notebooks to compare with local");
699 remoteList = noteStore.listNotebooks(authToken);
700 } catch (EDAMUserException e1) {
701 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks getting remote Notebook List");
702 status.message.emit(tr("Error: ") +e1);
703 logger.log(logger.LOW, e1.toString());
705 } catch (EDAMSystemException e1) {
706 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks getting remote Notebook List");
707 status.message.emit(tr("Error: ") +e1);
708 logger.log(logger.LOW, e1.toString());
710 } catch (TException e1) {
711 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalNotebooks getting remote Notebook List");
712 status.message.emit(tr("Error: ") +e1);
713 logger.log(logger.LOW, e1.toString());
716 logger.log(logger.EXTREME, "Getting local dirty notebooks");
717 List<Notebook> notebooks = conn.getNotebookTable().getDirty();
719 // Sync the local notebooks with Evernote's
720 for (int i=0; i<notebooks.size() && keepRunning; i++) {
722 // if (authRefreshNeeded)
723 // if (!refreshConnection())
726 Notebook enNotebook = notebooks.get(i);
728 if (enNotebook.getUpdateSequenceNum() > 0) {
729 logger.log(logger.EXTREME, "Existing notebook is dirty");
730 sequence = noteStore.updateNotebook(authToken, enNotebook);
732 logger.log(logger.EXTREME, "New dirty notebook found");
733 String oldGuid = enNotebook.getGuid();
734 boolean found = false;
736 // Look for a notebook with the same name. If one is found, we don't need
737 // to create another one
738 logger.log(logger.EXTREME, "Looking for matching notebook name");
739 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
740 if (remoteList.get(k).getName().equalsIgnoreCase(enNotebook.getName())) {
741 enNotebook = remoteList.get(k);
742 logger.log(logger.EXTREME, "Matching notebook found");
747 enNotebook = noteStore.createNotebook(authToken, enNotebook);
749 logger.log(logger.EXTREME, "Updating notebook in database");
750 conn.getNotebookTable().updateNotebookGuid(oldGuid, enNotebook.getGuid());
751 sequence = enNotebook.getUpdateSequenceNum();
753 logger.log(logger.EXTREME, "Updating notebook sequence in database");
754 conn.getNotebookTable().updateNotebookSequence(enNotebook.getGuid(), sequence);
755 logger.log(logger.EXTREME, "Resetting dirty flag in notebook");
756 conn.getNotebookTable().resetDirtyFlag(enNotebook.getGuid());
757 updateSequenceNumber = sequence;
758 logger.log(logger.EXTREME, "Emitting sequence number to main thread");
759 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
760 } catch (EDAMUserException e) {
761 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks");
762 logger.log(logger.LOW, e.toString() + ": Stack : " +enNotebook.getStack());
764 } catch (EDAMSystemException e) {
765 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks");
766 logger.log(logger.LOW, e.toString());
768 } catch (EDAMNotFoundException e) {
769 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotebooks");
770 logger.log(logger.LOW, e.toString());
772 } catch (TException e) {
773 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotebooks");
774 logger.log(logger.LOW, e.toString());
778 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNotebooks");
781 // Sync Tags with Evernote
782 private void syncLocalTags(Client noteStore) {
783 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalTags");
784 List<Tag> remoteList = new ArrayList<Tag>();
785 status.message.emit(tr("Sending local tags."));
788 logger.log(logger.EXTREME, "Getting remote tags to compare names with the local tags");
789 remoteList = noteStore.listTags(authToken);
790 } catch (EDAMUserException e1) {
791 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote Tag List");
792 status.message.emit(tr("Error: ") +e1);
793 logger.log(logger.LOW, e1.toString());
795 } catch (EDAMSystemException e1) {
796 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote Tag List");
797 status.message.emit(tr("Error: ") +e1);
798 logger.log(logger.LOW, e1.toString());
800 } catch (TException e1) {
801 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote Tag List");
802 status.message.emit(tr("Error: ") +e1);
803 logger.log(logger.LOW, e1.toString());
809 if (badTagSync == null)
810 badTagSync = new HashMap<String,String>();
814 Tag enTag = findNextTag();
816 // This is a hack. Sometimes this function goes flookey and goes into a
817 // perpetual loop. This causes NeverNote to flood Evernote's servers.
818 // This is a safety valve to prevent unlimited loops.
819 int maxCount = conn.getTagTable().getDirty().size()+10;
822 while(enTag!=null && loopCount < maxCount) {
824 // if (authRefreshNeeded)
825 // if (!refreshConnection())
829 if (enTag.getUpdateSequenceNum() > 0) {
830 logger.log(logger.EXTREME, "Updating tag");
831 sequence = noteStore.updateTag(authToken, enTag);
834 // Look for a tag with the same name. If one is found, we don't need
835 // to create another one
836 logger.log(logger.EXTREME, "New tag. Comparing with remote names");
837 boolean found = false;
838 String oldGuid = enTag.getGuid();
839 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
840 if (remoteList.get(k).getName().equalsIgnoreCase(enTag.getName())) {
841 conn.getTagTable().updateTagGuid(enTag.getGuid(), remoteList.get(k).getGuid());
842 enTag = remoteList.get(k);
843 logger.log(logger.EXTREME, "Matching tag name found");
848 enTag = noteStore.createTag(authToken, enTag);
850 enTag.setUpdateSequenceNum(noteStore.updateTag(authToken,enTag));
851 sequence = enTag.getUpdateSequenceNum();
852 if (!oldGuid.equals(enTag.getGuid())) {
853 logger.log(logger.EXTREME, "Updating tag guid");
854 conn.getTagTable().updateTagGuid(oldGuid, enTag.getGuid());
857 logger.log(logger.EXTREME, "Updating tag sequence number");
858 conn.getTagTable().updateTagSequence(enTag.getGuid(), sequence);
859 logger.log(logger.EXTREME, "Resetting tag dirty flag");
860 conn.getTagTable().resetDirtyFlag(enTag.getGuid());
861 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");
862 updateSequenceNumber = sequence;
863 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
864 } catch (EDAMUserException e) {
865 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags: " +enTag.getName());
866 logger.log(logger.LOW, e.toString());
867 badTagSync.put(enTag.getGuid(),null);
869 } catch (EDAMSystemException e) {
870 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags: " +enTag.getName());
871 logger.log(logger.LOW, e.toString());
872 badTagSync.put(enTag.getGuid(),null);
874 } catch (EDAMNotFoundException e) {
875 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags: " +enTag.getName());
876 logger.log(logger.LOW, e.toString());
877 badTagSync.put(enTag.getGuid(),null);
879 } catch (TException e) {
880 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags: " +enTag.getName());
881 logger.log(logger.LOW, e.toString());
882 badTagSync.put(enTag.getGuid(),null);
887 logger.log(logger.EXTREME, "Finding next tag");
888 enTag = findNextTag();
890 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalTags");
892 private void syncLocalLinkedNotebooks(Client noteStore) {
893 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalLinkedNotebooks");
895 List<String> list = conn.getLinkedNotebookTable().getDirtyGuids();
896 for (int i=0; i<list.size(); i++) {
897 LinkedNotebook book = conn.getLinkedNotebookTable().getNotebook(list.get(i));
899 noteStore.updateLinkedNotebook(authToken, book);
900 } catch (EDAMUserException e) {
901 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalLinkedNotebooks");
902 status.message.emit(tr("Error: ") +e);
903 logger.log(logger.LOW, e.toString());
906 } catch (EDAMNotFoundException e) {
907 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalLinkedNotebooks");
908 status.message.emit(tr("Error: ") +e);
909 logger.log(logger.LOW, e.toString());
912 } catch (EDAMSystemException e) {
913 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalLinkedNotebooks");
914 status.message.emit(tr("Error: ") +e);
915 logger.log(logger.LOW, e.toString());
918 } catch (TException e) {
919 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalLinkedNotebooks");
920 status.message.emit(tr("Error: ") +e);
921 logger.log(logger.LOW, e.toString());
926 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalLinkedNotebooks");
928 // Sync Saved Searches with Evernote
929 private void syncLocalSavedSearches(Client noteStore) {
930 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");
931 List<SavedSearch> remoteList = new ArrayList<SavedSearch>();
932 status.message.emit(tr("Sending saved searches."));
934 logger.log(logger.EXTREME, "Getting saved searches to compare with local");
936 remoteList = noteStore.listSearches(authToken);
937 } catch (EDAMUserException e1) {
938 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote saved search List");
939 status.message.emit(tr("Error: ") +e1);
940 logger.log(logger.LOW, e1.toString());
942 } catch (EDAMSystemException e1) {
943 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote saved search List");
944 status.message.emit(tr("Error: ") +e1);
945 logger.log(logger.LOW, e1.toString());
947 } catch (TException e1) {
948 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote saved search List");
949 status.message.emit(tr("Error: ") +e1);
950 logger.log(logger.LOW, e1.toString());
954 List<SavedSearch> searches = conn.getSavedSearchTable().getDirty();
956 // Sync the local notebooks with Evernote's
957 logger.log(logger.EXTREME, "Beginning to send saved searches");
958 for (int i=0; i<searches.size() && keepRunning; i++) {
960 // if (authRefreshNeeded)
961 // if (!refreshConnection())
964 SavedSearch enSearch = searches.get(i);
966 if (enSearch.getUpdateSequenceNum() > 0)
967 sequence = noteStore.updateSearch(authToken, enSearch);
969 logger.log(logger.EXTREME, "New saved search found.");
970 // Look for a tag with the same name. If one is found, we don't need
971 // to create another one
972 boolean found = false;
973 logger.log(logger.EXTREME, "Matching remote saved search names with local");
974 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
975 if (remoteList.get(k).getName().equalsIgnoreCase(enSearch.getName())) {
976 enSearch = remoteList.get(k);
978 logger.log(logger.EXTREME, "Matching saved search found");
979 sequence = enSearch.getUpdateSequenceNum();
983 String oldGuid = enSearch.getGuid();
985 enSearch = noteStore.createSearch(authToken, enSearch);
986 sequence = enSearch.getUpdateSequenceNum();
987 logger.log(logger.EXTREME, "Updating tag guid in local database");
988 conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, enSearch.getGuid());
990 logger.log(logger.EXTREME, "Updating tag sequence in local database");
991 conn.getSavedSearchTable().updateSavedSearchSequence(enSearch.getGuid(), sequence);
992 logger.log(logger.EXTREME, "Resetting tag dirty flag");
993 conn.getSavedSearchTable().resetDirtyFlag(enSearch.getGuid());
994 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");
995 updateSequenceNumber = sequence;
996 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
997 } catch (EDAMUserException e) {
998 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");
999 logger.log(logger.LOW, e.toString());
1001 } catch (EDAMSystemException e) {
1002 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");
1003 logger.log(logger.LOW, e.toString());
1005 } catch (EDAMNotFoundException e) {
1006 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");
1007 logger.log(logger.LOW, e.toString());
1009 } catch (TException e) {
1010 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");
1011 logger.log(logger.LOW, e.toString());
1016 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");
1019 // Sync evernote changes with local database
1020 private void syncRemoteToLocal(Client noteStore) {
1021 logger.log(logger.HIGH, "Entering SyncRunner.syncRemoteToLocal");
1023 List<Note> dirtyNotes = conn.getNoteTable().getDirty();
1024 dirtyNoteGuids = new ArrayList<String>();
\r
1025 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {
1026 dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());
1030 SyncChunk chunk = null;
1031 boolean fullSync = false;
1032 boolean more = true;
1034 if (updateSequenceNumber == 0)
1037 status.message.emit(tr("Downloading 0% complete."));
1039 while(more && keepRunning) {
1041 // if (authRefreshNeeded)
1042 // if (!refreshConnection())
1045 int sequence = updateSequenceNumber;
1047 // conn.beginTransaction();
1048 logger.log(logger.EXTREME, "Getting chunk from Evernote");
1049 chunk = noteStore.getSyncChunk(authToken, sequence, chunkSize, fullSync);
1050 logger.log(logger.LOW, "Chunk High Sequence: " +chunk.getChunkHighUSN());
1051 } catch (EDAMUserException e) {
1053 e.printStackTrace();
1054 status.message.emit(e.getMessage());
1055 } catch (EDAMSystemException e) {
1057 e.printStackTrace();
1058 status.message.emit(e.getMessage());
1059 } catch (TException e) {
1061 e.printStackTrace();
1062 status.message.emit(e.getMessage());
1064 if (error || chunk == null)
1069 syncRemoteTags(chunk.getTags());
1070 syncRemoteSavedSearches(chunk.getSearches());
1071 syncRemoteNotebooks(chunk.getNotebooks());
1072 syncRemoteNotes(noteStore, chunk.getNotes(), fullSync, authToken);
1073 syncRemoteResources(noteStore, chunk.getResources());
1074 syncRemoteLinkedNotebooks(chunk.getLinkedNotebooks());
1076 // Signal about any updated notes to invalidate the cache
1077 for (int i=0; i<chunk.getNotesSize(); i++)
1078 noteSignal.noteChanged.emit(chunk.getNotes().get(i).getGuid(), null);
1079 syncExpungedNotes(chunk);
1082 // Check for more notes
1083 if (chunk.getChunkHighUSN() <= updateSequenceNumber)
1087 logger.log(logger.EXTREME, "More notes? " +more);
1090 // Save the chunk sequence number
1091 if (!error && chunk.getChunkHighUSN() > 0 && keepRunning) {
1092 logger.log(logger.EXTREME, "emitting sequence number to main thread");
1093 updateSequenceNumber = chunk.getChunkHighUSN();
1094 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());
1095 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
1096 // conn.commitTransaction();
1101 long pct = chunk.getChunkHighUSN() * 100;
1102 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());
1103 pct = pct/evernoteUpdateCount;
1104 status.message.emit(tr("Downloading ") +new Long(pct).toString()+tr("% complete."));
1106 // conn.commitTransaction();
1108 logger.log(logger.HIGH, "Leaving SyncRunner.syncRemoteToLocal");
1110 // Sync expunged notes
1111 private void syncExpungedNotes(SyncChunk chunk) {
1112 // Do the local deletes
1113 logger.log(logger.EXTREME, "Doing local deletes");
1114 List<String> guid = chunk.getExpungedNotes();
1116 for (int i=0; i<guid.size() && keepRunning; i++) {
1117 String notebookGuid = "";
1118 Note localNote = conn.getNoteTable().getNote(guid.get(i), false, false, false, false, false);
1119 if (localNote != null) {
1120 conn.getNoteTable().updateNoteSequence(guid.get(i), 0);
1121 notebookGuid = localNote.getNotebookGuid();
1123 // If the note is in a local notebook (which means we moved it) or if the
\r
1124 // note returned is null (which means it is already deleted or flagged expunged)
\r
1126 if (!conn.getNotebookTable().isNotebookLocal(notebookGuid) || localNote == null) {
\r
1127 logger.log(logger.EXTREME, "Expunging local note from database");
1128 conn.getNoteTable().expungeNote(guid.get(i), true, false);
1132 guid = chunk.getExpungedNotebooks();
1134 for (int i=0; i<guid.size() && keepRunning; i++) {
1135 logger.log(logger.EXTREME, "Expunging local notebook from database");
1136 conn.getNotebookTable().expungeNotebook(guid.get(i), false);
1138 guid = chunk.getExpungedTags();
1140 for (int i=0; i<guid.size() && keepRunning; i++) {
1141 logger.log(logger.EXTREME, "Expunging tags from local database");
1142 conn.getTagTable().expungeTag(guid.get(i), false);
1144 guid = chunk.getExpungedSearches();
1146 for (int i=0; i<guid.size() && keepRunning; i++) {
1147 logger.log(logger.EXTREME, "Expunging saved search from local database");
1148 conn.getSavedSearchTable().expungeSavedSearch(guid.get(i), false);
1150 guid = chunk.getExpungedLinkedNotebooks();
1152 for (int i=0; i<guid.size() && keepRunning; i++) {
1153 logger.log(logger.EXTREME, "Expunging linked notebook from local database");
1154 conn.getLinkedNotebookTable().expungeNotebook(guid.get(i), false);
1159 private void syncRemoteTags(List<Tag> tags) {
1160 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");
1162 for (int i=0; i<tags.size() && keepRunning; i++) {
1164 oldGuid = conn.getTagTable().findTagByName(tags.get(i).getName());
1165 if (oldGuid != null && !tags.get(i).getGuid().equalsIgnoreCase(oldGuid))
1166 conn.getTagTable().updateTagGuid(oldGuid, tags.get(i).getGuid());
1167 conn.getTagTable().syncTag(tags.get(i), false);
1170 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");
1172 // Sync remote saved searches
1173 private void syncRemoteSavedSearches(List<SavedSearch> searches) {
1174 logger.log(logger.EXTREME, "Entering SyncRunner.syncSavedSearches");
1175 if (searches != null) {
1176 for (int i=0; i<searches.size() && keepRunning; i++) {
1178 oldGuid = conn.getSavedSearchTable().findSavedSearchByName(searches.get(i).getName());
1179 if (oldGuid != null && !searches.get(i).getGuid().equalsIgnoreCase(oldGuid))
1180 conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, searches.get(i).getGuid());
1181 conn.getSavedSearchTable().syncSavedSearch(searches.get(i), false);
1184 logger.log(logger.EXTREME, "Leaving SyncRunner.syncSavedSearches");
1186 // Sync remote linked notebooks
1187 private void syncRemoteLinkedNotebooks(List<LinkedNotebook> books) {
1188 logger.log(logger.EXTREME, "Entering SyncRunner.syncLinkedNotebooks");
1189 if (books != null) {
1190 for (int i=0; i<books.size() && keepRunning; i++) {
1191 conn.getLinkedNotebookTable().updateNotebook(books.get(i), false);
1194 logger.log(logger.EXTREME, "Leaving SyncRunner.syncLinkedNotebooks");
1196 // Sync remote Notebooks 2
1197 private void syncRemoteNotebooks(List<Notebook> notebooks) {
1198 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");
1199 if (notebooks != null) {
1200 for (int i=0; i<notebooks.size() && keepRunning; i++) {
1202 oldGuid = conn.getNotebookTable().findNotebookByName(notebooks.get(i).getName());
1203 if (oldGuid != null && !conn.getNotebookTable().isNotebookLocal(oldGuid) && !notebooks.get(i).getGuid().equalsIgnoreCase(oldGuid))
1204 conn.getNotebookTable().updateNotebookGuid(oldGuid, notebooks.get(i).getGuid());
1205 conn.getNotebookTable().syncNotebook(notebooks.get(i), false);
1207 // Synchronize shared notebook information
1208 // if (notebooks.get(i).getSharedNotebookIdsSize() > 0) {
1209 // conn.getSharedNotebookTable().expungeNotebookByGuid(notebooks.get(i).getGuid(), false);
1210 // for (int j=0; j<notebooks.get(i).getSharedNotebookIdsSize(); j++) {
1211 // syncRemoteSharedNotebook(notebooks.get(i).getGuid(), notebooks.get(i).getSharedNotebookIds().get(j), authToken);
1216 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");
1218 // Sync remote shared notebook
1219 // private void syncRemoteSharedNotebook(String guid, Long id, String token) {
1220 // List<SharedNotebook> books = noteStore.getSharedNotebookByAuth(authToken);
1222 // Sync remote Resources
1223 private void syncRemoteResources(Client noteStore, List<Resource> resource) {
1224 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteResources");
1225 if (resource != null) {
1226 for (int i=0; i<resource.size() && keepRunning; i++) {
1227 syncRemoteResource(noteStore, resource.get(i), authToken);
1230 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteResources");
1232 // Sync remote resource
1233 private void syncRemoteResource(Client noteStore, Resource resource, String authToken) {
1234 // This is how the logic for this works.
1235 // 1.) If the resource is not in the local database, we add it.
1236 // 2.) If a copy of the resource is in the local database and the note isn't dirty, we update the local copy
1237 // 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
1238 // is a conflict. The note conflict should get a copy of the resource at that time.
1240 Note n = conn.getNoteTable().getNote(resource.getNoteGuid(), false, false, false, false, false);
1242 logger.log(logger.HIGH, "Resource for note " +n.getGuid() +" : " +n.getTitle());
1244 boolean saveNeeded = false;
1245 /* #1 */ Resource r = getEvernoteResource(noteStore, resource.getGuid(), true,true,true, authToken);
1246 Resource l = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), false);
1248 logger.log(logger.HIGH, "Local resource not found");
1251 /* #2 */ boolean isNoteDirty = conn.getNoteTable().isNoteDirty(r.getNoteGuid());
1253 logger.log(logger.HIGH, "Local resource found, but is not dirty");
1256 /* #3 */ String remoteHash = "";
1257 if (r != null && r.getData() != null && r.getData().getBodyHash() != null)
1258 remoteHash = byteArrayToHexString(r.getData().getBodyHash());
1259 String localHash = "";
1260 if (l != null && l.getData() != null && l.getData().getBodyHash() != null)
1261 remoteHash = byteArrayToHexString(l.getData().getBodyHash());
1263 if (localHash.equalsIgnoreCase(remoteHash))
1268 logger.log(logger.HIGH, "Resource save needed: " +saveNeeded);
1270 conn.getNoteTable().noteResourceTable.updateNoteResource(r, false);
1271 if (r.getMime().equalsIgnoreCase("application/vnd.evernote.ink"))
1272 downloadInkNoteImage(r.getGuid(), authToken);
1276 // Sync remote notes
1277 private void syncRemoteNotes(Client noteStore, List<Note> note, boolean fullSync, String token) {
1281 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotes");
\r
1282 logger.log(logger.LOW, "Local Dirty Notes: ");
\r
1283 logger.log(logger.LOW, "Remote Dirty Notes:");
\r
1284 for (int i=0; i<note.size();i++) {
\r
1285 logger.log(logger.LOW, i +" : " +note.get(i).getGuid() + " : " +note.get(i).getTitle() );
\r
1287 logger.log(logger.LOW, "---");
\r
1289 for (int i=0; i<note.size() && keepRunning; i++) {
1290 Note n = getEvernoteNote(noteStore, note.get(i).getGuid(), true, fullSync, true,true, token);
1291 syncRemoteNote(n, fullSync, token);
1294 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotes");
1296 private void syncRemoteNote(Note n, boolean fullSync, String token) {
1299 // Basically, this is how the sync logic for a note works.
1300 // If the remote note has changed and the local has not, we
1301 // accept the change.
1302 // If both the local & remote have changed but the sequence
1303 // numbers are the same, we don't accept the change. This
1304 // seems to happen when attachments are indexed by the server.
1305 // If both the local & remote have changed and the sequence numbers
1306 // are different we move the local copy to a local notebook (making sure
1307 // to copy all resources) and we accept the new one.
1308 boolean conflictingNote = true;
1309 logger.log(logger.EXTREME, "Checking for duplicate note " +n.getGuid() +" : " +n.getTitle());
\r
1310 if (dirtyNoteGuids != null && dirtyNoteGuids.contains(n.getGuid())) {
1311 logger.log(logger.EXTREME, "Conflict check beginning");
1312 conflictingNote = checkForConflict(n);
1313 logger.log(logger.EXTREME, "Conflict check results " +conflictingNote);
1314 if (conflictingNote)
1315 moveConflictingNote(n.getGuid());
1317 boolean ignoreNote = false;
1318 if (ignoreNotebooks.contains(n.getNotebookGuid()))
1320 for (int i=0; i<n.getTagGuidsSize(); i++) {
1321 if (ignoreTags.contains(n.getTagGuids().get(i))) {
1323 i=n.getTagGuidsSize();
1327 if ((conflictingNote || fullSync) && !ignoreNote) {
1328 logger.log(logger.EXTREME, "Saving Note");
1329 conn.getNoteTable().syncNote(n);
1330 // The following was commented out because it caused a race condition on the database where resources
1331 // may be lost. We do the same thing elsewhere;.
1332 // noteSignal.noteChanged.emit(n.getGuid(), null); // Signal to ivalidate note cache
1333 noteSignal.noteDownloaded.emit(n, true); // Signal to add note to index
1334 logger.log(logger.EXTREME, "Note Saved");
1335 if (fullSync && n.getResources() != null) {
1336 for (int q=0; q<n.getResources().size() && keepRunning; q++) {
1337 logger.log(logger.EXTREME, "Getting note resources.");
1338 conn.getNoteTable().noteResourceTable.updateNoteResource(n.getResources().get(q), false);
1339 if (n.getResources().get(q).getMime().equalsIgnoreCase("application/vnd.evernote.ink"))
1340 downloadInkNoteImage(n.getResources().get(q).getGuid(), token);
1346 private Note getEvernoteNote(Client noteStore, String guid, boolean withContent, boolean withResourceData, boolean withResourceRecognition, boolean withResourceAlternateData, String token) {
1349 logger.log(logger.EXTREME, "Retrieving note " +guid);
1350 n = noteStore.getNote(token, guid, withContent, withResourceData, withResourceRecognition, withResourceAlternateData);
1351 logger.log(logger.EXTREME, "Note " +guid +" has been retrieved.");
1352 } catch (EDAMUserException e) {
1353 logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");
1354 logger.log(logger.LOW, e.toString());
1356 e.printStackTrace();
1357 } catch (EDAMSystemException e) {
1358 logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");
1359 logger.log(logger.LOW, e.toString());
1361 e.printStackTrace();
1362 } catch (EDAMNotFoundException e) {
1363 logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");
1364 logger.log(logger.LOW, e.toString());
1366 e.printStackTrace();
1367 } catch (TException e) {
1368 logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");
1369 logger.log(logger.LOW, e.toString());
1371 e.printStackTrace();
1375 private Resource getEvernoteResource(Client noteStore, String guid, boolean withData, boolean withRecognition, boolean withAttributes, String token) {
1378 logger.log(logger.EXTREME, "Retrieving resource " +guid);
1379 n = noteStore.getResource(token, guid, withData, withRecognition, withAttributes, withAttributes);
1380 logger.log(logger.EXTREME, "Resource " +guid +" has been retrieved.");
1381 } catch (EDAMUserException e) {
1382 logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");
1383 logger.log(logger.LOW, e.toString());
1385 e.printStackTrace();
1386 } catch (EDAMSystemException e) {
1387 logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");
1388 logger.log(logger.LOW, e.toString());
1390 e.printStackTrace();
1391 } catch (EDAMNotFoundException e) {
1392 logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");
1393 logger.log(logger.LOW, e.toString());
1395 e.printStackTrace();
1396 } catch (TException e) {
1397 logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");
1398 logger.log(logger.LOW, e.toString());
1400 e.printStackTrace();
1406 private boolean checkForConflict(Note n) {
1407 logger.log(logger.EXTREME, "Checking note sequence number " +n.getGuid());
1408 Note oldNote = conn.getNoteTable().getNote(n.getGuid(), false, false, false, false, false);
1409 logger.log(logger.EXTREME, "Local/Remote sequence numbers: " +oldNote.getUpdateSequenceNum()+"/"+n.getUpdateSequenceNum());
1410 logger.log(logger.LOW, "Remote Note Title:" +n.getTitle());
\r
1411 logger.log(logger.LOW, "Local Note Title:" +oldNote.getTitle());
\r
1412 if (oldNote.getUpdateSequenceNum() == n.getUpdateSequenceNum()) {
\r
1415 boolean oldIsDirty = conn.getNoteTable().isNoteDirty(n.getGuid());
\r
1421 private void moveConflictingNote(String guid) {
1422 logger.log(logger.EXTREME, "Conflicting change found for note " +guid);
1423 List<Notebook> books = conn.getNotebookTable().getAllLocal();
1424 String notebookGuid = null;
1425 for (int i=0; i<books.size() && keepRunning; i++) {
1426 if (books.get(i).getName().equalsIgnoreCase("Conflicting Changes (local)") ||
1427 books.get(i).getName().equalsIgnoreCase("Conflicting Changes")) {
1428 notebookGuid = books.get(i).getGuid();
1433 if (notebookGuid == null) {
1434 logger.log(logger.EXTREME, "Building conflicting change notebook " +guid);
1435 Calendar currentTime = new GregorianCalendar();
1436 Long l = new Long(currentTime.getTimeInMillis());
1438 while (prevTime==l) {
1439 currentTime = new GregorianCalendar();
1440 l=currentTime.getTimeInMillis();
1442 String randint = new String(Long.toString(l));
1444 Notebook newBook = new Notebook();
1445 newBook.setUpdateSequenceNum(0);
1446 newBook.setGuid(randint);
1447 newBook.setName("Conflicting Changes");
1448 newBook.setServiceCreated(new Date().getTime());
1449 newBook.setServiceUpdated(new Date().getTime());
1450 newBook.setDefaultNotebook(false);
1451 newBook.setPublished(false);
1453 conn.getNotebookTable().addNotebook(newBook, false, true);
1454 notebookSignal.listChanged.emit();
1455 notebookGuid = newBook.getGuid();
1456 refreshNeeded = true;
1459 // Now that we have a good notebook guid, we need to move the conflicting note
1460 // to the local notebook
1461 logger.log(logger.EXTREME, "Moving conflicting note " +guid);
1462 Calendar currentTime = new GregorianCalendar();
1463 Long l = new Long(currentTime.getTimeInMillis());
1465 while (prevTime==l) {
1466 currentTime = new GregorianCalendar();
1467 l = currentTime.getTimeInMillis();
1469 String newGuid = new String(Long.toString(l));
1471 Note oldNote = conn.getNoteTable().getNote(guid, true, true, false, false, false);
1472 for (int i=0; i<oldNote.getResources().size() && keepRunning; i++) {
1473 l = new Long(currentTime.getTimeInMillis());
1474 String newResG = new String(Long.toString(l));
1475 String oldResG = oldNote.getResources().get(i).getGuid();
1476 conn.getNoteTable().noteResourceTable.resetUpdateSequenceNumber(oldResG, true);
1477 conn.getNoteTable().noteResourceTable.updateNoteResourceGuid(oldResG, newResG, true);
1480 conn.getNoteTable().resetNoteSequence(guid);
1481 conn.getNoteTable().updateNoteGuid(guid, newGuid);
1482 conn.getNoteTable().updateNoteNotebook(newGuid, notebookGuid, true);
1484 noteSignal.notebookChanged.emit(newGuid, notebookGuid);
1485 refreshNeeded = true;
1486 noteSignal.guidChanged.emit(guid,newGuid);
1492 //******************************************************
1493 //******************************************************
1494 //** Utility Functions
1495 //******************************************************
1496 //******************************************************
1497 // Convert a byte array to a hex string
1498 private static String byteArrayToHexString(byte data[]) {
1499 StringBuffer buf = new StringBuffer();
1500 for (byte element : data) {
1501 int halfbyte = (element >>> 4) & 0x0F;
1504 if ((0 <= halfbyte) && (halfbyte <= 9))
1505 buf.append((char) ('0' + halfbyte));
1507 buf.append((char) ('a' + (halfbyte - 10)));
1508 halfbyte = element & 0x0F;
1509 } while(two_halfs++ < 1);
1511 return buf.toString();
1516 //*******************************************************
1517 //* Find dirty tags, which do not have newly created parents
1518 //*******************************************************
1519 private Tag findNextTag() {
1520 logger.log(logger.HIGH, "Entering SyncRunner.findNextTag");
1522 List<Tag> tags = conn.getTagTable().getDirty();
1524 // Find the parent. If the parent has a sequence > 0 then it is a good
1526 for (int i=0; i<tags.size() && keepRunning; i++) {
1527 if (!badTagSync.containsKey(tags.get(i).getGuid())) {
1528 if (tags.get(i).getParentGuid() == null) {
1529 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found without parent");
1532 Tag parentTag = conn.getTagTable().getTag(tags.get(i).getParentGuid());
1533 if (parentTag.getUpdateSequenceNum() > 0) {
1534 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found");
1540 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - no tags returned");
1545 // Connect to Evernote
1546 public boolean enConnect() {
1548 userStoreTrans = new THttpClient(userStoreUrl);
1549 userStoreTrans.setCustomHeader("User-Agent", userAgent);
1550 } catch (TTransportException e) {
1551 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1553 e.printStackTrace();
1555 userStoreProt = new TBinaryProtocol(userStoreTrans);
1556 userStore = new UserStore.Client(userStoreProt, userStoreProt);
1557 syncSignal.saveUserStore.emit(userStore);
1559 //authResult = userStore.authenticate(username, password, consumerKey, consumerSecret);
1560 user = userStore.getUser(authToken);
1561 } catch (EDAMUserException e) {
1562 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Error", "Invalid Authorization");
1564 isConnected = false;
1566 } catch (EDAMSystemException e) {
1567 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "EDAM System Excepton", e.getLocalizedMessage());
1569 e.printStackTrace();
1570 isConnected = false;
1571 } catch (TException e) {
1572 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1574 e.printStackTrace();
1575 isConnected = false;
1578 boolean versionOk = false;
1580 versionOk = userStore.checkVersion("NeighborNote",
1581 com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR,
1582 com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR);
1583 } catch (TException e) {
1584 e.printStackTrace();
1585 isConnected = false;
1588 System.err.println("Incomatible EDAM client protocol version");
1589 isConnected = false;
1591 //if (authResult != null) {
1592 //user = authResult.getUser();
1593 //authToken = authResult.getAuthenticationToken();
1594 if (user == null || noteStoreUrlBase == null) {
\r
1595 logger.log(logger.LOW, "Error retrieving user information. Aborting.");
\r
1596 System.err.println("Error retrieving user information.");
\r
1597 isConnected = false;
\r
1598 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, tr("Connection Error"), tr("Error retrieving user information. Synchronization not complete"));
\r
1603 noteStoreUrl = noteStoreUrlBase + user.getShardId();
1604 syncSignal.saveAuthToken.emit(authToken);
1605 syncSignal.saveNoteStore.emit(localNoteStore);
1609 noteStoreTrans = new THttpClient(noteStoreUrl);
1610 noteStoreTrans.setCustomHeader("User-Agent", userAgent);
1611 } catch (TTransportException e) {
1612 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
1614 e.printStackTrace();
1615 isConnected = false;
1617 noteStoreProt = new TBinaryProtocol(noteStoreTrans);
1619 new NoteStore.Client(noteStoreProt, noteStoreProt);
1621 //authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();
1622 //authRefreshTime = authTimeRemaining / 2;
1625 // Get user information
1627 User user = userStore.getUser(authToken);
1628 syncSignal.saveUserInformation.emit(user);
1629 } catch (EDAMUserException e1) {
1630 e1.printStackTrace();
1631 } catch (EDAMSystemException e1) {
1632 e1.printStackTrace();
1633 } catch (TException e1) {
1634 e1.printStackTrace();
1639 // Disconnect from the database
1640 public void enDisconnect() {
1641 isConnected = false;
1645 // Refresh the connection
1646 private synchronized boolean refreshConnection() {
1648 logger.log(logger.EXTREME, "Entering SyncRunner.refreshConnection()");
1649 // Calendar cal = Calendar.getInstance();
1651 // If we are not connected let's get out of here
1655 // If we fail too many times, then let's give up.
1656 if (failedRefreshes >=5) {
1657 logger.log(logger.EXTREME, "Refresh attempts have failed. Disconnecting.");
1658 isConnected = false;
1659 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1663 // If this is the first time through, then we need to set this
1664 // if (authRefreshTime == 0 || cal.getTimeInMillis() > authRefreshTime)
1665 // authRefreshTime = cal.getTimeInMillis();
1667 // // Default to checking again in 5 min. This in case we fail.
1668 // authRefreshTime = authRefreshTime +(5*60*1000);
1670 // Try to get a new token
1671 AuthenticationResult newAuth = null;
1672 logger.log(logger.EXTREME, "Beginning to try authentication refresh");
1674 if (userStore != null && authToken != null)
1675 newAuth = userStore.refreshAuthentication(authToken);
1678 logger.log(logger.EXTREME, "UserStore.refreshAuthentication has succeeded.");
1679 } catch (EDAMUserException e) {
1680 e.printStackTrace();
1681 syncSignal.authRefreshComplete.emit(false);
1684 } catch (EDAMSystemException e) {
1685 e.printStackTrace();
1686 syncSignal.authRefreshComplete.emit(false);
1689 } catch (TException e) {
1690 e.printStackTrace();
1691 syncSignal.authRefreshComplete.emit(false);
1696 // If we didn't get a good auth, then we've failed
1697 if (newAuth == null) {
1699 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1700 logger.log(logger.EXTREME, "Authentication failure #" +failedRefreshes);
1701 status.message.emit(tr("Unable to synchronize - Authentication failed"));
1702 syncSignal.authRefreshComplete.emit(false);
1706 // We got a good token. Now we need to setup the time to renew it.
1707 logger.log(logger.EXTREME, "Saving authentication tokens");
1708 authResult = newAuth;
1709 authToken = new String(newAuth.getAuthenticationToken());
1710 // authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();
1711 // authRefreshTime = cal.getTimeInMillis() + (authTimeRemaining/4);
1713 syncSignal.authRefreshComplete.emit(true);
1714 authRefreshNeeded = false;
1716 // This should never happen, but if it does we consider this a faild attempt.
1717 // if (authTimeRemaining <= 0) {
1718 // failedRefreshes++;
1719 // syncSignal.authRefreshComplete.emit(false);
1727 public synchronized boolean addWork(String request) {
1728 if (workQueue.offer(request))
1733 private Note getNoteContent(Note n) {
1734 QTextCodec codec = QTextCodec.codecForLocale();
1735 codec = QTextCodec.codecForName("UTF-8");
1736 n.setContent(codec.toUnicode(new QByteArray(n.getContent())));
1742 //*********************************************************
1743 //* Special download instructions. Used for DB upgrades
1744 //*********************************************************
1745 private void downloadAllSharedNotebooks(Client noteStore) {
1747 List<SharedNotebook> books = noteStore.listSharedNotebooks(authToken);
1748 logger.log(logger.LOW, "Shared notebooks found = " +books.size());
1749 for (int i=0; i<books.size(); i++) {
1750 conn.getSharedNotebookTable().updateNotebook(books.get(i), false);
1752 conn.getSyncTable().deleteRecord("FullSharedNotebookSync");
1753 } catch (EDAMUserException e1) {
1754 e1.printStackTrace();
1755 status.message.emit(tr("User exception Listing shared notebooks."));
1756 logger.log(logger.LOW, e1.getMessage());
1758 } catch (EDAMSystemException e1) {
1759 e1.printStackTrace();
1760 status.message.emit(tr("System exception Listing shared notebooks."));
1761 logger.log(logger.LOW, e1.getMessage());
1763 } catch (TException e1) {
1764 e1.printStackTrace();
1765 status.message.emit(tr("Transaction exception Listing shared notebooks."));
1766 logger.log(logger.LOW, e1.getMessage());
1768 } catch (EDAMNotFoundException e1) {
1769 e1.printStackTrace();
1770 status.message.emit(tr("EDAM Not Found exception Listing shared notebooks."));
1771 logger.log(logger.LOW, e1.getMessage());
1774 private void downloadAllNotebooks(Client noteStore) {
1776 List<Notebook> books = noteStore.listNotebooks(authToken);
1777 logger.log(logger.LOW, "Shared notebooks found = " +books.size());
1778 for (int i=0; i<books.size(); i++) {
1779 conn.getNotebookTable().updateNotebook(books.get(i), false);
1781 conn.getSyncTable().deleteRecord("FullNotebookSync");
1782 } catch (EDAMUserException e1) {
1783 e1.printStackTrace();
1784 status.message.emit(tr("User exception Listing notebooks."));
1785 logger.log(logger.LOW, e1.getMessage());
1787 } catch (EDAMSystemException e1) {
1788 e1.printStackTrace();
1789 status.message.emit(tr("System exception Listing notebooks."));
1790 logger.log(logger.LOW, e1.getMessage());
1792 } catch (TException e1) {
1793 e1.printStackTrace();
1794 status.message.emit(tr("Transaction exception Listing notebooks."));
1795 logger.log(logger.LOW, e1.getMessage());
1799 private void downloadAllLinkedNotebooks(Client noteStore) {
1801 List<LinkedNotebook> books = noteStore.listLinkedNotebooks(authToken);
1802 logger.log(logger.LOW, "Linked notebooks found = " +books.size());
1803 for (int i=0; i<books.size(); i++) {
1804 conn.getLinkedNotebookTable().updateNotebook(books.get(i), false);
1806 conn.getSyncTable().deleteRecord("FullLinkedNotebookSync");
1807 } catch (EDAMUserException e1) {
1808 e1.printStackTrace();
1809 status.message.emit(tr("User exception Listing linked notebooks."));
1810 logger.log(logger.LOW, e1.getMessage());
1812 } catch (EDAMSystemException e1) {
1813 e1.printStackTrace();
1814 status.message.emit(tr("System exception Listing linked notebooks."));
1815 logger.log(logger.LOW, e1.getMessage());
1817 } catch (TException e1) {
1818 e1.printStackTrace();
1819 status.message.emit(tr("Transaction exception Listing lineked notebooks."));
1820 logger.log(logger.LOW, e1.getMessage());
1822 } catch (EDAMNotFoundException e1) {
1823 e1.printStackTrace();
1824 status.message.emit(tr("EDAM Not Found exception Listing linked notebooks."));
1825 logger.log(logger.LOW, e1.getMessage());
1830 private void downloadInkNoteImage(String guid, String authToken) {
1831 String urlBase = noteStoreUrl.replace("/edam/note/", "/shard/") + "/res/"+guid+".ink?slice=";
1832 // urlBase = "https://www.evernote.com/shard/s1/res/52b567a9-54ae-4a08-afc5-d5bae275b2a8.ink?slice=";
1834 Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(guid, false);
1835 conn.getInkImagesTable().expungeImage(r.getGuid());
1836 int sliceCount = 1+((r.getHeight()-1)/480);
1837 HttpClient http = new DefaultHttpClient();
1838 for (int i=0; i<sliceCount; i++) {
1839 String url = urlBase + slice.toString();
1840 HttpPost post = new HttpPost(url);
1841 post.getParams().setParameter("auth", authToken);
1842 List <NameValuePair> nvps = new ArrayList <NameValuePair>();
1843 nvps.add(new BasicNameValuePair("auth", authToken));
1846 post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
1847 } catch (UnsupportedEncodingException e1) {
1848 e1.printStackTrace();
1851 HttpResponse response = http.execute(post);
1852 HttpEntity resEntity = response.getEntity();
1853 InputStream is = resEntity.getContent();
1854 QByteArray data = writeToFile(is);
1855 conn.getInkImagesTable().saveImage(guid, slice, data);
1856 } catch (ClientProtocolException e) {
1857 e.printStackTrace();
1858 } catch (IOException e) {
1859 e.printStackTrace();
1864 http.getConnectionManager().shutdown();
1865 noteSignal.noteChanged.emit(r.getNoteGuid(), null); // Signal to ivalidate note cache
1869 public QByteArray writeToFile(InputStream iStream) throws IOException {
1871 File temp = File.createTempFile("nn-inknote-temp", ".png");
1873 // Save InputStream to the file.
1874 BufferedOutputStream fOut = null;
1876 fOut = new BufferedOutputStream(new FileOutputStream(temp));
1877 byte[] buffer = new byte[32 * 1024];
1879 while ((bytesRead = iStream.read(buffer)) != -1) {
1880 fOut.write(buffer, 0, bytesRead);
1887 QFile tempFile = new QFile(temp.getAbsoluteFile().toString());
1888 tempFile.open(OpenModeFlag.ReadOnly);
1889 QByteArray data = tempFile.readAll();
1896 //******************************************
1897 //* Begin syncing shared notebooks
1898 //******************************************
1899 private void syncLinkedNotebooks() {
1900 logger.log(logger.MEDIUM, "Authenticating linked Notebooks");
1901 status.message.emit(tr("Synchronizing shared notebooks."));
1902 List<LinkedNotebook> books = conn.getLinkedNotebookTable().getAll();
1904 errorSharedNotebooks.clear();
1906 for (int i=0; i<books.size(); i++) {
1907 if (errorSharedNotebooksIgnored.containsKey(books.get(i).getGuid()))
1910 logger.log(logger.EXTREME, "Checking notebook: " +books.get(i).getShareName());
1911 long lastSyncDate = conn.getLinkedNotebookTable().getLastSequenceDate(books.get(i).getGuid());
1912 int lastSequenceNumber = conn.getLinkedNotebookTable().getLastSequenceNumber(books.get(i).getGuid());
1914 logger.log(logger.EXTREME, "Last Sequence Number on file: " +lastSequenceNumber);
1916 // Authenticate to the owner's shard
1917 String linkedNoteStoreUrl = noteStoreUrlBase + books.get(i).getShardId();
1918 logger.log(logger.EXTREME, "linkedNoteStoreURL: " +linkedNoteStoreUrl);
1919 THttpClient linkedNoteStoreTrans = new THttpClient(linkedNoteStoreUrl);
1920 TBinaryProtocol linkedNoteStoreProt = new TBinaryProtocol(linkedNoteStoreTrans);
1921 Client linkedNoteStore = new NoteStore.Client(linkedNoteStoreProt, linkedNoteStoreProt);
1923 linkedAuthResult = null;
1924 if (books.get(i).getShareKey() != null) {
1925 logger.log(logger.EXTREME, "Share Key Not Null: " +books.get(i).getShareKey());
1926 linkedAuthResult = linkedNoteStore.authenticateToSharedNotebook(books.get(i).getShareKey(), authToken);
1927 logger.log(logger.EXTREME, "Authentication Token" +linkedAuthResult.getAuthenticationToken());
1929 logger.log(logger.EXTREME, "Share key is null");
1930 linkedAuthResult = new AuthenticationResult();
1931 linkedAuthResult.setAuthenticationToken("");
1933 SyncState linkedSyncState =
1934 linkedNoteStore.getLinkedNotebookSyncState(linkedAuthResult.getAuthenticationToken(), books.get(i));
1935 if (linkedSyncState.getUpdateCount() > lastSequenceNumber) {
1936 logger.log(logger.EXTREME, "Remote changes found");
1937 if (lastSyncDate < linkedSyncState.getFullSyncBefore()) {
1938 lastSequenceNumber = 0;
1940 logger.log(logger.EXTREME, "Calling syncLinkedNotebook for " +books.get(i).getShareName());
1941 syncLinkedNotebook(linkedNoteStore, books.get(i),
1942 lastSequenceNumber, linkedSyncState.getUpdateCount(), authToken);
1945 // Synchronize local changes
1946 syncLocalLinkedNoteChanges(linkedNoteStore, books.get(i));
1948 } catch (EDAMUserException e) {
1949 e.printStackTrace();
1950 } catch (EDAMNotFoundException e) {
1951 status.message.emit(tr("Error synchronizing \" " +
1952 books.get(i).getShareName()+"\". Please verify you still have access to that shared notebook."));
1953 errorSharedNotebooks.add(books.get(i).getGuid());
1954 errorSharedNotebooksIgnored.put(books.get(i).getGuid(), books.get(i).getGuid());
1955 logger.log(logger.LOW, "Error synchronizing shared notebook. EDAMNotFound: "+e.getMessage());
1956 logger.log(logger.LOW, e.getStackTrace());
1958 e.printStackTrace();
1959 } catch (EDAMSystemException e) {
1961 logger.log(logger.LOW, "System error authenticating against shared notebook. "+
1962 "Key: "+books.get(i).getShareKey() +" Error:" +e.getMessage());
1963 e.printStackTrace();
1964 } catch (TException e) {
1966 e.printStackTrace();
1971 conn.getTagTable().removeUnusedLinkedTags();
1972 conn.getTagTable().cleanupTags();
1973 tagSignal.listChanged.emit();
1978 //**************************************************************
1979 //* Linked notebook contents (from someone else's account)
1980 //*************************************************************
1981 private void syncLinkedNotebook(Client linkedNoteStore, LinkedNotebook book, int usn, int highSequence, String token) {
1982 logger.log(logger.EXTREME, "Entering syncLinkedNotebook");
1983 if (ignoreLinkedNotebooks.contains(book.getGuid()))
1985 List<Note> dirtyNotes = conn.getNoteTable().getDirtyLinkedNotes();
1986 if (dirtyNoteGuids == null)
1987 dirtyNoteGuids = new ArrayList<String>();
\r
1989 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {
1990 dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());
1992 boolean fullSync = false;
1995 boolean syncError = false;
1996 while (usn < highSequence && !syncError) {
1997 refreshNeeded = true;
2000 linkedNoteStore.getLinkedNotebookSyncChunk(token, book, usn, 10, fullSync);
2003 syncExpungedNotes(chunk);
2005 logger.log(logger.EXTREME, "Syncing remote notes: " +chunk.getNotesSize());
2006 syncRemoteNotes(linkedNoteStore, chunk.getNotes(), fullSync, linkedAuthResult.getAuthenticationToken());
2007 logger.log(logger.EXTREME, "Finding new linked tags");
2008 findNewLinkedTags(linkedNoteStore, chunk.getNotes(), linkedAuthResult.getAuthenticationToken());
2010 logger.log(logger.EXTREME, "Synchronizing tags: " +chunk.getTagsSize());
2011 for (int i=0; i<chunk.getResourcesSize(); i++) {
2012 syncRemoteResource(linkedNoteStore, chunk.getResources().get(i), linkedAuthResult.getAuthenticationToken());
2014 logger.log(logger.EXTREME, "Synchronizing linked notebooks: " +chunk.getNotebooksSize());
2015 syncRemoteLinkedNotebooks(linkedNoteStore, chunk.getNotebooks(), false, book);
2016 syncLinkedTags(chunk.getTags(), book.getGuid());
2018 // Go through & signal any notes that have changed so we can refresh the user's view
2019 for (int i=0; i<chunk.getNotesSize(); i++)
2020 noteSignal.noteChanged.emit(chunk.getNotes().get(i).getGuid(), null);
2022 // Expunge Notebook records
2023 logger.log(logger.EXTREME, "Expunging linked notebooks: " +chunk.getExpungedLinkedNotebooksSize());
2024 for (int i=0; i<chunk.getExpungedLinkedNotebooksSize(); i++) {
2025 conn.getLinkedNotebookTable().expungeNotebook(chunk.getExpungedLinkedNotebooks().get(i), false);
2027 usn = chunk.getChunkHighUSN();
2028 conn.getLinkedNotebookTable().setLastSequenceDate(book.getGuid(),chunk.getCurrentTime());
2029 conn.getLinkedNotebookTable().setLastSequenceNumber(book.getGuid(),chunk.getChunkHighUSN());
2030 } catch (EDAMUserException e) {
2032 status.message.emit(tr("EDAM UserException synchronizing linked notbook. See the log for datails."));
2033 e.printStackTrace();
2034 logger.log(logger.LOW, tr("EDAM UserException synchronizing linked notbook ")+ e.getMessage());
2035 } catch (EDAMSystemException e) {
2037 status.message.emit(tr("EDAM SystemException synchronizing linked notbook. See the log for datails."));
2038 e.printStackTrace();
2039 logger.log(logger.LOW, tr("EDAM SystemException synchronizing linked notbook. See the log for datails") +e.getMessage());
2040 } catch (EDAMNotFoundException e) {
2042 status.message.emit(tr("Notebook URL not found. Removing notobook ") +book.getShareName());
2043 conn.getNotebookTable().deleteLinkedTags(book.getGuid());
2044 conn.getLinkedNotebookTable().expungeNotebook(book.getGuid(), false);
2045 logger.log(logger.LOW, tr("Notebook URL not found. Removing notobook ") +e.getMessage());
2046 } catch (TException e) {
2048 status.message.emit(tr("EDAM TException synchronizing linked notbook. See the log for datails."));
2049 e.printStackTrace();
2050 logger.log(logger.LOW, tr("EDAM TException synchronizing linked notbook. See the log for datails." )+e.getMessage());
2053 logger.log(logger.EXTREME, "leaving syncLinkedNotebook");
2056 private void syncLinkedTags(List<Tag> tags, String notebookGuid) {
2057 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");
2059 for (int i=0; i<tags.size() && keepRunning; i++) {
2060 conn.getTagTable().syncLinkedTag(tags.get(i), notebookGuid, false);
2063 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");
2066 // Sync notebooks from a linked notebook
2067 private void syncRemoteLinkedNotebooks(Client noteStore, List<Notebook> notebooks, boolean readOnly, LinkedNotebook linked) {
2068 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");
2069 if (notebooks != null) {
2070 for (int i=0; i<notebooks.size() && keepRunning; i++) {
2072 logger.log(logger.EXTREME, "auth token:" +linkedAuthResult.getAuthenticationToken());
2073 if (!linkedAuthResult.getAuthenticationToken().equals("")) {
2074 SharedNotebook s = noteStore.getSharedNotebookByAuth(linkedAuthResult.getAuthenticationToken());
2075 logger.log(logger.EXTREME, "share key:"+s.getShareKey() +" notebookGuid" +s.getNotebookGuid());
2076 conn.getLinkedNotebookTable().setNotebookGuid(s.getShareKey(), s.getNotebookGuid());
2077 readOnly = !s.isNotebookModifiable();
2081 notebooks.get(i).setName(linked.getShareName());
2082 notebooks.get(i).setDefaultNotebook(false);
2083 conn.getNotebookTable().syncLinkedNotebook(notebooks.get(i), false, readOnly);
2084 } catch (EDAMUserException e) {
2086 e.printStackTrace();
2087 } catch (EDAMNotFoundException e) {
2089 e.printStackTrace();
2090 } catch (EDAMSystemException e) {
2092 e.printStackTrace();
2093 } catch (TException e) {
2095 e.printStackTrace();
2100 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");
2103 private void findNewLinkedTags(Client noteStore, List<Note> newNotes, String token) {
2104 if (newNotes == null)
2106 for (int i=0; i<newNotes.size(); i++) {
2107 Note n = newNotes.get(i);
2108 for (int j=0; j<n.getTagGuidsSize(); j++) {
2109 String tag = n.getTagGuids().get(j);
2110 if (!conn.getTagTable().exists(tag)) {
2113 newTag = noteStore.getTag(token, tag);
2114 conn.getTagTable().addTag(newTag, false);
2115 } catch (EDAMUserException e) {
2116 e.printStackTrace();
2117 } catch (EDAMSystemException e) {
2118 e.printStackTrace();
2119 } catch (EDAMNotFoundException e) {
2120 e.printStackTrace();
2121 } catch (TException e) {
2122 e.printStackTrace();
2130 // Synchronize changes locally done to linked notes
2131 private void syncLocalLinkedNoteChanges(Client noteStore, LinkedNotebook book) {
2132 logger.log(logger.EXTREME, "Entering SyncRunner.synclocalLinkedNoteChanges");
2133 String notebookGuid = conn.getLinkedNotebookTable().getNotebookGuid(book.getGuid());
2134 logger.log(logger.EXTREME, "Finding changes for " +book.getShareName() +":" +book.getGuid() + ":" +notebookGuid);
2135 List<Note> notes = conn.getNoteTable().getDirtyLinked(notebookGuid);
2136 logger.log(logger.EXTREME, "Number of changes found: " +notes.size());
2137 for (int i=0; i<notes.size(); i++) {
2138 logger.log(logger.EXTREME, "Calling syncLocalNote with key " +linkedAuthResult.getAuthenticationToken());
2139 syncLocalNote(noteStore, notes.get(i), linkedAuthResult.getAuthenticationToken());
2141 logger.log(logger.EXTREME, "Leaving SyncRunner.synclocalLinkedNoteChanges");